Skip to content

Commit 5ac8c35

Browse files
committed
apparmor: allow introspecting the loaded policy pre internal transform
Store loaded policy and allow introspecting it through apparmorfs. This has several uses from debugging, policy validation, and policy checkpoint and restore for containers. Signed-off-by: John Johansen <[email protected]>
1 parent fc1c9fd commit 5ac8c35

File tree

8 files changed

+278
-58
lines changed

8 files changed

+278
-58
lines changed

security/apparmor/apparmorfs.c

Lines changed: 172 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
#include "include/policy.h"
3434
#include "include/policy_ns.h"
3535
#include "include/resource.h"
36+
#include "include/policy_unpack.h"
3637

3738
/**
3839
* aa_mangle_name - mangle a profile name to std profile layout form
@@ -84,58 +85,69 @@ static int mangle_name(const char *name, char *target)
8485
* Returns: kernel buffer containing copy of user buffer data or an
8586
* ERR_PTR on failure.
8687
*/
87-
static char *aa_simple_write_to_buffer(int op, const char __user *userbuf,
88-
size_t alloc_size, size_t copy_size,
89-
loff_t *pos)
88+
static struct aa_loaddata *aa_simple_write_to_buffer(int op,
89+
const char __user *userbuf,
90+
size_t alloc_size,
91+
size_t copy_size,
92+
loff_t *pos)
9093
{
91-
char *data;
94+
struct aa_loaddata *data;
9295

9396
BUG_ON(copy_size > alloc_size);
9497

9598
if (*pos != 0)
9699
/* only writes from pos 0, that is complete writes */
97100
return ERR_PTR(-ESPIPE);
98101

99-
/*
100-
* Don't allow profile load/replace/remove from profiles that don't
101-
* have CAP_MAC_ADMIN
102-
*/
103-
if (!aa_may_manage_policy(__aa_current_profile(), NULL, op))
104-
return ERR_PTR(-EACCES);
105-
106102
/* freed by caller to simple_write_to_buffer */
107-
data = kvmalloc(alloc_size);
103+
data = kvmalloc(sizeof(*data) + alloc_size);
108104
if (data == NULL)
109105
return ERR_PTR(-ENOMEM);
106+
kref_init(&data->count);
107+
data->size = copy_size;
108+
data->hash = NULL;
109+
data->abi = 0;
110110

111-
if (copy_from_user(data, userbuf, copy_size)) {
111+
if (copy_from_user(data->data, userbuf, copy_size)) {
112112
kvfree(data);
113113
return ERR_PTR(-EFAULT);
114114
}
115115

116116
return data;
117117
}
118118

119-
120-
/* .load file hook fn to load policy */
121-
static ssize_t profile_load(struct file *f, const char __user *buf, size_t size,
122-
loff_t *pos)
119+
static ssize_t policy_update(int binop, const char __user *buf, size_t size,
120+
loff_t *pos)
123121
{
124-
char *data;
125122
ssize_t error;
123+
struct aa_loaddata *data;
124+
struct aa_profile *profile = aa_current_profile();
125+
int op = binop == PROF_ADD ? OP_PROF_LOAD : OP_PROF_REPL;
126+
/* high level check about policy management - fine grained in
127+
* below after unpack
128+
*/
129+
error = aa_may_manage_policy(profile, profile->ns, op);
130+
if (error)
131+
return error;
126132

127-
data = aa_simple_write_to_buffer(OP_PROF_LOAD, buf, size, size, pos);
128-
133+
data = aa_simple_write_to_buffer(op, buf, size, size, pos);
129134
error = PTR_ERR(data);
130135
if (!IS_ERR(data)) {
131-
error = aa_replace_profiles(__aa_current_profile()->ns, data,
132-
size, PROF_ADD);
133-
kvfree(data);
136+
error = aa_replace_profiles(profile->ns, binop, data);
137+
aa_put_loaddata(data);
134138
}
135139

136140
return error;
137141
}
138142

143+
static ssize_t profile_load(struct file *f, const char __user *buf, size_t size,
144+
loff_t *pos)
145+
{
146+
int error = policy_update(PROF_ADD, buf, size, pos);
147+
148+
return error;
149+
}
150+
139151
static const struct file_operations aa_fs_profile_load = {
140152
.write = profile_load,
141153
.llseek = default_llseek,
@@ -145,16 +157,7 @@ static const struct file_operations aa_fs_profile_load = {
145157
static ssize_t profile_replace(struct file *f, const char __user *buf,
146158
size_t size, loff_t *pos)
147159
{
148-
char *data;
149-
ssize_t error;
150-
151-
data = aa_simple_write_to_buffer(OP_PROF_REPL, buf, size, size, pos);
152-
error = PTR_ERR(data);
153-
if (!IS_ERR(data)) {
154-
error = aa_replace_profiles(__aa_current_profile()->ns, data,
155-
size, PROF_REPLACE);
156-
kvfree(data);
157-
}
160+
int error = policy_update(PROF_REPLACE, buf, size, pos);
158161

159162
return error;
160163
}
@@ -164,27 +167,35 @@ static const struct file_operations aa_fs_profile_replace = {
164167
.llseek = default_llseek,
165168
};
166169

167-
/* .remove file hook fn to remove loaded policy */
168170
static ssize_t profile_remove(struct file *f, const char __user *buf,
169171
size_t size, loff_t *pos)
170172
{
171-
char *data;
173+
struct aa_loaddata *data;
174+
struct aa_profile *profile;
172175
ssize_t error;
173176

177+
profile = aa_current_profile();
178+
/* high level check about policy management - fine grained in
179+
* below after unpack
180+
*/
181+
error = aa_may_manage_policy(profile, profile->ns, OP_PROF_RM);
182+
if (error)
183+
goto out;
184+
174185
/*
175186
* aa_remove_profile needs a null terminated string so 1 extra
176187
* byte is allocated and the copied data is null terminated.
177188
*/
178-
data = aa_simple_write_to_buffer(OP_PROF_RM, buf, size + 1, size, pos);
189+
data = aa_simple_write_to_buffer(OP_PROF_RM, buf, size + 1, size,
190+
pos);
179191

180192
error = PTR_ERR(data);
181193
if (!IS_ERR(data)) {
182-
data[size] = 0;
183-
error = aa_remove_profiles(__aa_current_profile()->ns, data,
184-
size);
185-
kvfree(data);
194+
data->data[size] = 0;
195+
error = aa_remove_profiles(profile->ns, data->data, size);
196+
aa_put_loaddata(data);
186197
}
187-
198+
out:
188199
return error;
189200
}
190201

@@ -401,6 +412,100 @@ static const struct file_operations aa_fs_ns_name = {
401412
.release = single_release,
402413
};
403414

415+
static int rawdata_release(struct inode *inode, struct file *file)
416+
{
417+
/* TODO: switch to loaddata when profile switched to symlink */
418+
aa_put_loaddata(file->private_data);
419+
420+
return 0;
421+
}
422+
423+
static int aa_fs_seq_raw_abi_show(struct seq_file *seq, void *v)
424+
{
425+
struct aa_proxy *proxy = seq->private;
426+
struct aa_profile *profile = aa_get_profile_rcu(&proxy->profile);
427+
428+
if (profile->rawdata->abi) {
429+
seq_printf(seq, "v%d", profile->rawdata->abi);
430+
seq_puts(seq, "\n");
431+
}
432+
aa_put_profile(profile);
433+
434+
return 0;
435+
}
436+
437+
static int aa_fs_seq_raw_abi_open(struct inode *inode, struct file *file)
438+
{
439+
return aa_fs_seq_profile_open(inode, file, aa_fs_seq_raw_abi_show);
440+
}
441+
442+
static const struct file_operations aa_fs_seq_raw_abi_fops = {
443+
.owner = THIS_MODULE,
444+
.open = aa_fs_seq_raw_abi_open,
445+
.read = seq_read,
446+
.llseek = seq_lseek,
447+
.release = aa_fs_seq_profile_release,
448+
};
449+
450+
static int aa_fs_seq_raw_hash_show(struct seq_file *seq, void *v)
451+
{
452+
struct aa_proxy *proxy = seq->private;
453+
struct aa_profile *profile = aa_get_profile_rcu(&proxy->profile);
454+
unsigned int i, size = aa_hash_size();
455+
456+
if (profile->rawdata->hash) {
457+
for (i = 0; i < size; i++)
458+
seq_printf(seq, "%.2x", profile->rawdata->hash[i]);
459+
seq_puts(seq, "\n");
460+
}
461+
aa_put_profile(profile);
462+
463+
return 0;
464+
}
465+
466+
static int aa_fs_seq_raw_hash_open(struct inode *inode, struct file *file)
467+
{
468+
return aa_fs_seq_profile_open(inode, file, aa_fs_seq_raw_hash_show);
469+
}
470+
471+
static const struct file_operations aa_fs_seq_raw_hash_fops = {
472+
.owner = THIS_MODULE,
473+
.open = aa_fs_seq_raw_hash_open,
474+
.read = seq_read,
475+
.llseek = seq_lseek,
476+
.release = aa_fs_seq_profile_release,
477+
};
478+
479+
static ssize_t rawdata_read(struct file *file, char __user *buf, size_t size,
480+
loff_t *ppos)
481+
{
482+
struct aa_loaddata *rawdata = file->private_data;
483+
484+
return simple_read_from_buffer(buf, size, ppos, rawdata->data,
485+
rawdata->size);
486+
}
487+
488+
static int rawdata_open(struct inode *inode, struct file *file)
489+
{
490+
struct aa_proxy *proxy = inode->i_private;
491+
struct aa_profile *profile;
492+
493+
if (!policy_view_capable(NULL))
494+
return -EACCES;
495+
profile = aa_get_profile_rcu(&proxy->profile);
496+
file->private_data = aa_get_loaddata(profile->rawdata);
497+
aa_put_profile(profile);
498+
499+
return 0;
500+
}
501+
502+
static const struct file_operations aa_fs_rawdata_fops = {
503+
.open = rawdata_open,
504+
.read = rawdata_read,
505+
.llseek = generic_file_llseek,
506+
.release = rawdata_release,
507+
};
508+
404509
/** fns to setup dynamic per profile/namespace files **/
405510
void __aa_fs_profile_rmdir(struct aa_profile *profile)
406511
{
@@ -512,6 +617,29 @@ int __aa_fs_profile_mkdir(struct aa_profile *profile, struct dentry *parent)
512617
profile->dents[AAFS_PROF_HASH] = dent;
513618
}
514619

620+
if (profile->rawdata) {
621+
dent = create_profile_file(dir, "raw_sha1", profile,
622+
&aa_fs_seq_raw_hash_fops);
623+
if (IS_ERR(dent))
624+
goto fail;
625+
profile->dents[AAFS_PROF_RAW_HASH] = dent;
626+
627+
dent = create_profile_file(dir, "raw_abi", profile,
628+
&aa_fs_seq_raw_abi_fops);
629+
if (IS_ERR(dent))
630+
goto fail;
631+
profile->dents[AAFS_PROF_RAW_ABI] = dent;
632+
633+
dent = securityfs_create_file("raw_data", S_IFREG | 0444, dir,
634+
profile->proxy,
635+
&aa_fs_rawdata_fops);
636+
if (IS_ERR(dent))
637+
goto fail;
638+
profile->dents[AAFS_PROF_RAW_DATA] = dent;
639+
d_inode(dent)->i_size = profile->rawdata->size;
640+
aa_get_proxy(profile->proxy);
641+
}
642+
515643
list_for_each_entry(child, &profile->base.profiles, base.list) {
516644
error = __aa_fs_profile_mkdir(child, prof_child_dir(profile));
517645
if (error)
@@ -817,6 +945,9 @@ static const struct seq_operations aa_fs_profiles_op = {
817945

818946
static int profiles_open(struct inode *inode, struct file *file)
819947
{
948+
if (!policy_view_capable(NULL))
949+
return -EACCES;
950+
820951
return seq_open(file, &aa_fs_profiles_op);
821952
}
822953

security/apparmor/crypto.c

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,43 @@ unsigned int aa_hash_size(void)
2929
return apparmor_hash_size;
3030
}
3131

32+
char *aa_calc_hash(void *data, size_t len)
33+
{
34+
struct {
35+
struct shash_desc shash;
36+
char ctx[crypto_shash_descsize(apparmor_tfm)];
37+
} desc;
38+
char *hash = NULL;
39+
int error = -ENOMEM;
40+
41+
if (!apparmor_tfm)
42+
return NULL;
43+
44+
hash = kzalloc(apparmor_hash_size, GFP_KERNEL);
45+
if (!hash)
46+
goto fail;
47+
48+
desc.shash.tfm = apparmor_tfm;
49+
desc.shash.flags = 0;
50+
51+
error = crypto_shash_init(&desc.shash);
52+
if (error)
53+
goto fail;
54+
error = crypto_shash_update(&desc.shash, (u8 *) data, len);
55+
if (error)
56+
goto fail;
57+
error = crypto_shash_final(&desc.shash, hash);
58+
if (error)
59+
goto fail;
60+
61+
return hash;
62+
63+
fail:
64+
kfree(hash);
65+
66+
return ERR_PTR(error);
67+
}
68+
3269
int aa_calc_profile_hash(struct aa_profile *profile, u32 version, void *start,
3370
size_t len)
3471
{
@@ -37,7 +74,7 @@ int aa_calc_profile_hash(struct aa_profile *profile, u32 version, void *start,
3774
char ctx[crypto_shash_descsize(apparmor_tfm)];
3875
} desc;
3976
int error = -ENOMEM;
40-
u32 le32_version = cpu_to_le32(version);
77+
__le32 le32_version = cpu_to_le32(version);
4178

4279
if (!aa_g_hash_policy)
4380
return 0;

security/apparmor/include/apparmorfs.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ enum aafs_ns_type {
7070
AAFS_NS_DIR,
7171
AAFS_NS_PROFS,
7272
AAFS_NS_NS,
73+
AAFS_NS_RAW_DATA,
7374
AAFS_NS_COUNT,
7475
AAFS_NS_MAX_COUNT,
7576
AAFS_NS_SIZE,
@@ -85,12 +86,16 @@ enum aafs_prof_type {
8586
AAFS_PROF_MODE,
8687
AAFS_PROF_ATTACH,
8788
AAFS_PROF_HASH,
89+
AAFS_PROF_RAW_DATA,
90+
AAFS_PROF_RAW_HASH,
91+
AAFS_PROF_RAW_ABI,
8892
AAFS_PROF_SIZEOF,
8993
};
9094

9195
#define ns_dir(X) ((X)->dents[AAFS_NS_DIR])
9296
#define ns_subns_dir(X) ((X)->dents[AAFS_NS_NS])
9397
#define ns_subprofs_dir(X) ((X)->dents[AAFS_NS_PROFS])
98+
#define ns_subdata_dir(X) ((X)->dents[AAFS_NS_RAW_DATA])
9499

95100
#define prof_dir(X) ((X)->dents[AAFS_PROF_DIR])
96101
#define prof_child_dir(X) ((X)->dents[AAFS_PROF_PROFS])

security/apparmor/include/crypto.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,14 @@
1818

1919
#ifdef CONFIG_SECURITY_APPARMOR_HASH
2020
unsigned int aa_hash_size(void);
21+
char *aa_calc_hash(void *data, size_t len);
2122
int aa_calc_profile_hash(struct aa_profile *profile, u32 version, void *start,
2223
size_t len);
2324
#else
25+
static inline char *aa_calc_hash(void *data, size_t len)
26+
{
27+
return NULL;
28+
}
2429
static inline int aa_calc_profile_hash(struct aa_profile *profile, u32 version,
2530
void *start, size_t len)
2631
{

0 commit comments

Comments
 (0)