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

Hello,

Here is another iteration of the patch set, including the kernel patch
from June which went stale due to upstream changes over the past six
months. Please review these and let me know of any revisions required
as soon as possible since the work on the dconf side has already begun
and is currently waiting on us.

Thanks,
Will



On 10/06/2015 03:24 PM, Christian Boltz wrote:
> Hello,
> 
> Am Dienstag, 6. Oktober 2015 schrieb John Johansen:
>> On 10/06/2015 11:05 AM, Christian Boltz wrote:
>>> Am Dienstag, 6. Oktober 2015 schrieb John Johansen:
>>>> diff --git a/parser/Makefile b/parser/Makefile index 
>>>> 1f0db8d..ec54f96 100644 --- a/parser/Makefile +++ 
>>>> b/parser/Makefile
> ...
>>> I know that list is chaotic already (probably for historical 
>>> reasons?), but what about sorting the HDRS files by alphabet? 
>>> (same question for SRCS and maybe some other file lists in the 
>>> Makefile)
>> 
>> yeah we can get to doing something like that, once my make file 
>> patches land.
> 
> Most of them are acked, so feel free to commit those ;-) I'd also 
> accept a *.h wildcard to make maintaining the Makefile easier.
> 
>> This is based on work William did months ago and I am only now 
>> getting a reply out to.
> 
> no problem ;-)
> 
>>>> --- a/parser/tst/equality.sh +++ b/parser/tst/equality.sh
>>>> 
>>>> +verify_binary_equality "dconf read" \ +   "/t { dconf / r, }" 
>>>> \ +        "/t { dconf / read, }" + +verify_binary_equality "dconf 
>>>> write" \ + "/t { dconf / w, }" \ + "/t { dconf / write, }" +
>>>>  +verify_binary_equality "dconf read-write" \ +    "/t { dconf
>>>> / rw, }" \ +       "/t { dconf / wr, }" \ +        "/t { dconf /
>>>> readwrite, }" \ +  "/t { dconf / writeread, }" \ + "/t { dconf
>>>> / read-write, }" \ +       "/t { dconf / write-read, }" \ +        "/t { 
>>>> dconf / read_write, }" \ + "/t { dconf / write_read, }"
> 
> BTW: I'd add another test here: "/t { dconf / r, dconf / w, }"
> 
>>> Seriously?
>>> 
>>> I have to admit that I don't really know dconf, but having 8 
>>> different ways to allow read and write (one letter vs. word, no
>>> separator vs - vs. _) is too much. We don't win anything with
>>> it, but it makes implementation of the parser and the tools
>>> more difficult than needed.
>>> 
>>> IMHO the single-letter syntax we already use in file rules 
>>> ("rw" or "wr") is enough and will save us some headache.
>> 
>> gah, no that was supposed to be cut out, notice in my intro
>> reply that I moved it back to an apparmor style syntax. I must
>> have either missed this block or missed git adding the change
>> back into the patch
> 
> Note that it's not only in the tests. The parsing code 
> (parser_lex.l) also allows "r(ead)?" and "w(rite)?", and maybe I 
> missed another place
> 
> I also just noticed another interesting bit in parser_yacc.y [1]
> 
> +       | TOK_WRITE { $$ = AA_DCONF_READWRITE; /* writable implies 
> readable */ }
> 
> This sounds like surprising behaviour to me - does this really
> make sense?,If yes, this needs to be documented in bold letters or
> - IMHO better - rules with only w permissions should be rejected
> as invalid to enforce that the profile always contains rw
> permissions, not only w.
> 
> 
> Regards,
> 
> Christian Boltz
> 
> [1] I should have read the patch a bit slower before writing the 
> previous mail ;-)
> 
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v2

iQEcBAEBCAAGBQJWboYEAAoJEGaNijJ4Mbw+QL8IAJZn4KlJBiYmsy+NQbNd732h
be2h8oI5kh/OzH/PMaPWaJF0WJKWM78py/pHwby5Jvksptw8cpsjoEV7fl9PfoNZ
RVrJ361YsgrEq0ibtVP9i4HqV+TUOCyrw7XNdJ+aWGO9kFaSSc5pPGyr0qo6otvI
OP99BJatf3THi/Ou6qill4P+KmSMIHSHJrZmtvTHFc3wspKkkmK4wffKFgo/tBN+
gDM1Zn+CCGSqBAlTdzwIu57GPP5FB/zMx4Zn80l+wZ484QrQsktjZnVDJavoQCz3
Wb1he1V8+EQbP20LuQR43rmx1RqA8LN5NYINemsiqpxNs4eRpchvZU1QotSNCHY=
=2ka8
-----END 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

From 5a7b10e3af189e19acdde0d828566deda1e3a67a 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  | 12 +++-
 libraries/libapparmor/include/sys/apparmor.h  |  2 +
 libraries/libapparmor/src/kernel.c            | 94 ++++++++++++++++++++-------
 libraries/libapparmor/swig/SWIG/libapparmor.i |  2 +
 4 files changed, 85 insertions(+), 25 deletions(-)

diff --git a/libraries/libapparmor/doc/aa_query_label.pod b/libraries/libapparmor/doc/aa_query_label.pod
index 3e943a7..900e2d0 100644
--- a/libraries/libapparmor/doc/aa_query_label.pod
+++ b/libraries/libapparmor/doc/aa_query_label.pod
@@ -28,13 +28,16 @@ aa_query_label - query access permission associated with a label
 
 B<#include E<lt>sys/apparmor.hE<gt>>
 
-B<int aa_query_label((uint32_t mask, char *query, size_t size,
+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_label(uint32_t mask, char *query, size_t size,
 		int *allowed, int *audited);>
 
-B<int aa_query_file_path((uint32_t mask, const char *label, size_t label_len,
+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,
+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);>
 
@@ -51,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.6.4

From e199be80d65c57524a83aaec30b5969ca0b12172 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 900e2d0..8717df0 100644
--- a/libraries/libapparmor/doc/aa_query_label.pod
+++ b/libraries/libapparmor/doc/aa_query_label.pod
@@ -31,6 +31,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.6.4

From 82c379157bf2f914312d17b4b83f5518009c7aa5 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.6.4

From 5876c90d610756dec425b947736d85e13512b51d 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                          |  21 +++-
 parser/parser_misc.c                         |   1 +
 parser/parser_yacc.y                         |  35 ++++++
 parser/profile.cc                            |   5 +
 parser/profile.h                             |  16 ++-
 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 ++++++++++
 18 files changed, 805 insertions(+), 12 deletions(-)
 create mode 100644 parser/dconf.cc
 create mode 100644 parser/dconf.h
 create mode 100644 tests/regression/apparmor/query_dconf.c
 create mode 100644 tests/regression/apparmor/query_dconf.sh

diff --git a/libraries/libapparmor/include/sys/apparmor.h b/libraries/libapparmor/include/sys/apparmor.h
index 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..e15ea12 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 */
+	size_t size = AA_QUERY_CMD_LABEL_SIZE + label_len + 1 + path_len;
+	query = malloc(size + 1);
+	if (!query)
+		return -1;
+	memcpy(query + AA_QUERY_CMD_LABEL_SIZE, label, label_len);
+	/* null separator */
+	query[AA_QUERY_CMD_LABEL_SIZE + label_len] = 0;
+	query[AA_QUERY_CMD_LABEL_SIZE + label_len + 1] = AA_CLASS_DCONF;
+	memcpy(query + AA_QUERY_CMD_LABEL_SIZE + label_len + 2, path, path_len);
+	return aa_query_label(mask, query, size , allowed, audited);
+}
+
+/**
+ * aa_query_dconf - query access permissions for a dconf @path
+ * @mask: permission bits to query
+ * @label: apparmor label
+ * @path: file path to query permissions for
+ * @allowed: upon successful return, will be 1 if query is allowed and 0 if not
+ * @audited: upon successful return, will be 1 if query should be audited and 0
+ *           if not
+ *
+ * Returns: 0 on success else -1 and sets errno. If -1 is returned and errno is
+ *          ENOENT, the subject label in the query string is unknown to the
+ *          kernel.
+ */
+int aa_query_dconf(uint32_t mask, const char *label, 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..7b4e6d4
--- /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 << "[^\\000]*";
+	else
+		rule << path;
+
+	if (!prof.policy.rules->add_rule(rule.str().c_str(),
+					 0,
+					 mode & AA_DCONF_READWRITE,
+					 audit ? mode & AA_DCONF_READWRITE : 0,
+					 dfaflags))
+		return RULE_ERROR;
+
+	gen_data(prof);
+
+	return RULE_OK;
+}
+
+
+void dconf_rule::post_process(Profile &prof unused)
+{
+}
diff --git a/parser/dconf.h b/parser/dconf.h
new file mode 100644
index 0000000..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..01f4781 100644
--- a/parser/parser_lex.l
+++ b/parser/parser_lex.l
@@ -250,6 +250,7 @@ LT_EQUAL	<=
 %x RLIMIT_MODE
 %x MOUNT_MODE
 %x DBUS_MODE
+%x DCONF_MODE
 %x SIGNAL_MODE
 %x PTRACE_MODE
 %x UNIX_MODE
@@ -267,7 +268,7 @@ LT_EQUAL	<=
 	}
 %}
 
-<INITIAL,INCLUDE,LIST_VAL_MODE,EXTCOND_MODE,LIST_COND_VAL,LIST_COND_PAREN_VAL,LIST_COND_MODE,EXTCONDLIST_MODE,ASSIGN_MODE,NETWORK_MODE,CHANGE_PROFILE_MODE,RLIMIT_MODE,MOUNT_MODE,DBUS_MODE,SIGNAL_MODE,PTRACE_MODE,UNIX_MODE>{
+<INITIAL,INCLUDE,LIST_VAL_MODE,EXTCOND_MODE,LIST_COND_VAL,LIST_COND_PAREN_VAL,LIST_COND_MODE,EXTCONDLIST_MODE,ASSIGN_MODE,NETWORK_MODE,CHANGE_PROFILE_MODE,RLIMIT_MODE,MOUNT_MODE,DBUS_MODE,DCONF_MODE,SIGNAL_MODE,PTRACE_MODE,UNIX_MODE>{
 	{WS}+	{  DUMP_PREPROCESS; /* Ignoring whitespace */ }
 }
 
@@ -505,6 +506,16 @@ LT_EQUAL	<=
 			}
 }
 
+<DCONF_MODE>{
+	r(ead)?					{ RETURN_TOKEN(TOK_READ); }
+	w(rite)?				{ RETURN_TOKEN(TOK_WRITE); }
+	(rw|wr)					{ RETURN_TOKEN(TOK_READWRITE); }
+	({PATHNAME}|{QPATHNAME})	{
+		yylval.id = processid(yytext, yyleng);
+		RETURN_TOKEN(TOK_ID);
+	}
+}
+
 <MOUNT_MODE>{
 	{ARROW}		{ RETURN_TOKEN(TOK_ARROW); }
 }
@@ -603,6 +614,9 @@ include/{WS}	{
 	case TOK_DBUS:
 		state = DBUS_MODE;
 		break;
+	case TOK_DCONF:
+		state = DCONF_MODE;
+		break;
 	case TOK_SIGNAL:
 		state = SIGNAL_MODE;
 		break;
@@ -618,7 +632,7 @@ include/{WS}	{
 	PUSH_AND_RETURN(state, token);
 }
 
-<INITIAL,NETWORK_MODE,RLIMIT_MODE,CHANGE_PROFILE_MODE,MOUNT_MODE,DBUS_MODE,SIGNAL_MODE,PTRACE_MODE,UNIX_MODE>{
+<INITIAL,NETWORK_MODE,RLIMIT_MODE,CHANGE_PROFILE_MODE,MOUNT_MODE,DBUS_MODE,SIGNAL_MODE,PTRACE_MODE,UNIX_MODE,DCONF_MODE>{
 	{END_OF_RULE}	{
 		if (YY_START != INITIAL)
 			POP_NODUMP();
@@ -631,7 +645,7 @@ include/{WS}	{
 	}
 }
 
-<INITIAL,SUB_ID,SUB_VALUE,LIST_VAL_MODE,EXTCOND_MODE,LIST_COND_VAL,LIST_COND_PAREN_VAL,LIST_COND_MODE,EXTCONDLIST_MODE,ASSIGN_MODE,NETWORK_MODE,CHANGE_PROFILE_MODE,MOUNT_MODE,DBUS_MODE,SIGNAL_MODE,PTRACE_MODE,UNIX_MODE>{
+<INITIAL,SUB_ID,SUB_VALUE,LIST_VAL_MODE,EXTCOND_MODE,LIST_COND_VAL,LIST_COND_PAREN_VAL,LIST_COND_MODE,EXTCONDLIST_MODE,ASSIGN_MODE,NETWORK_MODE,CHANGE_PROFILE_MODE,MOUNT_MODE,DBUS_MODE,DCONF_MODE,SIGNAL_MODE,PTRACE_MODE,UNIX_MODE>{
 	[^\n]	{
 		DUMP_PREPROCESS;
 		/* Something we didn't expect */
@@ -658,6 +672,7 @@ unordered_map<int, string> state_names = {
 	STATE_TABLE_ENT(RLIMIT_MODE),
 	STATE_TABLE_ENT(MOUNT_MODE),
 	STATE_TABLE_ENT(DBUS_MODE),
+	STATE_TABLE_ENT(DCONF_MODE),
 	STATE_TABLE_ENT(SIGNAL_MODE),
 	STATE_TABLE_ENT(PTRACE_MODE),
 	STATE_TABLE_ENT(UNIX_MODE),
diff --git a/parser/parser_misc.c b/parser/parser_misc.c
index 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 d17eab9..d87c019 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
@@ -741,6 +750,13 @@ rules:  rules opt_prefix dbus_rule
 		$$ = $1;
 	}
 
+rules:  rules opt_audit_flag dconf_rule
+	{
+		$3->audit = $2;
+		$1->rule_ents.push_back($3);
+		$$ = $1;
+	}
+
 rules:  rules opt_prefix signal_rule
 	{
 		if ($2.owner)
@@ -1313,6 +1329,25 @@ dbus_rule: TOK_DBUS opt_dbus_perm opt_conds opt_cond_list TOK_END_OF_RULE
 		$$ = ent;
 	}
 
+dconf_perm: TOK_READ { $$ = AA_DCONF_READ; }
+	| TOK_WRITE { $$ = AA_DCONF_READWRITE; /* writable implies readable */ }
+	| TOK_READWRITE { $$ = AA_DCONF_READWRITE; }
+
+dconf_perms: { /* nothing */ $$ = 0; }
+	| dconf_perms dconf_perm { $$ = $1 | $2; }
+
+opt_dconf_perms: { /* nothing */ $$ = AA_DCONF_READWRITE; /* default read-write */ }
+	| dconf_perms dconf_perm { $$ = $1 | $2; }
+
+dconf_rule: TOK_DCONF opt_dconf_perms TOK_ID TOK_END_OF_RULE
+	{
+		dconf_rule *ent = new dconf_rule($3, $2);
+		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..9740322 100644
--- a/parser/profile.h
+++ b/parser/profile.h
@@ -110,6 +110,18 @@ struct dfa_stuff {
 	dfa_stuff(void): rules(NULL), dfa(NULL), size(0) { }
 };
 
+
+class DataEnt {
+public:
+	virtual ~DataEnt() = 0;
+	virtual void serialize(std::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 +155,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/tests/regression/apparmor/Makefile b/tests/regression/apparmor/Makefile
index c0aad62..72a3ebb 100644
--- a/tests/regression/apparmor/Makefile
+++ b/tests/regression/apparmor/Makefile
@@ -93,6 +93,7 @@ SRC=access.c \
     ptrace.c \
     ptrace_helper.c \
     pwrite.c \
+    query_dconf.c \
     query_label.c \
     rename.c \
     readdir.c \
@@ -185,6 +186,7 @@ TESTS=access \
       pivot_root \
       ptrace \
       pwrite \
+      query_dconf \
       query_label \
       regex \
       rename \
diff --git a/tests/regression/apparmor/query_dconf.c b/tests/regression/apparmor/query_dconf.c
new file mode 100644
index 0000000..7fc7290
--- /dev/null
+++ b/tests/regression/apparmor/query_dconf.c
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2015 Canonical, Ltd.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, contact Canonical Ltd.
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <sys/apparmor.h>
+
+int main(int argc, char *argv[])
+{
+	FILE *file = stdin;
+	char *con = NULL;
+	aa_dconf_info info = { 0 };
+	char *line = NULL;
+	const char *path;
+	size_t len = 0;
+	aa_dconf_strv *paths;
+	int i;
+
+	if (aa_getcon(&con, NULL) == -1) {
+		perror("FAIL: aa_getcon()");
+		goto done;
+	}
+
+	if (aa_query_dconf(con, &info)) {
+		perror("FAIL: aa_query_dconf()");
+		goto done;
+	}
+
+	if (info.r.p[info.r.n]) {
+		fprintf(stderr, "FAIL: aa_dconf_info.r.p not NULL-terminated\n");
+		goto done;
+	}
+	if (info.rw.p[info.rw.n]) {
+		fprintf(stderr, "FAIL: aa_dconf_info.rw.p not NULL-terminated\n");
+		goto done;
+	}
+	if (info.ar.p[info.ar.n]) {
+		fprintf(stderr, "FAIL: aa_dconf_info.ar.p not NULL-terminated\n");
+		goto done;
+	}
+	if (info.arw.p[info.arw.n]) {
+		fprintf(stderr, "FAIL: aa_dconf_info.arw.p not NULL-terminated\n");
+		goto done;
+	}
+
+	if (argc >= 2)
+		file = fopen(argv[1], "r");
+
+	while (getline(&line, &len, file) >= 0) {
+		len = strlen(line);
+		if (line[len - 1] == '\n')
+			line[--len] = '\0';
+
+		for (path = line; *path == ' '; path++);
+
+		if (len) {
+			paths = NULL;
+			if (!strncmp(path, "r ", 2)) {
+				paths = &info.r;
+				path += 2;
+			}
+			else if (!strncmp(path, "rw ", 3)) {
+				paths = &info.rw;
+				path += 3;
+			}
+			else if (!strncmp(path, "ar ", 3)) {
+				paths = &info.ar;
+				path += 3;
+			}
+			else if (!strncmp(path, "arw ", 4)) {
+				paths = &info.arw;
+				path += 4;
+			}
+
+			if (path) {
+				for (i = 0; i < paths->n; i++) {
+					if (paths->p[i] && !strcmp(paths->p[i], path)) {
+						paths->p[i] = NULL;
+						break;
+					}
+				}
+
+				if (i == paths->n) {
+					fprintf(stderr, "FAIL: path '%s' not found\n", path);
+					goto done;
+				}
+			}
+		}
+
+		free(line);
+		line = NULL;
+		len = 0;
+	}
+
+	for (i = 0; i < info.r.n; i++) {
+		if (info.r.p[i]) {
+			fprintf(stderr, "FAIL: unmatched r path '%s'\n", info.r.p[i]);
+			goto done;
+		}
+	}
+	for (i = 0; i < info.rw.n; i++) {
+		if (info.rw.p[i]) {
+			fprintf(stderr, "FAIL: unmatched rw path '%s'\n", info.rw.p[i]);
+			goto done;
+		}
+	}
+	for (i = 0; i < info.ar.n; i++) {
+		if (info.ar.p[i]) {
+			fprintf(stderr, "FAIL: unmatched ar path '%s'\n", info.ar.p[i]);
+			goto done;
+		}
+	}
+	for (i = 0; i < info.arw.n; i++) {
+		if (info.arw.p[i]) {
+			fprintf(stderr, "FAIL: unmatched arw path '%s'\n", info.arw.p[i]);
+			goto done;
+		}
+	}
+
+	printf("PASS\n");
+
+done:
+	free(line);
+	if (file != stdin)
+		fclose(file);
+	aa_clear_dconf_info(&info);
+	free(con);
+
+	return errno;
+}
diff --git a/tests/regression/apparmor/query_dconf.sh b/tests/regression/apparmor/query_dconf.sh
new file mode 100644
index 0000000..08875a6
--- /dev/null
+++ b/tests/regression/apparmor/query_dconf.sh
@@ -0,0 +1,76 @@
+#! /bin/bash
+#	Copyright (C) 2015 Canonical, Ltd.
+#
+#	This program is free software; you can redistribute it and/or
+#	modify it under the terms of the GNU General Public License as
+#	published by the Free Software Foundation, version 2 of the
+#	License.
+
+#=NAME query_dconf
+#=DESCRIPTION
+# This test verifies the results returned from aa_query_dconf()
+#=END
+
+pwd=`dirname $0`
+pwd=`cd $pwd ; /bin/pwd`
+
+bin=$pwd
+
+. $bin/prologue.inc
+requires_query_interface
+
+settest query_dconf
+
+# Check read-write, no audit by default
+genprofile --stdin <<EOF
+$test {
+  file,
+  dconf /a1 r,
+  dconf /a2 r,
+  dconf /b/c/ rw,
+  audit dconf /d1 r,
+  audit dconf /d2 r,
+  audit dconf /e/f/ rw,
+}
+EOF
+
+# Gather up the globals ($expect, $label, $perms) and call runchecktest
+# @1: the test description
+# @2: pass or fail
+# @3: the query string
+querytest()
+{
+	local desc=$1
+	local pf=$2
+
+	shift
+	shift
+	runchecktest "$desc" "$pf" <(echo "$*")
+}
+
+querytest "dconf / rw" pass "
+  arw /e/f/
+  ar /d2
+  ar /d1
+  rw /b/c/
+  r /a2
+  r /a1
+"
+
+querytest "dconf / rw" fail "
+  ar /d2
+  ar /d1
+  rw /b/c/
+  r /a2
+  r /a1
+"
+
+querytest "dconf / rw" fail "
+  arw /fail
+  arw /e/f/
+  ar /d2
+  ar /d1
+  rw /b/c/
+  r /a2
+  r /a1
+"
diff --git a/tests/regression/apparmor/query_label.c b/tests/regression/apparmor/query_label.c
index 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.6.4

Attachment: 0001-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

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

Reply via email to