-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA256 Added aa_query_label() support and tests. Haven't changed the kernel patch, but attaching it again for reference.
On 2015-06-05 10:03 AM, William Hua wrote: > Sorry about the delay. Here's a second pass at the original pass, > with fewer memory allocations, and a more general API for > requesting data in general. > > The corresponding apparmor branch is at > https://code.launchpad.net/~attente/apparmor/dconf-rules-4. > > > > On 2015-06-05 09:12 AM, Simon McVittie wrote: >> On 05/06/15 12:13, John Johansen wrote: >>> On 05/29/2015 09:29 AM, Simon McVittie wrote: >>>> Here's a sketch of how [polkit mediation] could look, for >>>> instance: >>>> >>>> audit polkit action=org.freedesktop.udisks2.filesystem-mount, >>>> audit deny polkit \ >>>> action=org.freedesktop.udisks2.filesystem-mount-system, >>>> >>>> or if the syntax in policy files was entirely generic, >>>> perhaps something more like: >>>> >>>> userspace class=polkit \ >>>> action=org.freedesktop.udisks2.filesystem-mount, audit deny >>>> userspace class=polkit \ >>>> action=org.freedesktop.udisks2.filesystem-mount-system, >>>> >>>> Does this sound like a reasonable generalization? >>>> >>> generally speaking, yes :) >>> >>> I can't say when polkit will get patched but I expect it will >>> happen sooner than later. > >> If this becomes something that is concretely required, please >> talk to the polkit mailing list - the polkit developers ought to >> have an opportunity to review this. I've subscribed to that list >> to be able to give D-Bus advice. > >> My colleague Philip Withnall and I are not (currently) polkit >> maintainers, but we would potentially be interested in reviewing >> and/or helping with implementation for this feature. > > -----BEGIN PGP SIGNATURE----- Version: GnuPG v2 iQEcBAEBCAAGBQJVecLQAAoJEGaNijJ4Mbw+wYAH/01DuR2LA8OeU0gjR7DFdwEB SN6+8/Dw95lmIJtwmjZr4ctkzzBDZfmofUzvT8Viy2oQMvk+11t5DQvZhf8LVkla JZgiSHK5ZNO54GPvNBzrHq31P4eQY3q6BO+Ycw5nmZja8st3rWy078c3bxlSrUXe Zt7wDXCeiv8s6jUCiQCLJ9FQVm/R67UrQr3j3S4I4lPbSz/P8z6a56UOFBw/CbF7 GiV5pz3zNOYjCWP6epHzzKZqE/+fkO3VB7xRg31K9TktMpKeKJ+l5WEP2xuHLpPO MhzsOr1BNAyPlgAL8yWKn2dYE7cSIGDC198Jm2ca8XZhXVPffYGEau+J152Sseg= =80FQ -----END PGP SIGNATURE-----
>From 1b566ff10a8dc1d0a5328f200dcbc51e0a67b85a Mon Sep 17 00:00:00 2001 From: William Hua <[email protected]> Date: Mon, 1 Jun 2015 18:33:25 -0400 Subject: [PATCH] apparmor: add data query support --- security/apparmor/apparmorfs.c | 115 +++++++++++++++++++++++++++++++++++-- security/apparmor/include/policy.h | 20 ++++++- security/apparmor/policy.c | 20 +++++++ security/apparmor/policy_unpack.c | 54 ++++++++++++++++- 4 files changed, 202 insertions(+), 7 deletions(-) diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index f86f56e..ab11bd1 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -189,6 +189,97 @@ static const struct file_operations aa_fs_profile_remove = { .llseek = default_llseek, }; +static bool keycmp(void *p1, void *p2) { + const char **k1 = p1; + const char **k2 = p2; + return !strcmp(*k1, *k2); +} + +/** + * 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; + ssize_t blocks = 0; + ssize_t bytes; + u32 hash; + + 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 < 2 * sizeof(ssize_t)) + 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, 2 * sizeof(ssize_t)); + out = buf + 2 * sizeof(ssize_t); + + label_for_each_confined(i, label, profile) { + if (!profile->data) + continue; + + hash = rhashtable_hashfn(profile->data, &key, sizeof(key)); + data = rhashtable_lookup_compare(profile->data, hash, keycmp, &key); + + if (data) { + if (out + sizeof(ssize_t) + data->size > buf + buf_len) { + aa_put_label(label); + return -EINVAL; /* not enough space */ + } + memcpy(out, &data->size, sizeof(ssize_t)); + out += sizeof(ssize_t); + memcpy(out, data->data, data->size); + out += data->size; + blocks++; + } + } + aa_put_label(label); + + bytes = out - buf - sizeof(ssize_t); + memcpy(buf, &bytes, sizeof(ssize_t)); + memcpy(buf + sizeof(ssize_t), &blocks, sizeof(ssize_t)); + + return out - buf; +} + /** * query_label - queries a label and writes permissions to buf * @buf: the resulting permissions string is stored here (NOT NULL) @@ -274,18 +365,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 +412,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..48e90a7 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 { + const 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,10 @@ 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 + * @blob: a single binary string holding the allocated data + * @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 +220,9 @@ struct aa_profile { unsigned char *hash; char *dirname; struct dentry *dents[AAFS_PROF_SIZEOF]; + + void *blob; + struct rhashtable *data; }; extern struct aa_namespace *root_ns; diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index 7a9d4c8..0d5c477 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -630,6 +630,11 @@ void __init aa_free_root_ns(void) */ void aa_free_profile(struct aa_profile *profile) { + struct rhashtable *rht; + struct aa_data *data; + struct aa_data *next; + size_t i; + AA_DEBUG("%s(%p)\n", __func__, profile); if (!profile) @@ -651,6 +656,21 @@ 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; + for (i = 0; i < rht->tbl->size; i++) { + rht_for_each_entry_safe(data, next, rht->tbl->buckets[i], rht, head) { + kzfree(data); + } + } + rhashtable_destroy(rht); + kzfree(rht); + } + + if (profile->blob) + kzfree(profile->blob); + kzfree(profile->hash); kzfree(profile); } diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index 7f63b67..185bbe0 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,12 @@ fail: return 0; } +static u32 shash(const void *data, u32 len, u32 seed) +{ + const char * const *key = data; + return jhash(*key, strlen(*key), seed); +} + /** * unpack_profile - unpack a serialized profile * @e: serialized data extent information (NOT NULL) @@ -494,6 +501,11 @@ static struct aa_profile *unpack_profile(struct aa_ext *e) { struct aa_profile *profile = NULL; const char *name = NULL; + const char *key = NULL; + char *blob = NULL; + struct rhashtable_params params = { 0 }; + struct aa_ext f; + struct aa_data *data; size_t size = 0; int i, error = -EPROTO; kernel_cap_t tmpcap; @@ -668,14 +680,54 @@ static struct aa_profile *unpack_profile(struct aa_ext *e) if (!unpack_trans_table(e, profile)) goto fail; + size = unpack_blob(e, &blob, "data"); + if (size) { + profile->blob = kmemdup(blob, size, GFP_KERNEL); + if (!profile->blob) + goto fail; + + profile->data = kzalloc(sizeof(*profile->data), GFP_KERNEL); + if (!profile->data) + goto fail; + + params.nelem_hint = 1; + params.key_len = sizeof(void *); + params.key_offset = offsetof(struct aa_data, key); + params.head_offset = offsetof(struct aa_data, head); + params.hashfn = shash; + + if (rhashtable_init(profile->data, ¶ms)) + goto fail; + + f.start = profile->blob; + f.end = f.start + size; + f.pos = f.start; + f.version = e->version; + + while (unpack_str(&f, &key, NULL)) { + data = kzalloc(sizeof(*data), GFP_KERNEL); + data->key = key; + data->size = unpack_blob(&f, &data->data, NULL); + if (!data->size) { + kzfree(data); + goto fail; + } + data->head.next = NULL; + rhashtable_insert(profile->data, &data->head); + } + } + if (!unpack_nameX(e, AA_STRUCTEND, NULL)) goto fail; return profile; fail: - if (profile) + /* don't worry about profile->data, aa_free_profile should free it */ + if (profile) { + kzfree(profile->blob); name = NULL; + } else if (!name) name = "unknown"; audit_iface(profile, name, "failed to unpack profile", e, error); -- 2.1.4
=== modified file 'libraries/libapparmor/include/sys/apparmor.h'
--- libraries/libapparmor/include/sys/apparmor.h 2015-05-20 02:28:47 +0000
+++ libraries/libapparmor/include/sys/apparmor.h 2015-06-03 20:08:21 +0000
@@ -29,6 +29,7 @@
*/
#define AA_CLASS_DBUS 32
+#define AA_CLASS_DCONF 33
/* Permission flags for the AA_CLASS_DBUS mediation class */
@@ -40,6 +41,12 @@
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);
@@ -81,6 +88,22 @@
extern int aa_query_label(uint32_t mask, char *query, size_t size, int *allow,
int *audit);
+typedef struct {
+ size_t n; /* length of p without NULL terminator */
+ const char **p; /* NULL-terminated, free p but not p[*] */
+} aa_dconf_strv;
+
+typedef struct {
+ char *d; /* free d */
+ aa_dconf_strv r; /* read-only */
+ aa_dconf_strv rw; /* read-write */
+ aa_dconf_strv ar; /* audit read-only */
+ aa_dconf_strv arw; /* audit read-write */
+} aa_dconf_info;
+
+extern int aa_query_dconf(const char *con, aa_dconf_info *out);
+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)
#define __macroarg_count2(_,x0,x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12,x13,x14,x15,n,Y...) n
=== modified file 'libraries/libapparmor/src/kernel.c'
--- libraries/libapparmor/src/kernel.c 2015-05-20 02:31:53 +0000
+++ libraries/libapparmor/src/kernel.c 2015-06-10 15:58:42 +0000
@@ -46,6 +46,9 @@
#define UNCONFINED "unconfined"
#define UNCONFINED_SIZE strlen(UNCONFINED)
+#define AA_QUERY_CMD_DATA "data"
+#define AA_QUERY_CMD_DCONF "dconf"
+
/**
* aa_find_mountpoint - find where the apparmor interface filesystem is mounted
* @mnt: returns buffer with the mountpoint string
@@ -849,3 +852,220 @@
extern typeof((query_label)) __aa_query_label __attribute__((alias ("query_label")));
symbol_version(__aa_query_label, aa_query_label, APPARMOR_1.1);
default_symbol_version(query_label, aa_query_label, APPARMOR_2.9);
+
+typedef struct {
+ size_t n; /* length of s */
+ const char *s; /* not NULL-terminated */
+} aa_data_str;
+
+typedef struct {
+ char *d; /* free d */
+ size_t n; /* length of p */
+ aa_data_str *p; /* free p */
+} aa_data_info;
+
+static void aa_clear_data_info(aa_data_info *info)
+{
+ free(info->p);
+ free(info->d);
+
+ memset(info, 0, sizeof(*info));
+}
+
+static int aa_query_data(const char *con, const char *key, aa_data_info *out)
+{
+ FILE *file = NULL;
+ char *label = NULL;
+ const char *p;
+ ssize_t bytes;
+ int ret;
+
+ if (!out)
+ return 0;
+
+ memset(out, 0, sizeof(*out));
+
+ if (!con) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ ret = pthread_once(&aafs_access_control, aafs_access_init_once);
+ if (ret) {
+ errno = EINVAL;
+ return -1;
+ } else if (!aafs_access) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ file = fopen(aafs_access, "r+");
+ if (!file) {
+ if (errno == ENOENT)
+ errno = EPROTONOSUPPORT;
+ return -1;
+ }
+
+ label = strdup(con);
+ if (!label) {
+ errno = ENOMEM;
+ goto done;
+ }
+
+ aa_splitcon(label, NULL);
+
+ if (fprintf(file, "%s%c%s%c%s%c", AA_QUERY_CMD_DATA, 0, label, 0, key, 0) < 0) {
+ errno = EPROTO;
+ goto done;
+ }
+
+ if (fread(&bytes, sizeof(bytes), 1, file) != 1 || bytes < 0) {
+ errno = EPROTO;
+ goto done;
+ }
+
+ out->d = malloc(bytes);
+ if (!out->d) {
+ errno = ENOMEM;
+ goto done;
+ }
+
+ if (fread(out->d, sizeof(char), bytes, file) != bytes) {
+ errno = EPROTO;
+ goto done;
+ }
+
+ p = out->d;
+ memcpy(&out->n, out->d, sizeof(out->n));
+ if (out->n < 0) {
+ errno = EPROTO;
+ goto done;
+ }
+ p += sizeof(out->n);
+
+ out->p = malloc(out->n * sizeof(*out->p));
+ if (!out->p) {
+ errno = ENOMEM;
+ goto done;
+ }
+
+ for (int i = 0; i < out->n; i++) {
+ memcpy(&out->p[i].n, p, sizeof(out->p[i].n));
+ if (out->p[i].n < 0) {
+ errno = EPROTO;
+ goto done;
+ }
+ p += sizeof(out->p[i].n);
+ out->p[i].s = p;
+ p += out->p[i].n;
+ }
+
+done:
+ if (errno)
+ aa_clear_data_info(out);
+
+ free(label);
+ fclose(file);
+
+ return errno ? -1 : 0;
+}
+
+/**
+ * aa_query_dconf - query the dconf paths of a label
+ * @con: security confinement context
+ * @out: destination for dconf strings
+ *
+ * Returns: 0 on success else -1 and sets errno.
+ */
+int aa_query_dconf(const char *con, aa_dconf_info *out)
+{
+ aa_data_info info;
+ size_t n[4];
+
+ if (!out)
+ return 0;
+
+ memset(out, 0, sizeof(*out));
+
+ if (aa_query_data(con, AA_QUERY_CMD_DCONF, &info))
+ return -1;
+
+ /* steal the data */
+ out->d = info.d;
+ info.d = NULL;
+
+ /* count the strings */
+ for (int i = 0; i < info.n; i++) {
+ memcpy(n, info.p[i].s, sizeof(n));
+ out->r.n += n[0];
+ out->rw.n += n[1];
+ out->ar.n += n[2];
+ out->arw.n += n[3];
+ }
+
+ out->r.p = malloc((out->r.n + 1) * sizeof(*out->r.p));
+ out->rw.p = malloc((out->rw.n + 1) * sizeof(*out->rw.p));
+ out->ar.p = malloc((out->ar.n + 1) * sizeof(*out->ar.p));
+ out->arw.p = malloc((out->arw.n + 1) * sizeof(*out->arw.p));
+
+ if (!out->r.p || !out->rw.p || !out->ar.p || !out->arw.p) {
+ errno = ENOMEM;
+ goto done;
+ }
+
+ out->r.p[out->r.n] = NULL;
+ out->rw.p[out->rw.n] = NULL;
+ out->ar.p[out->ar.n] = NULL;
+ out->arw.p[out->arw.n] = NULL;
+
+ memset(n, 0, sizeof(n));
+
+ for (int i = 0; i < info.n; i++) {
+ const char *p = info.p[i].s + sizeof(n);
+
+ while (n[0] < out->r.n) {
+ out->r.p[n[0]++] = p;
+ p += strlen(p) + 1;
+ }
+ while (n[1] < out->rw.n) {
+ out->rw.p[n[1]++] = p;
+ p += strlen(p) + 1;
+ }
+ while (n[2] < out->ar.n) {
+ out->ar.p[n[2]++] = p;
+ p += strlen(p) + 1;
+ }
+ while (n[3] < out->arw.n) {
+ out->arw.p[n[3]++] = p;
+ p += strlen(p) + 1;
+ }
+ }
+
+done:
+ if (errno)
+ aa_clear_dconf_info(out);
+
+ aa_clear_data_info(&info);
+
+ return errno ? -1 : 0;
+}
+
+/**
+ * aa_clear_dconf_info - release memory associated with aa_query_dconf
+ * @info: structure to clear
+ *
+ * aa_clear_dconf_info() does not release the aa_dconf_info structure passed
+ * in. Does nothing if info is NULL.
+ */
+void aa_clear_dconf_info(aa_dconf_info *info)
+{
+ if (info) {
+ free(info->arw.p);
+ free(info->ar.p);
+ free(info->rw.p);
+ free(info->r.p);
+ free(info->d);
+
+ memset(info, 0, sizeof(*info));
+ }
+}
=== modified file 'libraries/libapparmor/src/libapparmor.map'
--- libraries/libapparmor/src/libapparmor.map 2015-05-20 02:28:47 +0000
+++ libraries/libapparmor/src/libapparmor.map 2015-06-03 20:09:07 +0000
@@ -80,6 +80,8 @@
aa_policy_cache_create;
aa_policy_cache_remove;
aa_policy_cache_replace_all;
+ aa_query_dconf;
+ aa_clear_dconf_info;
aa_splitcon;
local:
*;
=== modified file 'parser/Makefile'
--- parser/Makefile 2015-05-03 11:42:39 +0000
+++ parser/Makefile 2015-06-11 16:48:18 +0000
@@ -74,11 +74,11 @@
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 @@
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 @@
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 $@ $<
=== added file 'parser/dconf.cc'
--- parser/dconf.cc 1970-01-01 00:00:00 +0000
+++ parser/dconf.cc 2015-06-11 15:53:10 +0000
@@ -0,0 +1,105 @@
+/*
+ * 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>
+
+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 " << path;
+
+ switch (mode & AA_DCONF_READWRITE) {
+ case AA_DCONF_READ:
+ os << " read";
+ break;
+ case AA_DCONF_WRITE:
+ case AA_DCONF_READWRITE:
+ os << " read write";
+ break;
+ }
+
+ os << ",\n";
+
+ return os;
+}
+
+int dconf_rule::expand_variables(void)
+{
+ return 0;
+}
+
+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;
+
+ switch (mode & AA_DCONF_READWRITE) {
+ case AA_DCONF_READ:
+ if (audit)
+ prof.dconf.ar.push_back(path);
+ else
+ prof.dconf.r.push_back(path);
+ break;
+ case AA_DCONF_READWRITE:
+ if (audit)
+ prof.dconf.arw.push_back(path);
+ else
+ prof.dconf.rw.push_back(path);
+ break;
+ }
+
+ return RULE_OK;
+}
+
+void dconf_rule::post_process(Profile &prof unused)
+{
+}
=== added file 'parser/dconf.h'
--- parser/dconf.h 1970-01-01 00:00:00 +0000
+++ parser/dconf.h 2015-06-02 00:26:17 +0000
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2015
+ * Canonical, Ltd. (All rights reserved)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, contact Novell, Inc. or Canonical
+ * Ltd.
+ */
+
+#ifndef __AA_DCONF_H
+#define __AA_DCONF_H
+
+#include "rule.h"
+
+class dconf_rule: public rule_t {
+public:
+ int audit;
+ char *path;
+ int mode;
+
+ explicit dconf_rule(char *path_p, int mode_p);
+ virtual ~dconf_rule(void);
+
+ virtual std::ostream &dump(std::ostream &os);
+ virtual int expand_variables(void);
+ virtual int gen_policy_re(Profile &prof);
+ virtual void post_process(Profile &prof);
+};
+
+#endif /* __AA_DCONF_H */
=== modified file 'parser/parser_interface.c'
--- parser/parser_interface.c 2015-03-25 22:09:27 +0000
+++ parser/parser_interface.c 2015-06-03 19:58:07 +0000
@@ -250,7 +250,7 @@
}
}
-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 @@
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 @@
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);
}
@@ -373,7 +373,10 @@
void sd_serialize_profile(std::ostringstream &buf, Profile *profile,
int flattened)
{
+ std::ostringstream buf2;
+ std::ostringstream buf3;
uint64_t allowed_caps;
+ size_t size;
sd_write_struct(buf, "profile");
if (flattened) {
@@ -457,6 +460,29 @@
sd_serialize_dfa(buf, profile->dfa.dfa, profile->dfa.size);
sd_serialize_xtable(buf, profile->exec_table);
+ /* write read-only and read-write dconf paths */
+ size = profile->dconf.r.size();
+ buf3.write(reinterpret_cast<const char *>(&size), sizeof(size));
+ size = profile->dconf.rw.size();
+ buf3.write(reinterpret_cast<const char *>(&size), sizeof(size));
+ size = profile->dconf.ar.size();
+ buf3.write(reinterpret_cast<const char *>(&size), sizeof(size));
+ size = profile->dconf.arw.size();
+ buf3.write(reinterpret_cast<const char *>(&size), sizeof(size));
+
+ for (auto i : profile->dconf.r)
+ buf3.write(i.c_str(), strlen(i.c_str()) + 1);
+ for (auto i : profile->dconf.rw)
+ buf3.write(i.c_str(), strlen(i.c_str()) + 1);
+ for (auto i : profile->dconf.ar)
+ buf3.write(i.c_str(), strlen(i.c_str()) + 1);
+ for (auto i : profile->dconf.arw)
+ buf3.write(i.c_str(), strlen(i.c_str()) + 1);
+
+ sd_write_string(buf2, "dconf", NULL);
+ sd_write_blob(buf2, buf3.str().c_str(), buf3.tellp(), NULL);
+ sd_write_blob(buf, buf2.str().c_str(), buf2.tellp(), "data");
+
sd_write_structend(buf);
}
=== modified file 'parser/parser_lex.l'
--- parser/parser_lex.l 2015-03-25 22:09:26 +0000
+++ parser/parser_lex.l 2015-06-02 00:26:17 +0000
@@ -228,6 +228,8 @@
PATHNAME (\/|{SET_VARIABLE}{POST_VAR_ID}){ID}*
QPATHNAME \"(\/|{SET_VAR_PREFIX})([^\0"]|\\\")*\"
+DPATHCOMP [^[:space:]{SLASH}]+
+DPATHNAME {SLASH}({DPATHCOMP}{SLASH})*{DPATHCOMP}?
OPEN_PAREN \(
CLOSE_PAREN \)
@@ -251,6 +253,7 @@
%x RLIMIT_MODE
%x MOUNT_MODE
%x DBUS_MODE
+%x DCONF_MODE
%x SIGNAL_MODE
%x PTRACE_MODE
%x UNIX_MODE
@@ -268,7 +271,7 @@
}
%}
-<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 */ }
}
@@ -506,6 +509,16 @@
}
}
+<DCONF_MODE>{
+ r(ead)? { RETURN_TOKEN(TOK_READ); }
+ w(rite)? { RETURN_TOKEN(TOK_WRITE); }
+ (rw|wr|read[-_]?write|write[-_]?read) { RETURN_TOKEN(TOK_READWRITE); }
+ {DPATHNAME} {
+ yylval.id = processid(yytext, yyleng);
+ RETURN_TOKEN(TOK_ID);
+ }
+}
+
<MOUNT_MODE>{
{ARROW} { RETURN_TOKEN(TOK_ARROW); }
}
@@ -597,6 +610,9 @@
case TOK_DBUS:
state = DBUS_MODE;
break;
+ case TOK_DCONF:
+ state = DCONF_MODE;
+ break;
case TOK_SIGNAL:
state = SIGNAL_MODE;
break;
@@ -612,7 +628,7 @@
PUSH_AND_RETURN(state, token);
}
-<INITIAL,NETWORK_MODE,RLIMIT_MODE,MOUNT_MODE,DBUS_MODE,SIGNAL_MODE,PTRACE_MODE,UNIX_MODE>{
+<INITIAL,NETWORK_MODE,RLIMIT_MODE,MOUNT_MODE,DBUS_MODE,DCONF_MODE,SIGNAL_MODE,PTRACE_MODE,UNIX_MODE>{
{END_OF_RULE} {
if (YY_START != INITIAL)
POP_NODUMP();
@@ -625,7 +641,7 @@
}
}
-<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 */
@@ -652,6 +668,7 @@
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),
=== modified file 'parser/parser_misc.c'
--- parser/parser_misc.c 2015-03-25 22:09:27 +0000
+++ parser/parser_misc.c 2015-06-02 00:26:17 +0000
@@ -100,6 +100,7 @@
{"pivot_root", TOK_PIVOTROOT},
{"in", TOK_IN},
{"dbus", TOK_DBUS},
+ {"dconf", TOK_DCONF},
{"signal", TOK_SIGNAL},
{"send", TOK_SEND},
{"receive", TOK_RECEIVE},
=== modified file 'parser/parser_yacc.y'
--- parser/parser_yacc.y 2015-03-25 22:09:26 +0000
+++ parser/parser_yacc.y 2015-06-11 16:48:18 +0000
@@ -36,6 +36,7 @@
#include "profile.h"
#include "mount.h"
#include "dbus.h"
+#include "dconf.h"
#include "af_unix.h"
#include "parser_include.h"
#include <unistd.h>
@@ -139,12 +140,14 @@
%token TOK_PIVOTROOT
%token TOK_IN
%token TOK_DBUS
+%token TOK_DCONF
%token TOK_SIGNAL
%token TOK_SEND
%token TOK_RECEIVE
%token TOK_BIND
%token TOK_READ
%token TOK_WRITE
+%token TOK_READWRITE
%token TOK_EAVESDROP
%token TOK_PEER
%token TOK_TRACE
@@ -182,6 +185,7 @@
#include "profile.h"
#include "mount.h"
#include "dbus.h"
+ #include "dconf.h"
#include "signal.h"
#include "ptrace.h"
#include "af_unix.h"
@@ -198,6 +202,7 @@
mnt_rule *mnt_entry;
dbus_rule *dbus_entry;
+ dconf_rule *dconf_entry;
signal_rule *signal_entry;
ptrace_rule *ptrace_entry;
unix_rule *unix_entry;
@@ -264,6 +269,10 @@
%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
@@ -737,6 +746,13 @@
$$ = $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)
@@ -1311,6 +1327,25 @@
$$ = 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 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)
=== modified file 'parser/profile.h'
--- parser/profile.h 2015-03-25 22:09:26 +0000
+++ parser/profile.h 2015-06-02 00:26:17 +0000
@@ -15,6 +15,7 @@
#define __AA_PROFILE_H
#include <set>
+#include <list>
#include <string>
#include <iostream>
@@ -143,6 +144,13 @@
struct dfa_stuff dfa;
struct dfa_stuff policy;
+ struct {
+ list<string> r; /* read-only */
+ list<string> rw; /* read-write */
+ list<string> ar; /* audit, read-only */
+ list<string> arw; /* audit, read-write */
+ } dconf;
+
Profile(void)
{
ns = name = attachment = NULL;
=== modified file 'parser/tst/equality.sh'
--- parser/tst/equality.sh 2015-03-23 18:55:48 +0000
+++ parser/tst/equality.sh 2015-06-11 16:53:13 +0000
@@ -265,6 +265,24 @@
peer=(name=org.freedesktop.DBus),
dbus send bus=session, }"
+verify_binary_equality "dconf read" \
+ "/t { dconf / r, }" \
+ "/t { dconf / read, }"
+
+verify_binary_equality "dconf write" \
+ "/t { dconf / w, }" \
+ "/t { dconf / write, }"
+
+verify_binary_equality "dconf read-write" \
+ "/t { dconf / rw, }" \
+ "/t { dconf / wr, }" \
+ "/t { dconf / readwrite, }" \
+ "/t { dconf / writeread, }" \
+ "/t { dconf / read-write, }" \
+ "/t { dconf / write-read, }" \
+ "/t { dconf / read_write, }" \
+ "/t { dconf / write_read, }"
+
# Rules compatible with audit, deny, and audit deny
# note: change_profile does not support audit/allow/deny atm
for rule in "capability" "capability mac_admin" \
=== modified file 'tests/regression/apparmor/Makefile'
--- tests/regression/apparmor/Makefile 2015-03-26 15:19:46 +0000
+++ tests/regression/apparmor/Makefile 2015-06-11 16:48:18 +0000
@@ -93,6 +93,7 @@
ptrace.c \
ptrace_helper.c \
pwrite.c \
+ query_dconf.c \
query_label.c \
rename.c \
readdir.c \
@@ -185,6 +186,7 @@
pivot_root \
ptrace \
pwrite \
+ query_dconf \
query_label \
regex \
rename \
=== added file 'tests/regression/apparmor/query_dconf.c'
--- tests/regression/apparmor/query_dconf.c 1970-01-01 00:00:00 +0000
+++ tests/regression/apparmor/query_dconf.c 2015-06-10 18:50:01 +0000
@@ -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;
+}
=== added file 'tests/regression/apparmor/query_dconf.sh'
--- tests/regression/apparmor/query_dconf.sh 1970-01-01 00:00:00 +0000
+++ tests/regression/apparmor/query_dconf.sh 2015-06-10 19:03:22 +0000
@@ -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
+"
=== modified file 'tests/regression/apparmor/query_label.c'
--- tests/regression/apparmor/query_label.c 2013-09-28 00:33:09 +0000
+++ tests/regression/apparmor/query_label.c 2015-06-11 15:52:26 +0000
@@ -12,6 +12,9 @@
#define OPT_TYPE_DBUS "--dbus="
#define OPT_TYPE_DBUS_LEN strlen(OPT_TYPE_DBUS)
+#define OPT_TYPE_DCONF "--dconf="
+#define OPT_TYPE_DCONF_LEN strlen(OPT_TYPE_DCONF)
+
static char *progname = NULL;
void usage(void)
@@ -26,9 +29,11 @@
fprintf(stderr, " LABEL\t\tThe AppArmor label to use in the query\n");
fprintf(stderr, " CLASS\t\tThe rule class and may consist of:\n");
fprintf(stderr, "\t\t dbus\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 dconf: read,write\n");
fprintf(stderr, "\t\tAdditionaly, PERMS can be empty to indicate an empty mask\n");
exit(1);
}
@@ -83,6 +88,29 @@
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)
{
@@ -149,6 +177,11 @@
rc = parse_dbus_perms(&mask, class_str + OPT_TYPE_DBUS_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();
=== modified file 'tests/regression/apparmor/query_label.sh'
--- tests/regression/apparmor/query_label.sh 2013-09-28 00:33:09 +0000
+++ tests/regression/apparmor/query_label.sh 2015-06-11 16:06:39 +0000
@@ -205,3 +205,61 @@
querytest "QUERY dbus (svc send)" fail $dbus_svc_query
perms dbus receive
querytest "QUERY dbus (svc receive)" fail $dbus_svc_query
+
+# 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"
0001-apparmor-add-data-query-support.patch.sig
Description: PGP signature
apparmor-dconf.patch.sig
Description: PGP signature
-- AppArmor mailing list [email protected] Modify settings or unsubscribe at: https://lists.ubuntu.com/mailman/listinfo/apparmor
