-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256

Hello,

If I may, I'd like to revive the old dconf confinement patches that we started 
over a year ago, but were never merged.

All necessary patches are attached, as well as an extra test profile and 
program. I've refreshed them to work properly against kernel 4.6.4 and current 
AppArmor trunk.

Thank you,
Will
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v2

iQIcBAEBCAAGBQJXoS1ZAAoJENtfC4FFqZmIOQkP/3qFjvuA72XKQuZ3kxtREW7W
bagFLWGqmtFYCIKhmT0dZKo3cMosDriML1DZCgzcAXcrZlfmkTdeHnp6hziX7CbA
765pjffpjFLKxIn5vmUtXzIDiZSG8jwaRoZCwB/B+GZoW/0dGUsl4z5fRnjU1Ouy
sJBKlYW/DwwoupXRfZiGTLkyXpUWeBHHP+UqvRFrg6EOH+g1CTFsuSMm3C1OBUd8
S7k4DiXjPboy6r5ZjzLAcQWdXA6ZU7osw7Y58xbCX5mV6r7GRpPR1PSnn0uSACNW
1k0auobDvkgAYeV2eq7Y4f41+yMiqxKmV6fBeOKlksjQD5EEZ8ykHMmqxv9W2Z2h
0YlObPA7nFvhSaM3aMqcVumlBOjQqTtcutVKKFgrTRDilDFLxdsnuqW+5wokhrP7
MjybUGy4finM5ICf5tzdspvyR9LL8ECpfw4s+pOL1P069f5JxQDCvT5YdUeZBLIB
So83DZLyOYdZTndeFL35lr66Ad/c+2tW+vA2wQ15nGjotupIflvTr6GX8EHxTBtM
+hsgHTwYPZu+IO4Voi1h/eZkrTV4jXCvB1uF9jIK0ihRAz+va3PiXKdALMm3m8XN
9SVSMqSY/B0xYDTr+XwBdrX+Tej/gcGQb5bShZiVmX+5RMWPyZA85I4TlPIY0XM0
QyETXn1tvazxenAnBZIN
=q8UD
-----END PGP SIGNATURE-----
From c9b9286947db297724732f38daf721340037a7ef Mon Sep 17 00:00:00 2001
From: William Hua <[email protected]>
Date: Thu, 28 Jul 2016 18:12:00 +0000
Subject: [PATCH] UBUNTU: SAUCE: apparmor: add data query support

Allow AppArmor to store and retrieve arbitrary free-form data. This
is needed for the dconf proxy.

Signed-off-by: William Hua <[email protected]>
---
 security/apparmor/apparmorfs.c     | 116 +++++++++++++++++++++++++++++++++++--
 security/apparmor/include/policy.h |  17 +++++-
 security/apparmor/policy.c         |  22 +++++++
 security/apparmor/policy_unpack.c  |  67 +++++++++++++++++++++
 4 files changed, 216 insertions(+), 6 deletions(-)

diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c
index ff76113..2c0a353 100644
--- a/security/apparmor/apparmorfs.c
+++ b/security/apparmor/apparmorfs.c
@@ -235,6 +235,98 @@ static void profile_query_cb(struct aa_profile *profile, struct aa_perms *perms,
 }
 
 /**
+ * query_data - queries a policy and writes its data to buf
+ * @buf: the resulting data is stored here (NOT NULL)
+ * @buf_len: size of buf
+ * @query: query string used to retrieve data
+ * @query_len: size of query including second NUL byte
+ *
+ * The buffers pointed to by buf and query may overlap. The query buffer is
+ * parsed before buf is written to.
+ *
+ * The query should look like "<LABEL>\0<KEY>\0", where <LABEL> is the name of
+ * the security confinement context and <KEY> is the name of the data to
+ * retrieve. <LABEL> and <KEY> must not be NUL-terminated.
+ *
+ * Don't expect the contents of buf to be preserved on failure.
+ *
+ * Returns: number of characters written to buf or -errno on failure
+ */
+static ssize_t query_data(char *buf, size_t buf_len,
+			  char *query, size_t query_len)
+{
+	char *out;
+	const char *key;
+	struct label_it i;
+	struct aa_label *label, *curr;
+	struct aa_profile *profile;
+	struct aa_data *data;
+	u32 bytes;
+	u32 blocks;
+	u32 size;
+
+	if (!query_len)
+		return -EINVAL; /* need a query */
+
+	key = query + strnlen(query, query_len) + 1;
+	if (key + 1 >= query + query_len)
+		return -EINVAL; /* not enough space for a non-empty key */
+	if (key + strnlen(key, query + query_len - key) >= query + query_len)
+		return -EINVAL; /* must end with NUL */
+
+	if (buf_len < sizeof(bytes) + sizeof(blocks))
+		return -EINVAL; /* not enough space */
+
+	curr = aa_begin_current_label(DO_UPDATE);
+	label = aa_label_parse(curr, query, GFP_KERNEL, false, false);
+	aa_end_current_label(curr);
+	if (IS_ERR(label))
+		return PTR_ERR(label);
+
+	/* We are going to leave space for two numbers. The first is the total
+	 * number of bytes we are writing after the first number. This is so
+	 * users can read the full output without reallocation.
+	 *
+	 * The second number is the number of data blocks we're writing. An
+	 * application might be confined by multiple policies having data in
+	 * the same key.
+	 */
+	memset(buf, 0, sizeof(bytes) + sizeof(blocks));
+	out = buf + sizeof(bytes) + sizeof(blocks);
+
+	blocks = 0;
+	label_for_each_confined(i, label, profile) {
+		if (!profile->data)
+			continue;
+
+		data = rhashtable_lookup_fast(profile->data, &key,
+					      profile->data->p);
+
+		if (data) {
+			if (out + sizeof(size) + data->size > buf + buf_len) {
+				aa_put_label(label);
+				return -EINVAL; /* not enough space */
+			}
+			size = __cpu_to_le32(data->size);
+			memcpy(out, &size, sizeof(size));
+			out += sizeof(size);
+			memcpy(out, data->data, data->size);
+			out += data->size;
+			blocks++;
+		}
+	}
+	aa_put_label(label);
+
+	bytes = out - buf - sizeof(bytes);
+	bytes = __cpu_to_le32(bytes);
+	blocks = __cpu_to_le32(blocks);
+	memcpy(buf, &bytes, sizeof(bytes));
+	memcpy(buf + sizeof(bytes), &blocks, sizeof(blocks));
+
+	return out - buf;
+}
+
+/**
  * query_label - queries a label and writes permissions to buf
  * @buf: the resulting permissions string is stored here (NOT NULL)
  * @buf_len: size of buf
@@ -309,18 +401,27 @@ static ssize_t query_label(char *buf, size_t buf_len,
 #define QUERY_CMD_PROFILE_LEN	8
 #define QUERY_CMD_LABELALL	"labelall\0"
 #define QUERY_CMD_LABELALL_LEN	9
+#define QUERY_CMD_DATA		"data\0"
+#define QUERY_CMD_DATA_LEN	5
 
 /**
- * aa_write_access - generic permissions query
+ * aa_write_access - generic permissions and data query
  * @file: pointer to open apparmorfs/access file
  * @ubuf: user buffer containing the complete query string (NOT NULL)
  * @count: size of ubuf
  * @ppos: position in the file (MUST BE ZERO)
  *
- * Allows for one permission query per open(), write(), and read() sequence.
- * The only query currently supported is a label-based query. For this query
- * ubuf must begin with "label\0", followed by the profile query specific
- * format described in the query_label() function documentation.
+ * Allows for one permissions or data query per open(), write(), and read()
+ * sequence. The only queries currently supported are label-based queries for
+ * permissions or data.
+ *
+ * For permissions queries, ubuf must begin with "label\0", followed by the
+ * profile query specific format described in the query_label() function
+ * documentation.
+ *
+ * For data queries, ubuf must have the form "data\0<LABEL>\0<KEY>\0", where
+ * <LABEL> is the name of the security confinement context and <KEY> is the
+ * name of the data to retrieve.
  *
  * Returns: number of bytes written or -errno on failure
  */
@@ -352,6 +453,11 @@ static ssize_t aa_write_access(struct file *file, const char __user *ubuf,
 		len = query_label(buf, SIMPLE_TRANSACTION_LIMIT,
 				  buf + QUERY_CMD_LABELALL_LEN,
 				  count - QUERY_CMD_LABELALL_LEN, false);
+	} else if (count > QUERY_CMD_DATA_LEN &&
+		   !memcmp(buf, QUERY_CMD_DATA, QUERY_CMD_DATA_LEN)) {
+		len = query_data(buf, SIMPLE_TRANSACTION_LIMIT,
+				 buf + QUERY_CMD_DATA_LEN,
+				 count - QUERY_CMD_DATA_LEN);
 	} else
 		len = -EINVAL;
 
diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h
index 5e563d7..e3af5ac 100644
--- a/security/apparmor/include/policy.h
+++ b/security/apparmor/include/policy.h
@@ -18,6 +18,7 @@
 #include <linux/capability.h>
 #include <linux/cred.h>
 #include <linux/kref.h>
+#include <linux/rhashtable.h>
 #include <linux/sched.h>
 #include <linux/slab.h>
 #include <linux/socket.h>
@@ -78,6 +79,19 @@ struct aa_policydb {
 
 };
 
+/* struct aa_data - generic data structure
+ * key: name for retrieving this data
+ * size: size of data in bytes
+ * data: binary data
+ * head: reserved for rhashtable
+ */
+struct aa_data {
+	char *key;
+	size_t size;
+	char *data;
+	struct rhash_head head;
+};
+
 /* struct aa_profile - basic confinement data
  * @base - base components of the profile (name, refcount, lists, lock ...)
  * @label - label this profile is an extension of
@@ -97,9 +111,9 @@ struct aa_policydb {
  * @caps: capabilities for the profile
  * @net: network controls for the profile
  * @rlimits: rlimits for the profile
- *
  * @dents: dentries for the profiles file entries in apparmorfs
  * @dirname: name of the profile dir in apparmorfs
+ * @data: hashtable for free-form policy aa_data
  *
  * The AppArmor profile contains the basic confinement data.  Each profile
  * has a name, and exists in a namespace.  The @name and @exec_match are
@@ -138,6 +152,7 @@ struct aa_profile {
 	unsigned char *hash;
 	char *dirname;
 	struct dentry *dents[AAFS_PROF_SIZEOF];
+	struct rhashtable *data;
 	struct aa_label label;
 };
 
diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c
index 12cd14f..5f9d405 100644
--- a/security/apparmor/policy.c
+++ b/security/apparmor/policy.c
@@ -186,6 +186,19 @@ void __aa_profile_list_release(struct list_head *head)
 		__remove_profile(profile);
 }
 
+/**
+ * aa_free_data - free a data blob
+ * @ptr: data to free
+ * @arg: unused
+ */
+static void aa_free_data(void *ptr, void *arg)
+{
+	struct aa_data *data = ptr;
+
+	kzfree(data->data);
+	kzfree(data->key);
+	kzfree(data);
+}
 
 /**
  * aa_free_profile - free a profile
@@ -199,6 +212,8 @@ void __aa_profile_list_release(struct list_head *head)
  */
 void aa_free_profile(struct aa_profile *profile)
 {
+	struct rhashtable *rht;
+
 	AA_DEBUG("%s(%p)\n", __func__, profile);
 
 	if (!profile)
@@ -220,6 +235,13 @@ void aa_free_profile(struct aa_profile *profile)
 	aa_put_dfa(profile->xmatch);
 	aa_put_dfa(profile->policy.dfa);
 
+	if (profile->data) {
+		rht = profile->data;
+		profile->data = NULL;
+		rhashtable_free_and_destroy(rht, aa_free_data, NULL);
+		kzfree(rht);
+	}
+
 	kzfree(profile->hash);
 	kzfree(profile);
 }
diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c
index c62815f..f74fcb9 100644
--- a/security/apparmor/policy_unpack.c
+++ b/security/apparmor/policy_unpack.c
@@ -21,6 +21,7 @@
 #include <linux/ctype.h>
 #include <linux/errno.h>
 #include <linux/string.h>
+#include <linux/jhash.h>
 
 #include "include/apparmor.h"
 #include "include/audit.h"
@@ -495,6 +496,30 @@ fail:
 	return 0;
 }
 
+static void *kvmemdup(const void *src, size_t len)
+{
+	void *p = kvmalloc(len);
+
+	if (p)
+		memcpy(p, src, len);
+	return p;
+}
+
+static u32 strhash(const void *data, u32 len, u32 seed)
+{
+	const char * const *key = data;
+
+	return jhash(*key, strlen(*key), seed);
+}
+
+static int datacmp(struct rhashtable_compare_arg *arg, const void *obj)
+{
+	const struct aa_data *data = obj;
+	const char * const *key = arg->key;
+
+	return strcmp(data->key, *key);
+}
+
 /**
  * unpack_profile - unpack a serialized profile
  * @e: serialized data extent information (NOT NULL)
@@ -507,6 +532,9 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
 	const char *tmpname, *tmpns = NULL, *name = NULL;
 	const char *info = "failed to unpack profile";
 	size_t size = 0, ns_len;
+	struct rhashtable_params params = { 0 };
+	char *key = NULL;
+	struct aa_data *data;
 	int i, error = -EPROTO;
 	kernel_cap_t tmpcap;
 	u32 tmp;
@@ -694,6 +722,45 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
 	if (!unpack_trans_table(e, profile))
 		goto fail;
 
+	if (unpack_nameX(e, AA_STRUCT, "data")) {
+		profile->data = kzalloc(sizeof(*profile->data), GFP_KERNEL);
+		if (!profile->data)
+			goto fail;
+
+		params.nelem_hint = 3;
+		params.key_len = sizeof(void *);
+		params.key_offset = offsetof(struct aa_data, key);
+		params.head_offset = offsetof(struct aa_data, head);
+		params.hashfn = strhash;
+		params.obj_cmpfn = datacmp;
+
+		if (rhashtable_init(profile->data, &params))
+			goto fail;
+
+		while (unpack_strdup(e, &key, NULL)) {
+			data = kzalloc(sizeof(*data), GFP_KERNEL);
+			if (!data) {
+				kzfree(key);
+				goto fail;
+			}
+
+			data->key = key;
+			data->size = unpack_blob(e, &data->data, NULL);
+			data->data = kvmemdup(data->data, data->size);
+			if (data->size && !data->data) {
+				kzfree(data->key);
+				kzfree(data);
+				goto fail;
+			}
+
+			rhashtable_insert_fast(profile->data, &data->head,
+					       profile->data->p);
+		}
+
+		if (!unpack_nameX(e, AA_STRUCTEND, NULL))
+			goto fail;
+	}
+
 	if (!unpack_nameX(e, AA_STRUCTEND, NULL))
 		goto fail;
 
-- 
2.8.1

From 463fc376b7f923665fac22fb87523b0b8a856942 Mon Sep 17 00:00:00 2001
From: John Johansen <[email protected]>
Date: Wed, 15 Jul 2015 05:36:32 -0700
Subject: [PATCH 1/4] Split aa_query_label into a base aa_query_cmd and it
 aa_query_label

Split the basic transaction file query out of aa_query_label so that
it can be reused by other query types.

Signed-off-by: John Johansen <[email protected]>
---
 libraries/libapparmor/doc/aa_query_label.pod  | 16 ++++-
 libraries/libapparmor/include/sys/apparmor.h  |  2 +
 libraries/libapparmor/src/kernel.c            | 93 +++++++++++++++++++++------
 libraries/libapparmor/src/libapparmor.map     |  1 +
 libraries/libapparmor/swig/SWIG/libapparmor.i |  2 +
 5 files changed, 90 insertions(+), 24 deletions(-)

diff --git a/libraries/libapparmor/doc/aa_query_label.pod b/libraries/libapparmor/doc/aa_query_label.pod
index 06129b6..73f430b 100644
--- a/libraries/libapparmor/doc/aa_query_label.pod
+++ b/libraries/libapparmor/doc/aa_query_label.pod
@@ -32,11 +32,18 @@ aa_query_link_path, aa_query_link_path_len - query access permissions of a link
 
 B<#include E<lt>sys/apparmor.hE<gt>>
 
-B<int aa_query_label(uint32_t mask, char *query, size_t size, int *allowed, int *audited);>
+B<int aa_query_cmd(const char *cmd, size_t cmd_size, char *query,
+		size_t size, char *buffer, size_t bsize);>
 
-B<int aa_query_file_path(uint32_t mask, const char *label, size_t label_len, const char *path, int *allowed, int *audited);>
+B<int aa_query_label(uint32_t mask, char *query, size_t size,
+		int *allowed, int *audited);>
 
-B<int aa_query_file_path_len(uint32_t mask, const char *label, size_t label_len, const char *path, size_t path_len, int *allowed, int *audited);>
+B<int aa_query_file_path(uint32_t mask, const char *label, size_t label_len,
+		const char *path, int *allowed, int *audited);>
+
+B<int aa_query_file_path_len(uint32_t mask, const char *label,
+		size_t label_len, const char *path, size_t path_len,
+		int *allowed, int *audited);>
 
 B<int aa_query_link_path(const char *label, const char *target, const char *link, int *allowed, int *audited);>
 
@@ -47,6 +54,9 @@ Link with B<-lapparmor> when compiling.
 
 =head1 DESCRIPTION
 
+The B<aa_query_cmd> function sets up and does a raw query of the kernel. It is
+the basis of the other query functions.
+
 The B<aa_query_label> function fetches the current permissions granted by the
 specified I<label> in the I<query> string.
 
diff --git a/libraries/libapparmor/include/sys/apparmor.h b/libraries/libapparmor/include/sys/apparmor.h
index 752a5bd..5e43ba2 100644
--- a/libraries/libapparmor/include/sys/apparmor.h
+++ b/libraries/libapparmor/include/sys/apparmor.h
@@ -101,6 +101,8 @@ extern int aa_getpeercon(int fd, char **label, char **mode);
 #define AA_QUERY_CMD_LABEL		"label"
 #define AA_QUERY_CMD_LABEL_SIZE		sizeof(AA_QUERY_CMD_LABEL)
 
+extern int aa_query_cmd(const char *cmd, size_t cmd_size, char *query,
+			size_t size, char *buffer, size_t bsize);
 extern int aa_query_label(uint32_t mask, char *query, size_t size, int *allow,
 			  int *audit);
 extern int aa_query_file_path_len(uint32_t mask, const char *label,
diff --git a/libraries/libapparmor/src/kernel.c b/libraries/libapparmor/src/kernel.c
index 49c74e1..1fe1b61 100644
--- a/libraries/libapparmor/src/kernel.c
+++ b/libraries/libapparmor/src/kernel.c
@@ -802,30 +802,22 @@ static void aafs_access_init_once(void)
 	free(aafs);
 }
 
-/* "allow 0x00000000\ndeny 0x00000000\naudit 0x00000000\nquiet 0x00000000\n" */
-#define QUERY_LABEL_REPLY_LEN	67
-
 /**
- * aa_query_label - query the access(es) of a label
- * @mask: permission bits to query
- * @query: binary query string, must be offset by AA_QUERY_CMD_LABEL_SIZE
- * @size: size of the query string must include AA_QUERY_CMD_LABEL_SIZE
- * @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
+ * aa_query_cmd_open - begin a query for labels @cmd info
+ * @cmd: query cmd to use
+ * @cmd_size: size of the cmd being used
+ * @query: binary query string, must be offset by @cmd_size
+ * @size: size of the query string must include @cmd_size
  *
- * 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.
+ * Returns: fd with the query issued and results waiting to be read 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 query_label(uint32_t mask, char *query, size_t size, int *allowed,
-		int *audited)
+static int aa_query_cmd_open(const char *cmd, size_t cmd_size, char *query, size_t size)
 {
-	char buf[QUERY_LABEL_REPLY_LEN];
-	uint32_t allow, deny, audit, quiet;
-	int fd, ret, saved;
+	int fd, ret;
 
-	if (!mask || size <= AA_QUERY_CMD_LABEL_SIZE) {
+	if (size <= cmd_size) {
 		errno = EINVAL;
 		return -1;
 	}
@@ -846,7 +838,7 @@ int query_label(uint32_t mask, char *query, size_t size, int *allowed,
 		return -1;
 	}
 
-	memcpy(query, AA_QUERY_CMD_LABEL, AA_QUERY_CMD_LABEL_SIZE);
+	memcpy(query, cmd, cmd_size);
 	errno = 0;
 	ret = write(fd, query, size);
 	if (ret != size) {
@@ -860,10 +852,69 @@ int query_label(uint32_t mask, char *query, size_t size, int *allowed,
 		return -1;
 	}
 
-	ret = read(fd, buf, QUERY_LABEL_REPLY_LEN);
+	return fd;
+}
+
+/**
+ * aa_query_cmd - make a query for labels @cmd info
+ * @cmd: query cmd to use
+ * @cmd_size: size of the cmd being used
+ * @query: binary query string, must be offset by @cmd_size
+ * @size: size of the query string must include @cmd_size
+ * @buffer: buffer to return query data in
+ * @bsize: size of @buffer
+ *
+ * Returns: size of data read 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_cmd(const char *cmd, size_t cmd_size, char *query, size_t size,
+		 char *buffer, size_t bsize)
+{
+	int fd, ret, saved;
+
+	fd = aa_query_cmd_open(cmd, cmd_size, query, size);
+	if (fd == -1)
+		return -1;
+
+	ret = read(fd, buffer, bsize);
 	saved = errno;
 	(void)close(fd);
 	errno = saved;
+
+	return ret;
+}
+
+/* "allow 0x00000000\ndeny 0x00000000\naudit 0x00000000\nquiet 0x00000000\n" */
+#define QUERY_LABEL_REPLY_LEN	67
+
+/**
+ * aa_query_label - query the access(es) of a label
+ * @mask: permission bits to query
+ * @query: binary query string, must be offset by AA_QUERY_CMD_LABEL_SIZE
+ * @size: size of the query string must include AA_QUERY_CMD_LABEL_SIZE
+ * @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 query_label(uint32_t mask, char *query, size_t size, int *allowed,
+		int *audited)
+{
+	char buf[QUERY_LABEL_REPLY_LEN];
+	uint32_t allow, deny, audit, quiet;
+	int ret;
+
+	if (!mask) {
+		errno = EINVAL;
+		return -1;
+	}
+
+	ret = aa_query_cmd(AA_QUERY_CMD_LABEL, AA_QUERY_CMD_LABEL_SIZE, query,
+			   size, buf, QUERY_LABEL_REPLY_LEN);
 	if (ret != QUERY_LABEL_REPLY_LEN) {
 		errno = EPROTO;
 		return -1;
diff --git a/libraries/libapparmor/src/libapparmor.map b/libraries/libapparmor/src/libapparmor.map
index 5cbd4e8..737b83d 100644
--- a/libraries/libapparmor/src/libapparmor.map
+++ b/libraries/libapparmor/src/libapparmor.map
@@ -91,6 +91,7 @@ APPARMOR_2.11 {
   global:
         aa_stack_profile;
         aa_stack_onexec;
+        aa_query_cmd;
   local:
         *;
 } APPARMOR_2.10;
diff --git a/libraries/libapparmor/swig/SWIG/libapparmor.i b/libraries/libapparmor/swig/SWIG/libapparmor.i
index 005dd7f..9165882 100644
--- a/libraries/libapparmor/swig/SWIG/libapparmor.i
+++ b/libraries/libapparmor/swig/SWIG/libapparmor.i
@@ -57,6 +57,8 @@ extern int aa_gettaskcon(pid_t target, char **label, char **mode);
 extern int aa_getcon(char **label, char **mode);
 extern int aa_getpeercon_raw(int fd, char *buf, int *len, char **mode);
 extern int aa_getpeercon(int fd, char **label, char **mode);
+extern int aa_query_cmd(const char *cmd, size_t cmd_size, char *query,
+			size_t size, char *buffer, size_t bsize);
 extern int aa_query_label(uint32_t mask, char *query, size_t size, int *allow,
 			  int *audit);
 extern int aa_query_file_path_len(uint32_t mask, const char *label,
-- 
2.8.1

From 96e53dfdd6944a9f46c7bbe1ef6c80479ad71c4e Mon Sep 17 00:00:00 2001
From: William Hua <[email protected]>
Date: Mon, 14 Dec 2015 03:32:41 -0500
Subject: [PATCH 2/4] Add base function to query generic label data under a
 given key

---
 libraries/libapparmor/doc/aa_query_label.pod  |   6 ++
 libraries/libapparmor/include/sys/apparmor.h  |  19 ++++
 libraries/libapparmor/src/kernel.c            | 120 ++++++++++++++++++++++++++
 libraries/libapparmor/src/libapparmor.map     |   2 +
 libraries/libapparmor/swig/SWIG/libapparmor.i |   2 +
 5 files changed, 149 insertions(+)

diff --git a/libraries/libapparmor/doc/aa_query_label.pod b/libraries/libapparmor/doc/aa_query_label.pod
index 73f430b..eadb8cd 100644
--- a/libraries/libapparmor/doc/aa_query_label.pod
+++ b/libraries/libapparmor/doc/aa_query_label.pod
@@ -38,6 +38,9 @@ B<int aa_query_cmd(const char *cmd, size_t cmd_size, char *query,
 B<int aa_query_label(uint32_t mask, char *query, size_t size,
 		int *allowed, int *audited);>
 
+B<int aa_query_label_data(const char *label, const char *key,
+		char *buffer, size_t bsize);>
+
 B<int aa_query_file_path(uint32_t mask, const char *label, size_t label_len,
 		const char *path, int *allowed, int *audited);>
 
@@ -60,6 +63,9 @@ the basis of the other query functions.
 The B<aa_query_label> function fetches the current permissions granted by the
 specified I<label> in the I<query> string.
 
+The B<aa_query_label_data> function does a raw query of any extra data stored
+as I<key> in the label.
+
 The query is a raw binary formatted query, containing the label and
 permission query to make. The returned I<allowed> and I<audited> values are
 interpreted boolean values, simply stating whether the query is allowed and
diff --git a/libraries/libapparmor/include/sys/apparmor.h b/libraries/libapparmor/include/sys/apparmor.h
index 5e43ba2..993bd10 100644
--- a/libraries/libapparmor/include/sys/apparmor.h
+++ b/libraries/libapparmor/include/sys/apparmor.h
@@ -100,6 +100,8 @@ extern int aa_getpeercon(int fd, char **label, char **mode);
  */
 #define AA_QUERY_CMD_LABEL		"label"
 #define AA_QUERY_CMD_LABEL_SIZE		sizeof(AA_QUERY_CMD_LABEL)
+#define AA_QUERY_CMD_DATA		"data"
+#define AA_QUERY_CMD_DATA_SIZE		sizeof(AA_QUERY_CMD_DATA)
 
 extern int aa_query_cmd(const char *cmd, size_t cmd_size, char *query,
 			size_t size, char *buffer, size_t bsize);
@@ -117,6 +119,23 @@ extern int aa_query_link_path_len(const char *label, size_t label_len,
 extern int aa_query_link_path(const char *label, const char *target,
 			      const char *link, int *allowed, int *audited);
 
+
+typedef struct {
+	size_t n;			/* length of s */
+	const char *entry;		/* not necessarily NULL-terminated */
+} aa_label_data_ent;
+
+typedef struct {
+	char *data;			/* free data */
+	size_t n;			/* number of ents */
+	aa_label_data_ent *ents;	/* free vec of entries */
+} aa_label_data_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);
+
+
 #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)
 #define __macroarg_count2(_,x0,x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12,x13,x14,x15,n,Y...) n
diff --git a/libraries/libapparmor/src/kernel.c b/libraries/libapparmor/src/kernel.c
index 1fe1b61..a2842fe 100644
--- a/libraries/libapparmor/src/kernel.c
+++ b/libraries/libapparmor/src/kernel.c
@@ -47,6 +47,7 @@
 #define UNCONFINED		"unconfined"
 #define UNCONFINED_SIZE		strlen(UNCONFINED)
 
+
 /**
  * aa_find_mountpoint - find where the apparmor interface filesystem is mounted
  * @mnt: returns buffer with the mountpoint string
@@ -1072,3 +1073,122 @@ int aa_query_link_path(const char *label, const char *target, const char *link,
 				      strlen(target), link, strlen(link),
 				      allowed, audited);
 }
+
+/**
+ * aa_query_label_data_open - query
+ * @label: security label
+ * @key: key of data blob to lookup
+ *
+ * Returns: fd waiting to read data else -1 and sets errno.
+ */
+static int aa_query_label_data_open(const char *label, const char *key)
+{
+	autofree char *query = NULL;
+
+	size_t label_len = strlen(label);
+	size_t key_len = strlen(key);
+
+	/* + 1s for null separators */
+	size_t size = AA_QUERY_CMD_DATA_SIZE + label_len + 1 + key_len + 1;
+	query = malloc(size + 1);
+	if (!query)
+		return -1;
+	memcpy(query + AA_QUERY_CMD_DATA_SIZE, label, label_len + 1);
+	memcpy(query + AA_QUERY_CMD_DATA_SIZE + label_len + 1, key, key_len + 1);
+	return aa_query_cmd_open(AA_QUERY_CMD_DATA, AA_QUERY_CMD_DATA_SIZE, query,
+				 size);
+}
+
+/**
+ * aa_query_label_data - query data associated with @key for @label
+ * @key: key of data blob to get data for
+ * @out: data found if any
+ *
+ * Returns: 0 on success, non-zero on failure
+ *
+ * This fn will extract any data associated with @key in @label. Since
+ * @label is a compound label there maybe multiple data items associated
+ * with the key (up to one per profile).
+ *
+ * When multiple items are returned for @key, this function does not
+ * provide any indication of which subcomponents of the @label the data
+ * belonged to.  To obtain that information use the compound label iterator
+ * and call this function once for each iteration.
+ */
+int aa_query_label_data(const char *label, const char *key, aa_label_data_info *out)
+{
+	autofclose FILE *file = NULL;
+	const char *p;
+	uint32_t bytes;
+	uint32_t tmp;
+	int fd;
+
+	if (!out)
+		return 0;
+
+	memset(out, 0, sizeof(*out));
+
+	fd = aa_query_label_data_open(label, key);
+	if (fd == -1)
+		return -1;
+
+	file = fdopen(fd, "r+");
+	if (!file)
+		return -1;
+
+	if (fread(&tmp, sizeof(tmp), 1, file) != 1) {
+		errno = EPROTO;
+		goto done;
+	}
+
+	bytes = le32toh(tmp);
+	out->data = malloc(bytes);
+	if (!out->data) {
+		errno = ENOMEM;
+		goto done;
+	}
+
+	if (fread(out->data, sizeof(char), bytes, file) != bytes) {
+		errno = EPROTO;
+		goto done;
+	}
+
+	p = out->data;
+	memcpy(&tmp, p, sizeof(tmp));
+	out->n = le32toh(tmp);
+	p += sizeof(tmp);
+
+	out->ents = malloc(out->n * sizeof(*out->ents));
+	if (!out->data) {
+		errno = ENOMEM;
+		goto done;
+	}
+
+	for (int i = 0; i < out->n; i++) {
+		memcpy(&tmp, p, sizeof(tmp));
+		out->ents[i].n = le32toh(tmp);
+		p += sizeof(tmp);
+		out->ents[i].entry = p;
+		p += out->ents[i].n;
+	}
+
+done:
+	if (errno)
+		aa_clear_label_data(out);
+
+	return errno ? -1 : 0;
+}
+
+/**
+ * aa_clear_label_data - release resources associated with @info
+ * @info: info struct
+ *
+ * Releases allocated memory associated with @info.
+ */
+void aa_clear_label_data(aa_label_data_info *info)
+{
+	free(info->ents);
+	free(info->data);
+
+	memset(info, 0, sizeof(*info));
+}
diff --git a/libraries/libapparmor/src/libapparmor.map b/libraries/libapparmor/src/libapparmor.map
index 737b83d..aa9c08f 100644
--- a/libraries/libapparmor/src/libapparmor.map
+++ b/libraries/libapparmor/src/libapparmor.map
@@ -92,6 +92,8 @@ APPARMOR_2.11 {
         aa_stack_profile;
         aa_stack_onexec;
         aa_query_cmd;
+        aa_clear_label_data;
+        aa_query_label_data;
   local:
         *;
 } APPARMOR_2.10;
diff --git a/libraries/libapparmor/swig/SWIG/libapparmor.i b/libraries/libapparmor/swig/SWIG/libapparmor.i
index 9165882..a2d85b4 100644
--- a/libraries/libapparmor/swig/SWIG/libapparmor.i
+++ b/libraries/libapparmor/swig/SWIG/libapparmor.i
@@ -72,5 +72,7 @@ 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_label_data(const char *label, const char *key,
+			       aa_label_data_info *out);
 
 %exception;
-- 
2.8.1

From 21e7a1a7626e98756b9949e9b4b8fbb68a397825 Mon Sep 17 00:00:00 2001
From: William Hua <[email protected]>
Date: Mon, 14 Dec 2015 03:32:53 -0500
Subject: [PATCH 3/4] Make some parameters of parser interface constant

---
 parser/parser_interface.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/parser/parser_interface.c b/parser/parser_interface.c
index 5d9e0a0..00a81f2 100644
--- a/parser/parser_interface.c
+++ b/parser/parser_interface.c
@@ -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, void *b, int buf_size, char *name)
+static inline 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);
@@ -273,7 +273,7 @@ static inline void sd_write_aligned_blob(std::ostringstream &buf, void *b, int b
 	buf.write((const char *) b, b_size);
 }
 
-static void sd_write_strn(std::ostringstream &buf, char *b, int size, const char *name)
+static void sd_write_strn(std::ostringstream &buf, const char *b, int size, const char *name)
 {
 	sd_write_name(buf, name);
 	sd_write8(buf, SD_STRING);
@@ -281,7 +281,7 @@ static void sd_write_strn(std::ostringstream &buf, char *b, int size, const char
 	buf.write(b, size);
 }
 
-static inline void sd_write_string(std::ostringstream &buf, char *b, const char *name)
+static inline void sd_write_string(std::ostringstream &buf, const char *b, const char *name)
 {
 	sd_write_strn(buf, b, strlen(b) + 1, name);
 }
-- 
2.8.1

From fbf6743f00c78baddd8e20a0ea6bb90801cf0afe Mon Sep 17 00:00:00 2001
From: William Hua <[email protected]>
Date: Mon, 14 Dec 2015 03:33:04 -0500
Subject: [PATCH 4/4] Add support for dconf confinement

---
 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 993bd10..827e3eb 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 a2842fe..3b13091 100644
--- a/libraries/libapparmor/src/kernel.c
+++ b/libraries/libapparmor/src/kernel.c
@@ -1075,6 +1075,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
@@ -1192,3 +1247,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 aa9c08f..b04da2a 100644
--- a/libraries/libapparmor/src/libapparmor.map
+++ b/libraries/libapparmor/src/libapparmor.map
@@ -94,6 +94,10 @@ APPARMOR_2.11 {
         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.10;
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..2d3abfd 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.8.1

#include <tunables/global>

/home/william/Code/apparmor/test/dconf-test {
  #include <abstractions/apparmor_api/examine>
  #include <abstractions/apparmor_api/introspect>
  #include <abstractions/base>
  #include <abstractions/bash>

  /sys/kernel/security/apparmor/.access rw,
  owner @{HOME}/** rw,

  dconf /a r,
  dconf /b/c/ r,
  dconf /d rw,
  dconf /e/f/ rw,
  audit dconf /g r,
  audit dconf /h/i/ r,
  audit dconf /j rw,
  audit dconf /k/l/ rw,
}
#include <stdlib.h>
#include <stdio.h>
#include <sys/apparmor.h>

static void
test (const char *label,
      const char *path,
      uint32_t    mask,
      int         should_allow,
      int         should_audit)
{
  int allowed;
  int audited;

  aa_query_dconf (mask, label, path, &allowed, &audited);

  if (allowed == should_allow && audited == should_audit)
    printf ("PASS: ");
  else
    printf ("FAIL: ");

  printf ("path = %s, mask = 0x%x", path, mask);

  if (allowed == should_allow)
    printf (", allowed = %d", allowed);
  else
    printf (", allowed = %d (expected %d)", allowed, should_allow);

  if (audited == should_audit)
    printf (", audited = %d", audited);
  else
    printf (", audited = %d (expected %d)", audited, should_audit);

  printf ("\n");
}

int
main (int   argc,
      char *argv[])
{
  char *label;
  aa_dconf_info info;
  unsigned int i;
  uint32_t mask;
  const char *path;
  int allowed;
  int audited;

  aa_getcon (&label, NULL);
  aa_query_dconf_info (label, &info);

  printf ("read-only paths:\n");
  for (i = 0; i < info.rn; i++)
    printf ("%s\n", info.rpaths[i]);

  printf ("\nread-write paths:\n");
  for (i = 0; i < info.rwn; i++)
    printf ("%s\n", info.rwpaths[i]);

  printf ("\naudited read-only paths:\n");
  for (i = 0; i < info.arn; i++)
    printf ("%s\n", info.arpaths[i]);

  printf ("\naudited read-write paths:\n");
  for (i = 0; i < info.arwn; i++)
    printf ("%s\n", info.arwpaths[i]);

  aa_clear_dconf_info (&info);

  printf ("\n");

  test (label, "/a", AA_DCONF_READ, 1, 0);
  test (label, "/a", AA_DCONF_WRITE, 0, 1);
  test (label, "/a", AA_DCONF_READWRITE, 0, 1);
  test (label, "/a/b", AA_DCONF_READ, 0, 1);
  test (label, "/b/c", AA_DCONF_READ, 0, 1);
  test (label, "/b/c/", AA_DCONF_READ, 1, 0);
  test (label, "/b/c/d", AA_DCONF_READ, 1, 0);
  test (label, "/d", AA_DCONF_READ, 1, 0);
  test (label, "/d", AA_DCONF_WRITE, 1, 0);
  test (label, "/d", AA_DCONF_READWRITE, 1, 0);
  test (label, "/e/f", AA_DCONF_READWRITE, 0, 1);
  test (label, "/e/f/", AA_DCONF_READWRITE, 1, 0);
  test (label, "/e/f/g", AA_DCONF_READWRITE, 1, 0);
  test (label, "/g", AA_DCONF_READ, 1, 1);
  test (label, "/g/h", AA_DCONF_READ, 0, 1);
  test (label, "/h/i", AA_DCONF_READ, 0, 1);
  test (label, "/h/i/", AA_DCONF_READ, 1, 1);
  test (label, "/h/i/j", AA_DCONF_READ, 1, 1);
  test (label, "/j", AA_DCONF_READ, 1, 1);
  test (label, "/j/k", AA_DCONF_READ, 0, 1);
  test (label, "/k/l/m", AA_DCONF_READ, 1, 1);
  test (label, "/k/l/m", AA_DCONF_WRITE, 1, 1);
  test (label, "/k/l/m", AA_DCONF_READWRITE, 1, 1);

  free (label);

  return 0;
}

Attachment: 0001-UBUNTU-SAUCE-apparmor-add-data-query-support.patch.sig
Description: PGP signature

Attachment: 0001-Split-aa_query_label-into-a-base-aa_query_cmd-and-it.patch.sig
Description: PGP signature

Attachment: 0002-Add-base-function-to-query-generic-label-data-under-.patch.sig
Description: PGP signature

Attachment: 0003-Make-some-parameters-of-parser-interface-constant.patch.sig
Description: PGP signature

Attachment: 0004-Add-support-for-dconf-confinement.patch.sig
Description: PGP signature

Attachment: dconf-test.profile.sig
Description: PGP signature

Attachment: dconf-test.c.sig
Description: PGP signature

-- 
AppArmor mailing list
[email protected]
Modify settings or unsubscribe at: 
https://lists.ubuntu.com/mailman/listinfo/apparmor

Reply via email to