Skip to content

Commit e025be0

Browse files
William Huajrjohansen
authored andcommitted
apparmor: support querying extended trusted helper extra data
Allow a profile to carry extra data that can be queried via userspace. This provides a means to store extra data in a profile that a trusted helper can extract and use from live policy. Signed-off-by: William Hua <[email protected]> Signed-off-by: John Johansen <[email protected]>
1 parent 12eb87d commit e025be0

File tree

5 files changed

+245
-0
lines changed

5 files changed

+245
-0
lines changed

security/apparmor/apparmorfs.c

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,144 @@ static const struct file_operations aa_fs_profile_remove = {
213213
.llseek = default_llseek,
214214
};
215215

216+
/**
217+
* query_data - queries a policy and writes its data to buf
218+
* @buf: the resulting data is stored here (NOT NULL)
219+
* @buf_len: size of buf
220+
* @query: query string used to retrieve data
221+
* @query_len: size of query including second NUL byte
222+
*
223+
* The buffers pointed to by buf and query may overlap. The query buffer is
224+
* parsed before buf is written to.
225+
*
226+
* The query should look like "<LABEL>\0<KEY>\0", where <LABEL> is the name of
227+
* the security confinement context and <KEY> is the name of the data to
228+
* retrieve. <LABEL> and <KEY> must not be NUL-terminated.
229+
*
230+
* Don't expect the contents of buf to be preserved on failure.
231+
*
232+
* Returns: number of characters written to buf or -errno on failure
233+
*/
234+
static ssize_t query_data(char *buf, size_t buf_len,
235+
char *query, size_t query_len)
236+
{
237+
char *out;
238+
const char *key;
239+
struct aa_profile *profile;
240+
struct aa_data *data;
241+
u32 bytes, blocks;
242+
__le32 outle32;
243+
244+
if (!query_len)
245+
return -EINVAL; /* need a query */
246+
247+
key = query + strnlen(query, query_len) + 1;
248+
if (key + 1 >= query + query_len)
249+
return -EINVAL; /* not enough space for a non-empty key */
250+
if (key + strnlen(key, query + query_len - key) >= query + query_len)
251+
return -EINVAL; /* must end with NUL */
252+
253+
if (buf_len < sizeof(bytes) + sizeof(blocks))
254+
return -EINVAL; /* not enough space */
255+
256+
profile = aa_current_profile();
257+
258+
/* We are going to leave space for two numbers. The first is the total
259+
* number of bytes we are writing after the first number. This is so
260+
* users can read the full output without reallocation.
261+
*
262+
* The second number is the number of data blocks we're writing. An
263+
* application might be confined by multiple policies having data in
264+
* the same key.
265+
*/
266+
memset(buf, 0, sizeof(bytes) + sizeof(blocks));
267+
out = buf + sizeof(bytes) + sizeof(blocks);
268+
269+
blocks = 0;
270+
if (profile->data) {
271+
data = rhashtable_lookup_fast(profile->data, &key,
272+
profile->data->p);
273+
274+
if (data) {
275+
if (out + sizeof(outle32) + data->size > buf + buf_len)
276+
return -EINVAL; /* not enough space */
277+
outle32 = __cpu_to_le32(data->size);
278+
memcpy(out, &outle32, sizeof(outle32));
279+
out += sizeof(outle32);
280+
memcpy(out, data->data, data->size);
281+
out += data->size;
282+
blocks++;
283+
}
284+
}
285+
286+
outle32 = __cpu_to_le32(out - buf - sizeof(bytes));
287+
memcpy(buf, &outle32, sizeof(outle32));
288+
outle32 = __cpu_to_le32(blocks);
289+
memcpy(buf + sizeof(bytes), &outle32, sizeof(outle32));
290+
291+
return out - buf;
292+
}
293+
294+
#define QUERY_CMD_DATA "data\0"
295+
#define QUERY_CMD_DATA_LEN 5
296+
297+
/**
298+
* aa_write_access - generic permissions and data query
299+
* @file: pointer to open apparmorfs/access file
300+
* @ubuf: user buffer containing the complete query string (NOT NULL)
301+
* @count: size of ubuf
302+
* @ppos: position in the file (MUST BE ZERO)
303+
*
304+
* Allows for one permissions or data query per open(), write(), and read()
305+
* sequence. The only queries currently supported are label-based queries for
306+
* permissions or data.
307+
*
308+
* For permissions queries, ubuf must begin with "label\0", followed by the
309+
* profile query specific format described in the query_label() function
310+
* documentation.
311+
*
312+
* For data queries, ubuf must have the form "data\0<LABEL>\0<KEY>\0", where
313+
* <LABEL> is the name of the security confinement context and <KEY> is the
314+
* name of the data to retrieve.
315+
*
316+
* Returns: number of bytes written or -errno on failure
317+
*/
318+
static ssize_t aa_write_access(struct file *file, const char __user *ubuf,
319+
size_t count, loff_t *ppos)
320+
{
321+
char *buf;
322+
ssize_t len;
323+
324+
if (*ppos)
325+
return -ESPIPE;
326+
327+
buf = simple_transaction_get(file, ubuf, count);
328+
if (IS_ERR(buf))
329+
return PTR_ERR(buf);
330+
331+
if (count > QUERY_CMD_DATA_LEN &&
332+
!memcmp(buf, QUERY_CMD_DATA, QUERY_CMD_DATA_LEN)) {
333+
len = query_data(buf, SIMPLE_TRANSACTION_LIMIT,
334+
buf + QUERY_CMD_DATA_LEN,
335+
count - QUERY_CMD_DATA_LEN);
336+
} else
337+
len = -EINVAL;
338+
339+
if (len < 0)
340+
return len;
341+
342+
simple_transaction_set(file, len);
343+
344+
return count;
345+
}
346+
347+
static const struct file_operations aa_fs_access = {
348+
.write = aa_write_access,
349+
.read = simple_transaction_read,
350+
.release = simple_transaction_release,
351+
.llseek = generic_file_llseek,
352+
};
353+
216354
static int aa_fs_seq_show(struct seq_file *seq, void *v)
217355
{
218356
struct aa_fs_entry *fs_file = seq->private;
@@ -1078,6 +1216,7 @@ static struct aa_fs_entry aa_fs_entry_features[] = {
10781216
};
10791217

10801218
static struct aa_fs_entry aa_fs_entry_apparmor[] = {
1219+
AA_FS_FILE_FOPS(".access", 0640, &aa_fs_access),
10811220
AA_FS_FILE_FOPS(".ns_level", 0666, &aa_fs_ns_level),
10821221
AA_FS_FILE_FOPS(".ns_name", 0640, &aa_fs_ns_name),
10831222
AA_FS_FILE_FOPS("profiles", 0440, &aa_fs_profiles_fops),

security/apparmor/include/policy.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include <linux/capability.h>
1919
#include <linux/cred.h>
2020
#include <linux/kref.h>
21+
#include <linux/rhashtable.h>
2122
#include <linux/sched.h>
2223
#include <linux/slab.h>
2324
#include <linux/socket.h>
@@ -98,6 +99,19 @@ struct aa_proxy {
9899
struct aa_profile __rcu *profile;
99100
};
100101

102+
/* struct aa_data - generic data structure
103+
* key: name for retrieving this data
104+
* size: size of data in bytes
105+
* data: binary data
106+
* head: reserved for rhashtable
107+
*/
108+
struct aa_data {
109+
char *key;
110+
u32 size;
111+
char *data;
112+
struct rhash_head head;
113+
};
114+
101115

102116
/* struct aa_profile - basic confinement data
103117
* @base - base components of the profile (name, refcount, lists, lock ...)
@@ -122,6 +136,7 @@ struct aa_proxy {
122136
*
123137
* @dents: dentries for the profiles file entries in apparmorfs
124138
* @dirname: name of the profile dir in apparmorfs
139+
* @data: hashtable for free-form policy aa_data
125140
*
126141
* The AppArmor profile contains the basic confinement data. Each profile
127142
* has a name, and exists in a namespace. The @name and @exec_match are
@@ -165,6 +180,7 @@ struct aa_profile {
165180
unsigned char *hash;
166181
char *dirname;
167182
struct dentry *dents[AAFS_PROF_SIZEOF];
183+
struct rhashtable *data;
168184
};
169185

170186
extern enum profile_mode aa_g_profile_mode;

security/apparmor/lsm.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include <linux/sysctl.h>
2424
#include <linux/audit.h>
2525
#include <linux/user_namespace.h>
26+
#include <linux/kmemleak.h>
2627
#include <net/sock.h>
2728

2829
#include "include/apparmor.h"

security/apparmor/policy.c

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,20 @@ void aa_free_proxy_kref(struct kref *kref)
194194
free_proxy(p);
195195
}
196196

197+
/**
198+
* aa_free_data - free a data blob
199+
* @ptr: data to free
200+
* @arg: unused
201+
*/
202+
static void aa_free_data(void *ptr, void *arg)
203+
{
204+
struct aa_data *data = ptr;
205+
206+
kzfree(data->data);
207+
kzfree(data->key);
208+
kzfree(data);
209+
}
210+
197211
/**
198212
* aa_free_profile - free a profile
199213
* @profile: the profile to free (MAYBE NULL)
@@ -206,6 +220,8 @@ void aa_free_proxy_kref(struct kref *kref)
206220
*/
207221
void aa_free_profile(struct aa_profile *profile)
208222
{
223+
struct rhashtable *rht;
224+
209225
AA_DEBUG("%s(%p)\n", __func__, profile);
210226

211227
if (!profile)
@@ -227,6 +243,13 @@ void aa_free_profile(struct aa_profile *profile)
227243
aa_put_dfa(profile->policy.dfa);
228244
aa_put_proxy(profile->proxy);
229245

246+
if (profile->data) {
247+
rht = profile->data;
248+
profile->data = NULL;
249+
rhashtable_free_and_destroy(rht, aa_free_data, NULL);
250+
kzfree(rht);
251+
}
252+
230253
kzfree(profile->hash);
231254
aa_put_loaddata(profile->rawdata);
232255
kzfree(profile);

security/apparmor/policy_unpack.c

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -485,6 +485,30 @@ static bool unpack_rlimits(struct aa_ext *e, struct aa_profile *profile)
485485
return 0;
486486
}
487487

488+
static void *kvmemdup(const void *src, size_t len)
489+
{
490+
void *p = kvmalloc(len);
491+
492+
if (p)
493+
memcpy(p, src, len);
494+
return p;
495+
}
496+
497+
static u32 strhash(const void *data, u32 len, u32 seed)
498+
{
499+
const char * const *key = data;
500+
501+
return jhash(*key, strlen(*key), seed);
502+
}
503+
504+
static int datacmp(struct rhashtable_compare_arg *arg, const void *obj)
505+
{
506+
const struct aa_data *data = obj;
507+
const char * const *key = arg->key;
508+
509+
return strcmp(data->key, *key);
510+
}
511+
488512
/**
489513
* unpack_profile - unpack a serialized profile
490514
* @e: serialized data extent information (NOT NULL)
@@ -496,6 +520,9 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
496520
struct aa_profile *profile = NULL;
497521
const char *tmpname, *tmpns = NULL, *name = NULL;
498522
size_t ns_len;
523+
struct rhashtable_params params = { 0 };
524+
char *key = NULL;
525+
struct aa_data *data;
499526
int i, error = -EPROTO;
500527
kernel_cap_t tmpcap;
501528
u32 tmp;
@@ -654,6 +681,45 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
654681
if (!unpack_trans_table(e, profile))
655682
goto fail;
656683

684+
if (unpack_nameX(e, AA_STRUCT, "data")) {
685+
profile->data = kzalloc(sizeof(*profile->data), GFP_KERNEL);
686+
if (!profile->data)
687+
goto fail;
688+
689+
params.nelem_hint = 3;
690+
params.key_len = sizeof(void *);
691+
params.key_offset = offsetof(struct aa_data, key);
692+
params.head_offset = offsetof(struct aa_data, head);
693+
params.hashfn = strhash;
694+
params.obj_cmpfn = datacmp;
695+
696+
if (rhashtable_init(profile->data, &params))
697+
goto fail;
698+
699+
while (unpack_strdup(e, &key, NULL)) {
700+
data = kzalloc(sizeof(*data), GFP_KERNEL);
701+
if (!data) {
702+
kzfree(key);
703+
goto fail;
704+
}
705+
706+
data->key = key;
707+
data->size = unpack_blob(e, &data->data, NULL);
708+
data->data = kvmemdup(data->data, data->size);
709+
if (data->size && !data->data) {
710+
kzfree(data->key);
711+
kzfree(data);
712+
goto fail;
713+
}
714+
715+
rhashtable_insert_fast(profile->data, &data->head,
716+
profile->data->p);
717+
}
718+
719+
if (!unpack_nameX(e, AA_STRUCTEND, NULL))
720+
goto fail;
721+
}
722+
657723
if (!unpack_nameX(e, AA_STRUCTEND, NULL))
658724
goto fail;
659725

0 commit comments

Comments
 (0)