[apparmor] [PATCH 4/5] Add support for dconf confinement
John Johansen
john.johansen at canonical.com
Fri Dec 16 18:00:35 UTC 2016
---
libraries/libapparmor/include/sys/apparmor.h | 27 +++++
libraries/libapparmor/src/kernel.c | 151 ++++++++++++++++++++++++++
libraries/libapparmor/src/libapparmor.map | 4 +
parser/Makefile | 13 ++-
parser/dconf.cc | 153 +++++++++++++++++++++++++++
parser/dconf.h | 53 ++++++++++
parser/parser.h | 3 +
parser/parser_interface.c | 21 +++-
parser/parser_lex.l | 23 +++-
parser/parser_misc.c | 1 +
parser/parser_yacc.y | 34 ++++++
parser/profile.cc | 5 +
parser/profile.h | 15 ++-
parser/tst/simple_tests/dconf/bad_path_01.sd | 8 ++
parser/tst/simple_tests/dconf/bad_path_02.sd | 8 ++
parser/tst/simple_tests/dconf/bad_path_03.sd | 8 ++
parser/tst/simple_tests/dconf/bad_path_04.sd | 8 ++
parser/tst/simple_tests/dconf/bad_perm_01.sd | 8 ++
parser/tst/simple_tests/dconf/ok_audit_01.sd | 9 ++
parser/tst/simple_tests/dconf/ok_dir_01.sd | 8 ++
parser/tst/simple_tests/dconf/ok_key_01.sd | 8 ++
tests/regression/apparmor/Makefile | 2 +
tests/regression/apparmor/query_dconf.c | 145 +++++++++++++++++++++++++
tests/regression/apparmor/query_dconf.sh | 76 +++++++++++++
tests/regression/apparmor/query_label.c | 33 ++++++
tests/regression/apparmor/query_label.sh | 58 ++++++++++
26 files changed, 870 insertions(+), 12 deletions(-)
create mode 100644 parser/dconf.cc
create mode 100644 parser/dconf.h
create mode 100644 parser/tst/simple_tests/dconf/bad_path_01.sd
create mode 100644 parser/tst/simple_tests/dconf/bad_path_02.sd
create mode 100644 parser/tst/simple_tests/dconf/bad_path_03.sd
create mode 100644 parser/tst/simple_tests/dconf/bad_path_04.sd
create mode 100644 parser/tst/simple_tests/dconf/bad_perm_01.sd
create mode 100644 parser/tst/simple_tests/dconf/ok_audit_01.sd
create mode 100644 parser/tst/simple_tests/dconf/ok_dir_01.sd
create mode 100644 parser/tst/simple_tests/dconf/ok_key_01.sd
create mode 100644 tests/regression/apparmor/query_dconf.c
create mode 100644 tests/regression/apparmor/query_dconf.sh
diff --git a/libraries/libapparmor/include/sys/apparmor.h b/libraries/libapparmor/include/sys/apparmor.h
index eda9441..6f79eff 100644
--- a/libraries/libapparmor/include/sys/apparmor.h
+++ b/libraries/libapparmor/include/sys/apparmor.h
@@ -29,6 +29,7 @@ __BEGIN_DECLS
*/
#define AA_CLASS_FILE 2
#define AA_CLASS_DBUS 32
+#define AA_CLASS_DCONF 33
/* Permission flags for the AA_CLASS_FILE mediation class */
@@ -61,6 +62,12 @@ __BEGIN_DECLS
AA_DBUS_BIND | AA_DBUS_EAVESDROP)
+/* Permission flags for the AA_CLASS_DCONF mediation class */
+#define AA_DCONF_READ (1 << 2)
+#define AA_DCONF_WRITE (1 << 1)
+#define AA_DCONF_READWRITE ((AA_DCONF_READ) | (AA_DCONF_WRITE))
+
+
/* Prototypes for apparmor state queries */
extern int aa_is_enabled(void);
extern int aa_find_mountpoint(char **mnt);
@@ -118,6 +125,11 @@ extern int aa_query_link_path_len(const char *label, size_t label_len,
int *allowed, int *audited);
extern int aa_query_link_path(const char *label, const char *target,
const char *link, int *allowed, int *audited);
+extern int aa_query_dconf_len(uint32_t mask, const char *label,
+ size_t label_len, const char *path,
+ size_t path_len, int *allowed, int *audited);
+extern int aa_query_dconf(uint32_t mask, const char *label, const char *path,
+ int *allowed, int *audited);
typedef struct {
@@ -131,10 +143,25 @@ typedef struct {
aa_label_data_ent *ents; /* free vec of entries */
} aa_label_data_info;
+typedef struct {
+ char *data; /* free data */
+ size_t rn; /* number of rpaths */
+ size_t rwn; /* number of rwpaths */
+ size_t arn; /* number of arpaths */
+ size_t arwn; /* number of arwpaths */
+ const char **rpaths; /* read-only paths in data */
+ const char **rwpaths; /* read-write paths in data */
+ const char **arpaths; /* audit read-only paths in data */
+ const char **arwpaths; /* audit read-write paths in data */
+} aa_dconf_info;
+
extern int aa_query_label_data(const char *label, const char *key,
aa_label_data_info *out);
extern void aa_clear_label_data(aa_label_data_info *info);
+extern int aa_query_dconf_info(const char *label, aa_dconf_info *info);
+extern void aa_clear_dconf_info(aa_dconf_info *info);
+
#define __macroarg_counter(Y...) __macroarg_count1 ( , ##Y)
#define __macroarg_count1(Y...) __macroarg_count2 (Y, 16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0)
diff --git a/libraries/libapparmor/src/kernel.c b/libraries/libapparmor/src/kernel.c
index fda4f0b..349290d 100644
--- a/libraries/libapparmor/src/kernel.c
+++ b/libraries/libapparmor/src/kernel.c
@@ -1074,6 +1074,61 @@ int aa_query_link_path(const char *label, const char *target, const char *link,
}
/**
+ * aa_query_dconf_len - query access permissions for a dconf @path
+ * @mask: permission bits to query
+ * @label: apparmor label
+ * @label_len: length of @label (does not include any terminating nul byte)
+ * @path: file path to query permissions for
+ * @path_len: length of @path (does not include any terminating nul byte)
+ * @allowed: upon successful return, will be 1 if query is allowed and 0 if not
+ * @audited: upon successful return, will be 1 if query should be audited and 0
+ * if not
+ *
+ * Returns: 0 on success else -1 and sets errno. If -1 is returned and errno is
+ * ENOENT, the subject label in the query string is unknown to the
+ * kernel.
+ */
+int aa_query_dconf_len(uint32_t mask, const char *label, size_t label_len,
+ const char *path, size_t path_len, int *allowed,
+ int *audited)
+{
+ autofree char *query = NULL;
+
+ /* + 1 for null separator, + 1 for AA_CLASS_DCONF */
+ size_t size = AA_QUERY_CMD_LABEL_SIZE + label_len + 1 + 1 + path_len;
+ query = malloc(size);
+ if (!query)
+ return -1;
+ memcpy(query + AA_QUERY_CMD_LABEL_SIZE, label, label_len);
+ /* null separator */
+ query[AA_QUERY_CMD_LABEL_SIZE + label_len] = 0;
+ query[AA_QUERY_CMD_LABEL_SIZE + label_len + 1] = AA_CLASS_DCONF;
+ memcpy(query + AA_QUERY_CMD_LABEL_SIZE + label_len + 2, path, path_len);
+ return aa_query_label(mask, query, size , allowed, audited);
+}
+
+/**
+ * aa_query_dconf - query access permissions for a dconf @path
+ * @mask: permission bits to query
+ * @label: apparmor label
+ * @path: file path to query permissions for
+ * @allowed: upon successful return, will be 1 if query is allowed and 0 if not
+ * @audited: upon successful return, will be 1 if query should be audited and 0
+ * if not
+ *
+ * Returns: 0 on success else -1 and sets errno. If -1 is returned and errno is
+ * ENOENT, the subject label in the query string is unknown to the
+ * kernel.
+ */
+int aa_query_dconf(uint32_t mask, const char *label, const char *path,
+ int *allowed, int *audited)
+{
+ return aa_query_dconf_len(mask, label, strlen(label), path,
+ strlen(path), allowed, audited);
+}
+
+
+/**
* aa_query_label_data_open - query
* @label: security label
* @key: key of data blob to lookup
@@ -1191,3 +1246,99 @@ void aa_clear_label_data(aa_label_data_info *info)
memset(info, 0, sizeof(*info));
}
+
+/**
+ * aa_query_dconf_info - query dconf info associated with @label
+ * @label: label of the confinement context
+ * @info: accessible dconf paths
+ *
+ * Returns: 0 on success, non-zero on failure
+ *
+ * Retrieves the lists of dconf paths that are accessible in this context.
+ */
+int aa_query_dconf_info(const char *label, aa_dconf_info *info)
+{
+ aa_label_data_info data_info;
+ const char *p;
+ uint32_t tmp;
+ int i;
+
+ memset(info, 0, sizeof (*info));
+
+ if (aa_query_label_data(label, "dconf", &data_info))
+ return -1;
+
+ if (data_info.n < 1)
+ return -1;
+
+ info->data = data_info.data;
+ p = data_info.ents[0].entry;
+
+ memcpy(&tmp, p, sizeof(tmp));
+ p += sizeof(tmp);
+ info->rn = le32toh(tmp);
+ memcpy(&tmp, p, sizeof(tmp));
+ p += sizeof(tmp);
+ info->rwn = le32toh(tmp);
+ memcpy(&tmp, p, sizeof(tmp));
+ p += sizeof(tmp);
+ info->arn = le32toh(tmp);
+ memcpy(&tmp, p, sizeof(tmp));
+ p += sizeof(tmp);
+ info->arwn = le32toh(tmp);
+
+ info->rpaths = malloc(info->rn * sizeof(*info->rpaths));
+ info->rwpaths = malloc(info->rwn * sizeof(*info->rwpaths));
+ info->arpaths = malloc(info->arn * sizeof(*info->arpaths));
+ info->arwpaths = malloc(info->arwn * sizeof(*info->arwpaths));
+
+ for (i = 0; i < info->rn; i++) {
+ memcpy(&tmp, p, sizeof(tmp));
+ p += sizeof(tmp);
+ info->rpaths[i] = p;
+ p += le32toh(tmp);
+ }
+
+ for (i = 0; i < info->rwn; i++) {
+ memcpy(&tmp, p, sizeof(tmp));
+ p += sizeof(tmp);
+ info->rwpaths[i] = p;
+ p += le32toh(tmp);
+ }
+
+ for (i = 0; i < info->arn; i++) {
+ memcpy(&tmp, p, sizeof(tmp));
+ p += sizeof(tmp);
+ info->arpaths[i] = p;
+ p += le32toh(tmp);
+ }
+
+ for (i = 0; i < info->arwn; i++) {
+ memcpy(&tmp, p, sizeof(tmp));
+ p += sizeof(tmp);
+ info->arwpaths[i] = p;
+ p += le32toh(tmp);
+ }
+
+ data_info.data = NULL;
+ aa_clear_label_data(&data_info);
+
+ return 0;
+}
+
+/**
+ * aa_clear_dconf_info - release resources associated with @info
+ * @info: info struct
+ *
+ * Releases allocated memory associated with @info.
+ */
+void aa_clear_dconf_info(aa_dconf_info *info)
+{
+ free(info->arwpaths);
+ free(info->arpaths);
+ free(info->rwpaths);
+ free(info->rpaths);
+ free(info->data);
+
+ memset(info, 0, sizeof(*info));
+}
diff --git a/libraries/libapparmor/src/libapparmor.map b/libraries/libapparmor/src/libapparmor.map
index 3ce786f..4598e15 100644
--- a/libraries/libapparmor/src/libapparmor.map
+++ b/libraries/libapparmor/src/libapparmor.map
@@ -100,6 +100,10 @@ APPARMOR_2.12 {
aa_query_cmd;
aa_clear_label_data;
aa_query_label_data;
+ aa_clear_dconf_info;
+ aa_query_dconf_info;
+ aa_query_dconf;
+ aa_query_dconf_len;
local:
*;
} APPARMOR_2.11;
diff --git a/parser/Makefile b/parser/Makefile
index 35c9bd4..61ae450 100644
--- a/parser/Makefile
+++ b/parser/Makefile
@@ -74,11 +74,11 @@ SRCS = parser_common.c parser_include.c parser_interface.c parser_lex.c \
parser_main.c parser_misc.c parser_merge.c parser_symtab.c \
parser_yacc.c parser_regex.c parser_variable.c parser_policy.c \
parser_alias.c common_optarg.c lib.c network.c \
- mount.cc dbus.cc profile.cc rule.cc signal.cc ptrace.cc \
+ mount.cc dbus.cc dconf.cc profile.cc rule.cc signal.cc ptrace.cc \
af_rule.cc af_unix.cc policy_cache.c
-HDRS = parser.h parser_include.h immunix.h mount.h dbus.h lib.h profile.h \
- rule.h common_optarg.h signal.h ptrace.h network.h af_rule.h af_unix.h \
- policy_cache.h
+HDRS = parser.h parser_include.h immunix.h mount.h dbus.h dconf.h lib.h \
+ profile.h rule.h common_optarg.h signal.h ptrace.h network.h af_rule.h \
+ af_unix.h policy_cache.h
TOOLS = apparmor_parser
OBJECTS = $(patsubst %.cc, %.o, $(SRCS:.c=.o))
@@ -189,7 +189,7 @@ apparmor_parser: $(OBJECTS) $(AAREOBJECTS) $(LIBAPPARMOR_A)
parser_yacc.c parser_yacc.h: parser_yacc.y parser.h profile.h
$(YACC) $(YFLAGS) -o parser_yacc.c parser_yacc.y
-parser_lex.c: parser_lex.l parser_yacc.h parser.h profile.h mount.h dbus.h policy_cache.h
+parser_lex.c: parser_lex.l parser_yacc.h parser.h profile.h mount.h dbus.h dconf.h policy_cache.h
$(LEX) ${LEXFLAGS} -o$@ $<
parser_lex.o: parser_lex.c parser.h parser_yacc.h
@@ -246,6 +246,9 @@ lib.o: lib.c lib.h parser.h
dbus.o: dbus.cc dbus.h parser.h immunix.h parser_yacc.h rule.h $(APPARMOR_H)
$(CXX) $(EXTRA_CFLAGS) -c -o $@ $<
+dconf.o: dconf.cc dconf.h parser.h immunix.h parser_yacc.h rule.h $(APPARMOR_H)
+ $(CXX) $(EXTRA_CFLAGS) -c -o $@ $<
+
signal.o: signal.cc signal.h parser.h immunix.h parser_yacc.h rule.h $(APPARMOR_H)
$(CXX) $(EXTRA_CFLAGS) -c -o $@ $<
diff --git a/parser/dconf.cc b/parser/dconf.cc
new file mode 100644
index 0000000..669bf80
--- /dev/null
+++ b/parser/dconf.cc
@@ -0,0 +1,153 @@
+/*
+ * Copyright (c) 2015
+ * Canonical, Ltd. (All rights reserved)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, contact Novell, Inc. or Canonical
+ * Ltd.
+ */
+
+#include "dconf.h"
+#include "parser.h"
+#include "profile.h"
+
+#include <sys/apparmor.h>
+#include <sstream>
+#include <iomanip>
+
+dconfDataEnt::~dconfDataEnt()
+{
+}
+
+void dconfDataEnt::serialize(std::ostringstream &buf)
+{
+ ostringstream tmp;
+
+ sd_write32(tmp, rpaths.size());
+ sd_write32(tmp, rwpaths.size());
+ sd_write32(tmp, arpaths.size());
+ sd_write32(tmp, arwpaths.size());
+
+ for (set<std::string>::iterator i = rpaths.begin(); i != rpaths.end(); i++) {
+ sd_write32(tmp, i->size() + 1);
+ tmp.write(i->c_str(), i->size() + 1);
+ }
+ for (set<std::string>::iterator i = rwpaths.begin(); i != rwpaths.end(); i++) {
+ sd_write32(tmp, i->size() + 1);
+ tmp.write(i->c_str(), i->size() + 1);
+ }
+ for (set<std::string>::iterator i = arpaths.begin(); i != arpaths.end(); i++) {
+ sd_write32(tmp, i->size() + 1);
+ tmp.write(i->c_str(), i->size() + 1);
+ }
+ for (set<std::string>::iterator i = arwpaths.begin(); i != arwpaths.end(); i++) {
+ sd_write32(tmp, i->size() + 1);
+ tmp.write(i->c_str(), i->size() + 1);
+ }
+
+ sd_write_string(buf, "dconf", NULL);
+ sd_write_blob(buf, tmp.str().data(), tmp.tellp(), NULL);
+}
+
+dconf_rule::dconf_rule(char *path_p, int mode_p):
+ path(path_p), mode(mode_p)
+{
+}
+
+dconf_rule::~dconf_rule(void)
+{
+ free(path);
+}
+
+std::ostream &dconf_rule::dump(std::ostream &os)
+{
+ if (audit)
+ os << "audit ";
+
+ os << "dconf (";
+
+ switch (mode & AA_DCONF_READWRITE) {
+ case AA_DCONF_READ:
+ os << "read";
+ break;
+ case AA_DCONF_WRITE:
+ os << " write";
+ break;
+ }
+ os << ") " << path << ",\n";
+
+ return os;
+}
+
+int dconf_rule::expand_variables(void)
+{
+ return 0;
+}
+
+void dconf_rule::gen_data(Profile &prof)
+{
+ dconfDataEnt *dconf;
+ DataMap::iterator i = prof.data.find("dconf");
+
+ if (i == prof.data.end()) {
+ dconf = new dconfDataEnt();
+ pair<DataMap::iterator,bool> j = prof.data.insert(make_pair("dconf", dconf));
+ i = j.first;
+ } else {
+ dconf = dynamic_cast<dconfDataEnt *>(i->second);
+ if (!dconf)
+ return;
+ }
+
+ if (mode & AA_DCONF_WRITE) {
+ if (audit)
+ dconf->arwpaths.insert(path);
+ else
+ dconf->rwpaths.insert(path);
+ } else {
+ if (audit)
+ dconf->arpaths.insert(path);
+ else
+ dconf->rpaths.insert(path);
+ }
+}
+
+int dconf_rule::gen_policy_re(Profile &prof)
+{
+ std::ostringstream rule;
+
+ if ((mode & AA_DCONF_READWRITE) == 0)
+ return RULE_OK;
+
+ rule << "\\x" << std::setfill('0') << std::setw(2) << std::hex << AA_CLASS_DCONF;
+
+ if (path[strlen(path) - 1] == '/')
+ rule << path << "[^\\x00]*";
+ else
+ rule << path;
+
+ if (!prof.policy.rules->add_rule(rule.str().c_str(),
+ 0,
+ mode & AA_DCONF_READWRITE,
+ audit ? mode & AA_DCONF_READWRITE : 0,
+ dfaflags))
+ return RULE_ERROR;
+
+ gen_data(prof);
+
+ return RULE_OK;
+}
+
+
+void dconf_rule::post_process(Profile &prof unused)
+{
+}
diff --git a/parser/dconf.h b/parser/dconf.h
new file mode 100644
index 0000000..0477f06
--- /dev/null
+++ b/parser/dconf.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2015
+ * Canonical, Ltd. (All rights reserved)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, contact Novell, Inc. or Canonical
+ * Ltd.
+ */
+
+#ifndef __AA_DCONF_H
+#define __AA_DCONF_H
+
+#include "profile.h"
+#include "rule.h"
+
+class dconfDataEnt: public DataEnt {
+public:
+ set<std::string> rpaths;
+ set<std::string> rwpaths;
+ set<std::string> arpaths;
+ set<std::string> arwpaths;
+
+ virtual ~dconfDataEnt();
+
+ void serialize(std::ostringstream &buf);
+};
+
+class dconf_rule: public rule_t {
+public:
+ int audit;
+ char *path;
+ int mode;
+
+ explicit dconf_rule(char *path_p, int mode_p);
+ virtual ~dconf_rule(void);
+
+ virtual std::ostream &dump(std::ostream &os);
+ virtual int expand_variables(void);
+ virtual int gen_policy_re(Profile &prof);
+ virtual void post_process(Profile &prof);
+ virtual void gen_data(Profile &prof);
+};
+
+#endif /* __AA_DCONF_H */
diff --git a/parser/parser.h b/parser/parser.h
index e7acda6..8f56d75 100644
--- a/parser/parser.h
+++ b/parser/parser.h
@@ -429,6 +429,9 @@ extern int profile_merge_rules(Profile *prof);
/* parser_interface.c */
extern int load_profile(int option, aa_kernel_interface *kernel_interface,
Profile *prof, int cache_fd);
+extern void sd_write32(std::ostringstream &buf, u32 b);
+extern void sd_write_blob(std::ostringstream &buf, const void *b, int buf_size, const char *name);
+extern void sd_write_string(std::ostringstream &buf, const char *b, const char *name);
extern void sd_serialize_profile(std::ostringstream &buf, Profile *prof,
int flatten);
extern int sd_load_buffer(int option, char *buffer, int size);
diff --git a/parser/parser_interface.c b/parser/parser_interface.c
index 00a81f2..dd5ebce 100644
--- a/parser/parser_interface.c
+++ b/parser/parser_interface.c
@@ -202,7 +202,7 @@ static inline void sd_write16(std::ostringstream &buf, u16 b)
buf.write((const char *) &tmp, 2);
}
-static inline void sd_write32(std::ostringstream &buf, u32 b)
+void sd_write32(std::ostringstream &buf, u32 b)
{
u32 tmp;
tmp = cpu_to_le32(b);
@@ -250,7 +250,7 @@ static inline void sd_write_name(std::ostringstream &buf, const char *name)
}
}
-static inline void sd_write_blob(std::ostringstream &buf, const void *b, int buf_size, const char *name)
+void sd_write_blob(std::ostringstream &buf, const void *b, int buf_size, const char *name)
{
sd_write_name(buf, name);
sd_write8(buf, SD_BLOB);
@@ -281,7 +281,7 @@ static void sd_write_strn(std::ostringstream &buf, const char *b, int size, cons
buf.write(b, size);
}
-static inline void sd_write_string(std::ostringstream &buf, const char *b, const char *name)
+void sd_write_string(std::ostringstream &buf, const char *b, const char *name)
{
sd_write_strn(buf, b, strlen(b) + 1, name);
}
@@ -370,9 +370,22 @@ void sd_serialize_xtable(std::ostringstream &buf, char **table)
sd_write_structend(buf);
}
+void sd_serialize_label_data(std::ostringstream &buf, Profile *profile)
+{
+ if (profile->data.size() == 0)
+ return;
+
+ sd_write_struct(buf, "data");
+ for (DataMap::iterator i = profile->data.begin(); i != profile->data.end(); i++) {
+ i->second->serialize(buf);
+ }
+ sd_write_structend(buf);
+}
+
void sd_serialize_profile(std::ostringstream &buf, Profile *profile,
int flattened)
{
+ std::ostringstream buf2;
uint64_t allowed_caps;
sd_write_struct(buf, "profile");
@@ -457,6 +470,8 @@ void sd_serialize_profile(std::ostringstream &buf, Profile *profile,
sd_serialize_dfa(buf, profile->dfa.dfa, profile->dfa.size);
sd_serialize_xtable(buf, profile->exec_table);
+ sd_serialize_label_data(buf, profile);
+
sd_write_structend(buf);
}
diff --git a/parser/parser_lex.l b/parser/parser_lex.l
index 6e5189f..82cd84c 100644
--- a/parser/parser_lex.l
+++ b/parser/parser_lex.l
@@ -229,6 +229,9 @@ BOOL_VARIABLE $(\{{VARIABLE_NAME}\}|{VARIABLE_NAME})
LABEL (\/|{SET_VARIABLE}{POST_VAR_ID}|{COLON}|{AMPERSAND}){ID}*
QUOTED_LABEL \"(\/|{SET_VAR_PREFIX}|{COLON}|{AMPERSAND})([^\0"]|\\\")*\"
+DPATHCOMP [^[:space:]/]+
+DPATHNAME {SLASH}({DPATHCOMP}{SLASH})*{DPATHCOMP}?
+
OPEN_PAREN \(
CLOSE_PAREN \)
COMMA \,
@@ -252,6 +255,7 @@ LT_EQUAL <=
%x RLIMIT_MODE
%x MOUNT_MODE
%x DBUS_MODE
+%x DCONF_MODE
%x SIGNAL_MODE
%x PTRACE_MODE
%x UNIX_MODE
@@ -269,7 +273,7 @@ LT_EQUAL <=
}
%}
-<INITIAL,SUB_ID_WS,INCLUDE,LIST_VAL_MODE,EXTCOND_MODE,LIST_COND_VAL,LIST_COND_PAREN_VAL,LIST_COND_MODE,EXTCONDLIST_MODE,ASSIGN_MODE,NETWORK_MODE,CHANGE_PROFILE_MODE,RLIMIT_MODE,MOUNT_MODE,DBUS_MODE,SIGNAL_MODE,PTRACE_MODE,UNIX_MODE>{
+<INITIAL,SUB_ID_WS,INCLUDE,LIST_VAL_MODE,EXTCOND_MODE,LIST_COND_VAL,LIST_COND_PAREN_VAL,LIST_COND_MODE,EXTCONDLIST_MODE,ASSIGN_MODE,NETWORK_MODE,CHANGE_PROFILE_MODE,RLIMIT_MODE,MOUNT_MODE,DBUS_MODE,DCONF_MODE,SIGNAL_MODE,PTRACE_MODE,UNIX_MODE>{
{WS}+ { DUMP_PREPROCESS; /* Ignoring whitespace */ }
}
@@ -516,6 +520,15 @@ LT_EQUAL <=
}
}
+<DCONF_MODE>{
+ r { RETURN_TOKEN(TOK_READ); }
+ rw { RETURN_TOKEN(TOK_READWRITE); }
+ {DPATHNAME} {
+ yylval.id = processid(yytext, yyleng);
+ RETURN_TOKEN(TOK_ID);
+ }
+}
+
<MOUNT_MODE>{
{ARROW} { RETURN_TOKEN(TOK_ARROW); }
}
@@ -614,6 +627,9 @@ include/{WS} {
case TOK_DBUS:
state = DBUS_MODE;
break;
+ case TOK_DCONF:
+ state = DCONF_MODE;
+ break;
case TOK_SIGNAL:
state = SIGNAL_MODE;
break;
@@ -629,7 +645,7 @@ include/{WS} {
PUSH_AND_RETURN(state, token);
}
-<INITIAL,NETWORK_MODE,RLIMIT_MODE,CHANGE_PROFILE_MODE,MOUNT_MODE,DBUS_MODE,SIGNAL_MODE,PTRACE_MODE,UNIX_MODE>{
+<INITIAL,NETWORK_MODE,RLIMIT_MODE,CHANGE_PROFILE_MODE,MOUNT_MODE,DBUS_MODE,SIGNAL_MODE,PTRACE_MODE,UNIX_MODE,DCONF_MODE>{
{END_OF_RULE} {
if (YY_START != INITIAL)
POP_NODUMP();
@@ -642,7 +658,7 @@ include/{WS} {
}
}
-<INITIAL,SUB_ID,SUB_ID_WS,SUB_VALUE,LIST_VAL_MODE,EXTCOND_MODE,LIST_COND_VAL,LIST_COND_PAREN_VAL,LIST_COND_MODE,EXTCONDLIST_MODE,ASSIGN_MODE,NETWORK_MODE,CHANGE_PROFILE_MODE,MOUNT_MODE,DBUS_MODE,SIGNAL_MODE,PTRACE_MODE,UNIX_MODE>{
+<INITIAL,SUB_ID,SUB_ID_WS,SUB_VALUE,LIST_VAL_MODE,EXTCOND_MODE,LIST_COND_VAL,LIST_COND_PAREN_VAL,LIST_COND_MODE,EXTCONDLIST_MODE,ASSIGN_MODE,NETWORK_MODE,CHANGE_PROFILE_MODE,MOUNT_MODE,DBUS_MODE,DCONF_MODE,SIGNAL_MODE,PTRACE_MODE,UNIX_MODE>{
[^\n] {
DUMP_PREPROCESS;
/* Something we didn't expect */
@@ -670,6 +686,7 @@ unordered_map<int, string> state_names = {
STATE_TABLE_ENT(RLIMIT_MODE),
STATE_TABLE_ENT(MOUNT_MODE),
STATE_TABLE_ENT(DBUS_MODE),
+ STATE_TABLE_ENT(DCONF_MODE),
STATE_TABLE_ENT(SIGNAL_MODE),
STATE_TABLE_ENT(PTRACE_MODE),
STATE_TABLE_ENT(UNIX_MODE),
diff --git a/parser/parser_misc.c b/parser/parser_misc.c
index f7772e6..6d5953b 100644
--- a/parser/parser_misc.c
+++ b/parser/parser_misc.c
@@ -100,6 +100,7 @@ static struct keyword_table keyword_table[] = {
{"pivot_root", TOK_PIVOTROOT},
{"in", TOK_IN},
{"dbus", TOK_DBUS},
+ {"dconf", TOK_DCONF},
{"signal", TOK_SIGNAL},
{"send", TOK_SEND},
{"receive", TOK_RECEIVE},
diff --git a/parser/parser_yacc.y b/parser/parser_yacc.y
index 3e2bcd2..75e9387 100644
--- a/parser/parser_yacc.y
+++ b/parser/parser_yacc.y
@@ -36,6 +36,7 @@
#include "profile.h"
#include "mount.h"
#include "dbus.h"
+#include "dconf.h"
#include "af_unix.h"
#include "parser_include.h"
#include <unistd.h>
@@ -141,12 +142,14 @@ void add_local_entry(Profile *prof);
%token TOK_PIVOTROOT
%token TOK_IN
%token TOK_DBUS
+%token TOK_DCONF
%token TOK_SIGNAL
%token TOK_SEND
%token TOK_RECEIVE
%token TOK_BIND
%token TOK_READ
%token TOK_WRITE
+%token TOK_READWRITE
%token TOK_EAVESDROP
%token TOK_PEER
%token TOK_TRACE
@@ -184,6 +187,7 @@ void add_local_entry(Profile *prof);
#include "profile.h"
#include "mount.h"
#include "dbus.h"
+ #include "dconf.h"
#include "signal.h"
#include "ptrace.h"
#include "af_unix.h"
@@ -200,6 +204,7 @@ void add_local_entry(Profile *prof);
mnt_rule *mnt_entry;
dbus_rule *dbus_entry;
+ dconf_rule *dconf_entry;
signal_rule *signal_entry;
ptrace_rule *ptrace_entry;
unix_rule *unix_entry;
@@ -265,6 +270,10 @@ void add_local_entry(Profile *prof);
%type <fmode> dbus_perms
%type <fmode> opt_dbus_perm
%type <dbus_entry> dbus_rule
+%type <fmode> dconf_perm
+%type <fmode> dconf_perms
+%type <fmode> opt_dconf_perms
+%type <dconf_entry> dconf_rule
%type <fmode> signal_perm
%type <fmode> signal_perms
%type <fmode> opt_signal_perm
@@ -759,6 +768,13 @@ rules: rules opt_prefix dbus_rule
$$ = $1;
}
+rules: rules opt_audit_flag dconf_rule
+ {
+ $3->audit = $2;
+ $1->rule_ents.push_back($3);
+ $$ = $1;
+ }
+
rules: rules opt_prefix signal_rule
{
if ($2.owner)
@@ -1308,6 +1324,24 @@ dbus_rule: TOK_DBUS opt_dbus_perm opt_conds opt_cond_list TOK_END_OF_RULE
$$ = ent;
}
+dconf_perm: TOK_READ { $$ = AA_DCONF_READ; }
+ | TOK_READWRITE { $$ = AA_DCONF_READWRITE; }
+
+dconf_perms: { /* nothing */ $$ = 0; }
+ | dconf_perms dconf_perm { $$ = $1 | $2; }
+
+opt_dconf_perms: { /* nothing */ $$ = AA_DCONF_READWRITE; /* default read-write */ }
+ | dconf_perms dconf_perm { $$ = $1 | $2; }
+
+dconf_rule: TOK_DCONF TOK_ID opt_dconf_perms TOK_END_OF_RULE
+ {
+ dconf_rule *ent = new dconf_rule($2, $3);
+ if (!ent) {
+ yyerror(_("Memory allocation error."));
+ }
+ $$ = ent;
+ }
+
net_perm: TOK_VALUE
{
if (strcmp($1, "create") == 0)
diff --git a/parser/profile.cc b/parser/profile.cc
index 7459967..c037c16 100644
--- a/parser/profile.cc
+++ b/parser/profile.cc
@@ -75,6 +75,11 @@ bool Profile::alloc_net_table()
Profile::~Profile()
{
+ for (DataMap::iterator i = data.begin(); i != data.end(); i++) {
+ delete i->second;
+ }
+ data.clear();
+
hat_table.clear();
free_cod_entries(entries);
diff --git a/parser/profile.h b/parser/profile.h
index 7121c0a..c06df07 100644
--- a/parser/profile.h
+++ b/parser/profile.h
@@ -110,6 +110,17 @@ struct dfa_stuff {
dfa_stuff(void): rules(NULL), dfa(NULL), size(0) { }
};
+
+class DataEnt {
+public:
+ virtual ~DataEnt() = 0;
+ virtual void serialize(std::ostringstream &buf) = 0;
+};
+/* just in case the base case destructor ever gets invoked */
+inline DataEnt::~DataEnt() { };
+
+typedef map<std::string,DataEnt *> DataMap;
+
class Profile {
public:
char *ns;
@@ -143,7 +154,9 @@ public:
struct dfa_stuff dfa;
struct dfa_stuff policy;
- Profile(void)
+ DataMap data;
+
+ Profile(void): data()
{
ns = name = attachment = NULL;
altnames = NULL;
diff --git a/parser/tst/simple_tests/dconf/bad_path_01.sd b/parser/tst/simple_tests/dconf/bad_path_01.sd
new file mode 100644
index 0000000..c78d407
--- /dev/null
+++ b/parser/tst/simple_tests/dconf/bad_path_01.sd
@@ -0,0 +1,8 @@
+#
+#=DESCRIPTION dconf rule missing path
+#=EXRESULT FAIL
+#
+
+profile bad_path {
+ dconf r,
+}
diff --git a/parser/tst/simple_tests/dconf/bad_path_02.sd b/parser/tst/simple_tests/dconf/bad_path_02.sd
new file mode 100644
index 0000000..e0ccc03
--- /dev/null
+++ b/parser/tst/simple_tests/dconf/bad_path_02.sd
@@ -0,0 +1,8 @@
+#
+#=DESCRIPTION dconf rule missing leading slash
+#=EXRESULT FAIL
+#
+
+profile bad_path {
+ dconf a/b/c r,
+}
diff --git a/parser/tst/simple_tests/dconf/bad_path_03.sd b/parser/tst/simple_tests/dconf/bad_path_03.sd
new file mode 100644
index 0000000..d9c93c9
--- /dev/null
+++ b/parser/tst/simple_tests/dconf/bad_path_03.sd
@@ -0,0 +1,8 @@
+#
+#=DESCRIPTION dconf rule missing component
+#=EXRESULT FAIL
+#
+
+profile bad_path {
+ dconf /a//b r,
+}
diff --git a/parser/tst/simple_tests/dconf/bad_path_04.sd b/parser/tst/simple_tests/dconf/bad_path_04.sd
new file mode 100644
index 0000000..a870c12
--- /dev/null
+++ b/parser/tst/simple_tests/dconf/bad_path_04.sd
@@ -0,0 +1,8 @@
+#
+#=DESCRIPTION dconf rule with space
+#=EXRESULT FAIL
+#
+
+profile bad_path {
+ dconf /a /b r,
+}
diff --git a/parser/tst/simple_tests/dconf/bad_perm_01.sd b/parser/tst/simple_tests/dconf/bad_perm_01.sd
new file mode 100644
index 0000000..3498572
--- /dev/null
+++ b/parser/tst/simple_tests/dconf/bad_perm_01.sd
@@ -0,0 +1,8 @@
+#
+#=DESCRIPTION write-only dconf rule
+#=EXRESULT FAIL
+#
+
+profile bad_perm {
+ dconf /a/b/c w,
+}
diff --git a/parser/tst/simple_tests/dconf/ok_audit_01.sd b/parser/tst/simple_tests/dconf/ok_audit_01.sd
new file mode 100644
index 0000000..d2e63f2
--- /dev/null
+++ b/parser/tst/simple_tests/dconf/ok_audit_01.sd
@@ -0,0 +1,9 @@
+#
+#=DESCRIPTION dconf rule with audit
+#=EXRESULT PASS
+#
+
+profile ok_audit {
+ audit dconf /a/b/c r,
+ audit dconf /d/e/f rw,
+}
diff --git a/parser/tst/simple_tests/dconf/ok_dir_01.sd b/parser/tst/simple_tests/dconf/ok_dir_01.sd
new file mode 100644
index 0000000..aa1f487
--- /dev/null
+++ b/parser/tst/simple_tests/dconf/ok_dir_01.sd
@@ -0,0 +1,8 @@
+#
+#=DESCRIPTION dconf rule for dir
+#=EXRESULT PASS
+#
+
+profile ok_audit {
+ dconf /a/b/c/ r,
+}
diff --git a/parser/tst/simple_tests/dconf/ok_key_01.sd b/parser/tst/simple_tests/dconf/ok_key_01.sd
new file mode 100644
index 0000000..5ec92fa
--- /dev/null
+++ b/parser/tst/simple_tests/dconf/ok_key_01.sd
@@ -0,0 +1,8 @@
+#
+#=DESCRIPTION dconf rule for key
+#=EXRESULT PASS
+#
+
+profile ok_audit {
+ dconf /a/b/c r,
+}
diff --git a/tests/regression/apparmor/Makefile b/tests/regression/apparmor/Makefile
index 198ca42..6f3d576 100644
--- a/tests/regression/apparmor/Makefile
+++ b/tests/regression/apparmor/Makefile
@@ -114,6 +114,7 @@ SRC=access.c \
ptrace.c \
ptrace_helper.c \
pwrite.c \
+ query_dconf.c \
query_label.c \
rename.c \
readdir.c \
@@ -221,6 +222,7 @@ TESTS=aa_exec \
pivot_root \
ptrace \
pwrite \
+ query_dconf \
query_label \
regex \
rename \
diff --git a/tests/regression/apparmor/query_dconf.c b/tests/regression/apparmor/query_dconf.c
new file mode 100644
index 0000000..7fc7290
--- /dev/null
+++ b/tests/regression/apparmor/query_dconf.c
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2015 Canonical, Ltd.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, contact Canonical Ltd.
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <sys/apparmor.h>
+
+int main(int argc, char *argv[])
+{
+ FILE *file = stdin;
+ char *con = NULL;
+ aa_dconf_info info = { 0 };
+ char *line = NULL;
+ const char *path;
+ size_t len = 0;
+ aa_dconf_strv *paths;
+ int i;
+
+ if (aa_getcon(&con, NULL) == -1) {
+ perror("FAIL: aa_getcon()");
+ goto done;
+ }
+
+ if (aa_query_dconf(con, &info)) {
+ perror("FAIL: aa_query_dconf()");
+ goto done;
+ }
+
+ if (info.r.p[info.r.n]) {
+ fprintf(stderr, "FAIL: aa_dconf_info.r.p not NULL-terminated\n");
+ goto done;
+ }
+ if (info.rw.p[info.rw.n]) {
+ fprintf(stderr, "FAIL: aa_dconf_info.rw.p not NULL-terminated\n");
+ goto done;
+ }
+ if (info.ar.p[info.ar.n]) {
+ fprintf(stderr, "FAIL: aa_dconf_info.ar.p not NULL-terminated\n");
+ goto done;
+ }
+ if (info.arw.p[info.arw.n]) {
+ fprintf(stderr, "FAIL: aa_dconf_info.arw.p not NULL-terminated\n");
+ goto done;
+ }
+
+ if (argc >= 2)
+ file = fopen(argv[1], "r");
+
+ while (getline(&line, &len, file) >= 0) {
+ len = strlen(line);
+ if (line[len - 1] == '\n')
+ line[--len] = '\0';
+
+ for (path = line; *path == ' '; path++);
+
+ if (len) {
+ paths = NULL;
+ if (!strncmp(path, "r ", 2)) {
+ paths = &info.r;
+ path += 2;
+ }
+ else if (!strncmp(path, "rw ", 3)) {
+ paths = &info.rw;
+ path += 3;
+ }
+ else if (!strncmp(path, "ar ", 3)) {
+ paths = &info.ar;
+ path += 3;
+ }
+ else if (!strncmp(path, "arw ", 4)) {
+ paths = &info.arw;
+ path += 4;
+ }
+
+ if (path) {
+ for (i = 0; i < paths->n; i++) {
+ if (paths->p[i] && !strcmp(paths->p[i], path)) {
+ paths->p[i] = NULL;
+ break;
+ }
+ }
+
+ if (i == paths->n) {
+ fprintf(stderr, "FAIL: path '%s' not found\n", path);
+ goto done;
+ }
+ }
+ }
+
+ free(line);
+ line = NULL;
+ len = 0;
+ }
+
+ for (i = 0; i < info.r.n; i++) {
+ if (info.r.p[i]) {
+ fprintf(stderr, "FAIL: unmatched r path '%s'\n", info.r.p[i]);
+ goto done;
+ }
+ }
+ for (i = 0; i < info.rw.n; i++) {
+ if (info.rw.p[i]) {
+ fprintf(stderr, "FAIL: unmatched rw path '%s'\n", info.rw.p[i]);
+ goto done;
+ }
+ }
+ for (i = 0; i < info.ar.n; i++) {
+ if (info.ar.p[i]) {
+ fprintf(stderr, "FAIL: unmatched ar path '%s'\n", info.ar.p[i]);
+ goto done;
+ }
+ }
+ for (i = 0; i < info.arw.n; i++) {
+ if (info.arw.p[i]) {
+ fprintf(stderr, "FAIL: unmatched arw path '%s'\n", info.arw.p[i]);
+ goto done;
+ }
+ }
+
+ printf("PASS\n");
+
+done:
+ free(line);
+ if (file != stdin)
+ fclose(file);
+ aa_clear_dconf_info(&info);
+ free(con);
+
+ return errno;
+}
diff --git a/tests/regression/apparmor/query_dconf.sh b/tests/regression/apparmor/query_dconf.sh
new file mode 100644
index 0000000..08875a6
--- /dev/null
+++ b/tests/regression/apparmor/query_dconf.sh
@@ -0,0 +1,76 @@
+#! /bin/bash
+# Copyright (C) 2015 Canonical, Ltd.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation, version 2 of the
+# License.
+
+#=NAME query_dconf
+#=DESCRIPTION
+# This test verifies the results returned from aa_query_dconf()
+#=END
+
+pwd=`dirname $0`
+pwd=`cd $pwd ; /bin/pwd`
+
+bin=$pwd
+
+. $bin/prologue.inc
+requires_query_interface
+
+settest query_dconf
+
+# Check read-write, no audit by default
+genprofile --stdin <<EOF
+$test {
+ file,
+ dconf /a1 r,
+ dconf /a2 r,
+ dconf /b/c/ rw,
+ audit dconf /d1 r,
+ audit dconf /d2 r,
+ audit dconf /e/f/ rw,
+}
+EOF
+
+# Gather up the globals ($expect, $label, $perms) and call runchecktest
+# @1: the test description
+# @2: pass or fail
+# @3: the query string
+querytest()
+{
+ local desc=$1
+ local pf=$2
+
+ shift
+ shift
+ runchecktest "$desc" "$pf" <(echo "$*")
+}
+
+querytest "dconf / rw" pass "
+ arw /e/f/
+ ar /d2
+ ar /d1
+ rw /b/c/
+ r /a2
+ r /a1
+"
+
+querytest "dconf / rw" fail "
+ ar /d2
+ ar /d1
+ rw /b/c/
+ r /a2
+ r /a1
+"
+
+querytest "dconf / rw" fail "
+ arw /fail
+ arw /e/f/
+ ar /d2
+ ar /d1
+ rw /b/c/
+ r /a2
+ r /a1
+"
diff --git a/tests/regression/apparmor/query_label.c b/tests/regression/apparmor/query_label.c
index e84d7f2..4d469e1 100644
--- a/tests/regression/apparmor/query_label.c
+++ b/tests/regression/apparmor/query_label.c
@@ -99,6 +99,9 @@
#define AA_MAY_CHANGE_PROFILE 0x40000000
#endif
+#define OPT_TYPE_DCONF "--dconf="
+#define OPT_TYPE_DCONF_LEN strlen(OPT_TYPE_DCONF)
+
static char *progname = NULL;
void usage(void)
@@ -114,10 +117,12 @@ void usage(void)
fprintf(stderr, " CLASS\t\tThe rule class and may consist of:\n");
fprintf(stderr, "\t\t dbus\n");
fprintf(stderr, "\t\t file\n");
+ fprintf(stderr, "\t\t dconf\n");
fprintf(stderr, " PERMS\t\tA comma separated list of permissions. Possibilities\n");
fprintf(stderr, "\t\tfor the supported rule classes are:\n");
fprintf(stderr, "\t\t dbus: send,receive,bind\n");
fprintf(stderr, "\t\t file: exec,write,read,append,link,lock,exec_mmap,exec_pux,exec_unsafe,exec_inherit\n");
+ fprintf(stderr, "\t\t dconf: read,write\n");
fprintf(stderr, "\t\tAdditionaly, PERMS can be empty to indicate an empty mask\n");
exit(1);
}
@@ -219,6 +224,29 @@ static int parse_file_perms(uint32_t *mask, char *perms)
return 0;
}
+static int parse_dconf_perms(uint32_t *mask, char *perms)
+{
+ char *perm;
+
+ *mask = 0;
+
+ perm = strtok(perms, ",");
+ while (perm) {
+ if (!strcmp(perm, "read"))
+ *mask |= AA_DCONF_READ;
+ else if (!strcmp(perm, "write"))
+ *mask |= AA_DCONF_WRITE;
+ else {
+ fprintf(stderr, "FAIL: unknown perm: %s\n", perm);
+ return 1;
+ }
+
+ perm = strtok(NULL, ",");
+ }
+
+ return 0;
+}
+
static ssize_t build_query(char **qstr, const char *label, int class,
int argc, char **argv)
{
@@ -290,6 +318,11 @@ int main(int argc, char **argv)
rc = parse_file_perms(&mask, class_str + OPT_TYPE_FILE_LEN);
if (rc)
usage();
+ } else if (!strncmp(class_str, OPT_TYPE_DCONF, OPT_TYPE_DCONF_LEN)) {
+ class = AA_CLASS_DCONF;
+ rc = parse_dconf_perms(&mask, class_str + OPT_TYPE_DCONF_LEN);
+ if (rc)
+ usage();
} else {
fprintf(stderr, "FAIL: unknown rule class: %s\n", class_str);
usage();
diff --git a/tests/regression/apparmor/query_label.sh b/tests/regression/apparmor/query_label.sh
index e9028f1..f177ea7 100755
--- a/tests/regression/apparmor/query_label.sh
+++ b/tests/regression/apparmor/query_label.sh
@@ -241,3 +241,61 @@ querytest "QUERY file (/tmp/ write only)" pass /tmp/
expect audit
perms file read,write
querytest "QUERY file (/tmp/ wrong dir)" pass /etc/
+
+# Check dconf key matching
+genqueryprofile "dconf /a/b rw,"
+expect allow
+perms dconf read write
+querytest "QUERY dconf /a/b ~ /a" fail "/a"
+querytest "QUERY dconf /a/b ~ /a/" fail "/a/"
+querytest "QUERY dconf /a/b ~ /a/b" pass "/a/b"
+querytest "QUERY dconf /a/b ~ /a/bb" fail "/a/bb"
+querytest "QUERY dconf /a/b ~ /a/b/" fail "/a/b/"
+querytest "QUERY dconf /a/b ~ /a/b/c" fail "/a/b/c"
+
+# Check dconf path matching
+genqueryprofile "dconf /a/b/ rw,"
+expect allow
+perms dconf read write
+querytest "QUERY dconf /a/b/ ~ /a" fail "/a"
+querytest "QUERY dconf /a/b/ ~ /a/" fail "/a/"
+querytest "QUERY dconf /a/b/ ~ /a/b" fail "/a/b"
+querytest "QUERY dconf /a/b/ ~ /a/bb" fail "/a/bb"
+querytest "QUERY dconf /a/b/ ~ /a/b/" pass "/a/b/"
+querytest "QUERY dconf /a/b/ ~ /a/b/c" pass "/a/b/c"
+
+# Check dconf read-only key matching
+genqueryprofile "dconf /a/b r,"
+expect allow
+perms dconf read
+querytest "QUERY dconf /a/b ~ /a/b" pass "/a/b"
+perms dconf write
+querytest "QUERY dconf /a/b ~ /a/b" fail "/a/b"
+
+# Check dconf read-only path matching
+genqueryprofile "dconf /a/b/ r,"
+expect allow
+perms dconf read
+querytest "QUERY dconf /a/b/ ~ /a/b/" pass "/a/b/"
+querytest "QUERY dconf /a/b/ ~ /a/b/c" pass "/a/b/c"
+perms dconf write
+querytest "QUERY dconf /a/b/ ~ /a/b/" fail "/a/b/"
+querytest "QUERY dconf /a/b/ ~ /a/b/c" fail "/a/b/c"
+
+# Check dconf audit read-only key matching
+genqueryprofile "audit dconf /a/b r,"
+perms dconf read
+expect allow audit
+querytest "QUERY dconf /a/b ~ /a/b" pass "/a/b"
+expect
+querytest "QUERY dconf /a/b ~ /a/b" fail "/a/b"
+
+# Check dconf audit read-only path matching
+genqueryprofile "audit dconf /a/b/ r,"
+perms dconf read
+expect allow audit
+querytest "QUERY dconf /a/b/ ~ /a/b/" pass "/a/b/"
+querytest "QUERY dconf /a/b/ ~ /a/b/c" pass "/a/b/c"
+expect
+querytest "QUERY dconf /a/b/ ~ /a/b/" fail "/a/b/"
+querytest "QUERY dconf /a/b/ ~ /a/b/c" fail "/a/b/c"
--
2.9.3
More information about the AppArmor
mailing list