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

Hi,

Currently, there's no way in Apparmor to sandbox applications from
accessing any setting in a user's dconf database other than preventing
access altogether. We want to add a new rule to the policy format to
permit this. Here's the proposed syntax:

[audit] dconf <dconf-path> [r|rw],

We need to make some small changes to the Ubuntu kernel for this due
to the internal workings of dconf. When the application starts, dconf
needs to know on the full list of readable/read-writable paths, so the
aa_query_label() function and its kernel implementation don't quite
fulfil this need.

I'm proposing for review the attached kernel patch. There is also the
corresponding Launchpad Apparmor branch at
https://code.launchpad.net/~attente/apparmor/dconf-rules-3, which
currently works with the patch, but is still a WIP (missing docs, and
we're considering adding label query support as well).

Thanks,
Will
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v2

iQEcBAEBCAAGBQJVZf1IAAoJEGaNijJ4Mbw+aUoH/jKYMHLCIcFCLpU3T0ZkqhtH
xQJHetSx0z0tHrv4ZRcCXpqG6MtuAHEiKZojAMpwFdyr5NEDhJ0zMDeLQAPjWp7N
6EJXyAwHkDQxpihNhEQ2OpjyO5zp8xPAORZVhOdElit370eavFbRI+HnihB+EHm1
v7bC/pOIQuFa+us0xh2QtUo00v0sWYN9PG7LXIGoY/5be4gpMEyxDvSde9f0lS8M
Za6f1kjpoHT5Az8Z6tMSQNGRv+reHaZ4bDV7RM1Ywa8QAVT5y+UnRqOCWz73N0fW
i9ztw10dOWamDxQAYilrzYAwVwaZjpJFYjRCrIs5ceJwrwVC7KC0mxBzNYgW0Wk=
=2VxM
-----END PGP SIGNATURE-----
From 13e403b84e861456caf2f7046acbf749dd34a105 Mon Sep 17 00:00:00 2001
From: William Hua <[email protected]>
Date: Fri, 22 May 2015 19:38:04 -0500
Subject: [PATCH] apparmor: add dconf query support

---
 security/apparmor/apparmorfs.c       | 80 ++++++++++++++++++++++++++++++++++--
 security/apparmor/include/apparmor.h |  3 +-
 security/apparmor/include/dconf.h    | 29 +++++++++++++
 security/apparmor/include/label.h    |  2 +-
 security/apparmor/include/policy.h   |  6 ++-
 security/apparmor/label.c            |  4 +-
 security/apparmor/lib.c              | 11 ++---
 security/apparmor/policy.c           | 22 ++++++++--
 security/apparmor/policy_unpack.c    | 31 ++++++++++++++
 9 files changed, 171 insertions(+), 17 deletions(-)
 create mode 100644 security/apparmor/include/dconf.h

diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c
index f86f56e..953bed9 100644
--- a/security/apparmor/apparmorfs.c
+++ b/security/apparmor/apparmorfs.c
@@ -270,10 +270,76 @@ static ssize_t query_label(char *buf, size_t buf_len,
 		      perms.allow, perms.deny, perms.audit, perms.quiet);
 }
 
+static ssize_t query_dconf(char *buf, size_t buf_len, const char *query)
+{
+	struct aa_label *label;
+	struct label_it i;
+	struct aa_profile *profile;
+	size_t n = 0, rn = 0, rwn = 0, arn = 0, arwn = 0, size = 0, j;
+
+	label = aa_label_parse(aa_current_label(), query, GFP_KERNEL, false);
+	if (IS_ERR(label))
+		return PTR_ERR(label);
+
+	label_for_each_confined(i, label, profile) {
+		rn += profile->dconf.r.n;
+		rwn += profile->dconf.rw.n;
+		arn += profile->dconf.ar.n;
+		arwn += profile->dconf.arw.n;
+	}
+
+	n = scnprintf(buf, buf_len, "%zu %zu %zu %zu\n", rn, rwn, arn, arwn);
+	buf += n;
+	buf_len -= n;
+	size += n;
+
+	label_for_each_confined(i, label, profile) {
+		for (j = 0; j < profile->dconf.r.n; j++) {
+			n = scnprintf(buf, buf_len, "%s\n", profile->dconf.r.p[j]);
+			buf += n;
+			buf_len -= n;
+			size += n;
+		}
+	}
+
+	label_for_each_confined(i, label, profile) {
+		for (j = 0; j < profile->dconf.rw.n; j++) {
+			n = scnprintf(buf, buf_len, "%s\n", profile->dconf.rw.p[j]);
+			buf += n;
+			buf_len -= n;
+			size += n;
+		}
+	}
+
+	label_for_each_confined(i, label, profile) {
+		for (j = 0; j < profile->dconf.ar.n; j++) {
+			n = scnprintf(buf, buf_len, "%s\n", profile->dconf.ar.p[j]);
+			buf += n;
+			buf_len -= n;
+			size += n;
+		}
+	}
+
+	label_for_each_confined(i, label, profile) {
+		for (j = 0; j < profile->dconf.arw.n; j++) {
+			n = scnprintf(buf, buf_len, "%s\n", profile->dconf.arw.p[j]);
+			buf += n;
+			buf_len -= n;
+			size += n;
+		}
+	}
+
+	aa_put_label(label);
+
+	return size;
+}
+
 #define QUERY_CMD_LABEL		"label\0"
 #define QUERY_CMD_LABEL_LEN	6
 #define QUERY_CMD_PROFILE	"profile\0"
 #define QUERY_CMD_PROFILE_LEN	8
+#define QUERY_CMD_DCONF		"dconf\0"
+#define QUERY_CMD_DCONF_LEN	6
 
 /**
  * aa_write_access - generic permissions query
@@ -283,9 +349,13 @@ static ssize_t query_label(char *buf, size_t buf_len,
  * @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.
+ * The only queries currently supported are label queries and dconf queries.
+ *
+ * For the label query, ubuf must begin with "label\0", followed by the profile
+ * query specific format described in the query_label() function documentation.
+ *
+ * For the dconf query, ubuf must begin with "dconf\0", followed by the
+ * security context as obtained from aa_gettaskcon().
  *
  * Returns: number of bytes written or -errno on failure
  */
@@ -312,6 +382,10 @@ 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_DCONF_LEN &&
+		   !memcmp(buf, QUERY_CMD_DCONF, QUERY_CMD_DCONF_LEN)) {
+		len = query_dconf(buf, SIMPLE_TRANSACTION_LIMIT,
+				  buf + QUERY_CMD_DCONF_LEN);
 	} else
 		len = -EINVAL;
 
diff --git a/security/apparmor/include/apparmor.h b/security/apparmor/include/apparmor.h
index 7d2f457..ebf9710 100644
--- a/security/apparmor/include/apparmor.h
+++ b/security/apparmor/include/apparmor.h
@@ -87,7 +87,8 @@ extern int apparmor_initialized __initdata;
 
 /* fn's in lib */
 char *aa_split_fqname(char *args, char **ns_name);
-char *aa_splitn_fqname(char *fqname, size_t n, char **ns_name, size_t *ns_len);
+const char *aa_splitn_fqname(const char *fqname, size_t n,
+			     const char **ns_name, size_t *ns_len);
 void aa_info_message(const char *str);
 void *__aa_kvmalloc(size_t size, gfp_t flags);
 
diff --git a/security/apparmor/include/dconf.h b/security/apparmor/include/dconf.h
new file mode 100644
index 0000000..42e82f9
--- /dev/null
+++ b/security/apparmor/include/dconf.h
@@ -0,0 +1,29 @@
+/*
+ * AppArmor security module
+ *
+ * This file contains AppArmor policy definitions.
+ *
+ * Copyright 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.
+ */
+
+#ifndef __AA_DCONF_H
+#define __AA_DCONF_H
+
+struct aa_paths {
+	size_t n;
+	char **p;
+};
+
+struct aa_dconf {
+	struct aa_paths r;
+	struct aa_paths rw;
+	struct aa_paths ar;
+	struct aa_paths arw;
+};
+
+#endif /* __AA_DCONF_H */
diff --git a/security/apparmor/include/label.h b/security/apparmor/include/label.h
index 071eabe..9c30889 100644
--- a/security/apparmor/include/label.h
+++ b/security/apparmor/include/label.h
@@ -332,7 +332,7 @@ void aa_label_seq_print(struct seq_file *f, struct aa_namespace *ns,
 			struct aa_label *label, bool mode, gfp_t gfp);
 void aa_label_printk(struct aa_namespace *ns, struct aa_label *label,
 		     bool mode, gfp_t gfp);
-struct aa_label *aa_label_parse(struct aa_label *base, char *str,
+struct aa_label *aa_label_parse(struct aa_label *base, const char *str,
 				gfp_t gfp, bool create);
 
 static inline struct aa_label *aa_get_label(struct aa_label *l)
diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h
index da71e27f..9e2aefb 100644
--- a/security/apparmor/include/policy.h
+++ b/security/apparmor/include/policy.h
@@ -30,6 +30,7 @@
 #include "label.h"
 #include "net.h"
 #include "resource.h"
+#include "dconf.h"
 
 extern const char *aa_hidden_ns_name;
 extern const char *const aa_profile_mode_names[];
@@ -201,6 +202,7 @@ struct aa_profile {
 	struct aa_caps caps;
 	struct aa_net net;
 	struct aa_rlimit rlimits;
+	struct aa_dconf dconf;
 
 	unsigned char *hash;
 	char *dirname;
@@ -235,8 +237,8 @@ struct aa_profile *aa_find_child(struct aa_profile *parent, const char *name);
 struct aa_profile *aa_lookupn_profile(struct aa_namespace *ns,
 				      const char *hname, size_t n);
 struct aa_profile *aa_lookup_profile(struct aa_namespace *ns, const char *name);
-struct aa_profile *aa_fqlookupn_profile(struct aa_label *base, char *fqname,
-					size_t n);
+struct aa_profile *aa_fqlookupn_profile(struct aa_label *base,
+					const char *fqname, size_t n);
 struct aa_profile *aa_match_profile(struct aa_namespace *ns, const char *name);
 
 ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace);
diff --git a/security/apparmor/label.c b/security/apparmor/label.c
index 311423e..c65af89 100644
--- a/security/apparmor/label.c
+++ b/security/apparmor/label.c
@@ -1589,8 +1589,8 @@ static int label_count_str_entries(const char *str)
  * Returns: the matching refcounted label if present
  *     else ERRPTR
  */
-struct aa_label *aa_label_parse(struct aa_label *base, char *str, gfp_t gfp,
-				bool create)
+struct aa_label *aa_label_parse(struct aa_label *base, const char *str,
+				gfp_t gfp, bool create)
 {
 	DEFINE_PROFILE_VEC(vec, tmp);
 	struct aa_label *l;
diff --git a/security/apparmor/lib.c b/security/apparmor/lib.c
index 1f810ef..ce4e16a 100644
--- a/security/apparmor/lib.c
+++ b/security/apparmor/lib.c
@@ -70,19 +70,20 @@ char *aa_split_fqname(char *fqname, char **ns_name)
  * if all whitespace will return NULL
  */
 
-static char *skipn_spaces(const char *str, size_t n)
+static const char *skipn_spaces(const char *str, size_t n)
 {
 	for (;n && isspace(*str); --n)
 		++str;
 	if (n)
-		return (char *)str;
+		return str;
 	return NULL;
 }
 
-char *aa_splitn_fqname(char *fqname, size_t n, char **ns_name, size_t *ns_len)
+const char *aa_splitn_fqname(const char *fqname, size_t n,
+		             const char **ns_name, size_t *ns_len)
 {
-	char *end = fqname + n;
-	char *name = skipn_spaces(fqname, n);
+	const char *end = fqname + n;
+	const char *name = skipn_spaces(fqname, n);
 	if (!name)
 		return NULL;
 	*ns_name = NULL;
diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c
index 7a9d4c8..25a2f0f 100644
--- a/security/apparmor/policy.c
+++ b/security/apparmor/policy.c
@@ -630,6 +630,8 @@ void __init aa_free_root_ns(void)
  */
 void aa_free_profile(struct aa_profile *profile)
 {
+	size_t i;
+
 	AA_DEBUG("%s(%p)\n", __func__, profile);
 
 	if (!profile)
@@ -651,6 +653,20 @@ void aa_free_profile(struct aa_profile *profile)
 	aa_put_dfa(profile->xmatch);
 	aa_put_dfa(profile->policy.dfa);
 
+	for (i = 0; i < profile->dconf.arw.n; i++)
+		kzfree(profile->dconf.arw.p[i]);
+	for (i = 0; i < profile->dconf.ar.n; i++)
+		kzfree(profile->dconf.ar.p[i]);
+	for (i = 0; i < profile->dconf.rw.n; i++)
+		kzfree(profile->dconf.rw.p[i]);
+	for (i = 0; i < profile->dconf.r.n; i++)
+		kzfree(profile->dconf.r.p[i]);
+
+	kzfree(profile->dconf.arw.p);
+	kzfree(profile->dconf.ar.p);
+	kzfree(profile->dconf.rw.p);
+	kzfree(profile->dconf.r.p);
+
 	kzfree(profile->hash);
 	kzfree(profile);
 }
@@ -929,12 +945,12 @@ struct aa_profile *aa_lookup_profile(struct aa_namespace *ns, const char *hname)
 	return aa_lookupn_profile(ns, hname, strlen(hname));
 }
 
-struct aa_profile *aa_fqlookupn_profile(struct aa_label *base, char *fqname,
-					size_t n)
+struct aa_profile *aa_fqlookupn_profile(struct aa_label *base,
+					const char *fqname, size_t n)
 {
 	struct aa_profile *profile;
 	struct aa_namespace *ns;
-	char *name, *ns_name;
+	const char *name, *ns_name;
 	size_t ns_len;
 
 	name = aa_splitn_fqname(fqname, n, &ns_name, &ns_len);
diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c
index 7f63b67..4af1f05 100644
--- a/security/apparmor/policy_unpack.c
+++ b/security/apparmor/policy_unpack.c
@@ -484,6 +484,24 @@ fail:
 	return 0;
 }
 
+static bool unpack_paths(struct aa_ext *e, struct aa_paths *paths, const char *name)
+{
+	size_t i;
+
+	paths->n = unpack_array(e, name);
+	paths->p = kzalloc(paths->n * sizeof(*paths->p), GFP_KERNEL);
+
+	for (i = 0; i < paths->n; i++) {
+		if (!unpack_strdup(e, paths->p + i, NULL))
+			return false;
+	}
+
+	if (!unpack_nameX(e, AA_ARRAYEND, NULL))
+		return false;
+
+	return true;
+}
+
 /**
  * unpack_profile - unpack a serialized profile
  * @e: serialized data extent information (NOT NULL)
@@ -668,6 +686,19 @@ static struct aa_profile *unpack_profile(struct aa_ext *e)
 	if (!unpack_trans_table(e, profile))
 		goto fail;
 
+	if (unpack_nameX(e, AA_STRUCT, "dconf")) {
+		if (!unpack_paths(e, &profile->dconf.r, "r"))
+			goto fail;
+		if (!unpack_paths(e, &profile->dconf.rw, "rw"))
+			goto fail;
+		if (!unpack_paths(e, &profile->dconf.ar, "ar"))
+			goto fail;
+		if (!unpack_paths(e, &profile->dconf.arw, "arw"))
+			goto fail;
+		if (!unpack_nameX(e, AA_STRUCTEND, NULL))
+			goto fail;
+	}
+
 	if (!unpack_nameX(e, AA_STRUCTEND, NULL))
 		goto fail;
 
-- 
2.1.4

Attachment: 0001-apparmor-add-dconf-query-support.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