-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA256 Just made one minor change to make dconf rules more consistent with other rules (parsing permissions after paths).
On 12/14/2015 04:04 AM, William Hua wrote:
> 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
iQEcBAEBCAAGBQJWbu7ZAAoJEGaNijJ4Mbw+OXQH/3mBrwqseHh0+bROwc5K4CUT
ke8NLSRm7W+yhU59XQ4R+9lsIUuqyZCJCsWz2gdDHjrq3wK/AjybIi4WAtnsZ1i1
2pXiZCNfwaBFZceMYwRztDa+jjJkyACzLfvMJ7aqP0qNF1Cq/i4ks1J/uyIGknhO
k0gysuZhRa3fBCaWDgpwLBNL12i1WdvZ6pbJPSBS8fwQdEBjER5Ha+C3Rkxona+Q
K1FrA6j6mq+b6yIBmIhAtp4T7KoZ1zlJrf8HORFgAI3UCqqHQLoX/s3gxjoRH66t
1yA4BuISHcBfKmpw/yjt+kka9N5guoXmpgHfgUN4e4UZSqlrcg49XUtDnLfAi20=
=pCp3
-----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, ¶ms)) + 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 e377ca87b9c85671bb695b92bd0d4c6235f53e2f 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..e84f1c8 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 TOK_ID opt_dconf_perms TOK_END_OF_RULE + { + dconf_rule *ent = new dconf_rule($2, $3); + if (!ent) { + yyerror(_("Memory allocation error.")); + } + $$ = ent; + } + net_perm: TOK_VALUE { if (strcmp($1, "create") == 0) diff --git a/parser/profile.cc b/parser/profile.cc index 7459967..c037c16 100644 --- a/parser/profile.cc +++ b/parser/profile.cc @@ -75,6 +75,11 @@ bool Profile::alloc_net_table() Profile::~Profile() { + for (DataMap::iterator i = data.begin(); i != data.end(); i++) { + delete i->second; + } + data.clear(); + hat_table.clear(); free_cod_entries(entries); diff --git a/parser/profile.h b/parser/profile.h index 5adbbcf..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
0001-apparmor-add-data-query-support.patch.sig
Description: PGP signature
0001-Split-aa_query_label-into-a-base-aa_query_cmd-and-it.patch.sig
Description: PGP signature
0002-Add-base-function-to-query-generic-label-data-under-.patch.sig
Description: PGP signature
0003-Make-some-parameters-of-parser-interface-constant.patch.sig
Description: PGP signature
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
