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

Thanks Christian,

I made a minor change in addition to fix the aa_query_dconf function (just to
account for the size of the buffer with the added AA_CLASS_DCONF byte). Also
re-attaching the kernel patch for convenience.



> Some notes about 0004-Add-support-for-dconf-confinement.patch:
> 
>> --- a/parser/parser_lex.l
>> +++ b/parser/parser_lex.l
> 
>> +<DCONF_MODE>{
>> +       r(ead)?                                 { RETURN_TOKEN(TOK_READ); }
>> +       w(rite)?                                { RETURN_TOKEN(TOK_WRITE); }
>> +       (rw|wr)                                 { 
>> RETURN_TOKEN(TOK_READWRITE); }
>> +       ({PATHNAME}|{QPATHNAME})        {
> 
> That's much better than the loooong list of possible keywords in the 
> last round. I still wonder if we really need "read" and "write" or if 
> "r" and "w" would be enough ;-)
> 
> (Yes, I know we allow "read" and "write" at other places, but we don't
> need to repeat that error ;-)

Sure, removed to only allow r and rw perms



>> --- a/parser/parser_yacc.y
>> +++ b/parser/parser_yacc.y
> 
>> +dconf_perm: TOK_READ { $$ = AA_DCONF_READ; }
>> +       | TOK_WRITE { $$ = AA_DCONF_READWRITE; /* writable implies readable 
>> */ }
>> +       | TOK_READWRITE { $$ = AA_DCONF_READWRITE; }
> 
> I still don't like the idea to implicitely grant read permissions if 
> something has write permissions.
> 
> This needs *at least* a very clear note in the documentation (BTW: did
> I overlook the apparmor.d.pod patch?). The more strict and IMHO better
> way would be to error out if only write is allowed in a profile.

Sure, removed the TOK_WRITE perm



> Also, can you please add a parser/tst/simple_tests/dconf/ directory 
> with some example profiles (some with valid, some with invalid syntax)?

Sure, added just a few examples since the syntax is pretty simple
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v2

iQIcBAEBCAAGBQJWl/RVAAoJEBUjayXdEP6B3OsP/35mKq7HJPu483U8H2nHKOVa
69ueRfQgh8gu7BB8SXFh97M8EIcLYihE3E8NurfBfm580vtZl+NSVJuhnSeAPBS3
aO2OD8GIC+UibpcoTuM1/ErwgmQT+p6nKxpdgDgbUuBJEj4ET0XVjF0DquwW93u5
o4H1oX6lb8k4wrszHivCOBvpf/g5+3wy1Y1DogwPBvREKx5MFVIJCS/IvNhoZ0qj
hPYp0snG5a5bF+3npQC/pMjAzTUjuTNSf77P/D1TBMgjUPeBoVRa7B8QnYlGZ+2z
vA1iVuqn3yMnnY+ZVBBazcvSvsUgD3pwYfkah+qe5c4V3spjuTUw0D5sLea/agMd
cTTvrO81DHiAx8pGSbilOJT4dRmKA14KDJojcBToLfWcsTTnMrCWW1nmbn1Zxfx1
wQa33vrMidmmFlDJZY4LAGfrSX495p5fa44H7Knd6Ab8JVHTHLHbqsLQRQSHDpsP
fCYB17WkCNqYO/CdWppbBtK4+KlZE28M7XV6t28s/BzD47svN0LoUexZo1q7MdBJ
oFQNX0n1WmtYOfiGvQwYoL5RsU8ZaJiQ+NI8Vqzx7cSHa4UzROx59IqWcttrWZ+r
31NGg1ho3tDWN6BvSGb+G9kYuDtxN6zEiOs4lJYvtx2kKWQclpq9jBt9epBW3X7h
YJR/qdal1yzIV0eq0pxK
=Ex+2
-----END PGP SIGNATURE-----

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

>From 8378c86b0b9b602d92a78bd1dd354d34a9f6f492 Mon Sep 17 00:00:00 2001
From: William Hua <[email protected]>
Date: Mon, 14 Dec 2015 03:35:54 -0500
Subject: [PATCH] apparmor: add data query support

---
 security/apparmor/apparmorfs.c     | 112 +++++++++++++++++++++++++++++++++++--
 security/apparmor/include/policy.h |  18 +++++-
 security/apparmor/policy.c         |  22 ++++++++
 security/apparmor/policy_unpack.c  |  63 +++++++++++++++++++++
 4 files changed, 209 insertions(+), 6 deletions(-)

diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c
index 8afb5f6..2cd4134 100644
--- a/security/apparmor/apparmorfs.c
+++ b/security/apparmor/apparmorfs.c
@@ -190,6 +190,94 @@ static const struct file_operations aa_fs_profile_remove = {
 };
 
 /**
+ * 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;
+	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 */
+
+	label = aa_label_parse(aa_current_label(), query, GFP_KERNEL, false);
+	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
@@ -274,18 +362,27 @@ static ssize_t query_label(char *buf, size_t buf_len,
 #define QUERY_CMD_LABEL_LEN	6
 #define QUERY_CMD_PROFILE	"profile\0"
 #define QUERY_CMD_PROFILE_LEN	8
+#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
  */
@@ -312,6 +409,11 @@ static ssize_t aa_write_access(struct file *file, const char __user *ubuf,
 		len = query_label(buf, SIMPLE_TRANSACTION_LIMIT,
 				  buf + QUERY_CMD_LABEL_LEN,
 				  count - QUERY_CMD_LABEL_LEN);
+	} 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 da71e27f..f358d0e 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>
@@ -142,6 +143,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
@@ -161,9 +175,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
@@ -205,6 +219,8 @@ struct aa_profile {
 	unsigned char *hash;
 	char *dirname;
 	struct dentry *dents[AAFS_PROF_SIZEOF];
+
+	struct rhashtable *data;
 };
 
 extern struct aa_namespace *root_ns;
diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c
index 7a9d4c8..39f51ef 100644
--- a/security/apparmor/policy.c
+++ b/security/apparmor/policy.c
@@ -617,6 +617,19 @@ void __init aa_free_root_ns(void)
 	 aa_put_namespace(ns);
 }
 
+/**
+ * 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
@@ -630,6 +643,8 @@ void __init aa_free_root_ns(void)
  */
 void aa_free_profile(struct aa_profile *profile)
 {
+	struct rhashtable *rht;
+
 	AA_DEBUG("%s(%p)\n", __func__, profile);
 
 	if (!profile)
@@ -651,6 +666,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 7f63b67..b1a2e51 100644
--- a/security/apparmor/policy_unpack.c
+++ b/security/apparmor/policy_unpack.c
@@ -20,6 +20,7 @@
 #include <asm/unaligned.h>
 #include <linux/ctype.h>
 #include <linux/errno.h>
+#include <linux/jhash.h>
 
 #include "include/apparmor.h"
 #include "include/audit.h"
@@ -484,6 +485,27 @@ 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)
@@ -494,6 +516,9 @@ static struct aa_profile *unpack_profile(struct aa_ext *e)
 {
 	struct aa_profile *profile = NULL;
 	const char *name = NULL;
+	char *key = NULL;
+	struct rhashtable_params params = { 0 };
+	struct aa_data *data;
 	size_t size = 0;
 	int i, error = -EPROTO;
 	kernel_cap_t tmpcap;
@@ -668,6 +693,44 @@ static struct aa_profile *unpack_profile(struct aa_ext *e)
 	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.6.4

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

>From 22a1b5ba783e2d4475751c14afcf7623cd5e46b0 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            | 94 ++++++++++++++++++++-------
 libraries/libapparmor/swig/SWIG/libapparmor.i |  2 +
 4 files changed, 89 insertions(+), 25 deletions(-)

diff --git a/libraries/libapparmor/doc/aa_query_label.pod b/libraries/libapparmor/doc/aa_query_label.pod
index 7531944..4088178 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 aa_query_cmd function setup and does the raw query of the kernel. It is
+the basis of the other query_X functions.
+
 The 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 13a6a8c..61629bc 100644
--- a/libraries/libapparmor/include/sys/apparmor.h
+++ b/libraries/libapparmor/include/sys/apparmor.h
@@ -99,6 +99,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 a3f8efa..4a97f63 100644
--- a/libraries/libapparmor/src/kernel.c
+++ b/libraries/libapparmor/src/kernel.c
@@ -760,30 +760,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;
 	}
@@ -804,7 +796,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) {
@@ -818,10 +810,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;
@@ -928,7 +979,6 @@ int aa_query_link_path_len(const char *label, size_t label_len,
 			   int *allowed, int *audited)
 {
 	autofree char *query = NULL;
-	int rc;
 
 	/* + 1 for null separators */
 	size_t size = AA_QUERY_CMD_LABEL_SIZE + label_len + 1 + target_len +
diff --git a/libraries/libapparmor/swig/SWIG/libapparmor.i b/libraries/libapparmor/swig/SWIG/libapparmor.i
index 69b4cc2..3e1950a 100644
--- a/libraries/libapparmor/swig/SWIG/libapparmor.i
+++ b/libraries/libapparmor/swig/SWIG/libapparmor.i
@@ -55,6 +55,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.7.0.rc3

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

>From 3cee79fc08b6b4dbcc55cfece0503cdb0ff887ee 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

Signed-off-by: John Johansen <[email protected]>
---
 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     |   9 ++
 libraries/libapparmor/swig/SWIG/libapparmor.i |   2 +
 5 files changed, 156 insertions(+)

diff --git a/libraries/libapparmor/doc/aa_query_label.pod b/libraries/libapparmor/doc/aa_query_label.pod
index 4088178..1f66a36 100644
--- a/libraries/libapparmor/doc/aa_query_label.pod
+++ b/libraries/libapparmor/doc/aa_query_label.pod
@@ -35,6 +35,9 @@ B<#include E<lt>sys/apparmor.hE<gt>>
 B<int aa_query_cmd(const char *cmd, size_t cmd_size, char *query,
 		size_t size, char *buffer, size_t bsize);>
 
+B<extern int aa_query_label_data(const char *label, const char *key,
+		char *buffer, size_t bsize);>
+
 B<int aa_query_label(uint32_t mask, char *query, size_t size,
 		int *allowed, int *audited);>
 
@@ -57,6 +60,9 @@ Link with B<-lapparmor> when compiling.
 The aa_query_cmd function setup and does the raw query of the kernel. It is
 the basis of the other query_X functions.
 
+The aa_query_label_data function does a raw query of any extra data
+stored as I<key> in the label.
+
 The 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 61629bc..cad8077 100644
--- a/libraries/libapparmor/include/sys/apparmor.h
+++ b/libraries/libapparmor/include/sys/apparmor.h
@@ -98,6 +98,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);
@@ -115,6 +117,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 4a97f63..e7d9baf 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
@@ -1030,3 +1031,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 98d97ea..48e10fc 100644
--- a/libraries/libapparmor/src/libapparmor.map
+++ b/libraries/libapparmor/src/libapparmor.map
@@ -87,6 +87,15 @@ APPARMOR_2.10 {
         *;
 } APPARMOR_2.9;
 
+APPARMOR_2.11 {
+  global:
+        aa_clear_label_data;
+        aa_query_label_data;
+  local:
+        *;
+} APPARMOR_2.10;
+
+
 PRIVATE {
 	global:
 		_aa_is_blacklisted;
diff --git a/libraries/libapparmor/swig/SWIG/libapparmor.i b/libraries/libapparmor/swig/SWIG/libapparmor.i
index 3e1950a..17a726f 100644
--- a/libraries/libapparmor/swig/SWIG/libapparmor.i
+++ b/libraries/libapparmor/swig/SWIG/libapparmor.i
@@ -70,5 +70,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.7.0.rc3

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

>From 11793e1e2aaebd0a0e29afe63aae8e09e0597872 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.7.0.rc3

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

>From 732d3a217435ae5d5232e30dde1505698d9e7089 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                          |  22 +++-
 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, 869 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 cad8077..18caba1 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,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 {
@@ -129,10 +141,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 e7d9baf..2359109 100644
--- a/libraries/libapparmor/src/kernel.c
+++ b/libraries/libapparmor/src/kernel.c
@@ -1033,6 +1033,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
@@ -1150,3 +1205,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 48e10fc..220ee9d 100644
--- a/libraries/libapparmor/src/libapparmor.map
+++ b/libraries/libapparmor/src/libapparmor.map
@@ -91,6 +91,10 @@ APPARMOR_2.11 {
   global:
         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 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..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 58bd00a..442901e 100644
--- a/parser/parser.h
+++ b/parser/parser.h
@@ -433,6 +433,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 af5dec4..ef826cc 100644
--- a/parser/parser_lex.l
+++ b/parser/parser_lex.l
@@ -227,6 +227,8 @@ BOOL_VARIABLE	$(\{{VARIABLE_NAME}\}|{VARIABLE_NAME})
 
 PATHNAME	(\/|{SET_VARIABLE}{POST_VAR_ID}){ID}*
 QPATHNAME	\"(\/|{SET_VAR_PREFIX})([^\0"]|\\\")*\"
+DPATHCOMP	[^[:space:]/]+
+DPATHNAME	{SLASH}({DPATHCOMP}{SLASH})*{DPATHCOMP}?
 
 OPEN_PAREN 	\(
 CLOSE_PAREN	\)
@@ -250,6 +252,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 +270,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 +508,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); }
 }
@@ -603,6 +615,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 +633,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 +646,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 +673,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 ca7f6f6..55a9e94 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 aa510c6..d126f3c 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>
@@ -138,12 +139,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
@@ -181,6 +184,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"
@@ -197,6 +201,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
@@ -753,6 +762,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,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 5adbbcf..9eed617 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 892f1c5..e491de7 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 \
@@ -207,6 +208,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.7.0.rc3

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

Reply via email to