[apparmor] [PATCH 4/4] dconf patch

John Johansen john.johansen at canonical.com
Tue Oct 6 17:16:12 UTC 2015


>From 2794a990b5f555f3ac8f4934200fa6ee09ce4ced Mon Sep 17 00:00:00 2001
From: John Johansen <john.johansen at canonical.com>
Date: Fri, 10 Jul 2015 03:33:47 -0700
Subject: [PATCH 4/4] dconf patch

---
 libraries/libapparmor/include/sys/apparmor.h |  15 ++-
 libraries/libapparmor/src/kernel.c           |  62 ++++++++++++
 libraries/libapparmor/src/libapparmor.map    |   3 +
 parser/Makefile                              |  13 ++-
 parser/dconf.cc                              | 122 ++++++++++++++++++++++
 parser/dconf.h                               |  48 +++++++++
 parser/parser_interface.c                    |  16 +++
 parser/parser_lex.l                          |  21 +++-
 parser/parser_misc.c                         |   1 +
 parser/parser_yacc.y                         |  35 +++++++
 parser/profile.h                             |  24 ++++-
 parser/tst/equality.sh                       |  18 ++++
 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 +++++++++++
 17 files changed, 681 insertions(+), 11 deletions(-)
 create mode 100644 parser/dconf.cc
 create mode 100644 parser/dconf.h
 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 b8d5601..3f05bbf 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);
@@ -116,6 +123,12 @@ 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, size_t label_len,
+			  const char *path, size_t path_len, int *allowed,
+			  int *audited);
 
 
 typedef struct {
@@ -133,7 +146,7 @@ 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_data(const char *label, aa_label_data_info *info);
 
 
 
diff --git a/libraries/libapparmor/src/kernel.c b/libraries/libapparmor/src/kernel.c
index 8371405..7270ff0 100644
--- a/libraries/libapparmor/src/kernel.c
+++ b/libraries/libapparmor/src/kernel.c
@@ -1033,6 +1033,62 @@ 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 */
+	size_t size = AA_QUERY_CMD_LABEL_SIZE + label_len + 1 + path_len;
+	query = malloc(size + 1);
+	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, size_t label_len,
+		   const char *path, size_t path_len, 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
@@ -1154,3 +1210,9 @@ void aa_clear_label_data(aa_label_data_info *info)
 
 	memset(info, 0, sizeof(*info));
 }
+
+/* TODO: just embed??? */
+int aa_query_dconf_data(const char *label, aa_label_data_info *info)
+{
+	return aa_query_label_data(label, "dconf", info);
+}
diff --git a/libraries/libapparmor/src/libapparmor.map b/libraries/libapparmor/src/libapparmor.map
index 9ce35ab..47368b1 100644
--- a/libraries/libapparmor/src/libapparmor.map
+++ b/libraries/libapparmor/src/libapparmor.map
@@ -91,6 +91,9 @@ APPARMOR_2.10 {
   global:
         aa_query_label_data;
 	aa_clear_label_data;
+        aa_query_dconf_len;
+        aa_query_dconf;
+        aa_query_dconf_data;
   local:
         *;
 } APPARMOR_2.10;
diff --git a/parser/Makefile b/parser/Makefile
index 1f0db8d..ec54f96 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..5fd566c
--- /dev/null
+++ b/parser/dconf.cc
@@ -0,0 +1,122 @@
+/*
+ *   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>
+
+int dconfDataEnt::serialize(std::ostringstream &buf)
+{
+	stringsteam tmp;
+	/* set up blob which contains list of strings
+	 * should there be a header with a count of the strings in the blob?
+	 * should the string have its size prepended ???
+	 */
+	for (set<std::string>::iterator i = paths.begin(); i != paths.end(); i++) {
+		/* includes null terminator ????? */
+		sd_write32(i->size() + 1);
+		tmp.write(i->c_str(), i->size() + 1);
+	}
+
+	sd_write_string(buf, "dconf", NULL);
+	sd_write_blob(buf, tmp.str(), 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;
+}
+
+int dconf_rule::gen_data(Profile &prof)
+{
+	DataMap::iterator i = prof->data.find("dconf");
+	if (i == prof->data.end()) {
+		pair<DataMap::iterator,bool> j = data.insert(makepair<"dconf", new dconfDataEnt()>);
+		i = j->first;
+	}
+
+	/* TODO: actually generate an approximate watch point from
+	 * the rule. ATM just use a global root
+	 */
+	i->paths.insert(dconfDataEnt("/"));
+}
+
+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 << "[^\\000]*";
+	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..60dec2c
--- /dev/null
+++ b/parser/dconf.h
@@ -0,0 +1,48 @@
+/*
+ *   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 "rule.h"
+
+class dconfDataEnt: public DataEnt {
+public:
+	set<std::string> paths;
+	dconfDataEnt(): paths() { };
+	virtual ~dconfDataEnt() { };
+
+	void serialize(std:ostrinstream &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);
+};
+
+#endif /* __AA_DCONF_H */
diff --git a/parser/parser_interface.c b/parser/parser_interface.c
index 00a81f2..4304361 100644
--- a/parser/parser_interface.c
+++ b/parser/parser_interface.c
@@ -370,10 +370,24 @@ void sd_serialize_xtable(std::ostringstream &buf, char **table)
 	sd_write_structend(buf);
 }
 
+void sd_serialize_label_data(std::ostringstream &buf, Profile *profile)
+{
+	if (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;
+	size_t size;
 
 	sd_write_struct(buf, "profile");
 	if (flattened) {
@@ -457,6 +471,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 8b0c436..056d33c 100644
--- a/parser/parser_lex.l
+++ b/parser/parser_lex.l
@@ -250,6 +250,7 @@ LT_EQUAL	<=
 %x RLIMIT_MODE
 %x MOUNT_MODE
 %x DBUS_MODE
+%x DCONF_MODE
 %x SIGNAL_MODE
 %x PTRACE_MODE
 %x UNIX_MODE
@@ -267,7 +268,7 @@ LT_EQUAL	<=
 	}
 %}
 
-<INITIAL,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,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 */ }
 }
 
@@ -505,6 +506,16 @@ LT_EQUAL	<=
 			}
 }
 
+<DCONF_MODE>{
+	r(ead)?					{ RETURN_TOKEN(TOK_READ); }
+	w(rite)?				{ RETURN_TOKEN(TOK_WRITE); }
+	(rw|wr)					{ RETURN_TOKEN(TOK_READWRITE); }
+	({PATHNAME}|{QPATHNAME})	{
+		yylval.id = processid(yytext, yyleng);
+		RETURN_TOKEN(TOK_ID);
+	}
+}
+
 <MOUNT_MODE>{
 	{ARROW}		{ RETURN_TOKEN(TOK_ARROW); }
 }
@@ -603,6 +614,9 @@ include/{WS}	{
 	case TOK_DBUS:
 		state = DBUS_MODE;
 		break;
+	case TOK_DCONF:
+		state = DCONF_MODE;
+		break;
 	case TOK_SIGNAL:
 		state = SIGNAL_MODE;
 		break;
@@ -618,7 +632,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();
@@ -631,7 +645,7 @@ include/{WS}	{
 	}
 }
 
-<INITIAL,SUB_ID,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_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 */
@@ -658,6 +672,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 e362f24..ef13404 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 b3083d5..cd25850 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>
@@ -139,12 +140,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
@@ -182,6 +185,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"
@@ -198,6 +202,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;
@@ -266,6 +271,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
@@ -741,6 +750,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)
@@ -1325,6 +1341,25 @@ 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_WRITE { $$ = AA_DCONF_READWRITE; /* writable implies readable */ }
+	| 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 opt_dconf_perms TOK_ID 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.h b/parser/profile.h
index 5adbbcf..f20ed04 100644
--- a/parser/profile.h
+++ b/parser/profile.h
@@ -110,6 +110,18 @@ struct dfa_stuff {
 	dfa_stuff(void): rules(NULL), dfa(NULL), size(0) { }
 };
 
+
+class DataEnt {
+public:
+	virtual ~DataEnt() = 0;
+	virtual void serialize(std:ostrinstream &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 +155,9 @@ public:
 	struct dfa_stuff dfa;
 	struct dfa_stuff policy;
 
-	Profile(void)
+	DataMap data;
+
+	Profile(void): data()
 	{
 		ns = name = attachment = NULL;
 		altnames = NULL;
@@ -163,7 +177,13 @@ public:
 		entries = NULL;
 	};
 
-	virtual ~Profile();
+	virtual ~Profile()
+	{
+		for (DataMap::iterator i = data.begin(); i != data.end(); i++) {
+			delete i.second;
+		}
+		map.clear();
+	}
 
 	bool operator<(Profile const &rhs)const
 	{
diff --git a/parser/tst/equality.sh b/parser/tst/equality.sh
index 3beed27..3f36256 100755
--- a/parser/tst/equality.sh
+++ b/parser/tst/equality.sh
@@ -265,6 +265,24 @@ verify_binary_equality "dbus minimization found in dbus abstractions" \
                    peer=(name=org.freedesktop.DBus),
 	      dbus send bus=session, }"
 
+verify_binary_equality "dconf read" \
+	"/t { dconf / r, }" \
+	"/t { dconf / read, }"
+
+verify_binary_equality "dconf write" \
+	"/t { dconf / w, }" \
+	"/t { dconf / write, }"
+
+verify_binary_equality "dconf read-write" \
+	"/t { dconf / rw, }" \
+	"/t { dconf / wr, }" \
+	"/t { dconf / readwrite, }" \
+	"/t { dconf / writeread, }" \
+	"/t { dconf / read-write, }" \
+	"/t { dconf / write-read, }" \
+	"/t { dconf / read_write, }" \
+	"/t { dconf / write_read, }"
+
 # Rules compatible with audit, deny, and audit deny
 # note: change_profile does not support audit/allow/deny atm
 for rule in "capability" "capability mac_admin" \
diff --git a/tests/regression/apparmor/Makefile b/tests/regression/apparmor/Makefile
index c0aad62..72a3ebb 100644
--- a/tests/regression/apparmor/Makefile
+++ b/tests/regression/apparmor/Makefile
@@ -93,6 +93,7 @@ SRC=access.c \
     ptrace.c \
     ptrace_helper.c \
     pwrite.c \
+    query_dconf.c \
     query_label.c \
     rename.c \
     readdir.c \
@@ -185,6 +186,7 @@ TESTS=access \
       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 bf8dfe9..be7a133 100644
--- a/tests/regression/apparmor/query_label.c
+++ b/tests/regression/apparmor/query_label.c
@@ -59,6 +59,9 @@
 #define AA_EXEC_INHERIT		(1 << 9)
 #endif
 
+#define OPT_TYPE_DCONF		"--dconf="
+#define OPT_TYPE_DCONF_LEN	strlen(OPT_TYPE_DCONF)
+
 static char *progname = NULL;
 
 void usage(void)
@@ -74,10 +77,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);
 }
@@ -171,6 +176,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)
 {
@@ -242,6 +270,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 01ec6d1..2ace04b 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.1.4





More information about the AppArmor mailing list