Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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 <john.johansen@canonical.com>

+280 -60
+174 -43
security/apparmor/apparmorfs.c
··· 33 33 #include "include/policy.h" 34 34 #include "include/policy_ns.h" 35 35 #include "include/resource.h" 36 + #include "include/policy_unpack.h" 36 37 37 38 /** 38 39 * aa_mangle_name - mangle a profile name to std profile layout form ··· 85 84 * Returns: kernel buffer containing copy of user buffer data or an 86 85 * ERR_PTR on failure. 87 86 */ 88 - static char *aa_simple_write_to_buffer(int op, const char __user *userbuf, 89 - size_t alloc_size, size_t copy_size, 90 - loff_t *pos) 87 + static struct aa_loaddata *aa_simple_write_to_buffer(int op, 88 + const char __user *userbuf, 89 + size_t alloc_size, 90 + size_t copy_size, 91 + loff_t *pos) 91 92 { 92 - char *data; 93 + struct aa_loaddata *data; 93 94 94 95 BUG_ON(copy_size > alloc_size); 95 96 ··· 99 96 /* only writes from pos 0, that is complete writes */ 100 97 return ERR_PTR(-ESPIPE); 101 98 102 - /* 103 - * Don't allow profile load/replace/remove from profiles that don't 104 - * have CAP_MAC_ADMIN 105 - */ 106 - if (!aa_may_manage_policy(__aa_current_profile(), NULL, op)) 107 - return ERR_PTR(-EACCES); 108 - 109 99 /* freed by caller to simple_write_to_buffer */ 110 - data = kvmalloc(alloc_size); 100 + data = kvmalloc(sizeof(*data) + alloc_size); 111 101 if (data == NULL) 112 102 return ERR_PTR(-ENOMEM); 103 + kref_init(&data->count); 104 + data->size = copy_size; 105 + data->hash = NULL; 106 + data->abi = 0; 113 107 114 - if (copy_from_user(data, userbuf, copy_size)) { 108 + if (copy_from_user(data->data, userbuf, copy_size)) { 115 109 kvfree(data); 116 110 return ERR_PTR(-EFAULT); 117 111 } ··· 116 116 return data; 117 117 } 118 118 119 + static ssize_t policy_update(int binop, const char __user *buf, size_t size, 120 + loff_t *pos) 121 + { 122 + 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; 119 132 120 - /* .load file hook fn to load policy */ 133 + data = aa_simple_write_to_buffer(op, buf, size, size, pos); 134 + error = PTR_ERR(data); 135 + if (!IS_ERR(data)) { 136 + error = aa_replace_profiles(profile->ns, binop, data); 137 + aa_put_loaddata(data); 138 + } 139 + 140 + return error; 141 + } 142 + 121 143 static ssize_t profile_load(struct file *f, const char __user *buf, size_t size, 122 144 loff_t *pos) 123 145 { 124 - char *data; 125 - ssize_t error; 126 - 127 - data = aa_simple_write_to_buffer(OP_PROF_LOAD, buf, size, size, pos); 128 - 129 - error = PTR_ERR(data); 130 - if (!IS_ERR(data)) { 131 - error = aa_replace_profiles(__aa_current_profile()->ns, data, 132 - size, PROF_ADD); 133 - kvfree(data); 134 - } 146 + int error = policy_update(PROF_ADD, buf, size, pos); 135 147 136 148 return error; 137 149 } ··· 157 145 static ssize_t profile_replace(struct file *f, const char __user *buf, 158 146 size_t size, loff_t *pos) 159 147 { 160 - char *data; 161 - ssize_t error; 162 - 163 - data = aa_simple_write_to_buffer(OP_PROF_REPL, buf, size, size, pos); 164 - error = PTR_ERR(data); 165 - if (!IS_ERR(data)) { 166 - error = aa_replace_profiles(__aa_current_profile()->ns, data, 167 - size, PROF_REPLACE); 168 - kvfree(data); 169 - } 148 + int error = policy_update(PROF_REPLACE, buf, size, pos); 170 149 171 150 return error; 172 151 } ··· 167 164 .llseek = default_llseek, 168 165 }; 169 166 170 - /* .remove file hook fn to remove loaded policy */ 171 167 static ssize_t profile_remove(struct file *f, const char __user *buf, 172 168 size_t size, loff_t *pos) 173 169 { 174 - char *data; 170 + struct aa_loaddata *data; 171 + struct aa_profile *profile; 175 172 ssize_t error; 173 + 174 + profile = aa_current_profile(); 175 + /* high level check about policy management - fine grained in 176 + * below after unpack 177 + */ 178 + error = aa_may_manage_policy(profile, profile->ns, OP_PROF_RM); 179 + if (error) 180 + goto out; 176 181 177 182 /* 178 183 * aa_remove_profile needs a null terminated string so 1 extra 179 184 * byte is allocated and the copied data is null terminated. 180 185 */ 181 - data = aa_simple_write_to_buffer(OP_PROF_RM, buf, size + 1, size, pos); 186 + data = aa_simple_write_to_buffer(OP_PROF_RM, buf, size + 1, size, 187 + pos); 182 188 183 189 error = PTR_ERR(data); 184 190 if (!IS_ERR(data)) { 185 - data[size] = 0; 186 - error = aa_remove_profiles(__aa_current_profile()->ns, data, 187 - size); 188 - kvfree(data); 191 + data->data[size] = 0; 192 + error = aa_remove_profiles(profile->ns, data->data, size); 193 + aa_put_loaddata(data); 189 194 } 190 - 195 + out: 191 196 return error; 192 197 } 193 198 ··· 412 401 .release = single_release, 413 402 }; 414 403 404 + static int rawdata_release(struct inode *inode, struct file *file) 405 + { 406 + /* TODO: switch to loaddata when profile switched to symlink */ 407 + aa_put_loaddata(file->private_data); 408 + 409 + return 0; 410 + } 411 + 412 + static int aa_fs_seq_raw_abi_show(struct seq_file *seq, void *v) 413 + { 414 + struct aa_proxy *proxy = seq->private; 415 + struct aa_profile *profile = aa_get_profile_rcu(&proxy->profile); 416 + 417 + if (profile->rawdata->abi) { 418 + seq_printf(seq, "v%d", profile->rawdata->abi); 419 + seq_puts(seq, "\n"); 420 + } 421 + aa_put_profile(profile); 422 + 423 + return 0; 424 + } 425 + 426 + static int aa_fs_seq_raw_abi_open(struct inode *inode, struct file *file) 427 + { 428 + return aa_fs_seq_profile_open(inode, file, aa_fs_seq_raw_abi_show); 429 + } 430 + 431 + static const struct file_operations aa_fs_seq_raw_abi_fops = { 432 + .owner = THIS_MODULE, 433 + .open = aa_fs_seq_raw_abi_open, 434 + .read = seq_read, 435 + .llseek = seq_lseek, 436 + .release = aa_fs_seq_profile_release, 437 + }; 438 + 439 + static int aa_fs_seq_raw_hash_show(struct seq_file *seq, void *v) 440 + { 441 + struct aa_proxy *proxy = seq->private; 442 + struct aa_profile *profile = aa_get_profile_rcu(&proxy->profile); 443 + unsigned int i, size = aa_hash_size(); 444 + 445 + if (profile->rawdata->hash) { 446 + for (i = 0; i < size; i++) 447 + seq_printf(seq, "%.2x", profile->rawdata->hash[i]); 448 + seq_puts(seq, "\n"); 449 + } 450 + aa_put_profile(profile); 451 + 452 + return 0; 453 + } 454 + 455 + static int aa_fs_seq_raw_hash_open(struct inode *inode, struct file *file) 456 + { 457 + return aa_fs_seq_profile_open(inode, file, aa_fs_seq_raw_hash_show); 458 + } 459 + 460 + static const struct file_operations aa_fs_seq_raw_hash_fops = { 461 + .owner = THIS_MODULE, 462 + .open = aa_fs_seq_raw_hash_open, 463 + .read = seq_read, 464 + .llseek = seq_lseek, 465 + .release = aa_fs_seq_profile_release, 466 + }; 467 + 468 + static ssize_t rawdata_read(struct file *file, char __user *buf, size_t size, 469 + loff_t *ppos) 470 + { 471 + struct aa_loaddata *rawdata = file->private_data; 472 + 473 + return simple_read_from_buffer(buf, size, ppos, rawdata->data, 474 + rawdata->size); 475 + } 476 + 477 + static int rawdata_open(struct inode *inode, struct file *file) 478 + { 479 + struct aa_proxy *proxy = inode->i_private; 480 + struct aa_profile *profile; 481 + 482 + if (!policy_view_capable(NULL)) 483 + return -EACCES; 484 + profile = aa_get_profile_rcu(&proxy->profile); 485 + file->private_data = aa_get_loaddata(profile->rawdata); 486 + aa_put_profile(profile); 487 + 488 + return 0; 489 + } 490 + 491 + static const struct file_operations aa_fs_rawdata_fops = { 492 + .open = rawdata_open, 493 + .read = rawdata_read, 494 + .llseek = generic_file_llseek, 495 + .release = rawdata_release, 496 + }; 497 + 415 498 /** fns to setup dynamic per profile/namespace files **/ 416 499 void __aa_fs_profile_rmdir(struct aa_profile *profile) 417 500 { ··· 615 510 if (IS_ERR(dent)) 616 511 goto fail; 617 512 profile->dents[AAFS_PROF_HASH] = dent; 513 + } 514 + 515 + if (profile->rawdata) { 516 + dent = create_profile_file(dir, "raw_sha1", profile, 517 + &aa_fs_seq_raw_hash_fops); 518 + if (IS_ERR(dent)) 519 + goto fail; 520 + profile->dents[AAFS_PROF_RAW_HASH] = dent; 521 + 522 + dent = create_profile_file(dir, "raw_abi", profile, 523 + &aa_fs_seq_raw_abi_fops); 524 + if (IS_ERR(dent)) 525 + goto fail; 526 + profile->dents[AAFS_PROF_RAW_ABI] = dent; 527 + 528 + dent = securityfs_create_file("raw_data", S_IFREG | 0444, dir, 529 + profile->proxy, 530 + &aa_fs_rawdata_fops); 531 + if (IS_ERR(dent)) 532 + goto fail; 533 + profile->dents[AAFS_PROF_RAW_DATA] = dent; 534 + d_inode(dent)->i_size = profile->rawdata->size; 535 + aa_get_proxy(profile->proxy); 618 536 } 619 537 620 538 list_for_each_entry(child, &profile->base.profiles, base.list) { ··· 945 817 946 818 static int profiles_open(struct inode *inode, struct file *file) 947 819 { 820 + if (!policy_view_capable(NULL)) 821 + return -EACCES; 822 + 948 823 return seq_open(file, &aa_fs_profiles_op); 949 824 } 950 825
+38 -1
security/apparmor/crypto.c
··· 29 29 return apparmor_hash_size; 30 30 } 31 31 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 + 32 69 int aa_calc_profile_hash(struct aa_profile *profile, u32 version, void *start, 33 70 size_t len) 34 71 { ··· 74 37 char ctx[crypto_shash_descsize(apparmor_tfm)]; 75 38 } desc; 76 39 int error = -ENOMEM; 77 - u32 le32_version = cpu_to_le32(version); 40 + __le32 le32_version = cpu_to_le32(version); 78 41 79 42 if (!aa_g_hash_policy) 80 43 return 0;
+5
security/apparmor/include/apparmorfs.h
··· 70 70 AAFS_NS_DIR, 71 71 AAFS_NS_PROFS, 72 72 AAFS_NS_NS, 73 + AAFS_NS_RAW_DATA, 73 74 AAFS_NS_COUNT, 74 75 AAFS_NS_MAX_COUNT, 75 76 AAFS_NS_SIZE, ··· 86 85 AAFS_PROF_MODE, 87 86 AAFS_PROF_ATTACH, 88 87 AAFS_PROF_HASH, 88 + AAFS_PROF_RAW_DATA, 89 + AAFS_PROF_RAW_HASH, 90 + AAFS_PROF_RAW_ABI, 89 91 AAFS_PROF_SIZEOF, 90 92 }; 91 93 92 94 #define ns_dir(X) ((X)->dents[AAFS_NS_DIR]) 93 95 #define ns_subns_dir(X) ((X)->dents[AAFS_NS_NS]) 94 96 #define ns_subprofs_dir(X) ((X)->dents[AAFS_NS_PROFS]) 97 + #define ns_subdata_dir(X) ((X)->dents[AAFS_NS_RAW_DATA]) 95 98 96 99 #define prof_dir(X) ((X)->dents[AAFS_PROF_DIR]) 97 100 #define prof_child_dir(X) ((X)->dents[AAFS_PROF_PROFS])
+5
security/apparmor/include/crypto.h
··· 18 18 19 19 #ifdef CONFIG_SECURITY_APPARMOR_HASH 20 20 unsigned int aa_hash_size(void); 21 + char *aa_calc_hash(void *data, size_t len); 21 22 int aa_calc_profile_hash(struct aa_profile *profile, u32 version, void *start, 22 23 size_t len); 23 24 #else 25 + static inline char *aa_calc_hash(void *data, size_t len) 26 + { 27 + return NULL; 28 + } 24 29 static inline int aa_calc_profile_hash(struct aa_profile *profile, u32 version, 25 30 void *start, size_t len) 26 31 {
+3 -2
security/apparmor/include/policy.h
··· 161 161 struct aa_caps caps; 162 162 struct aa_rlimit rlimits; 163 163 164 + struct aa_loaddata *rawdata; 164 165 unsigned char *hash; 165 166 char *dirname; 166 167 struct dentry *dents[AAFS_PROF_SIZEOF]; ··· 188 187 const char *fqname, size_t n); 189 188 struct aa_profile *aa_match_profile(struct aa_ns *ns, const char *name); 190 189 191 - ssize_t aa_replace_profiles(struct aa_ns *view, void *udata, size_t size, 192 - bool noreplace); 190 + ssize_t aa_replace_profiles(struct aa_ns *view, bool noreplace, 191 + struct aa_loaddata *udata); 193 192 ssize_t aa_remove_profiles(struct aa_ns *view, char *name, size_t size); 194 193 void __aa_profile_list_release(struct list_head *head); 195 194
+26 -1
security/apparmor/include/policy_unpack.h
··· 16 16 #define __POLICY_INTERFACE_H 17 17 18 18 #include <linux/list.h> 19 + #include <linux/kref.h> 19 20 20 21 struct aa_load_ent { 21 22 struct list_head list; ··· 35 34 #define PACKED_MODE_KILL 2 36 35 #define PACKED_MODE_UNCONFINED 3 37 36 38 - int aa_unpack(void *udata, size_t size, struct list_head *lh, const char **ns); 37 + /* struct aa_loaddata - buffer of policy load data set */ 38 + struct aa_loaddata { 39 + struct kref count; 40 + size_t size; 41 + int abi; 42 + unsigned char *hash; 43 + char data[]; 44 + }; 45 + 46 + int aa_unpack(struct aa_loaddata *udata, struct list_head *lh, const char **ns); 47 + 48 + static inline struct aa_loaddata * 49 + aa_get_loaddata(struct aa_loaddata *data) 50 + { 51 + if (data) 52 + kref_get(&(data->count)); 53 + return data; 54 + } 55 + 56 + void aa_loaddata_kref(struct kref *kref); 57 + static inline void aa_put_loaddata(struct aa_loaddata *data) 58 + { 59 + if (data) 60 + kref_put(&data->count, aa_loaddata_kref); 61 + } 39 62 40 63 #endif /* __POLICY_INTERFACE_H */
+7 -7
security/apparmor/policy.c
··· 228 228 aa_put_proxy(profile->proxy); 229 229 230 230 kzfree(profile->hash); 231 + aa_put_loaddata(profile->rawdata); 231 232 kzfree(profile); 232 233 } 233 234 ··· 803 802 /** 804 803 * aa_replace_profiles - replace profile(s) on the profile list 805 804 * @view: namespace load is viewed from 806 - * @profile: profile that is attempting to load/replace policy 807 - * @udata: serialized data stream (NOT NULL) 808 - * @size: size of the serialized data stream 809 805 * @noreplace: true if only doing addition, no replacement allowed 806 + * @udata: serialized data stream (NOT NULL) 810 807 * 811 808 * unpack and replace a profile on the profile list and uses of that profile 812 809 * by any aa_task_cxt. If the profile does not exist on the profile list ··· 812 813 * 813 814 * Returns: size of data consumed else error code on failure. 814 815 */ 815 - ssize_t aa_replace_profiles(struct aa_ns *view, void *udata, size_t size, 816 - bool noreplace) 816 + ssize_t aa_replace_profiles(struct aa_ns *view, bool noreplace, 817 + struct aa_loaddata *udata) 817 818 { 818 819 const char *ns_name, *info = NULL; 819 820 struct aa_ns *ns = NULL; ··· 823 824 LIST_HEAD(lh); 824 825 825 826 /* released below */ 826 - error = aa_unpack(udata, size, &lh, &ns_name); 827 + error = aa_unpack(udata, &lh, &ns_name); 827 828 if (error) 828 829 goto out; 829 830 ··· 840 841 /* setup parent and ns info */ 841 842 list_for_each_entry(ent, &lh, list) { 842 843 struct aa_policy *policy; 844 + ent->new->rawdata = aa_get_loaddata(udata); 843 845 error = __lookup_replace(ns, ent->new->base.hname, noreplace, 844 846 &ent->old, &info); 845 847 if (error) ··· 957 957 958 958 if (error) 959 959 return error; 960 - return size; 960 + return udata->size; 961 961 962 962 fail_lock: 963 963 mutex_unlock(&ns->lock);
+22 -6
security/apparmor/policy_unpack.c
··· 117 117 audit_cb); 118 118 } 119 119 120 + void aa_loaddata_kref(struct kref *kref) 121 + { 122 + struct aa_loaddata *d = container_of(kref, struct aa_loaddata, count); 123 + 124 + if (d) { 125 + kzfree(d->hash); 126 + kvfree(d); 127 + } 128 + } 129 + 120 130 /* test if read will be in packed data bounds */ 121 131 static bool inbounds(struct aa_ext *e, size_t size) 122 132 { ··· 759 749 /** 760 750 * aa_unpack - unpack packed binary profile(s) data loaded from user space 761 751 * @udata: user data copied to kmem (NOT NULL) 762 - * @size: the size of the user data 763 752 * @lh: list to place unpacked profiles in a aa_repl_ws 764 753 * @ns: Returns namespace profile is in if specified else NULL (NOT NULL) 765 754 * ··· 768 759 * 769 760 * Returns: profile(s) on @lh else error pointer if fails to unpack 770 761 */ 771 - int aa_unpack(void *udata, size_t size, struct list_head *lh, const char **ns) 762 + int aa_unpack(struct aa_loaddata *udata, struct list_head *lh, 763 + const char **ns) 772 764 { 773 765 struct aa_load_ent *tmp, *ent; 774 766 struct aa_profile *profile = NULL; 775 767 int error; 776 768 struct aa_ext e = { 777 - .start = udata, 778 - .end = udata + size, 779 - .pos = udata, 769 + .start = udata->data, 770 + .end = udata->data + udata->size, 771 + .pos = udata->data, 780 772 }; 781 773 782 774 *ns = NULL; ··· 812 802 ent->new = profile; 813 803 list_add_tail(&ent->list, lh); 814 804 } 815 - 805 + udata->abi = e.version & K_ABI_MASK; 806 + udata->hash = aa_calc_hash(udata->data, udata->size); 807 + if (IS_ERR(udata->hash)) { 808 + error = PTR_ERR(udata->hash); 809 + udata->hash = NULL; 810 + goto fail; 811 + } 816 812 return 0; 817 813 818 814 fail_profile: