diff options
author | John Johansen <john.johansen@canonical.com> | 2013-08-14 14:27:36 -0400 |
---|---|---|
committer | John Johansen <john.johansen@canonical.com> | 2013-08-14 14:42:08 -0400 |
commit | f8eb8a1324e81927b2c64823b2fc38386efd3fef (patch) | |
tree | 78ef80523807aeb5b084b29f8b698601c71292b2 /security | |
parent | 84f1f787421cd83bb7dfb34d584586f6a5fe7baa (diff) |
apparmor: add the ability to report a sha1 hash of loaded policy
Provide userspace the ability to introspect a sha1 hash value for each
profile currently loaded.
Signed-off-by: John Johansen <john.johansen@canonical.com>
Acked-by: Seth Arnold <seth.arnold@canonical.com>
Diffstat (limited to 'security')
-rw-r--r-- | security/apparmor/Kconfig | 12 | ||||
-rw-r--r-- | security/apparmor/Makefile | 1 | ||||
-rw-r--r-- | security/apparmor/apparmorfs.c | 37 | ||||
-rw-r--r-- | security/apparmor/crypto.c | 97 | ||||
-rw-r--r-- | security/apparmor/include/apparmorfs.h | 1 | ||||
-rw-r--r-- | security/apparmor/include/crypto.h | 36 | ||||
-rw-r--r-- | security/apparmor/include/policy.h | 1 | ||||
-rw-r--r-- | security/apparmor/policy_unpack.c | 20 |
8 files changed, 199 insertions, 6 deletions
diff --git a/security/apparmor/Kconfig b/security/apparmor/Kconfig index 9b9013b2e321..d49c53960b60 100644 --- a/security/apparmor/Kconfig +++ b/security/apparmor/Kconfig | |||
@@ -29,3 +29,15 @@ config SECURITY_APPARMOR_BOOTPARAM_VALUE | |||
29 | boot. | 29 | boot. |
30 | 30 | ||
31 | If you are unsure how to answer this question, answer 1. | 31 | If you are unsure how to answer this question, answer 1. |
32 | |||
33 | config SECURITY_APPARMOR_HASH | ||
34 | bool "SHA1 hash of loaded profiles" | ||
35 | depends on SECURITY_APPARMOR | ||
36 | depends on CRYPTO | ||
37 | select CRYPTO_SHA1 | ||
38 | default y | ||
39 | |||
40 | help | ||
41 | This option selects whether sha1 hashing is done against loaded | ||
42 | profiles and exported for inspection to user space via the apparmor | ||
43 | filesystem. | ||
diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile index 0831e049072d..d693df874818 100644 --- a/security/apparmor/Makefile +++ b/security/apparmor/Makefile | |||
@@ -5,6 +5,7 @@ obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o | |||
5 | apparmor-y := apparmorfs.o audit.o capability.o context.o ipc.o lib.o match.o \ | 5 | apparmor-y := apparmorfs.o audit.o capability.o context.o ipc.o lib.o match.o \ |
6 | path.o domain.o policy.o policy_unpack.o procattr.o lsm.o \ | 6 | path.o domain.o policy.o policy_unpack.o procattr.o lsm.o \ |
7 | resource.o sid.o file.o | 7 | resource.o sid.o file.o |
8 | apparmor-$(CONFIG_SECURITY_APPARMOR_HASH) += crypto.o | ||
8 | 9 | ||
9 | clean-files := capability_names.h rlim_names.h | 10 | clean-files := capability_names.h rlim_names.h |
10 | 11 | ||
diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index d708a55d072f..95c2b2689a03 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c | |||
@@ -26,6 +26,7 @@ | |||
26 | #include "include/apparmorfs.h" | 26 | #include "include/apparmorfs.h" |
27 | #include "include/audit.h" | 27 | #include "include/audit.h" |
28 | #include "include/context.h" | 28 | #include "include/context.h" |
29 | #include "include/crypto.h" | ||
29 | #include "include/policy.h" | 30 | #include "include/policy.h" |
30 | #include "include/resource.h" | 31 | #include "include/resource.h" |
31 | 32 | ||
@@ -319,6 +320,34 @@ static const struct file_operations aa_fs_profattach_fops = { | |||
319 | .release = aa_fs_seq_profile_release, | 320 | .release = aa_fs_seq_profile_release, |
320 | }; | 321 | }; |
321 | 322 | ||
323 | static int aa_fs_seq_hash_show(struct seq_file *seq, void *v) | ||
324 | { | ||
325 | struct aa_replacedby *r = seq->private; | ||
326 | struct aa_profile *profile = aa_get_profile_rcu(&r->profile); | ||
327 | unsigned int i, size = aa_hash_size(); | ||
328 | |||
329 | if (profile->hash) { | ||
330 | for (i = 0; i < size; i++) | ||
331 | seq_printf(seq, "%.2x", profile->hash[i]); | ||
332 | seq_puts(seq, "\n"); | ||
333 | } | ||
334 | |||
335 | return 0; | ||
336 | } | ||
337 | |||
338 | static int aa_fs_seq_hash_open(struct inode *inode, struct file *file) | ||
339 | { | ||
340 | return single_open(file, aa_fs_seq_hash_show, inode->i_private); | ||
341 | } | ||
342 | |||
343 | static const struct file_operations aa_fs_seq_hash_fops = { | ||
344 | .owner = THIS_MODULE, | ||
345 | .open = aa_fs_seq_hash_open, | ||
346 | .read = seq_read, | ||
347 | .llseek = seq_lseek, | ||
348 | .release = single_release, | ||
349 | }; | ||
350 | |||
322 | /** fns to setup dynamic per profile/namespace files **/ | 351 | /** fns to setup dynamic per profile/namespace files **/ |
323 | void __aa_fs_profile_rmdir(struct aa_profile *profile) | 352 | void __aa_fs_profile_rmdir(struct aa_profile *profile) |
324 | { | 353 | { |
@@ -420,6 +449,14 @@ int __aa_fs_profile_mkdir(struct aa_profile *profile, struct dentry *parent) | |||
420 | goto fail; | 449 | goto fail; |
421 | profile->dents[AAFS_PROF_ATTACH] = dent; | 450 | profile->dents[AAFS_PROF_ATTACH] = dent; |
422 | 451 | ||
452 | if (profile->hash) { | ||
453 | dent = create_profile_file(dir, "sha1", profile, | ||
454 | &aa_fs_seq_hash_fops); | ||
455 | if (IS_ERR(dent)) | ||
456 | goto fail; | ||
457 | profile->dents[AAFS_PROF_HASH] = dent; | ||
458 | } | ||
459 | |||
423 | list_for_each_entry(child, &profile->base.profiles, base.list) { | 460 | list_for_each_entry(child, &profile->base.profiles, base.list) { |
424 | error = __aa_fs_profile_mkdir(child, prof_child_dir(profile)); | 461 | error = __aa_fs_profile_mkdir(child, prof_child_dir(profile)); |
425 | if (error) | 462 | if (error) |
diff --git a/security/apparmor/crypto.c b/security/apparmor/crypto.c new file mode 100644 index 000000000000..d6222ba4e919 --- /dev/null +++ b/security/apparmor/crypto.c | |||
@@ -0,0 +1,97 @@ | |||
1 | /* | ||
2 | * AppArmor security module | ||
3 | * | ||
4 | * This file contains AppArmor policy loading interface function definitions. | ||
5 | * | ||
6 | * Copyright 2013 Canonical Ltd. | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or | ||
9 | * modify it under the terms of the GNU General Public License as | ||
10 | * published by the Free Software Foundation, version 2 of the | ||
11 | * License. | ||
12 | * | ||
13 | * Fns to provide a checksum of policy that has been loaded this can be | ||
14 | * compared to userspace policy compiles to check loaded policy is what | ||
15 | * it should be. | ||
16 | */ | ||
17 | |||
18 | #include <linux/crypto.h> | ||
19 | |||
20 | #include "include/apparmor.h" | ||
21 | #include "include/crypto.h" | ||
22 | |||
23 | static unsigned int apparmor_hash_size; | ||
24 | |||
25 | static struct crypto_hash *apparmor_tfm; | ||
26 | |||
27 | unsigned int aa_hash_size(void) | ||
28 | { | ||
29 | return apparmor_hash_size; | ||
30 | } | ||
31 | |||
32 | int aa_calc_profile_hash(struct aa_profile *profile, u32 version, void *start, | ||
33 | size_t len) | ||
34 | { | ||
35 | struct scatterlist sg[2]; | ||
36 | struct hash_desc desc = { | ||
37 | .tfm = apparmor_tfm, | ||
38 | .flags = 0 | ||
39 | }; | ||
40 | int error = -ENOMEM; | ||
41 | u32 le32_version = cpu_to_le32(version); | ||
42 | |||
43 | if (!apparmor_tfm) | ||
44 | return 0; | ||
45 | |||
46 | sg_init_table(sg, 2); | ||
47 | sg_set_buf(&sg[0], &le32_version, 4); | ||
48 | sg_set_buf(&sg[1], (u8 *) start, len); | ||
49 | |||
50 | profile->hash = kzalloc(apparmor_hash_size, GFP_KERNEL); | ||
51 | if (!profile->hash) | ||
52 | goto fail; | ||
53 | |||
54 | error = crypto_hash_init(&desc); | ||
55 | if (error) | ||
56 | goto fail; | ||
57 | error = crypto_hash_update(&desc, &sg[0], 4); | ||
58 | if (error) | ||
59 | goto fail; | ||
60 | error = crypto_hash_update(&desc, &sg[1], len); | ||
61 | if (error) | ||
62 | goto fail; | ||
63 | error = crypto_hash_final(&desc, profile->hash); | ||
64 | if (error) | ||
65 | goto fail; | ||
66 | |||
67 | return 0; | ||
68 | |||
69 | fail: | ||
70 | kfree(profile->hash); | ||
71 | profile->hash = NULL; | ||
72 | |||
73 | return error; | ||
74 | } | ||
75 | |||
76 | static int __init init_profile_hash(void) | ||
77 | { | ||
78 | struct crypto_hash *tfm; | ||
79 | |||
80 | if (!apparmor_initialized) | ||
81 | return 0; | ||
82 | |||
83 | tfm = crypto_alloc_hash("sha1", 0, CRYPTO_ALG_ASYNC); | ||
84 | if (IS_ERR(tfm)) { | ||
85 | int error = PTR_ERR(tfm); | ||
86 | AA_ERROR("failed to setup profile sha1 hashing: %d\n", error); | ||
87 | return error; | ||
88 | } | ||
89 | apparmor_tfm = tfm; | ||
90 | apparmor_hash_size = crypto_hash_digestsize(apparmor_tfm); | ||
91 | |||
92 | aa_info_message("AppArmor sha1 policy hashing enabled"); | ||
93 | |||
94 | return 0; | ||
95 | } | ||
96 | |||
97 | late_initcall(init_profile_hash); | ||
diff --git a/security/apparmor/include/apparmorfs.h b/security/apparmor/include/apparmorfs.h index f91712cf1b30..414e56878dd0 100644 --- a/security/apparmor/include/apparmorfs.h +++ b/security/apparmor/include/apparmorfs.h | |||
@@ -82,6 +82,7 @@ enum aafs_prof_type { | |||
82 | AAFS_PROF_NAME, | 82 | AAFS_PROF_NAME, |
83 | AAFS_PROF_MODE, | 83 | AAFS_PROF_MODE, |
84 | AAFS_PROF_ATTACH, | 84 | AAFS_PROF_ATTACH, |
85 | AAFS_PROF_HASH, | ||
85 | AAFS_PROF_SIZEOF, | 86 | AAFS_PROF_SIZEOF, |
86 | }; | 87 | }; |
87 | 88 | ||
diff --git a/security/apparmor/include/crypto.h b/security/apparmor/include/crypto.h new file mode 100644 index 000000000000..dc418e5024d9 --- /dev/null +++ b/security/apparmor/include/crypto.h | |||
@@ -0,0 +1,36 @@ | |||
1 | /* | ||
2 | * AppArmor security module | ||
3 | * | ||
4 | * This file contains AppArmor policy loading interface function definitions. | ||
5 | * | ||
6 | * Copyright 2013 Canonical Ltd. | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or | ||
9 | * modify it under the terms of the GNU General Public License as | ||
10 | * published by the Free Software Foundation, version 2 of the | ||
11 | * License. | ||
12 | */ | ||
13 | |||
14 | #ifndef __APPARMOR_CRYPTO_H | ||
15 | #define __APPARMOR_CRYPTO_H | ||
16 | |||
17 | #include "policy.h" | ||
18 | |||
19 | #ifdef CONFIG_SECURITY_APPARMOR_HASH | ||
20 | unsigned int aa_hash_size(void); | ||
21 | int aa_calc_profile_hash(struct aa_profile *profile, u32 version, void *start, | ||
22 | size_t len); | ||
23 | #else | ||
24 | static inline int aa_calc_profile_hash(struct aa_profile *profile, u32 version, | ||
25 | void *start, size_t len) | ||
26 | { | ||
27 | return 0; | ||
28 | } | ||
29 | |||
30 | static inline unsigned int aa_hash_size(void) | ||
31 | { | ||
32 | return 0; | ||
33 | } | ||
34 | #endif | ||
35 | |||
36 | #endif /* __APPARMOR_CRYPTO_H */ | ||
diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h index 59b36372ae40..f2d4b6348cbc 100644 --- a/security/apparmor/include/policy.h +++ b/security/apparmor/include/policy.h | |||
@@ -219,6 +219,7 @@ struct aa_profile { | |||
219 | struct aa_caps caps; | 219 | struct aa_caps caps; |
220 | struct aa_rlimit rlimits; | 220 | struct aa_rlimit rlimits; |
221 | 221 | ||
222 | unsigned char *hash; | ||
222 | char *dirname; | 223 | char *dirname; |
223 | struct dentry *dents[AAFS_PROF_SIZEOF]; | 224 | struct dentry *dents[AAFS_PROF_SIZEOF]; |
224 | }; | 225 | }; |
diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index bdaef2e1b2a0..a689f10930b5 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c | |||
@@ -24,6 +24,7 @@ | |||
24 | #include "include/apparmor.h" | 24 | #include "include/apparmor.h" |
25 | #include "include/audit.h" | 25 | #include "include/audit.h" |
26 | #include "include/context.h" | 26 | #include "include/context.h" |
27 | #include "include/crypto.h" | ||
27 | #include "include/match.h" | 28 | #include "include/match.h" |
28 | #include "include/policy.h" | 29 | #include "include/policy.h" |
29 | #include "include/policy_unpack.h" | 30 | #include "include/policy_unpack.h" |
@@ -758,10 +759,12 @@ int aa_unpack(void *udata, size_t size, struct list_head *lh, const char **ns) | |||
758 | 759 | ||
759 | *ns = NULL; | 760 | *ns = NULL; |
760 | while (e.pos < e.end) { | 761 | while (e.pos < e.end) { |
762 | void *start; | ||
761 | error = verify_header(&e, e.pos == e.start, ns); | 763 | error = verify_header(&e, e.pos == e.start, ns); |
762 | if (error) | 764 | if (error) |
763 | goto fail; | 765 | goto fail; |
764 | 766 | ||
767 | start = e.pos; | ||
765 | profile = unpack_profile(&e); | 768 | profile = unpack_profile(&e); |
766 | if (IS_ERR(profile)) { | 769 | if (IS_ERR(profile)) { |
767 | error = PTR_ERR(profile); | 770 | error = PTR_ERR(profile); |
@@ -769,16 +772,18 @@ int aa_unpack(void *udata, size_t size, struct list_head *lh, const char **ns) | |||
769 | } | 772 | } |
770 | 773 | ||
771 | error = verify_profile(profile); | 774 | error = verify_profile(profile); |
772 | if (error) { | 775 | if (error) |
773 | aa_free_profile(profile); | 776 | goto fail_profile; |
774 | goto fail; | 777 | |
775 | } | 778 | error = aa_calc_profile_hash(profile, e.version, start, |
779 | e.pos - start); | ||
780 | if (error) | ||
781 | goto fail_profile; | ||
776 | 782 | ||
777 | ent = aa_load_ent_alloc(); | 783 | ent = aa_load_ent_alloc(); |
778 | if (!ent) { | 784 | if (!ent) { |
779 | error = -ENOMEM; | 785 | error = -ENOMEM; |
780 | aa_put_profile(profile); | 786 | goto fail_profile; |
781 | goto fail; | ||
782 | } | 787 | } |
783 | 788 | ||
784 | ent->new = profile; | 789 | ent->new = profile; |
@@ -787,6 +792,9 @@ int aa_unpack(void *udata, size_t size, struct list_head *lh, const char **ns) | |||
787 | 792 | ||
788 | return 0; | 793 | return 0; |
789 | 794 | ||
795 | fail_profile: | ||
796 | aa_put_profile(profile); | ||
797 | |||
790 | fail: | 798 | fail: |
791 | list_for_each_entry_safe(ent, tmp, lh, list) { | 799 | list_for_each_entry_safe(ent, tmp, lh, list) { |
792 | list_del_init(&ent->list); | 800 | list_del_init(&ent->list); |