diff options
Diffstat (limited to 'security')
51 files changed, 1404 insertions, 674 deletions
diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile index 806bd19af7f2..5706b74c857f 100644 --- a/security/apparmor/Makefile +++ b/security/apparmor/Makefile | |||
| @@ -57,9 +57,9 @@ cmd_make-rlim = echo "static const char *const rlim_names[RLIM_NLIMITS] = {" \ | |||
| 57 | 57 | ||
| 58 | $(obj)/capability.o : $(obj)/capability_names.h | 58 | $(obj)/capability.o : $(obj)/capability_names.h |
| 59 | $(obj)/resource.o : $(obj)/rlim_names.h | 59 | $(obj)/resource.o : $(obj)/rlim_names.h |
| 60 | $(obj)/capability_names.h : $(srctree)/include/linux/capability.h \ | 60 | $(obj)/capability_names.h : $(srctree)/include/uapi/linux/capability.h \ |
| 61 | $(src)/Makefile | 61 | $(src)/Makefile |
| 62 | $(call cmd,make-caps) | 62 | $(call cmd,make-caps) |
| 63 | $(obj)/rlim_names.h : $(srctree)/include/asm-generic/resource.h \ | 63 | $(obj)/rlim_names.h : $(srctree)/include/uapi/asm-generic/resource.h \ |
| 64 | $(src)/Makefile | 64 | $(src)/Makefile |
| 65 | $(call cmd,make-rlim) | 65 | $(call cmd,make-rlim) |
diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c index b81ea10a17a3..60f0c76a27d3 100644 --- a/security/apparmor/domain.c +++ b/security/apparmor/domain.c | |||
| @@ -721,7 +721,7 @@ audit: | |||
| 721 | if (!permtest) | 721 | if (!permtest) |
| 722 | error = aa_audit_file(profile, &perms, GFP_KERNEL, | 722 | error = aa_audit_file(profile, &perms, GFP_KERNEL, |
| 723 | OP_CHANGE_HAT, AA_MAY_CHANGEHAT, NULL, | 723 | OP_CHANGE_HAT, AA_MAY_CHANGEHAT, NULL, |
| 724 | target, 0, info, error); | 724 | target, GLOBAL_ROOT_UID, info, error); |
| 725 | 725 | ||
| 726 | out: | 726 | out: |
| 727 | aa_put_profile(hat); | 727 | aa_put_profile(hat); |
| @@ -848,7 +848,7 @@ int aa_change_profile(const char *ns_name, const char *hname, bool onexec, | |||
| 848 | audit: | 848 | audit: |
| 849 | if (!permtest) | 849 | if (!permtest) |
| 850 | error = aa_audit_file(profile, &perms, GFP_KERNEL, op, request, | 850 | error = aa_audit_file(profile, &perms, GFP_KERNEL, op, request, |
| 851 | name, hname, 0, info, error); | 851 | name, hname, GLOBAL_ROOT_UID, info, error); |
| 852 | 852 | ||
| 853 | aa_put_namespace(ns); | 853 | aa_put_namespace(ns); |
| 854 | aa_put_profile(target); | 854 | aa_put_profile(target); |
diff --git a/security/apparmor/file.c b/security/apparmor/file.c index cf19d4093ca4..cd21ec5b90af 100644 --- a/security/apparmor/file.c +++ b/security/apparmor/file.c | |||
| @@ -65,7 +65,7 @@ static void audit_file_mask(struct audit_buffer *ab, u32 mask) | |||
| 65 | static void file_audit_cb(struct audit_buffer *ab, void *va) | 65 | static void file_audit_cb(struct audit_buffer *ab, void *va) |
| 66 | { | 66 | { |
| 67 | struct common_audit_data *sa = va; | 67 | struct common_audit_data *sa = va; |
| 68 | uid_t fsuid = current_fsuid(); | 68 | kuid_t fsuid = current_fsuid(); |
| 69 | 69 | ||
| 70 | if (sa->aad->fs.request & AA_AUDIT_FILE_MASK) { | 70 | if (sa->aad->fs.request & AA_AUDIT_FILE_MASK) { |
| 71 | audit_log_format(ab, " requested_mask="); | 71 | audit_log_format(ab, " requested_mask="); |
| @@ -76,8 +76,10 @@ static void file_audit_cb(struct audit_buffer *ab, void *va) | |||
| 76 | audit_file_mask(ab, sa->aad->fs.denied); | 76 | audit_file_mask(ab, sa->aad->fs.denied); |
| 77 | } | 77 | } |
| 78 | if (sa->aad->fs.request & AA_AUDIT_FILE_MASK) { | 78 | if (sa->aad->fs.request & AA_AUDIT_FILE_MASK) { |
| 79 | audit_log_format(ab, " fsuid=%d", fsuid); | 79 | audit_log_format(ab, " fsuid=%d", |
| 80 | audit_log_format(ab, " ouid=%d", sa->aad->fs.ouid); | 80 | from_kuid(&init_user_ns, fsuid)); |
| 81 | audit_log_format(ab, " ouid=%d", | ||
| 82 | from_kuid(&init_user_ns, sa->aad->fs.ouid)); | ||
| 81 | } | 83 | } |
| 82 | 84 | ||
| 83 | if (sa->aad->fs.target) { | 85 | if (sa->aad->fs.target) { |
| @@ -103,7 +105,7 @@ static void file_audit_cb(struct audit_buffer *ab, void *va) | |||
| 103 | */ | 105 | */ |
| 104 | int aa_audit_file(struct aa_profile *profile, struct file_perms *perms, | 106 | int aa_audit_file(struct aa_profile *profile, struct file_perms *perms, |
| 105 | gfp_t gfp, int op, u32 request, const char *name, | 107 | gfp_t gfp, int op, u32 request, const char *name, |
| 106 | const char *target, uid_t ouid, const char *info, int error) | 108 | const char *target, kuid_t ouid, const char *info, int error) |
| 107 | { | 109 | { |
| 108 | int type = AUDIT_APPARMOR_AUTO; | 110 | int type = AUDIT_APPARMOR_AUTO; |
| 109 | struct common_audit_data sa; | 111 | struct common_audit_data sa; |
| @@ -201,7 +203,7 @@ static struct file_perms compute_perms(struct aa_dfa *dfa, unsigned int state, | |||
| 201 | */ | 203 | */ |
| 202 | perms.kill = 0; | 204 | perms.kill = 0; |
| 203 | 205 | ||
| 204 | if (current_fsuid() == cond->uid) { | 206 | if (uid_eq(current_fsuid(), cond->uid)) { |
| 205 | perms.allow = map_old_perms(dfa_user_allow(dfa, state)); | 207 | perms.allow = map_old_perms(dfa_user_allow(dfa, state)); |
| 206 | perms.audit = map_old_perms(dfa_user_audit(dfa, state)); | 208 | perms.audit = map_old_perms(dfa_user_audit(dfa, state)); |
| 207 | perms.quiet = map_old_perms(dfa_user_quiet(dfa, state)); | 209 | perms.quiet = map_old_perms(dfa_user_quiet(dfa, state)); |
diff --git a/security/apparmor/include/audit.h b/security/apparmor/include/audit.h index 4b7e18951aea..69d8cae634e7 100644 --- a/security/apparmor/include/audit.h +++ b/security/apparmor/include/audit.h | |||
| @@ -125,7 +125,7 @@ struct apparmor_audit_data { | |||
| 125 | const char *target; | 125 | const char *target; |
| 126 | u32 request; | 126 | u32 request; |
| 127 | u32 denied; | 127 | u32 denied; |
| 128 | uid_t ouid; | 128 | kuid_t ouid; |
| 129 | } fs; | 129 | } fs; |
| 130 | }; | 130 | }; |
| 131 | }; | 131 | }; |
diff --git a/security/apparmor/include/file.h b/security/apparmor/include/file.h index f98fd4701d80..967b2deda376 100644 --- a/security/apparmor/include/file.h +++ b/security/apparmor/include/file.h | |||
| @@ -71,7 +71,7 @@ struct path; | |||
| 71 | 71 | ||
| 72 | /* need to make conditional which ones are being set */ | 72 | /* need to make conditional which ones are being set */ |
| 73 | struct path_cond { | 73 | struct path_cond { |
| 74 | uid_t uid; | 74 | kuid_t uid; |
| 75 | umode_t mode; | 75 | umode_t mode; |
| 76 | }; | 76 | }; |
| 77 | 77 | ||
| @@ -146,7 +146,7 @@ static inline u16 dfa_map_xindex(u16 mask) | |||
| 146 | 146 | ||
| 147 | int aa_audit_file(struct aa_profile *profile, struct file_perms *perms, | 147 | int aa_audit_file(struct aa_profile *profile, struct file_perms *perms, |
| 148 | gfp_t gfp, int op, u32 request, const char *name, | 148 | gfp_t gfp, int op, u32 request, const char *name, |
| 149 | const char *target, uid_t ouid, const char *info, int error); | 149 | const char *target, kuid_t ouid, const char *info, int error); |
| 150 | 150 | ||
| 151 | /** | 151 | /** |
| 152 | * struct aa_file_rules - components used for file rule permissions | 152 | * struct aa_file_rules - components used for file rule permissions |
diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index 8ea39aabe948..8c2a7f6b35e2 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c | |||
| @@ -352,7 +352,7 @@ static int apparmor_path_chmod(struct path *path, umode_t mode) | |||
| 352 | return common_perm_mnt_dentry(OP_CHMOD, path->mnt, path->dentry, AA_MAY_CHMOD); | 352 | return common_perm_mnt_dentry(OP_CHMOD, path->mnt, path->dentry, AA_MAY_CHMOD); |
| 353 | } | 353 | } |
| 354 | 354 | ||
| 355 | static int apparmor_path_chown(struct path *path, uid_t uid, gid_t gid) | 355 | static int apparmor_path_chown(struct path *path, kuid_t uid, kgid_t gid) |
| 356 | { | 356 | { |
| 357 | struct path_cond cond = { path->dentry->d_inode->i_uid, | 357 | struct path_cond cond = { path->dentry->d_inode->i_uid, |
| 358 | path->dentry->d_inode->i_mode | 358 | path->dentry->d_inode->i_mode |
diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index cf5fd220309b..813200384d97 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c | |||
| @@ -724,6 +724,8 @@ fail: | |||
| 724 | */ | 724 | */ |
| 725 | static void free_profile(struct aa_profile *profile) | 725 | static void free_profile(struct aa_profile *profile) |
| 726 | { | 726 | { |
| 727 | struct aa_profile *p; | ||
| 728 | |||
| 727 | AA_DEBUG("%s(%p)\n", __func__, profile); | 729 | AA_DEBUG("%s(%p)\n", __func__, profile); |
| 728 | 730 | ||
| 729 | if (!profile) | 731 | if (!profile) |
| @@ -751,7 +753,27 @@ static void free_profile(struct aa_profile *profile) | |||
| 751 | aa_put_dfa(profile->xmatch); | 753 | aa_put_dfa(profile->xmatch); |
| 752 | aa_put_dfa(profile->policy.dfa); | 754 | aa_put_dfa(profile->policy.dfa); |
| 753 | 755 | ||
| 754 | aa_put_profile(profile->replacedby); | 756 | /* put the profile reference for replacedby, but not via |
| 757 | * put_profile(kref_put). | ||
| 758 | * replacedby can form a long chain that can result in cascading | ||
| 759 | * frees that blows the stack because kref_put makes a nested fn | ||
| 760 | * call (it looks like recursion, with free_profile calling | ||
| 761 | * free_profile) for each profile in the chain lp#1056078. | ||
| 762 | */ | ||
| 763 | for (p = profile->replacedby; p; ) { | ||
| 764 | if (atomic_dec_and_test(&p->base.count.refcount)) { | ||
| 765 | /* no more refs on p, grab its replacedby */ | ||
| 766 | struct aa_profile *next = p->replacedby; | ||
| 767 | /* break the chain */ | ||
| 768 | p->replacedby = NULL; | ||
| 769 | /* now free p, chain is broken */ | ||
| 770 | free_profile(p); | ||
| 771 | |||
| 772 | /* follow up with next profile in the chain */ | ||
| 773 | p = next; | ||
| 774 | } else | ||
| 775 | break; | ||
| 776 | } | ||
| 755 | 777 | ||
| 756 | kzfree(profile); | 778 | kzfree(profile); |
| 757 | } | 779 | } |
diff --git a/security/capability.c b/security/capability.c index 61095df8b89a..b14a30c234b8 100644 --- a/security/capability.c +++ b/security/capability.c | |||
| @@ -74,8 +74,8 @@ static int cap_sb_statfs(struct dentry *dentry) | |||
| 74 | return 0; | 74 | return 0; |
| 75 | } | 75 | } |
| 76 | 76 | ||
| 77 | static int cap_sb_mount(char *dev_name, struct path *path, char *type, | 77 | static int cap_sb_mount(const char *dev_name, struct path *path, |
| 78 | unsigned long flags, void *data) | 78 | const char *type, unsigned long flags, void *data) |
| 79 | { | 79 | { |
| 80 | return 0; | 80 | return 0; |
| 81 | } | 81 | } |
| @@ -284,7 +284,7 @@ static int cap_path_chmod(struct path *path, umode_t mode) | |||
| 284 | return 0; | 284 | return 0; |
| 285 | } | 285 | } |
| 286 | 286 | ||
| 287 | static int cap_path_chown(struct path *path, uid_t uid, gid_t gid) | 287 | static int cap_path_chown(struct path *path, kuid_t uid, kgid_t gid) |
| 288 | { | 288 | { |
| 289 | return 0; | 289 | return 0; |
| 290 | } | 290 | } |
diff --git a/security/device_cgroup.c b/security/device_cgroup.c index 442204cc22d9..b08d20c66c2e 100644 --- a/security/device_cgroup.c +++ b/security/device_cgroup.c | |||
| @@ -26,12 +26,12 @@ | |||
| 26 | static DEFINE_MUTEX(devcgroup_mutex); | 26 | static DEFINE_MUTEX(devcgroup_mutex); |
| 27 | 27 | ||
| 28 | /* | 28 | /* |
| 29 | * whitelist locking rules: | 29 | * exception list locking rules: |
| 30 | * hold devcgroup_mutex for update/read. | 30 | * hold devcgroup_mutex for update/read. |
| 31 | * hold rcu_read_lock() for read. | 31 | * hold rcu_read_lock() for read. |
| 32 | */ | 32 | */ |
| 33 | 33 | ||
| 34 | struct dev_whitelist_item { | 34 | struct dev_exception_item { |
| 35 | u32 major, minor; | 35 | u32 major, minor; |
| 36 | short type; | 36 | short type; |
| 37 | short access; | 37 | short access; |
| @@ -41,7 +41,11 @@ struct dev_whitelist_item { | |||
| 41 | 41 | ||
| 42 | struct dev_cgroup { | 42 | struct dev_cgroup { |
| 43 | struct cgroup_subsys_state css; | 43 | struct cgroup_subsys_state css; |
| 44 | struct list_head whitelist; | 44 | struct list_head exceptions; |
| 45 | enum { | ||
| 46 | DEVCG_DEFAULT_ALLOW, | ||
| 47 | DEVCG_DEFAULT_DENY, | ||
| 48 | } behavior; | ||
| 45 | }; | 49 | }; |
| 46 | 50 | ||
| 47 | static inline struct dev_cgroup *css_to_devcgroup(struct cgroup_subsys_state *s) | 51 | static inline struct dev_cgroup *css_to_devcgroup(struct cgroup_subsys_state *s) |
| @@ -74,12 +78,12 @@ static int devcgroup_can_attach(struct cgroup *new_cgrp, | |||
| 74 | /* | 78 | /* |
| 75 | * called under devcgroup_mutex | 79 | * called under devcgroup_mutex |
| 76 | */ | 80 | */ |
| 77 | static int dev_whitelist_copy(struct list_head *dest, struct list_head *orig) | 81 | static int dev_exceptions_copy(struct list_head *dest, struct list_head *orig) |
| 78 | { | 82 | { |
| 79 | struct dev_whitelist_item *wh, *tmp, *new; | 83 | struct dev_exception_item *ex, *tmp, *new; |
| 80 | 84 | ||
| 81 | list_for_each_entry(wh, orig, list) { | 85 | list_for_each_entry(ex, orig, list) { |
| 82 | new = kmemdup(wh, sizeof(*wh), GFP_KERNEL); | 86 | new = kmemdup(ex, sizeof(*ex), GFP_KERNEL); |
| 83 | if (!new) | 87 | if (!new) |
| 84 | goto free_and_exit; | 88 | goto free_and_exit; |
| 85 | list_add_tail(&new->list, dest); | 89 | list_add_tail(&new->list, dest); |
| @@ -88,64 +92,60 @@ static int dev_whitelist_copy(struct list_head *dest, struct list_head *orig) | |||
| 88 | return 0; | 92 | return 0; |
| 89 | 93 | ||
| 90 | free_and_exit: | 94 | free_and_exit: |
| 91 | list_for_each_entry_safe(wh, tmp, dest, list) { | 95 | list_for_each_entry_safe(ex, tmp, dest, list) { |
| 92 | list_del(&wh->list); | 96 | list_del(&ex->list); |
| 93 | kfree(wh); | 97 | kfree(ex); |
| 94 | } | 98 | } |
| 95 | return -ENOMEM; | 99 | return -ENOMEM; |
| 96 | } | 100 | } |
| 97 | 101 | ||
| 98 | /* Stupid prototype - don't bother combining existing entries */ | ||
| 99 | /* | 102 | /* |
| 100 | * called under devcgroup_mutex | 103 | * called under devcgroup_mutex |
| 101 | */ | 104 | */ |
| 102 | static int dev_whitelist_add(struct dev_cgroup *dev_cgroup, | 105 | static int dev_exception_add(struct dev_cgroup *dev_cgroup, |
| 103 | struct dev_whitelist_item *wh) | 106 | struct dev_exception_item *ex) |
| 104 | { | 107 | { |
| 105 | struct dev_whitelist_item *whcopy, *walk; | 108 | struct dev_exception_item *excopy, *walk; |
| 106 | 109 | ||
| 107 | whcopy = kmemdup(wh, sizeof(*wh), GFP_KERNEL); | 110 | excopy = kmemdup(ex, sizeof(*ex), GFP_KERNEL); |
| 108 | if (!whcopy) | 111 | if (!excopy) |
| 109 | return -ENOMEM; | 112 | return -ENOMEM; |
| 110 | 113 | ||
| 111 | list_for_each_entry(walk, &dev_cgroup->whitelist, list) { | 114 | list_for_each_entry(walk, &dev_cgroup->exceptions, list) { |
| 112 | if (walk->type != wh->type) | 115 | if (walk->type != ex->type) |
| 113 | continue; | 116 | continue; |
| 114 | if (walk->major != wh->major) | 117 | if (walk->major != ex->major) |
| 115 | continue; | 118 | continue; |
| 116 | if (walk->minor != wh->minor) | 119 | if (walk->minor != ex->minor) |
| 117 | continue; | 120 | continue; |
| 118 | 121 | ||
| 119 | walk->access |= wh->access; | 122 | walk->access |= ex->access; |
| 120 | kfree(whcopy); | 123 | kfree(excopy); |
| 121 | whcopy = NULL; | 124 | excopy = NULL; |
| 122 | } | 125 | } |
| 123 | 126 | ||
| 124 | if (whcopy != NULL) | 127 | if (excopy != NULL) |
| 125 | list_add_tail_rcu(&whcopy->list, &dev_cgroup->whitelist); | 128 | list_add_tail_rcu(&excopy->list, &dev_cgroup->exceptions); |
| 126 | return 0; | 129 | return 0; |
| 127 | } | 130 | } |
| 128 | 131 | ||
| 129 | /* | 132 | /* |
| 130 | * called under devcgroup_mutex | 133 | * called under devcgroup_mutex |
| 131 | */ | 134 | */ |
| 132 | static void dev_whitelist_rm(struct dev_cgroup *dev_cgroup, | 135 | static void dev_exception_rm(struct dev_cgroup *dev_cgroup, |
| 133 | struct dev_whitelist_item *wh) | 136 | struct dev_exception_item *ex) |
| 134 | { | 137 | { |
| 135 | struct dev_whitelist_item *walk, *tmp; | 138 | struct dev_exception_item *walk, *tmp; |
| 136 | 139 | ||
| 137 | list_for_each_entry_safe(walk, tmp, &dev_cgroup->whitelist, list) { | 140 | list_for_each_entry_safe(walk, tmp, &dev_cgroup->exceptions, list) { |
| 138 | if (walk->type == DEV_ALL) | 141 | if (walk->type != ex->type) |
| 139 | goto remove; | ||
| 140 | if (walk->type != wh->type) | ||
| 141 | continue; | 142 | continue; |
| 142 | if (walk->major != ~0 && walk->major != wh->major) | 143 | if (walk->major != ex->major) |
| 143 | continue; | 144 | continue; |
| 144 | if (walk->minor != ~0 && walk->minor != wh->minor) | 145 | if (walk->minor != ex->minor) |
| 145 | continue; | 146 | continue; |
| 146 | 147 | ||
| 147 | remove: | 148 | walk->access &= ~ex->access; |
| 148 | walk->access &= ~wh->access; | ||
| 149 | if (!walk->access) { | 149 | if (!walk->access) { |
| 150 | list_del_rcu(&walk->list); | 150 | list_del_rcu(&walk->list); |
| 151 | kfree_rcu(walk, rcu); | 151 | kfree_rcu(walk, rcu); |
| @@ -153,6 +153,22 @@ remove: | |||
| 153 | } | 153 | } |
| 154 | } | 154 | } |
| 155 | 155 | ||
| 156 | /** | ||
| 157 | * dev_exception_clean - frees all entries of the exception list | ||
| 158 | * @dev_cgroup: dev_cgroup with the exception list to be cleaned | ||
| 159 | * | ||
| 160 | * called under devcgroup_mutex | ||
| 161 | */ | ||
| 162 | static void dev_exception_clean(struct dev_cgroup *dev_cgroup) | ||
| 163 | { | ||
| 164 | struct dev_exception_item *ex, *tmp; | ||
| 165 | |||
| 166 | list_for_each_entry_safe(ex, tmp, &dev_cgroup->exceptions, list) { | ||
| 167 | list_del_rcu(&ex->list); | ||
| 168 | kfree_rcu(ex, rcu); | ||
| 169 | } | ||
| 170 | } | ||
| 171 | |||
| 156 | /* | 172 | /* |
| 157 | * called from kernel/cgroup.c with cgroup_lock() held. | 173 | * called from kernel/cgroup.c with cgroup_lock() held. |
| 158 | */ | 174 | */ |
| @@ -165,25 +181,17 @@ static struct cgroup_subsys_state *devcgroup_create(struct cgroup *cgroup) | |||
| 165 | dev_cgroup = kzalloc(sizeof(*dev_cgroup), GFP_KERNEL); | 181 | dev_cgroup = kzalloc(sizeof(*dev_cgroup), GFP_KERNEL); |
| 166 | if (!dev_cgroup) | 182 | if (!dev_cgroup) |
| 167 | return ERR_PTR(-ENOMEM); | 183 | return ERR_PTR(-ENOMEM); |
| 168 | INIT_LIST_HEAD(&dev_cgroup->whitelist); | 184 | INIT_LIST_HEAD(&dev_cgroup->exceptions); |
| 169 | parent_cgroup = cgroup->parent; | 185 | parent_cgroup = cgroup->parent; |
| 170 | 186 | ||
| 171 | if (parent_cgroup == NULL) { | 187 | if (parent_cgroup == NULL) |
| 172 | struct dev_whitelist_item *wh; | 188 | dev_cgroup->behavior = DEVCG_DEFAULT_ALLOW; |
| 173 | wh = kmalloc(sizeof(*wh), GFP_KERNEL); | 189 | else { |
| 174 | if (!wh) { | ||
| 175 | kfree(dev_cgroup); | ||
| 176 | return ERR_PTR(-ENOMEM); | ||
| 177 | } | ||
| 178 | wh->minor = wh->major = ~0; | ||
| 179 | wh->type = DEV_ALL; | ||
| 180 | wh->access = ACC_MASK; | ||
| 181 | list_add(&wh->list, &dev_cgroup->whitelist); | ||
| 182 | } else { | ||
| 183 | parent_dev_cgroup = cgroup_to_devcgroup(parent_cgroup); | 190 | parent_dev_cgroup = cgroup_to_devcgroup(parent_cgroup); |
| 184 | mutex_lock(&devcgroup_mutex); | 191 | mutex_lock(&devcgroup_mutex); |
| 185 | ret = dev_whitelist_copy(&dev_cgroup->whitelist, | 192 | ret = dev_exceptions_copy(&dev_cgroup->exceptions, |
| 186 | &parent_dev_cgroup->whitelist); | 193 | &parent_dev_cgroup->exceptions); |
| 194 | dev_cgroup->behavior = parent_dev_cgroup->behavior; | ||
| 187 | mutex_unlock(&devcgroup_mutex); | 195 | mutex_unlock(&devcgroup_mutex); |
| 188 | if (ret) { | 196 | if (ret) { |
| 189 | kfree(dev_cgroup); | 197 | kfree(dev_cgroup); |
| @@ -197,13 +205,9 @@ static struct cgroup_subsys_state *devcgroup_create(struct cgroup *cgroup) | |||
| 197 | static void devcgroup_destroy(struct cgroup *cgroup) | 205 | static void devcgroup_destroy(struct cgroup *cgroup) |
| 198 | { | 206 | { |
| 199 | struct dev_cgroup *dev_cgroup; | 207 | struct dev_cgroup *dev_cgroup; |
| 200 | struct dev_whitelist_item *wh, *tmp; | ||
| 201 | 208 | ||
| 202 | dev_cgroup = cgroup_to_devcgroup(cgroup); | 209 | dev_cgroup = cgroup_to_devcgroup(cgroup); |
| 203 | list_for_each_entry_safe(wh, tmp, &dev_cgroup->whitelist, list) { | 210 | dev_exception_clean(dev_cgroup); |
| 204 | list_del(&wh->list); | ||
| 205 | kfree(wh); | ||
| 206 | } | ||
| 207 | kfree(dev_cgroup); | 211 | kfree(dev_cgroup); |
| 208 | } | 212 | } |
| 209 | 213 | ||
| @@ -249,59 +253,87 @@ static int devcgroup_seq_read(struct cgroup *cgroup, struct cftype *cft, | |||
| 249 | struct seq_file *m) | 253 | struct seq_file *m) |
| 250 | { | 254 | { |
| 251 | struct dev_cgroup *devcgroup = cgroup_to_devcgroup(cgroup); | 255 | struct dev_cgroup *devcgroup = cgroup_to_devcgroup(cgroup); |
| 252 | struct dev_whitelist_item *wh; | 256 | struct dev_exception_item *ex; |
| 253 | char maj[MAJMINLEN], min[MAJMINLEN], acc[ACCLEN]; | 257 | char maj[MAJMINLEN], min[MAJMINLEN], acc[ACCLEN]; |
| 254 | 258 | ||
| 255 | rcu_read_lock(); | 259 | rcu_read_lock(); |
| 256 | list_for_each_entry_rcu(wh, &devcgroup->whitelist, list) { | 260 | /* |
| 257 | set_access(acc, wh->access); | 261 | * To preserve the compatibility: |
| 258 | set_majmin(maj, wh->major); | 262 | * - Only show the "all devices" when the default policy is to allow |
| 259 | set_majmin(min, wh->minor); | 263 | * - List the exceptions in case the default policy is to deny |
| 260 | seq_printf(m, "%c %s:%s %s\n", type_to_char(wh->type), | 264 | * This way, the file remains as a "whitelist of devices" |
| 265 | */ | ||
| 266 | if (devcgroup->behavior == DEVCG_DEFAULT_ALLOW) { | ||
| 267 | set_access(acc, ACC_MASK); | ||
| 268 | set_majmin(maj, ~0); | ||
| 269 | set_majmin(min, ~0); | ||
| 270 | seq_printf(m, "%c %s:%s %s\n", type_to_char(DEV_ALL), | ||
| 261 | maj, min, acc); | 271 | maj, min, acc); |
| 272 | } else { | ||
| 273 | list_for_each_entry_rcu(ex, &devcgroup->exceptions, list) { | ||
| 274 | set_access(acc, ex->access); | ||
| 275 | set_majmin(maj, ex->major); | ||
| 276 | set_majmin(min, ex->minor); | ||
| 277 | seq_printf(m, "%c %s:%s %s\n", type_to_char(ex->type), | ||
| 278 | maj, min, acc); | ||
| 279 | } | ||
| 262 | } | 280 | } |
| 263 | rcu_read_unlock(); | 281 | rcu_read_unlock(); |
| 264 | 282 | ||
| 265 | return 0; | 283 | return 0; |
| 266 | } | 284 | } |
| 267 | 285 | ||
| 268 | /* | 286 | /** |
| 269 | * may_access_whitelist: | 287 | * may_access - verifies if a new exception is part of what is allowed |
| 270 | * does the access granted to dev_cgroup c contain the access | 288 | * by a dev cgroup based on the default policy + |
| 271 | * requested in whitelist item refwh. | 289 | * exceptions. This is used to make sure a child cgroup |
| 272 | * return 1 if yes, 0 if no. | 290 | * won't have more privileges than its parent or to |
| 273 | * call with devcgroup_mutex held | 291 | * verify if a certain access is allowed. |
| 292 | * @dev_cgroup: dev cgroup to be tested against | ||
| 293 | * @refex: new exception | ||
| 274 | */ | 294 | */ |
| 275 | static int may_access_whitelist(struct dev_cgroup *c, | 295 | static int may_access(struct dev_cgroup *dev_cgroup, |
| 276 | struct dev_whitelist_item *refwh) | 296 | struct dev_exception_item *refex) |
| 277 | { | 297 | { |
| 278 | struct dev_whitelist_item *whitem; | 298 | struct dev_exception_item *ex; |
| 299 | bool match = false; | ||
| 279 | 300 | ||
| 280 | list_for_each_entry(whitem, &c->whitelist, list) { | 301 | list_for_each_entry_rcu(ex, &dev_cgroup->exceptions, list) { |
| 281 | if (whitem->type & DEV_ALL) | 302 | if ((refex->type & DEV_BLOCK) && !(ex->type & DEV_BLOCK)) |
| 282 | return 1; | ||
| 283 | if ((refwh->type & DEV_BLOCK) && !(whitem->type & DEV_BLOCK)) | ||
| 284 | continue; | 303 | continue; |
| 285 | if ((refwh->type & DEV_CHAR) && !(whitem->type & DEV_CHAR)) | 304 | if ((refex->type & DEV_CHAR) && !(ex->type & DEV_CHAR)) |
| 286 | continue; | 305 | continue; |
| 287 | if (whitem->major != ~0 && whitem->major != refwh->major) | 306 | if (ex->major != ~0 && ex->major != refex->major) |
| 288 | continue; | 307 | continue; |
| 289 | if (whitem->minor != ~0 && whitem->minor != refwh->minor) | 308 | if (ex->minor != ~0 && ex->minor != refex->minor) |
| 290 | continue; | 309 | continue; |
| 291 | if (refwh->access & (~whitem->access)) | 310 | if (refex->access & (~ex->access)) |
| 292 | continue; | 311 | continue; |
| 293 | return 1; | 312 | match = true; |
| 313 | break; | ||
| 294 | } | 314 | } |
| 315 | |||
| 316 | /* | ||
| 317 | * In two cases we'll consider this new exception valid: | ||
| 318 | * - the dev cgroup has its default policy to allow + exception list: | ||
| 319 | * the new exception should *not* match any of the exceptions | ||
| 320 | * (behavior == DEVCG_DEFAULT_ALLOW, !match) | ||
| 321 | * - the dev cgroup has its default policy to deny + exception list: | ||
| 322 | * the new exception *should* match the exceptions | ||
| 323 | * (behavior == DEVCG_DEFAULT_DENY, match) | ||
| 324 | */ | ||
| 325 | if ((dev_cgroup->behavior == DEVCG_DEFAULT_DENY) == match) | ||
| 326 | return 1; | ||
| 295 | return 0; | 327 | return 0; |
| 296 | } | 328 | } |
| 297 | 329 | ||
| 298 | /* | 330 | /* |
| 299 | * parent_has_perm: | 331 | * parent_has_perm: |
| 300 | * when adding a new allow rule to a device whitelist, the rule | 332 | * when adding a new allow rule to a device exception list, the rule |
| 301 | * must be allowed in the parent device | 333 | * must be allowed in the parent device |
| 302 | */ | 334 | */ |
| 303 | static int parent_has_perm(struct dev_cgroup *childcg, | 335 | static int parent_has_perm(struct dev_cgroup *childcg, |
| 304 | struct dev_whitelist_item *wh) | 336 | struct dev_exception_item *ex) |
| 305 | { | 337 | { |
| 306 | struct cgroup *pcg = childcg->css.cgroup->parent; | 338 | struct cgroup *pcg = childcg->css.cgroup->parent; |
| 307 | struct dev_cgroup *parent; | 339 | struct dev_cgroup *parent; |
| @@ -309,17 +341,30 @@ static int parent_has_perm(struct dev_cgroup *childcg, | |||
| 309 | if (!pcg) | 341 | if (!pcg) |
| 310 | return 1; | 342 | return 1; |
| 311 | parent = cgroup_to_devcgroup(pcg); | 343 | parent = cgroup_to_devcgroup(pcg); |
| 312 | return may_access_whitelist(parent, wh); | 344 | return may_access(parent, ex); |
| 345 | } | ||
| 346 | |||
| 347 | /** | ||
| 348 | * may_allow_all - checks if it's possible to change the behavior to | ||
| 349 | * allow based on parent's rules. | ||
| 350 | * @parent: device cgroup's parent | ||
| 351 | * returns: != 0 in case it's allowed, 0 otherwise | ||
| 352 | */ | ||
| 353 | static inline int may_allow_all(struct dev_cgroup *parent) | ||
| 354 | { | ||
| 355 | if (!parent) | ||
| 356 | return 1; | ||
| 357 | return parent->behavior == DEVCG_DEFAULT_ALLOW; | ||
| 313 | } | 358 | } |
| 314 | 359 | ||
| 315 | /* | 360 | /* |
| 316 | * Modify the whitelist using allow/deny rules. | 361 | * Modify the exception list using allow/deny rules. |
| 317 | * CAP_SYS_ADMIN is needed for this. It's at least separate from CAP_MKNOD | 362 | * CAP_SYS_ADMIN is needed for this. It's at least separate from CAP_MKNOD |
| 318 | * so we can give a container CAP_MKNOD to let it create devices but not | 363 | * so we can give a container CAP_MKNOD to let it create devices but not |
| 319 | * modify the whitelist. | 364 | * modify the exception list. |
| 320 | * It seems likely we'll want to add a CAP_CONTAINER capability to allow | 365 | * It seems likely we'll want to add a CAP_CONTAINER capability to allow |
| 321 | * us to also grant CAP_SYS_ADMIN to containers without giving away the | 366 | * us to also grant CAP_SYS_ADMIN to containers without giving away the |
| 322 | * device whitelist controls, but for now we'll stick with CAP_SYS_ADMIN | 367 | * device exception list controls, but for now we'll stick with CAP_SYS_ADMIN |
| 323 | * | 368 | * |
| 324 | * Taking rules away is always allowed (given CAP_SYS_ADMIN). Granting | 369 | * Taking rules away is always allowed (given CAP_SYS_ADMIN). Granting |
| 325 | * new access is only allowed if you're in the top-level cgroup, or your | 370 | * new access is only allowed if you're in the top-level cgroup, or your |
| @@ -329,28 +374,50 @@ static int devcgroup_update_access(struct dev_cgroup *devcgroup, | |||
| 329 | int filetype, const char *buffer) | 374 | int filetype, const char *buffer) |
| 330 | { | 375 | { |
| 331 | const char *b; | 376 | const char *b; |
| 332 | char *endp; | 377 | char temp[12]; /* 11 + 1 characters needed for a u32 */ |
| 333 | int count; | 378 | int count, rc; |
| 334 | struct dev_whitelist_item wh; | 379 | struct dev_exception_item ex; |
| 380 | struct cgroup *p = devcgroup->css.cgroup; | ||
| 381 | struct dev_cgroup *parent = NULL; | ||
| 335 | 382 | ||
| 336 | if (!capable(CAP_SYS_ADMIN)) | 383 | if (!capable(CAP_SYS_ADMIN)) |
| 337 | return -EPERM; | 384 | return -EPERM; |
| 338 | 385 | ||
| 339 | memset(&wh, 0, sizeof(wh)); | 386 | if (p->parent) |
| 387 | parent = cgroup_to_devcgroup(p->parent); | ||
| 388 | |||
| 389 | memset(&ex, 0, sizeof(ex)); | ||
| 340 | b = buffer; | 390 | b = buffer; |
| 341 | 391 | ||
| 342 | switch (*b) { | 392 | switch (*b) { |
| 343 | case 'a': | 393 | case 'a': |
| 344 | wh.type = DEV_ALL; | 394 | switch (filetype) { |
| 345 | wh.access = ACC_MASK; | 395 | case DEVCG_ALLOW: |
| 346 | wh.major = ~0; | 396 | if (!may_allow_all(parent)) |
| 347 | wh.minor = ~0; | 397 | return -EPERM; |
| 348 | goto handle; | 398 | dev_exception_clean(devcgroup); |
| 399 | devcgroup->behavior = DEVCG_DEFAULT_ALLOW; | ||
| 400 | if (!parent) | ||
| 401 | break; | ||
| 402 | |||
| 403 | rc = dev_exceptions_copy(&devcgroup->exceptions, | ||
| 404 | &parent->exceptions); | ||
| 405 | if (rc) | ||
| 406 | return rc; | ||
| 407 | break; | ||
| 408 | case DEVCG_DENY: | ||
| 409 | dev_exception_clean(devcgroup); | ||
| 410 | devcgroup->behavior = DEVCG_DEFAULT_DENY; | ||
| 411 | break; | ||
| 412 | default: | ||
| 413 | return -EINVAL; | ||
| 414 | } | ||
| 415 | return 0; | ||
| 349 | case 'b': | 416 | case 'b': |
| 350 | wh.type = DEV_BLOCK; | 417 | ex.type = DEV_BLOCK; |
| 351 | break; | 418 | break; |
| 352 | case 'c': | 419 | case 'c': |
| 353 | wh.type = DEV_CHAR; | 420 | ex.type = DEV_CHAR; |
| 354 | break; | 421 | break; |
| 355 | default: | 422 | default: |
| 356 | return -EINVAL; | 423 | return -EINVAL; |
| @@ -360,11 +427,19 @@ static int devcgroup_update_access(struct dev_cgroup *devcgroup, | |||
| 360 | return -EINVAL; | 427 | return -EINVAL; |
| 361 | b++; | 428 | b++; |
| 362 | if (*b == '*') { | 429 | if (*b == '*') { |
| 363 | wh.major = ~0; | 430 | ex.major = ~0; |
| 364 | b++; | 431 | b++; |
| 365 | } else if (isdigit(*b)) { | 432 | } else if (isdigit(*b)) { |
| 366 | wh.major = simple_strtoul(b, &endp, 10); | 433 | memset(temp, 0, sizeof(temp)); |
| 367 | b = endp; | 434 | for (count = 0; count < sizeof(temp) - 1; count++) { |
| 435 | temp[count] = *b; | ||
| 436 | b++; | ||
| 437 | if (!isdigit(*b)) | ||
| 438 | break; | ||
| 439 | } | ||
| 440 | rc = kstrtou32(temp, 10, &ex.major); | ||
| 441 | if (rc) | ||
| 442 | return -EINVAL; | ||
| 368 | } else { | 443 | } else { |
| 369 | return -EINVAL; | 444 | return -EINVAL; |
| 370 | } | 445 | } |
| @@ -374,11 +449,19 @@ static int devcgroup_update_access(struct dev_cgroup *devcgroup, | |||
| 374 | 449 | ||
| 375 | /* read minor */ | 450 | /* read minor */ |
| 376 | if (*b == '*') { | 451 | if (*b == '*') { |
| 377 | wh.minor = ~0; | 452 | ex.minor = ~0; |
| 378 | b++; | 453 | b++; |
| 379 | } else if (isdigit(*b)) { | 454 | } else if (isdigit(*b)) { |
| 380 | wh.minor = simple_strtoul(b, &endp, 10); | 455 | memset(temp, 0, sizeof(temp)); |
| 381 | b = endp; | 456 | for (count = 0; count < sizeof(temp) - 1; count++) { |
| 457 | temp[count] = *b; | ||
| 458 | b++; | ||
| 459 | if (!isdigit(*b)) | ||
| 460 | break; | ||
| 461 | } | ||
| 462 | rc = kstrtou32(temp, 10, &ex.minor); | ||
| 463 | if (rc) | ||
| 464 | return -EINVAL; | ||
| 382 | } else { | 465 | } else { |
| 383 | return -EINVAL; | 466 | return -EINVAL; |
| 384 | } | 467 | } |
| @@ -387,13 +470,13 @@ static int devcgroup_update_access(struct dev_cgroup *devcgroup, | |||
| 387 | for (b++, count = 0; count < 3; count++, b++) { | 470 | for (b++, count = 0; count < 3; count++, b++) { |
| 388 | switch (*b) { | 471 | switch (*b) { |
| 389 | case 'r': | 472 | case 'r': |
| 390 | wh.access |= ACC_READ; | 473 | ex.access |= ACC_READ; |
| 391 | break; | 474 | break; |
| 392 | case 'w': | 475 | case 'w': |
| 393 | wh.access |= ACC_WRITE; | 476 | ex.access |= ACC_WRITE; |
| 394 | break; | 477 | break; |
| 395 | case 'm': | 478 | case 'm': |
| 396 | wh.access |= ACC_MKNOD; | 479 | ex.access |= ACC_MKNOD; |
| 397 | break; | 480 | break; |
| 398 | case '\n': | 481 | case '\n': |
| 399 | case '\0': | 482 | case '\0': |
| @@ -404,15 +487,31 @@ static int devcgroup_update_access(struct dev_cgroup *devcgroup, | |||
| 404 | } | 487 | } |
| 405 | } | 488 | } |
| 406 | 489 | ||
| 407 | handle: | ||
| 408 | switch (filetype) { | 490 | switch (filetype) { |
| 409 | case DEVCG_ALLOW: | 491 | case DEVCG_ALLOW: |
| 410 | if (!parent_has_perm(devcgroup, &wh)) | 492 | if (!parent_has_perm(devcgroup, &ex)) |
| 411 | return -EPERM; | 493 | return -EPERM; |
| 412 | return dev_whitelist_add(devcgroup, &wh); | 494 | /* |
| 495 | * If the default policy is to allow by default, try to remove | ||
| 496 | * an matching exception instead. And be silent about it: we | ||
| 497 | * don't want to break compatibility | ||
| 498 | */ | ||
| 499 | if (devcgroup->behavior == DEVCG_DEFAULT_ALLOW) { | ||
| 500 | dev_exception_rm(devcgroup, &ex); | ||
| 501 | return 0; | ||
| 502 | } | ||
| 503 | return dev_exception_add(devcgroup, &ex); | ||
| 413 | case DEVCG_DENY: | 504 | case DEVCG_DENY: |
| 414 | dev_whitelist_rm(devcgroup, &wh); | 505 | /* |
| 415 | break; | 506 | * If the default policy is to deny by default, try to remove |
| 507 | * an matching exception instead. And be silent about it: we | ||
| 508 | * don't want to break compatibility | ||
| 509 | */ | ||
| 510 | if (devcgroup->behavior == DEVCG_DEFAULT_DENY) { | ||
| 511 | dev_exception_rm(devcgroup, &ex); | ||
| 512 | return 0; | ||
| 513 | } | ||
| 514 | return dev_exception_add(devcgroup, &ex); | ||
| 416 | default: | 515 | default: |
| 417 | return -EINVAL; | 516 | return -EINVAL; |
| 418 | } | 517 | } |
| @@ -457,75 +556,81 @@ struct cgroup_subsys devices_subsys = { | |||
| 457 | .destroy = devcgroup_destroy, | 556 | .destroy = devcgroup_destroy, |
| 458 | .subsys_id = devices_subsys_id, | 557 | .subsys_id = devices_subsys_id, |
| 459 | .base_cftypes = dev_cgroup_files, | 558 | .base_cftypes = dev_cgroup_files, |
| 559 | |||
| 560 | /* | ||
| 561 | * While devices cgroup has the rudimentary hierarchy support which | ||
| 562 | * checks the parent's restriction, it doesn't properly propagates | ||
| 563 | * config changes in ancestors to their descendents. A child | ||
| 564 | * should only be allowed to add more restrictions to the parent's | ||
| 565 | * configuration. Fix it and remove the following. | ||
| 566 | */ | ||
| 567 | .broken_hierarchy = true, | ||
| 460 | }; | 568 | }; |
| 461 | 569 | ||
| 462 | int __devcgroup_inode_permission(struct inode *inode, int mask) | 570 | /** |
| 571 | * __devcgroup_check_permission - checks if an inode operation is permitted | ||
| 572 | * @dev_cgroup: the dev cgroup to be tested against | ||
| 573 | * @type: device type | ||
| 574 | * @major: device major number | ||
| 575 | * @minor: device minor number | ||
| 576 | * @access: combination of ACC_WRITE, ACC_READ and ACC_MKNOD | ||
| 577 | * | ||
| 578 | * returns 0 on success, -EPERM case the operation is not permitted | ||
| 579 | */ | ||
| 580 | static int __devcgroup_check_permission(short type, u32 major, u32 minor, | ||
| 581 | short access) | ||
| 463 | { | 582 | { |
| 464 | struct dev_cgroup *dev_cgroup; | 583 | struct dev_cgroup *dev_cgroup; |
| 465 | struct dev_whitelist_item *wh; | 584 | struct dev_exception_item ex; |
| 585 | int rc; | ||
| 466 | 586 | ||
| 467 | rcu_read_lock(); | 587 | memset(&ex, 0, sizeof(ex)); |
| 588 | ex.type = type; | ||
| 589 | ex.major = major; | ||
| 590 | ex.minor = minor; | ||
| 591 | ex.access = access; | ||
| 468 | 592 | ||
| 593 | rcu_read_lock(); | ||
| 469 | dev_cgroup = task_devcgroup(current); | 594 | dev_cgroup = task_devcgroup(current); |
| 595 | rc = may_access(dev_cgroup, &ex); | ||
| 596 | rcu_read_unlock(); | ||
| 470 | 597 | ||
| 471 | list_for_each_entry_rcu(wh, &dev_cgroup->whitelist, list) { | 598 | if (!rc) |
| 472 | if (wh->type & DEV_ALL) | 599 | return -EPERM; |
| 473 | goto found; | ||
| 474 | if ((wh->type & DEV_BLOCK) && !S_ISBLK(inode->i_mode)) | ||
| 475 | continue; | ||
| 476 | if ((wh->type & DEV_CHAR) && !S_ISCHR(inode->i_mode)) | ||
| 477 | continue; | ||
| 478 | if (wh->major != ~0 && wh->major != imajor(inode)) | ||
| 479 | continue; | ||
| 480 | if (wh->minor != ~0 && wh->minor != iminor(inode)) | ||
| 481 | continue; | ||
| 482 | |||
| 483 | if ((mask & MAY_WRITE) && !(wh->access & ACC_WRITE)) | ||
| 484 | continue; | ||
| 485 | if ((mask & MAY_READ) && !(wh->access & ACC_READ)) | ||
| 486 | continue; | ||
| 487 | found: | ||
| 488 | rcu_read_unlock(); | ||
| 489 | return 0; | ||
| 490 | } | ||
| 491 | 600 | ||
| 492 | rcu_read_unlock(); | 601 | return 0; |
| 602 | } | ||
| 493 | 603 | ||
| 494 | return -EPERM; | 604 | int __devcgroup_inode_permission(struct inode *inode, int mask) |
| 605 | { | ||
| 606 | short type, access = 0; | ||
| 607 | |||
| 608 | if (S_ISBLK(inode->i_mode)) | ||
| 609 | type = DEV_BLOCK; | ||
| 610 | if (S_ISCHR(inode->i_mode)) | ||
| 611 | type = DEV_CHAR; | ||
| 612 | if (mask & MAY_WRITE) | ||
| 613 | access |= ACC_WRITE; | ||
| 614 | if (mask & MAY_READ) | ||
| 615 | access |= ACC_READ; | ||
| 616 | |||
| 617 | return __devcgroup_check_permission(type, imajor(inode), iminor(inode), | ||
| 618 | access); | ||
| 495 | } | 619 | } |
| 496 | 620 | ||
| 497 | int devcgroup_inode_mknod(int mode, dev_t dev) | 621 | int devcgroup_inode_mknod(int mode, dev_t dev) |
| 498 | { | 622 | { |
| 499 | struct dev_cgroup *dev_cgroup; | 623 | short type; |
| 500 | struct dev_whitelist_item *wh; | ||
| 501 | 624 | ||
| 502 | if (!S_ISBLK(mode) && !S_ISCHR(mode)) | 625 | if (!S_ISBLK(mode) && !S_ISCHR(mode)) |
| 503 | return 0; | 626 | return 0; |
| 504 | 627 | ||
| 505 | rcu_read_lock(); | 628 | if (S_ISBLK(mode)) |
| 506 | 629 | type = DEV_BLOCK; | |
| 507 | dev_cgroup = task_devcgroup(current); | 630 | else |
| 508 | 631 | type = DEV_CHAR; | |
| 509 | list_for_each_entry_rcu(wh, &dev_cgroup->whitelist, list) { | ||
| 510 | if (wh->type & DEV_ALL) | ||
| 511 | goto found; | ||
| 512 | if ((wh->type & DEV_BLOCK) && !S_ISBLK(mode)) | ||
| 513 | continue; | ||
| 514 | if ((wh->type & DEV_CHAR) && !S_ISCHR(mode)) | ||
| 515 | continue; | ||
| 516 | if (wh->major != ~0 && wh->major != MAJOR(dev)) | ||
| 517 | continue; | ||
| 518 | if (wh->minor != ~0 && wh->minor != MINOR(dev)) | ||
| 519 | continue; | ||
| 520 | |||
| 521 | if (!(wh->access & ACC_MKNOD)) | ||
| 522 | continue; | ||
| 523 | found: | ||
| 524 | rcu_read_unlock(); | ||
| 525 | return 0; | ||
| 526 | } | ||
| 527 | 632 | ||
| 528 | rcu_read_unlock(); | 633 | return __devcgroup_check_permission(type, MAJOR(dev), MINOR(dev), |
| 634 | ACC_MKNOD); | ||
| 529 | 635 | ||
| 530 | return -EPERM; | ||
| 531 | } | 636 | } |
diff --git a/security/integrity/evm/evm_crypto.c b/security/integrity/evm/evm_crypto.c index 49a464f5595b..dfb26918699c 100644 --- a/security/integrity/evm/evm_crypto.c +++ b/security/integrity/evm/evm_crypto.c | |||
| @@ -106,8 +106,8 @@ static void hmac_add_misc(struct shash_desc *desc, struct inode *inode, | |||
| 106 | memset(&hmac_misc, 0, sizeof hmac_misc); | 106 | memset(&hmac_misc, 0, sizeof hmac_misc); |
| 107 | hmac_misc.ino = inode->i_ino; | 107 | hmac_misc.ino = inode->i_ino; |
| 108 | hmac_misc.generation = inode->i_generation; | 108 | hmac_misc.generation = inode->i_generation; |
| 109 | hmac_misc.uid = inode->i_uid; | 109 | hmac_misc.uid = from_kuid(&init_user_ns, inode->i_uid); |
| 110 | hmac_misc.gid = inode->i_gid; | 110 | hmac_misc.gid = from_kgid(&init_user_ns, inode->i_gid); |
| 111 | hmac_misc.mode = inode->i_mode; | 111 | hmac_misc.mode = inode->i_mode; |
| 112 | crypto_shash_update(desc, (const u8 *)&hmac_misc, sizeof hmac_misc); | 112 | crypto_shash_update(desc, (const u8 *)&hmac_misc, sizeof hmac_misc); |
| 113 | crypto_shash_final(desc, digest); | 113 | crypto_shash_final(desc, digest); |
diff --git a/security/integrity/evm/evm_main.c b/security/integrity/evm/evm_main.c index 8901501425f4..eb5484504f50 100644 --- a/security/integrity/evm/evm_main.c +++ b/security/integrity/evm/evm_main.c | |||
| @@ -34,6 +34,9 @@ char *evm_config_xattrnames[] = { | |||
| 34 | #ifdef CONFIG_SECURITY_SMACK | 34 | #ifdef CONFIG_SECURITY_SMACK |
| 35 | XATTR_NAME_SMACK, | 35 | XATTR_NAME_SMACK, |
| 36 | #endif | 36 | #endif |
| 37 | #ifdef CONFIG_IMA_APPRAISE | ||
| 38 | XATTR_NAME_IMA, | ||
| 39 | #endif | ||
| 37 | XATTR_NAME_CAPS, | 40 | XATTR_NAME_CAPS, |
| 38 | NULL | 41 | NULL |
| 39 | }; | 42 | }; |
diff --git a/security/integrity/iint.c b/security/integrity/iint.c index 399641c3e846..d82a5a13d855 100644 --- a/security/integrity/iint.c +++ b/security/integrity/iint.c | |||
| @@ -22,7 +22,7 @@ | |||
| 22 | #include "integrity.h" | 22 | #include "integrity.h" |
| 23 | 23 | ||
| 24 | static struct rb_root integrity_iint_tree = RB_ROOT; | 24 | static struct rb_root integrity_iint_tree = RB_ROOT; |
| 25 | static DEFINE_SPINLOCK(integrity_iint_lock); | 25 | static DEFINE_RWLOCK(integrity_iint_lock); |
| 26 | static struct kmem_cache *iint_cache __read_mostly; | 26 | static struct kmem_cache *iint_cache __read_mostly; |
| 27 | 27 | ||
| 28 | int iint_initialized; | 28 | int iint_initialized; |
| @@ -35,8 +35,6 @@ static struct integrity_iint_cache *__integrity_iint_find(struct inode *inode) | |||
| 35 | struct integrity_iint_cache *iint; | 35 | struct integrity_iint_cache *iint; |
| 36 | struct rb_node *n = integrity_iint_tree.rb_node; | 36 | struct rb_node *n = integrity_iint_tree.rb_node; |
| 37 | 37 | ||
| 38 | assert_spin_locked(&integrity_iint_lock); | ||
| 39 | |||
| 40 | while (n) { | 38 | while (n) { |
| 41 | iint = rb_entry(n, struct integrity_iint_cache, rb_node); | 39 | iint = rb_entry(n, struct integrity_iint_cache, rb_node); |
| 42 | 40 | ||
| @@ -63,9 +61,9 @@ struct integrity_iint_cache *integrity_iint_find(struct inode *inode) | |||
| 63 | if (!IS_IMA(inode)) | 61 | if (!IS_IMA(inode)) |
| 64 | return NULL; | 62 | return NULL; |
| 65 | 63 | ||
| 66 | spin_lock(&integrity_iint_lock); | 64 | read_lock(&integrity_iint_lock); |
| 67 | iint = __integrity_iint_find(inode); | 65 | iint = __integrity_iint_find(inode); |
| 68 | spin_unlock(&integrity_iint_lock); | 66 | read_unlock(&integrity_iint_lock); |
| 69 | 67 | ||
| 70 | return iint; | 68 | return iint; |
| 71 | } | 69 | } |
| @@ -74,59 +72,53 @@ static void iint_free(struct integrity_iint_cache *iint) | |||
| 74 | { | 72 | { |
| 75 | iint->version = 0; | 73 | iint->version = 0; |
| 76 | iint->flags = 0UL; | 74 | iint->flags = 0UL; |
| 75 | iint->ima_status = INTEGRITY_UNKNOWN; | ||
| 77 | iint->evm_status = INTEGRITY_UNKNOWN; | 76 | iint->evm_status = INTEGRITY_UNKNOWN; |
| 78 | kmem_cache_free(iint_cache, iint); | 77 | kmem_cache_free(iint_cache, iint); |
| 79 | } | 78 | } |
| 80 | 79 | ||
| 81 | /** | 80 | /** |
| 82 | * integrity_inode_alloc - allocate an iint associated with an inode | 81 | * integrity_inode_get - find or allocate an iint associated with an inode |
| 83 | * @inode: pointer to the inode | 82 | * @inode: pointer to the inode |
| 83 | * @return: allocated iint | ||
| 84 | * | ||
| 85 | * Caller must lock i_mutex | ||
| 84 | */ | 86 | */ |
| 85 | int integrity_inode_alloc(struct inode *inode) | 87 | struct integrity_iint_cache *integrity_inode_get(struct inode *inode) |
| 86 | { | 88 | { |
| 87 | struct rb_node **p; | 89 | struct rb_node **p; |
| 88 | struct rb_node *new_node, *parent = NULL; | 90 | struct rb_node *node, *parent = NULL; |
| 89 | struct integrity_iint_cache *new_iint, *test_iint; | 91 | struct integrity_iint_cache *iint, *test_iint; |
| 90 | int rc; | ||
| 91 | 92 | ||
| 92 | new_iint = kmem_cache_alloc(iint_cache, GFP_NOFS); | 93 | iint = integrity_iint_find(inode); |
| 93 | if (!new_iint) | 94 | if (iint) |
| 94 | return -ENOMEM; | 95 | return iint; |
| 95 | 96 | ||
| 96 | new_iint->inode = inode; | 97 | iint = kmem_cache_alloc(iint_cache, GFP_NOFS); |
| 97 | new_node = &new_iint->rb_node; | 98 | if (!iint) |
| 99 | return NULL; | ||
| 98 | 100 | ||
| 99 | mutex_lock(&inode->i_mutex); /* i_flags */ | 101 | write_lock(&integrity_iint_lock); |
| 100 | spin_lock(&integrity_iint_lock); | ||
| 101 | 102 | ||
| 102 | p = &integrity_iint_tree.rb_node; | 103 | p = &integrity_iint_tree.rb_node; |
| 103 | while (*p) { | 104 | while (*p) { |
| 104 | parent = *p; | 105 | parent = *p; |
| 105 | test_iint = rb_entry(parent, struct integrity_iint_cache, | 106 | test_iint = rb_entry(parent, struct integrity_iint_cache, |
| 106 | rb_node); | 107 | rb_node); |
| 107 | rc = -EEXIST; | ||
| 108 | if (inode < test_iint->inode) | 108 | if (inode < test_iint->inode) |
| 109 | p = &(*p)->rb_left; | 109 | p = &(*p)->rb_left; |
| 110 | else if (inode > test_iint->inode) | ||
| 111 | p = &(*p)->rb_right; | ||
| 112 | else | 110 | else |
| 113 | goto out_err; | 111 | p = &(*p)->rb_right; |
| 114 | } | 112 | } |
| 115 | 113 | ||
| 114 | iint->inode = inode; | ||
| 115 | node = &iint->rb_node; | ||
| 116 | inode->i_flags |= S_IMA; | 116 | inode->i_flags |= S_IMA; |
| 117 | rb_link_node(new_node, parent, p); | 117 | rb_link_node(node, parent, p); |
| 118 | rb_insert_color(new_node, &integrity_iint_tree); | 118 | rb_insert_color(node, &integrity_iint_tree); |
| 119 | 119 | ||
| 120 | spin_unlock(&integrity_iint_lock); | 120 | write_unlock(&integrity_iint_lock); |
| 121 | mutex_unlock(&inode->i_mutex); /* i_flags */ | 121 | return iint; |
| 122 | |||
| 123 | return 0; | ||
| 124 | out_err: | ||
| 125 | spin_unlock(&integrity_iint_lock); | ||
| 126 | mutex_unlock(&inode->i_mutex); /* i_flags */ | ||
| 127 | iint_free(new_iint); | ||
| 128 | |||
| 129 | return rc; | ||
| 130 | } | 122 | } |
| 131 | 123 | ||
| 132 | /** | 124 | /** |
| @@ -142,10 +134,10 @@ void integrity_inode_free(struct inode *inode) | |||
| 142 | if (!IS_IMA(inode)) | 134 | if (!IS_IMA(inode)) |
| 143 | return; | 135 | return; |
| 144 | 136 | ||
| 145 | spin_lock(&integrity_iint_lock); | 137 | write_lock(&integrity_iint_lock); |
| 146 | iint = __integrity_iint_find(inode); | 138 | iint = __integrity_iint_find(inode); |
| 147 | rb_erase(&iint->rb_node, &integrity_iint_tree); | 139 | rb_erase(&iint->rb_node, &integrity_iint_tree); |
| 148 | spin_unlock(&integrity_iint_lock); | 140 | write_unlock(&integrity_iint_lock); |
| 149 | 141 | ||
| 150 | iint_free(iint); | 142 | iint_free(iint); |
| 151 | } | 143 | } |
| @@ -157,7 +149,7 @@ static void init_once(void *foo) | |||
| 157 | memset(iint, 0, sizeof *iint); | 149 | memset(iint, 0, sizeof *iint); |
| 158 | iint->version = 0; | 150 | iint->version = 0; |
| 159 | iint->flags = 0UL; | 151 | iint->flags = 0UL; |
| 160 | mutex_init(&iint->mutex); | 152 | iint->ima_status = INTEGRITY_UNKNOWN; |
| 161 | iint->evm_status = INTEGRITY_UNKNOWN; | 153 | iint->evm_status = INTEGRITY_UNKNOWN; |
| 162 | } | 154 | } |
| 163 | 155 | ||
diff --git a/security/integrity/ima/Kconfig b/security/integrity/ima/Kconfig index b9c1219924f1..d232c73647ae 100644 --- a/security/integrity/ima/Kconfig +++ b/security/integrity/ima/Kconfig | |||
| @@ -11,6 +11,7 @@ config IMA | |||
| 11 | select CRYPTO_SHA1 | 11 | select CRYPTO_SHA1 |
| 12 | select TCG_TPM if HAS_IOMEM && !UML | 12 | select TCG_TPM if HAS_IOMEM && !UML |
| 13 | select TCG_TIS if TCG_TPM && X86 | 13 | select TCG_TIS if TCG_TPM && X86 |
| 14 | select TCG_IBMVTPM if TCG_TPM && PPC64 | ||
| 14 | help | 15 | help |
| 15 | The Trusted Computing Group(TCG) runtime Integrity | 16 | The Trusted Computing Group(TCG) runtime Integrity |
| 16 | Measurement Architecture(IMA) maintains a list of hash | 17 | Measurement Architecture(IMA) maintains a list of hash |
| @@ -55,3 +56,18 @@ config IMA_LSM_RULES | |||
| 55 | default y | 56 | default y |
| 56 | help | 57 | help |
| 57 | Disabling this option will disregard LSM based policy rules. | 58 | Disabling this option will disregard LSM based policy rules. |
| 59 | |||
| 60 | config IMA_APPRAISE | ||
| 61 | bool "Appraise integrity measurements" | ||
| 62 | depends on IMA | ||
| 63 | default n | ||
| 64 | help | ||
| 65 | This option enables local measurement integrity appraisal. | ||
| 66 | It requires the system to be labeled with a security extended | ||
| 67 | attribute containing the file hash measurement. To protect | ||
| 68 | the security extended attributes from offline attack, enable | ||
| 69 | and configure EVM. | ||
| 70 | |||
| 71 | For more information on integrity appraisal refer to: | ||
| 72 | <http://linux-ima.sourceforge.net> | ||
| 73 | If unsure, say N. | ||
diff --git a/security/integrity/ima/Makefile b/security/integrity/ima/Makefile index 5f740f6971e1..3f2ca6bdc384 100644 --- a/security/integrity/ima/Makefile +++ b/security/integrity/ima/Makefile | |||
| @@ -8,3 +8,4 @@ obj-$(CONFIG_IMA) += ima.o | |||
| 8 | ima-y := ima_fs.o ima_queue.o ima_init.o ima_main.o ima_crypto.o ima_api.o \ | 8 | ima-y := ima_fs.o ima_queue.o ima_init.o ima_main.o ima_crypto.o ima_api.o \ |
| 9 | ima_policy.o | 9 | ima_policy.o |
| 10 | ima-$(CONFIG_IMA_AUDIT) += ima_audit.o | 10 | ima-$(CONFIG_IMA_AUDIT) += ima_audit.o |
| 11 | ima-$(CONFIG_IMA_APPRAISE) += ima_appraise.o | ||
diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h index e7c99fd0d223..6ee8826662cc 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h | |||
| @@ -40,6 +40,7 @@ enum tpm_pcrs { TPM_PCR0 = 0, TPM_PCR8 = 8 }; | |||
| 40 | extern int ima_initialized; | 40 | extern int ima_initialized; |
| 41 | extern int ima_used_chip; | 41 | extern int ima_used_chip; |
| 42 | extern char *ima_hash; | 42 | extern char *ima_hash; |
| 43 | extern int ima_appraise; | ||
| 43 | 44 | ||
| 44 | /* IMA inode template definition */ | 45 | /* IMA inode template definition */ |
| 45 | struct ima_template_data { | 46 | struct ima_template_data { |
| @@ -107,11 +108,14 @@ static inline unsigned long ima_hash_key(u8 *digest) | |||
| 107 | } | 108 | } |
| 108 | 109 | ||
| 109 | /* LIM API function definitions */ | 110 | /* LIM API function definitions */ |
| 111 | int ima_get_action(struct inode *inode, int mask, int function); | ||
| 110 | int ima_must_measure(struct inode *inode, int mask, int function); | 112 | int ima_must_measure(struct inode *inode, int mask, int function); |
| 111 | int ima_collect_measurement(struct integrity_iint_cache *iint, | 113 | int ima_collect_measurement(struct integrity_iint_cache *iint, |
| 112 | struct file *file); | 114 | struct file *file); |
| 113 | void ima_store_measurement(struct integrity_iint_cache *iint, struct file *file, | 115 | void ima_store_measurement(struct integrity_iint_cache *iint, struct file *file, |
| 114 | const unsigned char *filename); | 116 | const unsigned char *filename); |
| 117 | void ima_audit_measurement(struct integrity_iint_cache *iint, | ||
| 118 | const unsigned char *filename); | ||
| 115 | int ima_store_template(struct ima_template_entry *entry, int violation, | 119 | int ima_store_template(struct ima_template_entry *entry, int violation, |
| 116 | struct inode *inode); | 120 | struct inode *inode); |
| 117 | void ima_template_show(struct seq_file *m, void *e, enum ima_show_type show); | 121 | void ima_template_show(struct seq_file *m, void *e, enum ima_show_type show); |
| @@ -123,14 +127,45 @@ struct integrity_iint_cache *integrity_iint_insert(struct inode *inode); | |||
| 123 | struct integrity_iint_cache *integrity_iint_find(struct inode *inode); | 127 | struct integrity_iint_cache *integrity_iint_find(struct inode *inode); |
| 124 | 128 | ||
| 125 | /* IMA policy related functions */ | 129 | /* IMA policy related functions */ |
| 126 | enum ima_hooks { FILE_CHECK = 1, FILE_MMAP, BPRM_CHECK }; | 130 | enum ima_hooks { FILE_CHECK = 1, FILE_MMAP, BPRM_CHECK, POST_SETATTR }; |
| 127 | 131 | ||
| 128 | int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask); | 132 | int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask, |
| 133 | int flags); | ||
| 129 | void ima_init_policy(void); | 134 | void ima_init_policy(void); |
| 130 | void ima_update_policy(void); | 135 | void ima_update_policy(void); |
| 131 | ssize_t ima_parse_add_rule(char *); | 136 | ssize_t ima_parse_add_rule(char *); |
| 132 | void ima_delete_rules(void); | 137 | void ima_delete_rules(void); |
| 133 | 138 | ||
| 139 | /* Appraise integrity measurements */ | ||
| 140 | #define IMA_APPRAISE_ENFORCE 0x01 | ||
| 141 | #define IMA_APPRAISE_FIX 0x02 | ||
| 142 | |||
| 143 | #ifdef CONFIG_IMA_APPRAISE | ||
| 144 | int ima_appraise_measurement(struct integrity_iint_cache *iint, | ||
| 145 | struct file *file, const unsigned char *filename); | ||
| 146 | int ima_must_appraise(struct inode *inode, int mask, enum ima_hooks func); | ||
| 147 | void ima_update_xattr(struct integrity_iint_cache *iint, struct file *file); | ||
| 148 | |||
| 149 | #else | ||
| 150 | static inline int ima_appraise_measurement(struct integrity_iint_cache *iint, | ||
| 151 | struct file *file, | ||
| 152 | const unsigned char *filename) | ||
| 153 | { | ||
| 154 | return INTEGRITY_UNKNOWN; | ||
| 155 | } | ||
| 156 | |||
| 157 | static inline int ima_must_appraise(struct inode *inode, int mask, | ||
| 158 | enum ima_hooks func) | ||
| 159 | { | ||
| 160 | return 0; | ||
| 161 | } | ||
| 162 | |||
| 163 | static inline void ima_update_xattr(struct integrity_iint_cache *iint, | ||
| 164 | struct file *file) | ||
| 165 | { | ||
| 166 | } | ||
| 167 | #endif | ||
| 168 | |||
| 134 | /* LSM based policy rules require audit */ | 169 | /* LSM based policy rules require audit */ |
| 135 | #ifdef CONFIG_IMA_LSM_RULES | 170 | #ifdef CONFIG_IMA_LSM_RULES |
| 136 | 171 | ||
diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c index 032ff03ad907..b356884fb3ef 100644 --- a/security/integrity/ima/ima_api.c +++ b/security/integrity/ima/ima_api.c | |||
| @@ -9,13 +9,17 @@ | |||
| 9 | * License. | 9 | * License. |
| 10 | * | 10 | * |
| 11 | * File: ima_api.c | 11 | * File: ima_api.c |
| 12 | * Implements must_measure, collect_measurement, store_measurement, | 12 | * Implements must_appraise_or_measure, collect_measurement, |
| 13 | * and store_template. | 13 | * appraise_measurement, store_measurement and store_template. |
| 14 | */ | 14 | */ |
| 15 | #include <linux/module.h> | 15 | #include <linux/module.h> |
| 16 | #include <linux/slab.h> | 16 | #include <linux/slab.h> |
| 17 | 17 | #include <linux/file.h> | |
| 18 | #include <linux/fs.h> | ||
| 19 | #include <linux/xattr.h> | ||
| 20 | #include <linux/evm.h> | ||
| 18 | #include "ima.h" | 21 | #include "ima.h" |
| 22 | |||
| 19 | static const char *IMA_TEMPLATE_NAME = "ima"; | 23 | static const char *IMA_TEMPLATE_NAME = "ima"; |
| 20 | 24 | ||
| 21 | /* | 25 | /* |
| @@ -93,7 +97,7 @@ err_out: | |||
| 93 | } | 97 | } |
| 94 | 98 | ||
| 95 | /** | 99 | /** |
| 96 | * ima_must_measure - measure decision based on policy. | 100 | * ima_get_action - appraise & measure decision based on policy. |
| 97 | * @inode: pointer to inode to measure | 101 | * @inode: pointer to inode to measure |
| 98 | * @mask: contains the permission mask (MAY_READ, MAY_WRITE, MAY_EXECUTE) | 102 | * @mask: contains the permission mask (MAY_READ, MAY_WRITE, MAY_EXECUTE) |
| 99 | * @function: calling function (FILE_CHECK, BPRM_CHECK, FILE_MMAP) | 103 | * @function: calling function (FILE_CHECK, BPRM_CHECK, FILE_MMAP) |
| @@ -105,15 +109,22 @@ err_out: | |||
| 105 | * mask: contains the permission mask | 109 | * mask: contains the permission mask |
| 106 | * fsmagic: hex value | 110 | * fsmagic: hex value |
| 107 | * | 111 | * |
| 108 | * Return 0 to measure. For matching a DONT_MEASURE policy, no policy, | 112 | * Returns IMA_MEASURE, IMA_APPRAISE mask. |
| 109 | * or other error, return an error code. | 113 | * |
| 110 | */ | 114 | */ |
| 111 | int ima_must_measure(struct inode *inode, int mask, int function) | 115 | int ima_get_action(struct inode *inode, int mask, int function) |
| 112 | { | 116 | { |
| 113 | int must_measure; | 117 | int flags = IMA_MEASURE | IMA_AUDIT | IMA_APPRAISE; |
| 118 | |||
| 119 | if (!ima_appraise) | ||
| 120 | flags &= ~IMA_APPRAISE; | ||
| 114 | 121 | ||
| 115 | must_measure = ima_match_policy(inode, function, mask); | 122 | return ima_match_policy(inode, function, mask, flags); |
| 116 | return must_measure ? 0 : -EACCES; | 123 | } |
| 124 | |||
| 125 | int ima_must_measure(struct inode *inode, int mask, int function) | ||
| 126 | { | ||
| 127 | return ima_match_policy(inode, function, mask, IMA_MEASURE); | ||
| 117 | } | 128 | } |
| 118 | 129 | ||
| 119 | /* | 130 | /* |
| @@ -129,16 +140,24 @@ int ima_must_measure(struct inode *inode, int mask, int function) | |||
| 129 | int ima_collect_measurement(struct integrity_iint_cache *iint, | 140 | int ima_collect_measurement(struct integrity_iint_cache *iint, |
| 130 | struct file *file) | 141 | struct file *file) |
| 131 | { | 142 | { |
| 132 | int result = -EEXIST; | 143 | struct inode *inode = file->f_dentry->d_inode; |
| 144 | const char *filename = file->f_dentry->d_name.name; | ||
| 145 | int result = 0; | ||
| 133 | 146 | ||
| 134 | if (!(iint->flags & IMA_MEASURED)) { | 147 | if (!(iint->flags & IMA_COLLECTED)) { |
| 135 | u64 i_version = file->f_dentry->d_inode->i_version; | 148 | u64 i_version = file->f_dentry->d_inode->i_version; |
| 136 | 149 | ||
| 137 | memset(iint->digest, 0, IMA_DIGEST_SIZE); | 150 | iint->ima_xattr.type = IMA_XATTR_DIGEST; |
| 138 | result = ima_calc_hash(file, iint->digest); | 151 | result = ima_calc_hash(file, iint->ima_xattr.digest); |
| 139 | if (!result) | 152 | if (!result) { |
| 140 | iint->version = i_version; | 153 | iint->version = i_version; |
| 154 | iint->flags |= IMA_COLLECTED; | ||
| 155 | } | ||
| 141 | } | 156 | } |
| 157 | if (result) | ||
| 158 | integrity_audit_msg(AUDIT_INTEGRITY_DATA, inode, | ||
| 159 | filename, "collect_data", "failed", | ||
| 160 | result, 0); | ||
| 142 | return result; | 161 | return result; |
| 143 | } | 162 | } |
| 144 | 163 | ||
| @@ -167,6 +186,9 @@ void ima_store_measurement(struct integrity_iint_cache *iint, | |||
| 167 | struct ima_template_entry *entry; | 186 | struct ima_template_entry *entry; |
| 168 | int violation = 0; | 187 | int violation = 0; |
| 169 | 188 | ||
| 189 | if (iint->flags & IMA_MEASURED) | ||
| 190 | return; | ||
| 191 | |||
| 170 | entry = kmalloc(sizeof(*entry), GFP_KERNEL); | 192 | entry = kmalloc(sizeof(*entry), GFP_KERNEL); |
| 171 | if (!entry) { | 193 | if (!entry) { |
| 172 | integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode, filename, | 194 | integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode, filename, |
| @@ -174,7 +196,7 @@ void ima_store_measurement(struct integrity_iint_cache *iint, | |||
| 174 | return; | 196 | return; |
| 175 | } | 197 | } |
| 176 | memset(&entry->template, 0, sizeof(entry->template)); | 198 | memset(&entry->template, 0, sizeof(entry->template)); |
| 177 | memcpy(entry->template.digest, iint->digest, IMA_DIGEST_SIZE); | 199 | memcpy(entry->template.digest, iint->ima_xattr.digest, IMA_DIGEST_SIZE); |
| 178 | strcpy(entry->template.file_name, | 200 | strcpy(entry->template.file_name, |
| 179 | (strlen(filename) > IMA_EVENT_NAME_LEN_MAX) ? | 201 | (strlen(filename) > IMA_EVENT_NAME_LEN_MAX) ? |
| 180 | file->f_dentry->d_name.name : filename); | 202 | file->f_dentry->d_name.name : filename); |
| @@ -185,3 +207,33 @@ void ima_store_measurement(struct integrity_iint_cache *iint, | |||
| 185 | if (result < 0) | 207 | if (result < 0) |
| 186 | kfree(entry); | 208 | kfree(entry); |
| 187 | } | 209 | } |
| 210 | |||
| 211 | void ima_audit_measurement(struct integrity_iint_cache *iint, | ||
| 212 | const unsigned char *filename) | ||
| 213 | { | ||
| 214 | struct audit_buffer *ab; | ||
| 215 | char hash[(IMA_DIGEST_SIZE * 2) + 1]; | ||
| 216 | int i; | ||
| 217 | |||
| 218 | if (iint->flags & IMA_AUDITED) | ||
| 219 | return; | ||
| 220 | |||
| 221 | for (i = 0; i < IMA_DIGEST_SIZE; i++) | ||
| 222 | hex_byte_pack(hash + (i * 2), iint->ima_xattr.digest[i]); | ||
| 223 | hash[i * 2] = '\0'; | ||
| 224 | |||
| 225 | ab = audit_log_start(current->audit_context, GFP_KERNEL, | ||
| 226 | AUDIT_INTEGRITY_RULE); | ||
| 227 | if (!ab) | ||
| 228 | return; | ||
| 229 | |||
| 230 | audit_log_format(ab, "file="); | ||
| 231 | audit_log_untrustedstring(ab, filename); | ||
| 232 | audit_log_format(ab, " hash="); | ||
| 233 | audit_log_untrustedstring(ab, hash); | ||
| 234 | |||
| 235 | audit_log_task_info(ab, current); | ||
| 236 | audit_log_end(ab); | ||
| 237 | |||
| 238 | iint->flags |= IMA_AUDITED; | ||
| 239 | } | ||
diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c new file mode 100644 index 000000000000..bdc8ba1d1d27 --- /dev/null +++ b/security/integrity/ima/ima_appraise.c | |||
| @@ -0,0 +1,263 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2011 IBM Corporation | ||
| 3 | * | ||
| 4 | * Author: | ||
| 5 | * Mimi Zohar <zohar@us.ibm.com> | ||
| 6 | * | ||
| 7 | * This program is free software; you can redistribute it and/or modify | ||
| 8 | * it under the terms of the GNU General Public License as published by | ||
| 9 | * the Free Software Foundation, version 2 of the License. | ||
| 10 | */ | ||
| 11 | #include <linux/module.h> | ||
| 12 | #include <linux/file.h> | ||
| 13 | #include <linux/fs.h> | ||
| 14 | #include <linux/xattr.h> | ||
| 15 | #include <linux/magic.h> | ||
| 16 | #include <linux/ima.h> | ||
| 17 | #include <linux/evm.h> | ||
| 18 | |||
| 19 | #include "ima.h" | ||
| 20 | |||
| 21 | static int __init default_appraise_setup(char *str) | ||
| 22 | { | ||
| 23 | if (strncmp(str, "off", 3) == 0) | ||
| 24 | ima_appraise = 0; | ||
| 25 | else if (strncmp(str, "fix", 3) == 0) | ||
| 26 | ima_appraise = IMA_APPRAISE_FIX; | ||
| 27 | return 1; | ||
| 28 | } | ||
| 29 | |||
| 30 | __setup("ima_appraise=", default_appraise_setup); | ||
| 31 | |||
| 32 | /* | ||
| 33 | * ima_must_appraise - set appraise flag | ||
| 34 | * | ||
| 35 | * Return 1 to appraise | ||
| 36 | */ | ||
| 37 | int ima_must_appraise(struct inode *inode, int mask, enum ima_hooks func) | ||
| 38 | { | ||
| 39 | if (!ima_appraise) | ||
| 40 | return 0; | ||
| 41 | |||
| 42 | return ima_match_policy(inode, func, mask, IMA_APPRAISE); | ||
| 43 | } | ||
| 44 | |||
| 45 | static void ima_fix_xattr(struct dentry *dentry, | ||
| 46 | struct integrity_iint_cache *iint) | ||
| 47 | { | ||
| 48 | iint->ima_xattr.type = IMA_XATTR_DIGEST; | ||
| 49 | __vfs_setxattr_noperm(dentry, XATTR_NAME_IMA, (u8 *)&iint->ima_xattr, | ||
| 50 | sizeof iint->ima_xattr, 0); | ||
| 51 | } | ||
| 52 | |||
| 53 | /* | ||
| 54 | * ima_appraise_measurement - appraise file measurement | ||
| 55 | * | ||
| 56 | * Call evm_verifyxattr() to verify the integrity of 'security.ima'. | ||
| 57 | * Assuming success, compare the xattr hash with the collected measurement. | ||
| 58 | * | ||
| 59 | * Return 0 on success, error code otherwise | ||
| 60 | */ | ||
| 61 | int ima_appraise_measurement(struct integrity_iint_cache *iint, | ||
| 62 | struct file *file, const unsigned char *filename) | ||
| 63 | { | ||
| 64 | struct dentry *dentry = file->f_dentry; | ||
| 65 | struct inode *inode = dentry->d_inode; | ||
| 66 | struct evm_ima_xattr_data *xattr_value = NULL; | ||
| 67 | enum integrity_status status = INTEGRITY_UNKNOWN; | ||
| 68 | const char *op = "appraise_data"; | ||
| 69 | char *cause = "unknown"; | ||
| 70 | int rc; | ||
| 71 | |||
| 72 | if (!ima_appraise) | ||
| 73 | return 0; | ||
| 74 | if (!inode->i_op->getxattr) | ||
| 75 | return INTEGRITY_UNKNOWN; | ||
| 76 | |||
| 77 | if (iint->flags & IMA_APPRAISED) | ||
| 78 | return iint->ima_status; | ||
| 79 | |||
| 80 | rc = vfs_getxattr_alloc(dentry, XATTR_NAME_IMA, (char **)&xattr_value, | ||
| 81 | 0, GFP_NOFS); | ||
| 82 | if (rc <= 0) { | ||
| 83 | if (rc && rc != -ENODATA) | ||
| 84 | goto out; | ||
| 85 | |||
| 86 | cause = "missing-hash"; | ||
| 87 | status = | ||
| 88 | (inode->i_size == 0) ? INTEGRITY_PASS : INTEGRITY_NOLABEL; | ||
| 89 | goto out; | ||
| 90 | } | ||
| 91 | |||
| 92 | status = evm_verifyxattr(dentry, XATTR_NAME_IMA, xattr_value, rc, iint); | ||
| 93 | if ((status != INTEGRITY_PASS) && (status != INTEGRITY_UNKNOWN)) { | ||
| 94 | if ((status == INTEGRITY_NOLABEL) | ||
| 95 | || (status == INTEGRITY_NOXATTRS)) | ||
| 96 | cause = "missing-HMAC"; | ||
| 97 | else if (status == INTEGRITY_FAIL) | ||
| 98 | cause = "invalid-HMAC"; | ||
| 99 | goto out; | ||
| 100 | } | ||
| 101 | |||
| 102 | switch (xattr_value->type) { | ||
| 103 | case IMA_XATTR_DIGEST: | ||
| 104 | rc = memcmp(xattr_value->digest, iint->ima_xattr.digest, | ||
| 105 | IMA_DIGEST_SIZE); | ||
| 106 | if (rc) { | ||
| 107 | cause = "invalid-hash"; | ||
| 108 | status = INTEGRITY_FAIL; | ||
| 109 | print_hex_dump_bytes("security.ima: ", DUMP_PREFIX_NONE, | ||
| 110 | xattr_value, sizeof(*xattr_value)); | ||
| 111 | print_hex_dump_bytes("collected: ", DUMP_PREFIX_NONE, | ||
| 112 | (u8 *)&iint->ima_xattr, | ||
| 113 | sizeof iint->ima_xattr); | ||
| 114 | break; | ||
| 115 | } | ||
| 116 | status = INTEGRITY_PASS; | ||
| 117 | break; | ||
| 118 | case EVM_IMA_XATTR_DIGSIG: | ||
| 119 | iint->flags |= IMA_DIGSIG; | ||
| 120 | rc = integrity_digsig_verify(INTEGRITY_KEYRING_IMA, | ||
| 121 | xattr_value->digest, rc - 1, | ||
| 122 | iint->ima_xattr.digest, | ||
| 123 | IMA_DIGEST_SIZE); | ||
| 124 | if (rc == -EOPNOTSUPP) { | ||
| 125 | status = INTEGRITY_UNKNOWN; | ||
| 126 | } else if (rc) { | ||
| 127 | cause = "invalid-signature"; | ||
| 128 | status = INTEGRITY_FAIL; | ||
| 129 | } else { | ||
| 130 | status = INTEGRITY_PASS; | ||
| 131 | } | ||
| 132 | break; | ||
| 133 | default: | ||
| 134 | status = INTEGRITY_UNKNOWN; | ||
| 135 | cause = "unknown-ima-data"; | ||
| 136 | break; | ||
| 137 | } | ||
| 138 | |||
| 139 | out: | ||
| 140 | if (status != INTEGRITY_PASS) { | ||
| 141 | if ((ima_appraise & IMA_APPRAISE_FIX) && | ||
| 142 | (!xattr_value || | ||
| 143 | xattr_value->type != EVM_IMA_XATTR_DIGSIG)) { | ||
| 144 | ima_fix_xattr(dentry, iint); | ||
| 145 | status = INTEGRITY_PASS; | ||
| 146 | } | ||
| 147 | integrity_audit_msg(AUDIT_INTEGRITY_DATA, inode, filename, | ||
| 148 | op, cause, rc, 0); | ||
| 149 | } else { | ||
| 150 | iint->flags |= IMA_APPRAISED; | ||
| 151 | } | ||
| 152 | iint->ima_status = status; | ||
| 153 | kfree(xattr_value); | ||
| 154 | return status; | ||
| 155 | } | ||
| 156 | |||
| 157 | /* | ||
| 158 | * ima_update_xattr - update 'security.ima' hash value | ||
| 159 | */ | ||
| 160 | void ima_update_xattr(struct integrity_iint_cache *iint, struct file *file) | ||
| 161 | { | ||
| 162 | struct dentry *dentry = file->f_dentry; | ||
| 163 | int rc = 0; | ||
| 164 | |||
| 165 | /* do not collect and update hash for digital signatures */ | ||
| 166 | if (iint->flags & IMA_DIGSIG) | ||
| 167 | return; | ||
| 168 | |||
| 169 | rc = ima_collect_measurement(iint, file); | ||
| 170 | if (rc < 0) | ||
| 171 | return; | ||
| 172 | |||
| 173 | ima_fix_xattr(dentry, iint); | ||
| 174 | } | ||
| 175 | |||
| 176 | /** | ||
| 177 | * ima_inode_post_setattr - reflect file metadata changes | ||
| 178 | * @dentry: pointer to the affected dentry | ||
| 179 | * | ||
| 180 | * Changes to a dentry's metadata might result in needing to appraise. | ||
| 181 | * | ||
| 182 | * This function is called from notify_change(), which expects the caller | ||
| 183 | * to lock the inode's i_mutex. | ||
| 184 | */ | ||
| 185 | void ima_inode_post_setattr(struct dentry *dentry) | ||
| 186 | { | ||
| 187 | struct inode *inode = dentry->d_inode; | ||
| 188 | struct integrity_iint_cache *iint; | ||
| 189 | int must_appraise, rc; | ||
| 190 | |||
| 191 | if (!ima_initialized || !ima_appraise || !S_ISREG(inode->i_mode) | ||
| 192 | || !inode->i_op->removexattr) | ||
| 193 | return; | ||
| 194 | |||
| 195 | must_appraise = ima_must_appraise(inode, MAY_ACCESS, POST_SETATTR); | ||
| 196 | iint = integrity_iint_find(inode); | ||
| 197 | if (iint) { | ||
| 198 | if (must_appraise) | ||
| 199 | iint->flags |= IMA_APPRAISE; | ||
| 200 | else | ||
| 201 | iint->flags &= ~(IMA_APPRAISE | IMA_APPRAISED); | ||
| 202 | } | ||
| 203 | if (!must_appraise) | ||
| 204 | rc = inode->i_op->removexattr(dentry, XATTR_NAME_IMA); | ||
| 205 | return; | ||
| 206 | } | ||
| 207 | |||
| 208 | /* | ||
| 209 | * ima_protect_xattr - protect 'security.ima' | ||
| 210 | * | ||
| 211 | * Ensure that not just anyone can modify or remove 'security.ima'. | ||
| 212 | */ | ||
| 213 | static int ima_protect_xattr(struct dentry *dentry, const char *xattr_name, | ||
| 214 | const void *xattr_value, size_t xattr_value_len) | ||
| 215 | { | ||
| 216 | if (strcmp(xattr_name, XATTR_NAME_IMA) == 0) { | ||
| 217 | if (!capable(CAP_SYS_ADMIN)) | ||
| 218 | return -EPERM; | ||
| 219 | return 1; | ||
| 220 | } | ||
| 221 | return 0; | ||
| 222 | } | ||
| 223 | |||
| 224 | static void ima_reset_appraise_flags(struct inode *inode) | ||
| 225 | { | ||
| 226 | struct integrity_iint_cache *iint; | ||
| 227 | |||
| 228 | if (!ima_initialized || !ima_appraise || !S_ISREG(inode->i_mode)) | ||
| 229 | return; | ||
| 230 | |||
| 231 | iint = integrity_iint_find(inode); | ||
| 232 | if (!iint) | ||
| 233 | return; | ||
| 234 | |||
| 235 | iint->flags &= ~IMA_DONE_MASK; | ||
| 236 | return; | ||
| 237 | } | ||
| 238 | |||
| 239 | int ima_inode_setxattr(struct dentry *dentry, const char *xattr_name, | ||
| 240 | const void *xattr_value, size_t xattr_value_len) | ||
| 241 | { | ||
| 242 | int result; | ||
| 243 | |||
| 244 | result = ima_protect_xattr(dentry, xattr_name, xattr_value, | ||
| 245 | xattr_value_len); | ||
| 246 | if (result == 1) { | ||
| 247 | ima_reset_appraise_flags(dentry->d_inode); | ||
| 248 | result = 0; | ||
| 249 | } | ||
| 250 | return result; | ||
| 251 | } | ||
| 252 | |||
| 253 | int ima_inode_removexattr(struct dentry *dentry, const char *xattr_name) | ||
| 254 | { | ||
| 255 | int result; | ||
| 256 | |||
| 257 | result = ima_protect_xattr(dentry, xattr_name, NULL, 0); | ||
| 258 | if (result == 1) { | ||
| 259 | ima_reset_appraise_flags(dentry->d_inode); | ||
| 260 | result = 0; | ||
| 261 | } | ||
| 262 | return result; | ||
| 263 | } | ||
diff --git a/security/integrity/ima/ima_audit.c b/security/integrity/ima/ima_audit.c index 7a57f6769e9c..c586faae8fd6 100644 --- a/security/integrity/ima/ima_audit.c +++ b/security/integrity/ima/ima_audit.c | |||
| @@ -39,8 +39,9 @@ void integrity_audit_msg(int audit_msgno, struct inode *inode, | |||
| 39 | 39 | ||
| 40 | ab = audit_log_start(current->audit_context, GFP_KERNEL, audit_msgno); | 40 | ab = audit_log_start(current->audit_context, GFP_KERNEL, audit_msgno); |
| 41 | audit_log_format(ab, "pid=%d uid=%u auid=%u ses=%u", | 41 | audit_log_format(ab, "pid=%d uid=%u auid=%u ses=%u", |
| 42 | current->pid, current_cred()->uid, | 42 | current->pid, |
| 43 | audit_get_loginuid(current), | 43 | from_kuid(&init_user_ns, current_cred()->uid), |
| 44 | from_kuid(&init_user_ns, audit_get_loginuid(current)), | ||
| 44 | audit_get_sessionid(current)); | 45 | audit_get_sessionid(current)); |
| 45 | audit_log_task_context(ab); | 46 | audit_log_task_context(ab); |
| 46 | audit_log_format(ab, " op="); | 47 | audit_log_format(ab, " op="); |
diff --git a/security/integrity/ima/ima_crypto.c b/security/integrity/ima/ima_crypto.c index 9b3ade7468b2..b21ee5b5495a 100644 --- a/security/integrity/ima/ima_crypto.c +++ b/security/integrity/ima/ima_crypto.c | |||
| @@ -48,7 +48,7 @@ int ima_calc_hash(struct file *file, char *digest) | |||
| 48 | struct scatterlist sg[1]; | 48 | struct scatterlist sg[1]; |
| 49 | loff_t i_size, offset = 0; | 49 | loff_t i_size, offset = 0; |
| 50 | char *rbuf; | 50 | char *rbuf; |
| 51 | int rc; | 51 | int rc, read = 0; |
| 52 | 52 | ||
| 53 | rc = init_desc(&desc); | 53 | rc = init_desc(&desc); |
| 54 | if (rc != 0) | 54 | if (rc != 0) |
| @@ -59,6 +59,10 @@ int ima_calc_hash(struct file *file, char *digest) | |||
| 59 | rc = -ENOMEM; | 59 | rc = -ENOMEM; |
| 60 | goto out; | 60 | goto out; |
| 61 | } | 61 | } |
| 62 | if (!(file->f_mode & FMODE_READ)) { | ||
| 63 | file->f_mode |= FMODE_READ; | ||
| 64 | read = 1; | ||
| 65 | } | ||
| 62 | i_size = i_size_read(file->f_dentry->d_inode); | 66 | i_size = i_size_read(file->f_dentry->d_inode); |
| 63 | while (offset < i_size) { | 67 | while (offset < i_size) { |
| 64 | int rbuf_len; | 68 | int rbuf_len; |
| @@ -80,6 +84,8 @@ int ima_calc_hash(struct file *file, char *digest) | |||
| 80 | kfree(rbuf); | 84 | kfree(rbuf); |
| 81 | if (!rc) | 85 | if (!rc) |
| 82 | rc = crypto_hash_final(&desc, digest); | 86 | rc = crypto_hash_final(&desc, digest); |
| 87 | if (read) | ||
| 88 | file->f_mode &= ~FMODE_READ; | ||
| 83 | out: | 89 | out: |
| 84 | crypto_free_hash(desc.tfm); | 90 | crypto_free_hash(desc.tfm); |
| 85 | return rc; | 91 | return rc; |
diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c index be8294915cf7..73c9a268253e 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c | |||
| @@ -22,12 +22,19 @@ | |||
| 22 | #include <linux/mount.h> | 22 | #include <linux/mount.h> |
| 23 | #include <linux/mman.h> | 23 | #include <linux/mman.h> |
| 24 | #include <linux/slab.h> | 24 | #include <linux/slab.h> |
| 25 | #include <linux/xattr.h> | ||
| 25 | #include <linux/ima.h> | 26 | #include <linux/ima.h> |
| 26 | 27 | ||
| 27 | #include "ima.h" | 28 | #include "ima.h" |
| 28 | 29 | ||
| 29 | int ima_initialized; | 30 | int ima_initialized; |
| 30 | 31 | ||
| 32 | #ifdef CONFIG_IMA_APPRAISE | ||
| 33 | int ima_appraise = IMA_APPRAISE_ENFORCE; | ||
| 34 | #else | ||
| 35 | int ima_appraise; | ||
| 36 | #endif | ||
| 37 | |||
| 31 | char *ima_hash = "sha1"; | 38 | char *ima_hash = "sha1"; |
| 32 | static int __init hash_setup(char *str) | 39 | static int __init hash_setup(char *str) |
| 33 | { | 40 | { |
| @@ -52,7 +59,7 @@ static void ima_rdwr_violation_check(struct file *file) | |||
| 52 | struct dentry *dentry = file->f_path.dentry; | 59 | struct dentry *dentry = file->f_path.dentry; |
| 53 | struct inode *inode = dentry->d_inode; | 60 | struct inode *inode = dentry->d_inode; |
| 54 | fmode_t mode = file->f_mode; | 61 | fmode_t mode = file->f_mode; |
| 55 | int rc; | 62 | int must_measure; |
| 56 | bool send_tomtou = false, send_writers = false; | 63 | bool send_tomtou = false, send_writers = false; |
| 57 | unsigned char *pathname = NULL, *pathbuf = NULL; | 64 | unsigned char *pathname = NULL, *pathbuf = NULL; |
| 58 | 65 | ||
| @@ -67,8 +74,8 @@ static void ima_rdwr_violation_check(struct file *file) | |||
| 67 | goto out; | 74 | goto out; |
| 68 | } | 75 | } |
| 69 | 76 | ||
| 70 | rc = ima_must_measure(inode, MAY_READ, FILE_CHECK); | 77 | must_measure = ima_must_measure(inode, MAY_READ, FILE_CHECK); |
| 71 | if (rc < 0) | 78 | if (!must_measure) |
| 72 | goto out; | 79 | goto out; |
| 73 | 80 | ||
| 74 | if (atomic_read(&inode->i_writecount) > 0) | 81 | if (atomic_read(&inode->i_writecount) > 0) |
| @@ -100,17 +107,21 @@ out: | |||
| 100 | } | 107 | } |
| 101 | 108 | ||
| 102 | static void ima_check_last_writer(struct integrity_iint_cache *iint, | 109 | static void ima_check_last_writer(struct integrity_iint_cache *iint, |
| 103 | struct inode *inode, | 110 | struct inode *inode, struct file *file) |
| 104 | struct file *file) | ||
| 105 | { | 111 | { |
| 106 | fmode_t mode = file->f_mode; | 112 | fmode_t mode = file->f_mode; |
| 107 | 113 | ||
| 108 | mutex_lock(&iint->mutex); | 114 | if (!(mode & FMODE_WRITE)) |
| 109 | if (mode & FMODE_WRITE && | 115 | return; |
| 110 | atomic_read(&inode->i_writecount) == 1 && | 116 | |
| 111 | iint->version != inode->i_version) | 117 | mutex_lock(&inode->i_mutex); |
| 112 | iint->flags &= ~IMA_MEASURED; | 118 | if (atomic_read(&inode->i_writecount) == 1 && |
| 113 | mutex_unlock(&iint->mutex); | 119 | iint->version != inode->i_version) { |
| 120 | iint->flags &= ~IMA_DONE_MASK; | ||
| 121 | if (iint->flags & IMA_APPRAISE) | ||
| 122 | ima_update_xattr(iint, file); | ||
| 123 | } | ||
| 124 | mutex_unlock(&inode->i_mutex); | ||
| 114 | } | 125 | } |
| 115 | 126 | ||
| 116 | /** | 127 | /** |
| @@ -140,28 +151,37 @@ static int process_measurement(struct file *file, const unsigned char *filename, | |||
| 140 | struct inode *inode = file->f_dentry->d_inode; | 151 | struct inode *inode = file->f_dentry->d_inode; |
| 141 | struct integrity_iint_cache *iint; | 152 | struct integrity_iint_cache *iint; |
| 142 | unsigned char *pathname = NULL, *pathbuf = NULL; | 153 | unsigned char *pathname = NULL, *pathbuf = NULL; |
| 143 | int rc = 0; | 154 | int rc = -ENOMEM, action, must_appraise; |
| 144 | 155 | ||
| 145 | if (!ima_initialized || !S_ISREG(inode->i_mode)) | 156 | if (!ima_initialized || !S_ISREG(inode->i_mode)) |
| 146 | return 0; | 157 | return 0; |
| 147 | 158 | ||
| 148 | rc = ima_must_measure(inode, mask, function); | 159 | /* Determine if in appraise/audit/measurement policy, |
| 149 | if (rc != 0) | 160 | * returns IMA_MEASURE, IMA_APPRAISE, IMA_AUDIT bitmask. */ |
| 150 | return rc; | 161 | action = ima_get_action(inode, mask, function); |
| 151 | retry: | 162 | if (!action) |
| 152 | iint = integrity_iint_find(inode); | 163 | return 0; |
| 153 | if (!iint) { | ||
| 154 | rc = integrity_inode_alloc(inode); | ||
| 155 | if (!rc || rc == -EEXIST) | ||
| 156 | goto retry; | ||
| 157 | return rc; | ||
| 158 | } | ||
| 159 | 164 | ||
| 160 | mutex_lock(&iint->mutex); | 165 | must_appraise = action & IMA_APPRAISE; |
| 161 | 166 | ||
| 162 | rc = iint->flags & IMA_MEASURED ? 1 : 0; | 167 | mutex_lock(&inode->i_mutex); |
| 163 | if (rc != 0) | 168 | |
| 169 | iint = integrity_inode_get(inode); | ||
| 170 | if (!iint) | ||
| 171 | goto out; | ||
| 172 | |||
| 173 | /* Determine if already appraised/measured based on bitmask | ||
| 174 | * (IMA_MEASURE, IMA_MEASURED, IMA_APPRAISE, IMA_APPRAISED, | ||
| 175 | * IMA_AUDIT, IMA_AUDITED) */ | ||
| 176 | iint->flags |= action; | ||
| 177 | action &= ~((iint->flags & IMA_DONE_MASK) >> 1); | ||
| 178 | |||
| 179 | /* Nothing to do, just return existing appraised status */ | ||
| 180 | if (!action) { | ||
| 181 | if (iint->flags & IMA_APPRAISED) | ||
| 182 | rc = iint->ima_status; | ||
| 164 | goto out; | 183 | goto out; |
| 184 | } | ||
| 165 | 185 | ||
| 166 | rc = ima_collect_measurement(iint, file); | 186 | rc = ima_collect_measurement(iint, file); |
| 167 | if (rc != 0) | 187 | if (rc != 0) |
| @@ -177,11 +197,18 @@ retry: | |||
| 177 | pathname = NULL; | 197 | pathname = NULL; |
| 178 | } | 198 | } |
| 179 | } | 199 | } |
| 180 | ima_store_measurement(iint, file, !pathname ? filename : pathname); | 200 | if (action & IMA_MEASURE) |
| 201 | ima_store_measurement(iint, file, | ||
| 202 | !pathname ? filename : pathname); | ||
| 203 | if (action & IMA_APPRAISE) | ||
| 204 | rc = ima_appraise_measurement(iint, file, | ||
| 205 | !pathname ? filename : pathname); | ||
| 206 | if (action & IMA_AUDIT) | ||
| 207 | ima_audit_measurement(iint, !pathname ? filename : pathname); | ||
| 181 | kfree(pathbuf); | 208 | kfree(pathbuf); |
| 182 | out: | 209 | out: |
| 183 | mutex_unlock(&iint->mutex); | 210 | mutex_unlock(&inode->i_mutex); |
| 184 | return rc; | 211 | return (rc && must_appraise) ? -EACCES : 0; |
| 185 | } | 212 | } |
| 186 | 213 | ||
| 187 | /** | 214 | /** |
| @@ -197,14 +224,14 @@ out: | |||
| 197 | */ | 224 | */ |
| 198 | int ima_file_mmap(struct file *file, unsigned long prot) | 225 | int ima_file_mmap(struct file *file, unsigned long prot) |
| 199 | { | 226 | { |
| 200 | int rc; | 227 | int rc = 0; |
| 201 | 228 | ||
| 202 | if (!file) | 229 | if (!file) |
| 203 | return 0; | 230 | return 0; |
| 204 | if (prot & PROT_EXEC) | 231 | if (prot & PROT_EXEC) |
| 205 | rc = process_measurement(file, file->f_dentry->d_name.name, | 232 | rc = process_measurement(file, file->f_dentry->d_name.name, |
| 206 | MAY_EXEC, FILE_MMAP); | 233 | MAY_EXEC, FILE_MMAP); |
| 207 | return 0; | 234 | return (ima_appraise & IMA_APPRAISE_ENFORCE) ? rc : 0; |
| 208 | } | 235 | } |
| 209 | 236 | ||
| 210 | /** | 237 | /** |
| @@ -228,7 +255,7 @@ int ima_bprm_check(struct linux_binprm *bprm) | |||
| 228 | (strcmp(bprm->filename, bprm->interp) == 0) ? | 255 | (strcmp(bprm->filename, bprm->interp) == 0) ? |
| 229 | bprm->filename : bprm->interp, | 256 | bprm->filename : bprm->interp, |
| 230 | MAY_EXEC, BPRM_CHECK); | 257 | MAY_EXEC, BPRM_CHECK); |
| 231 | return 0; | 258 | return (ima_appraise & IMA_APPRAISE_ENFORCE) ? rc : 0; |
| 232 | } | 259 | } |
| 233 | 260 | ||
| 234 | /** | 261 | /** |
| @@ -249,7 +276,7 @@ int ima_file_check(struct file *file, int mask) | |||
| 249 | rc = process_measurement(file, file->f_dentry->d_name.name, | 276 | rc = process_measurement(file, file->f_dentry->d_name.name, |
| 250 | mask & (MAY_READ | MAY_WRITE | MAY_EXEC), | 277 | mask & (MAY_READ | MAY_WRITE | MAY_EXEC), |
| 251 | FILE_CHECK); | 278 | FILE_CHECK); |
| 252 | return 0; | 279 | return (ima_appraise & IMA_APPRAISE_ENFORCE) ? rc : 0; |
| 253 | } | 280 | } |
| 254 | EXPORT_SYMBOL_GPL(ima_file_check); | 281 | EXPORT_SYMBOL_GPL(ima_file_check); |
| 255 | 282 | ||
diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c index 1a9583008aae..c7dacd2eab7a 100644 --- a/security/integrity/ima/ima_policy.c +++ b/security/integrity/ima/ima_policy.c | |||
| @@ -24,22 +24,29 @@ | |||
| 24 | #define IMA_MASK 0x0002 | 24 | #define IMA_MASK 0x0002 |
| 25 | #define IMA_FSMAGIC 0x0004 | 25 | #define IMA_FSMAGIC 0x0004 |
| 26 | #define IMA_UID 0x0008 | 26 | #define IMA_UID 0x0008 |
| 27 | #define IMA_FOWNER 0x0010 | ||
| 27 | 28 | ||
| 28 | enum ima_action { UNKNOWN = -1, DONT_MEASURE = 0, MEASURE }; | 29 | #define UNKNOWN 0 |
| 30 | #define MEASURE 0x0001 /* same as IMA_MEASURE */ | ||
| 31 | #define DONT_MEASURE 0x0002 | ||
| 32 | #define APPRAISE 0x0004 /* same as IMA_APPRAISE */ | ||
| 33 | #define DONT_APPRAISE 0x0008 | ||
| 34 | #define AUDIT 0x0040 | ||
| 29 | 35 | ||
| 30 | #define MAX_LSM_RULES 6 | 36 | #define MAX_LSM_RULES 6 |
| 31 | enum lsm_rule_types { LSM_OBJ_USER, LSM_OBJ_ROLE, LSM_OBJ_TYPE, | 37 | enum lsm_rule_types { LSM_OBJ_USER, LSM_OBJ_ROLE, LSM_OBJ_TYPE, |
| 32 | LSM_SUBJ_USER, LSM_SUBJ_ROLE, LSM_SUBJ_TYPE | 38 | LSM_SUBJ_USER, LSM_SUBJ_ROLE, LSM_SUBJ_TYPE |
| 33 | }; | 39 | }; |
| 34 | 40 | ||
| 35 | struct ima_measure_rule_entry { | 41 | struct ima_rule_entry { |
| 36 | struct list_head list; | 42 | struct list_head list; |
| 37 | enum ima_action action; | 43 | int action; |
| 38 | unsigned int flags; | 44 | unsigned int flags; |
| 39 | enum ima_hooks func; | 45 | enum ima_hooks func; |
| 40 | int mask; | 46 | int mask; |
| 41 | unsigned long fsmagic; | 47 | unsigned long fsmagic; |
| 42 | uid_t uid; | 48 | kuid_t uid; |
| 49 | kuid_t fowner; | ||
| 43 | struct { | 50 | struct { |
| 44 | void *rule; /* LSM file metadata specific */ | 51 | void *rule; /* LSM file metadata specific */ |
| 45 | int type; /* audit type */ | 52 | int type; /* audit type */ |
| @@ -48,7 +55,7 @@ struct ima_measure_rule_entry { | |||
| 48 | 55 | ||
| 49 | /* | 56 | /* |
| 50 | * Without LSM specific knowledge, the default policy can only be | 57 | * Without LSM specific knowledge, the default policy can only be |
| 51 | * written in terms of .action, .func, .mask, .fsmagic, and .uid | 58 | * written in terms of .action, .func, .mask, .fsmagic, .uid, and .fowner |
| 52 | */ | 59 | */ |
| 53 | 60 | ||
| 54 | /* | 61 | /* |
| @@ -57,7 +64,7 @@ struct ima_measure_rule_entry { | |||
| 57 | * normal users can easily run the machine out of memory simply building | 64 | * normal users can easily run the machine out of memory simply building |
| 58 | * and running executables. | 65 | * and running executables. |
| 59 | */ | 66 | */ |
| 60 | static struct ima_measure_rule_entry default_rules[] = { | 67 | static struct ima_rule_entry default_rules[] = { |
| 61 | {.action = DONT_MEASURE,.fsmagic = PROC_SUPER_MAGIC,.flags = IMA_FSMAGIC}, | 68 | {.action = DONT_MEASURE,.fsmagic = PROC_SUPER_MAGIC,.flags = IMA_FSMAGIC}, |
| 62 | {.action = DONT_MEASURE,.fsmagic = SYSFS_MAGIC,.flags = IMA_FSMAGIC}, | 69 | {.action = DONT_MEASURE,.fsmagic = SYSFS_MAGIC,.flags = IMA_FSMAGIC}, |
| 63 | {.action = DONT_MEASURE,.fsmagic = DEBUGFS_MAGIC,.flags = IMA_FSMAGIC}, | 70 | {.action = DONT_MEASURE,.fsmagic = DEBUGFS_MAGIC,.flags = IMA_FSMAGIC}, |
| @@ -71,23 +78,45 @@ static struct ima_measure_rule_entry default_rules[] = { | |||
| 71 | .flags = IMA_FUNC | IMA_MASK}, | 78 | .flags = IMA_FUNC | IMA_MASK}, |
| 72 | {.action = MEASURE,.func = BPRM_CHECK,.mask = MAY_EXEC, | 79 | {.action = MEASURE,.func = BPRM_CHECK,.mask = MAY_EXEC, |
| 73 | .flags = IMA_FUNC | IMA_MASK}, | 80 | .flags = IMA_FUNC | IMA_MASK}, |
| 74 | {.action = MEASURE,.func = FILE_CHECK,.mask = MAY_READ,.uid = 0, | 81 | {.action = MEASURE,.func = FILE_CHECK,.mask = MAY_READ,.uid = GLOBAL_ROOT_UID, |
| 75 | .flags = IMA_FUNC | IMA_MASK | IMA_UID}, | 82 | .flags = IMA_FUNC | IMA_MASK | IMA_UID}, |
| 76 | }; | 83 | }; |
| 77 | 84 | ||
| 78 | static LIST_HEAD(measure_default_rules); | 85 | static struct ima_rule_entry default_appraise_rules[] = { |
| 79 | static LIST_HEAD(measure_policy_rules); | 86 | {.action = DONT_APPRAISE,.fsmagic = PROC_SUPER_MAGIC,.flags = IMA_FSMAGIC}, |
| 80 | static struct list_head *ima_measure; | 87 | {.action = DONT_APPRAISE,.fsmagic = SYSFS_MAGIC,.flags = IMA_FSMAGIC}, |
| 88 | {.action = DONT_APPRAISE,.fsmagic = DEBUGFS_MAGIC,.flags = IMA_FSMAGIC}, | ||
| 89 | {.action = DONT_APPRAISE,.fsmagic = TMPFS_MAGIC,.flags = IMA_FSMAGIC}, | ||
| 90 | {.action = DONT_APPRAISE,.fsmagic = RAMFS_MAGIC,.flags = IMA_FSMAGIC}, | ||
| 91 | {.action = DONT_APPRAISE,.fsmagic = DEVPTS_SUPER_MAGIC,.flags = IMA_FSMAGIC}, | ||
| 92 | {.action = DONT_APPRAISE,.fsmagic = BINFMTFS_MAGIC,.flags = IMA_FSMAGIC}, | ||
| 93 | {.action = DONT_APPRAISE,.fsmagic = SECURITYFS_MAGIC,.flags = IMA_FSMAGIC}, | ||
| 94 | {.action = DONT_APPRAISE,.fsmagic = SELINUX_MAGIC,.flags = IMA_FSMAGIC}, | ||
| 95 | {.action = DONT_APPRAISE,.fsmagic = CGROUP_SUPER_MAGIC,.flags = IMA_FSMAGIC}, | ||
| 96 | {.action = APPRAISE,.fowner = GLOBAL_ROOT_UID,.flags = IMA_FOWNER}, | ||
| 97 | }; | ||
| 98 | |||
| 99 | static LIST_HEAD(ima_default_rules); | ||
| 100 | static LIST_HEAD(ima_policy_rules); | ||
| 101 | static struct list_head *ima_rules; | ||
| 81 | 102 | ||
| 82 | static DEFINE_MUTEX(ima_measure_mutex); | 103 | static DEFINE_MUTEX(ima_rules_mutex); |
| 83 | 104 | ||
| 84 | static bool ima_use_tcb __initdata; | 105 | static bool ima_use_tcb __initdata; |
| 85 | static int __init default_policy_setup(char *str) | 106 | static int __init default_measure_policy_setup(char *str) |
| 86 | { | 107 | { |
| 87 | ima_use_tcb = 1; | 108 | ima_use_tcb = 1; |
| 88 | return 1; | 109 | return 1; |
| 89 | } | 110 | } |
| 90 | __setup("ima_tcb", default_policy_setup); | 111 | __setup("ima_tcb", default_measure_policy_setup); |
| 112 | |||
| 113 | static bool ima_use_appraise_tcb __initdata; | ||
| 114 | static int __init default_appraise_policy_setup(char *str) | ||
| 115 | { | ||
| 116 | ima_use_appraise_tcb = 1; | ||
| 117 | return 1; | ||
| 118 | } | ||
| 119 | __setup("ima_appraise_tcb", default_appraise_policy_setup); | ||
| 91 | 120 | ||
| 92 | /** | 121 | /** |
| 93 | * ima_match_rules - determine whether an inode matches the measure rule. | 122 | * ima_match_rules - determine whether an inode matches the measure rule. |
| @@ -98,7 +127,7 @@ __setup("ima_tcb", default_policy_setup); | |||
| 98 | * | 127 | * |
| 99 | * Returns true on rule match, false on failure. | 128 | * Returns true on rule match, false on failure. |
| 100 | */ | 129 | */ |
| 101 | static bool ima_match_rules(struct ima_measure_rule_entry *rule, | 130 | static bool ima_match_rules(struct ima_rule_entry *rule, |
| 102 | struct inode *inode, enum ima_hooks func, int mask) | 131 | struct inode *inode, enum ima_hooks func, int mask) |
| 103 | { | 132 | { |
| 104 | struct task_struct *tsk = current; | 133 | struct task_struct *tsk = current; |
| @@ -112,7 +141,9 @@ static bool ima_match_rules(struct ima_measure_rule_entry *rule, | |||
| 112 | if ((rule->flags & IMA_FSMAGIC) | 141 | if ((rule->flags & IMA_FSMAGIC) |
| 113 | && rule->fsmagic != inode->i_sb->s_magic) | 142 | && rule->fsmagic != inode->i_sb->s_magic) |
| 114 | return false; | 143 | return false; |
| 115 | if ((rule->flags & IMA_UID) && rule->uid != cred->uid) | 144 | if ((rule->flags & IMA_UID) && !uid_eq(rule->uid, cred->uid)) |
| 145 | return false; | ||
| 146 | if ((rule->flags & IMA_FOWNER) && !uid_eq(rule->fowner, inode->i_uid)) | ||
| 116 | return false; | 147 | return false; |
| 117 | for (i = 0; i < MAX_LSM_RULES; i++) { | 148 | for (i = 0; i < MAX_LSM_RULES; i++) { |
| 118 | int rc = 0; | 149 | int rc = 0; |
| @@ -163,39 +194,61 @@ static bool ima_match_rules(struct ima_measure_rule_entry *rule, | |||
| 163 | * as elements in the list are never deleted, nor does the list | 194 | * as elements in the list are never deleted, nor does the list |
| 164 | * change.) | 195 | * change.) |
| 165 | */ | 196 | */ |
| 166 | int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask) | 197 | int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask, |
| 198 | int flags) | ||
| 167 | { | 199 | { |
| 168 | struct ima_measure_rule_entry *entry; | 200 | struct ima_rule_entry *entry; |
| 201 | int action = 0, actmask = flags | (flags << 1); | ||
| 202 | |||
| 203 | list_for_each_entry(entry, ima_rules, list) { | ||
| 204 | |||
| 205 | if (!(entry->action & actmask)) | ||
| 206 | continue; | ||
| 207 | |||
| 208 | if (!ima_match_rules(entry, inode, func, mask)) | ||
| 209 | continue; | ||
| 169 | 210 | ||
| 170 | list_for_each_entry(entry, ima_measure, list) { | 211 | action |= entry->action & IMA_DO_MASK; |
| 171 | bool rc; | 212 | if (entry->action & IMA_DO_MASK) |
| 213 | actmask &= ~(entry->action | entry->action << 1); | ||
| 214 | else | ||
| 215 | actmask &= ~(entry->action | entry->action >> 1); | ||
| 172 | 216 | ||
| 173 | rc = ima_match_rules(entry, inode, func, mask); | 217 | if (!actmask) |
| 174 | if (rc) | 218 | break; |
| 175 | return entry->action; | ||
| 176 | } | 219 | } |
| 177 | return 0; | 220 | |
| 221 | return action; | ||
| 178 | } | 222 | } |
| 179 | 223 | ||
| 180 | /** | 224 | /** |
| 181 | * ima_init_policy - initialize the default measure rules. | 225 | * ima_init_policy - initialize the default measure rules. |
| 182 | * | 226 | * |
| 183 | * ima_measure points to either the measure_default_rules or the | 227 | * ima_rules points to either the ima_default_rules or the |
| 184 | * the new measure_policy_rules. | 228 | * the new ima_policy_rules. |
| 185 | */ | 229 | */ |
| 186 | void __init ima_init_policy(void) | 230 | void __init ima_init_policy(void) |
| 187 | { | 231 | { |
| 188 | int i, entries; | 232 | int i, measure_entries, appraise_entries; |
| 189 | 233 | ||
| 190 | /* if !ima_use_tcb set entries = 0 so we load NO default rules */ | 234 | /* if !ima_use_tcb set entries = 0 so we load NO default rules */ |
| 191 | if (ima_use_tcb) | 235 | measure_entries = ima_use_tcb ? ARRAY_SIZE(default_rules) : 0; |
| 192 | entries = ARRAY_SIZE(default_rules); | 236 | appraise_entries = ima_use_appraise_tcb ? |
| 193 | else | 237 | ARRAY_SIZE(default_appraise_rules) : 0; |
| 194 | entries = 0; | 238 | |
| 195 | 239 | for (i = 0; i < measure_entries + appraise_entries; i++) { | |
| 196 | for (i = 0; i < entries; i++) | 240 | if (i < measure_entries) |
| 197 | list_add_tail(&default_rules[i].list, &measure_default_rules); | 241 | list_add_tail(&default_rules[i].list, |
| 198 | ima_measure = &measure_default_rules; | 242 | &ima_default_rules); |
| 243 | else { | ||
| 244 | int j = i - measure_entries; | ||
| 245 | |||
| 246 | list_add_tail(&default_appraise_rules[j].list, | ||
| 247 | &ima_default_rules); | ||
| 248 | } | ||
| 249 | } | ||
| 250 | |||
| 251 | ima_rules = &ima_default_rules; | ||
| 199 | } | 252 | } |
| 200 | 253 | ||
| 201 | /** | 254 | /** |
| @@ -212,8 +265,8 @@ void ima_update_policy(void) | |||
| 212 | int result = 1; | 265 | int result = 1; |
| 213 | int audit_info = 0; | 266 | int audit_info = 0; |
| 214 | 267 | ||
| 215 | if (ima_measure == &measure_default_rules) { | 268 | if (ima_rules == &ima_default_rules) { |
| 216 | ima_measure = &measure_policy_rules; | 269 | ima_rules = &ima_policy_rules; |
| 217 | cause = "complete"; | 270 | cause = "complete"; |
| 218 | result = 0; | 271 | result = 0; |
| 219 | } | 272 | } |
| @@ -224,14 +277,19 @@ void ima_update_policy(void) | |||
| 224 | enum { | 277 | enum { |
| 225 | Opt_err = -1, | 278 | Opt_err = -1, |
| 226 | Opt_measure = 1, Opt_dont_measure, | 279 | Opt_measure = 1, Opt_dont_measure, |
| 280 | Opt_appraise, Opt_dont_appraise, | ||
| 281 | Opt_audit, | ||
| 227 | Opt_obj_user, Opt_obj_role, Opt_obj_type, | 282 | Opt_obj_user, Opt_obj_role, Opt_obj_type, |
| 228 | Opt_subj_user, Opt_subj_role, Opt_subj_type, | 283 | Opt_subj_user, Opt_subj_role, Opt_subj_type, |
| 229 | Opt_func, Opt_mask, Opt_fsmagic, Opt_uid | 284 | Opt_func, Opt_mask, Opt_fsmagic, Opt_uid, Opt_fowner |
| 230 | }; | 285 | }; |
| 231 | 286 | ||
| 232 | static match_table_t policy_tokens = { | 287 | static match_table_t policy_tokens = { |
| 233 | {Opt_measure, "measure"}, | 288 | {Opt_measure, "measure"}, |
| 234 | {Opt_dont_measure, "dont_measure"}, | 289 | {Opt_dont_measure, "dont_measure"}, |
| 290 | {Opt_appraise, "appraise"}, | ||
| 291 | {Opt_dont_appraise, "dont_appraise"}, | ||
| 292 | {Opt_audit, "audit"}, | ||
| 235 | {Opt_obj_user, "obj_user=%s"}, | 293 | {Opt_obj_user, "obj_user=%s"}, |
| 236 | {Opt_obj_role, "obj_role=%s"}, | 294 | {Opt_obj_role, "obj_role=%s"}, |
| 237 | {Opt_obj_type, "obj_type=%s"}, | 295 | {Opt_obj_type, "obj_type=%s"}, |
| @@ -242,10 +300,11 @@ static match_table_t policy_tokens = { | |||
| 242 | {Opt_mask, "mask=%s"}, | 300 | {Opt_mask, "mask=%s"}, |
| 243 | {Opt_fsmagic, "fsmagic=%s"}, | 301 | {Opt_fsmagic, "fsmagic=%s"}, |
| 244 | {Opt_uid, "uid=%s"}, | 302 | {Opt_uid, "uid=%s"}, |
| 303 | {Opt_fowner, "fowner=%s"}, | ||
| 245 | {Opt_err, NULL} | 304 | {Opt_err, NULL} |
| 246 | }; | 305 | }; |
| 247 | 306 | ||
| 248 | static int ima_lsm_rule_init(struct ima_measure_rule_entry *entry, | 307 | static int ima_lsm_rule_init(struct ima_rule_entry *entry, |
| 249 | char *args, int lsm_rule, int audit_type) | 308 | char *args, int lsm_rule, int audit_type) |
| 250 | { | 309 | { |
| 251 | int result; | 310 | int result; |
| @@ -269,7 +328,7 @@ static void ima_log_string(struct audit_buffer *ab, char *key, char *value) | |||
| 269 | audit_log_format(ab, " "); | 328 | audit_log_format(ab, " "); |
| 270 | } | 329 | } |
| 271 | 330 | ||
| 272 | static int ima_parse_rule(char *rule, struct ima_measure_rule_entry *entry) | 331 | static int ima_parse_rule(char *rule, struct ima_rule_entry *entry) |
| 273 | { | 332 | { |
| 274 | struct audit_buffer *ab; | 333 | struct audit_buffer *ab; |
| 275 | char *p; | 334 | char *p; |
| @@ -277,7 +336,8 @@ static int ima_parse_rule(char *rule, struct ima_measure_rule_entry *entry) | |||
| 277 | 336 | ||
| 278 | ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_INTEGRITY_RULE); | 337 | ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_INTEGRITY_RULE); |
| 279 | 338 | ||
| 280 | entry->uid = -1; | 339 | entry->uid = INVALID_UID; |
| 340 | entry->fowner = INVALID_UID; | ||
| 281 | entry->action = UNKNOWN; | 341 | entry->action = UNKNOWN; |
| 282 | while ((p = strsep(&rule, " \t")) != NULL) { | 342 | while ((p = strsep(&rule, " \t")) != NULL) { |
| 283 | substring_t args[MAX_OPT_ARGS]; | 343 | substring_t args[MAX_OPT_ARGS]; |
| @@ -306,11 +366,35 @@ static int ima_parse_rule(char *rule, struct ima_measure_rule_entry *entry) | |||
| 306 | 366 | ||
| 307 | entry->action = DONT_MEASURE; | 367 | entry->action = DONT_MEASURE; |
| 308 | break; | 368 | break; |
| 369 | case Opt_appraise: | ||
| 370 | ima_log_string(ab, "action", "appraise"); | ||
| 371 | |||
| 372 | if (entry->action != UNKNOWN) | ||
| 373 | result = -EINVAL; | ||
| 374 | |||
| 375 | entry->action = APPRAISE; | ||
| 376 | break; | ||
| 377 | case Opt_dont_appraise: | ||
| 378 | ima_log_string(ab, "action", "dont_appraise"); | ||
| 379 | |||
| 380 | if (entry->action != UNKNOWN) | ||
| 381 | result = -EINVAL; | ||
| 382 | |||
| 383 | entry->action = DONT_APPRAISE; | ||
| 384 | break; | ||
| 385 | case Opt_audit: | ||
| 386 | ima_log_string(ab, "action", "audit"); | ||
| 387 | |||
| 388 | if (entry->action != UNKNOWN) | ||
| 389 | result = -EINVAL; | ||
| 390 | |||
| 391 | entry->action = AUDIT; | ||
| 392 | break; | ||
| 309 | case Opt_func: | 393 | case Opt_func: |
| 310 | ima_log_string(ab, "func", args[0].from); | 394 | ima_log_string(ab, "func", args[0].from); |
| 311 | 395 | ||
| 312 | if (entry->func) | 396 | if (entry->func) |
| 313 | result = -EINVAL; | 397 | result = -EINVAL; |
| 314 | 398 | ||
| 315 | if (strcmp(args[0].from, "FILE_CHECK") == 0) | 399 | if (strcmp(args[0].from, "FILE_CHECK") == 0) |
| 316 | entry->func = FILE_CHECK; | 400 | entry->func = FILE_CHECK; |
| @@ -361,20 +445,37 @@ static int ima_parse_rule(char *rule, struct ima_measure_rule_entry *entry) | |||
| 361 | case Opt_uid: | 445 | case Opt_uid: |
| 362 | ima_log_string(ab, "uid", args[0].from); | 446 | ima_log_string(ab, "uid", args[0].from); |
| 363 | 447 | ||
| 364 | if (entry->uid != -1) { | 448 | if (uid_valid(entry->uid)) { |
| 365 | result = -EINVAL; | 449 | result = -EINVAL; |
| 366 | break; | 450 | break; |
| 367 | } | 451 | } |
| 368 | 452 | ||
| 369 | result = strict_strtoul(args[0].from, 10, &lnum); | 453 | result = strict_strtoul(args[0].from, 10, &lnum); |
| 370 | if (!result) { | 454 | if (!result) { |
| 371 | entry->uid = (uid_t) lnum; | 455 | entry->uid = make_kuid(current_user_ns(), (uid_t)lnum); |
| 372 | if (entry->uid != lnum) | 456 | if (!uid_valid(entry->uid) || (((uid_t)lnum) != lnum)) |
| 373 | result = -EINVAL; | 457 | result = -EINVAL; |
| 374 | else | 458 | else |
| 375 | entry->flags |= IMA_UID; | 459 | entry->flags |= IMA_UID; |
| 376 | } | 460 | } |
| 377 | break; | 461 | break; |
| 462 | case Opt_fowner: | ||
| 463 | ima_log_string(ab, "fowner", args[0].from); | ||
| 464 | |||
| 465 | if (uid_valid(entry->fowner)) { | ||
| 466 | result = -EINVAL; | ||
| 467 | break; | ||
| 468 | } | ||
| 469 | |||
| 470 | result = strict_strtoul(args[0].from, 10, &lnum); | ||
| 471 | if (!result) { | ||
| 472 | entry->fowner = make_kuid(current_user_ns(), (uid_t)lnum); | ||
| 473 | if (!uid_valid(entry->fowner) || (((uid_t)lnum) != lnum)) | ||
| 474 | result = -EINVAL; | ||
| 475 | else | ||
| 476 | entry->flags |= IMA_FOWNER; | ||
| 477 | } | ||
| 478 | break; | ||
| 378 | case Opt_obj_user: | 479 | case Opt_obj_user: |
| 379 | ima_log_string(ab, "obj_user", args[0].from); | 480 | ima_log_string(ab, "obj_user", args[0].from); |
| 380 | result = ima_lsm_rule_init(entry, args[0].from, | 481 | result = ima_lsm_rule_init(entry, args[0].from, |
| @@ -426,7 +527,7 @@ static int ima_parse_rule(char *rule, struct ima_measure_rule_entry *entry) | |||
| 426 | } | 527 | } |
| 427 | 528 | ||
| 428 | /** | 529 | /** |
| 429 | * ima_parse_add_rule - add a rule to measure_policy_rules | 530 | * ima_parse_add_rule - add a rule to ima_policy_rules |
| 430 | * @rule - ima measurement policy rule | 531 | * @rule - ima measurement policy rule |
| 431 | * | 532 | * |
| 432 | * Uses a mutex to protect the policy list from multiple concurrent writers. | 533 | * Uses a mutex to protect the policy list from multiple concurrent writers. |
| @@ -436,12 +537,12 @@ ssize_t ima_parse_add_rule(char *rule) | |||
| 436 | { | 537 | { |
| 437 | const char *op = "update_policy"; | 538 | const char *op = "update_policy"; |
| 438 | char *p; | 539 | char *p; |
| 439 | struct ima_measure_rule_entry *entry; | 540 | struct ima_rule_entry *entry; |
| 440 | ssize_t result, len; | 541 | ssize_t result, len; |
| 441 | int audit_info = 0; | 542 | int audit_info = 0; |
| 442 | 543 | ||
| 443 | /* Prevent installed policy from changing */ | 544 | /* Prevent installed policy from changing */ |
| 444 | if (ima_measure != &measure_default_rules) { | 545 | if (ima_rules != &ima_default_rules) { |
| 445 | integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL, | 546 | integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL, |
| 446 | NULL, op, "already exists", | 547 | NULL, op, "already exists", |
| 447 | -EACCES, audit_info); | 548 | -EACCES, audit_info); |
| @@ -474,9 +575,9 @@ ssize_t ima_parse_add_rule(char *rule) | |||
| 474 | return result; | 575 | return result; |
| 475 | } | 576 | } |
| 476 | 577 | ||
| 477 | mutex_lock(&ima_measure_mutex); | 578 | mutex_lock(&ima_rules_mutex); |
| 478 | list_add_tail(&entry->list, &measure_policy_rules); | 579 | list_add_tail(&entry->list, &ima_policy_rules); |
| 479 | mutex_unlock(&ima_measure_mutex); | 580 | mutex_unlock(&ima_rules_mutex); |
| 480 | 581 | ||
| 481 | return len; | 582 | return len; |
| 482 | } | 583 | } |
| @@ -484,12 +585,12 @@ ssize_t ima_parse_add_rule(char *rule) | |||
| 484 | /* ima_delete_rules called to cleanup invalid policy */ | 585 | /* ima_delete_rules called to cleanup invalid policy */ |
| 485 | void ima_delete_rules(void) | 586 | void ima_delete_rules(void) |
| 486 | { | 587 | { |
| 487 | struct ima_measure_rule_entry *entry, *tmp; | 588 | struct ima_rule_entry *entry, *tmp; |
| 488 | 589 | ||
| 489 | mutex_lock(&ima_measure_mutex); | 590 | mutex_lock(&ima_rules_mutex); |
| 490 | list_for_each_entry_safe(entry, tmp, &measure_policy_rules, list) { | 591 | list_for_each_entry_safe(entry, tmp, &ima_policy_rules, list) { |
| 491 | list_del(&entry->list); | 592 | list_del(&entry->list); |
| 492 | kfree(entry); | 593 | kfree(entry); |
| 493 | } | 594 | } |
| 494 | mutex_unlock(&ima_measure_mutex); | 595 | mutex_unlock(&ima_rules_mutex); |
| 495 | } | 596 | } |
diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h index 7a25ecec5aaa..e9db763a875e 100644 --- a/security/integrity/integrity.h +++ b/security/integrity/integrity.h | |||
| @@ -15,8 +15,22 @@ | |||
| 15 | #include <linux/integrity.h> | 15 | #include <linux/integrity.h> |
| 16 | #include <crypto/sha.h> | 16 | #include <crypto/sha.h> |
| 17 | 17 | ||
| 18 | /* iint action cache flags */ | ||
| 19 | #define IMA_MEASURE 0x0001 | ||
| 20 | #define IMA_MEASURED 0x0002 | ||
| 21 | #define IMA_APPRAISE 0x0004 | ||
| 22 | #define IMA_APPRAISED 0x0008 | ||
| 23 | /*#define IMA_COLLECT 0x0010 do not use this flag */ | ||
| 24 | #define IMA_COLLECTED 0x0020 | ||
| 25 | #define IMA_AUDIT 0x0040 | ||
| 26 | #define IMA_AUDITED 0x0080 | ||
| 27 | |||
| 18 | /* iint cache flags */ | 28 | /* iint cache flags */ |
| 19 | #define IMA_MEASURED 0x01 | 29 | #define IMA_DIGSIG 0x0100 |
| 30 | |||
| 31 | #define IMA_DO_MASK (IMA_MEASURE | IMA_APPRAISE | IMA_AUDIT) | ||
| 32 | #define IMA_DONE_MASK (IMA_MEASURED | IMA_APPRAISED | IMA_AUDITED \ | ||
| 33 | | IMA_COLLECTED) | ||
| 20 | 34 | ||
| 21 | enum evm_ima_xattr_type { | 35 | enum evm_ima_xattr_type { |
| 22 | IMA_XATTR_DIGEST = 0x01, | 36 | IMA_XATTR_DIGEST = 0x01, |
| @@ -34,9 +48,9 @@ struct integrity_iint_cache { | |||
| 34 | struct rb_node rb_node; /* rooted in integrity_iint_tree */ | 48 | struct rb_node rb_node; /* rooted in integrity_iint_tree */ |
| 35 | struct inode *inode; /* back pointer to inode in question */ | 49 | struct inode *inode; /* back pointer to inode in question */ |
| 36 | u64 version; /* track inode changes */ | 50 | u64 version; /* track inode changes */ |
| 37 | unsigned char flags; | 51 | unsigned short flags; |
| 38 | u8 digest[SHA1_DIGEST_SIZE]; | 52 | struct evm_ima_xattr_data ima_xattr; |
| 39 | struct mutex mutex; /* protects: version, flags, digest */ | 53 | enum integrity_status ima_status; |
| 40 | enum integrity_status evm_status; | 54 | enum integrity_status evm_status; |
| 41 | }; | 55 | }; |
| 42 | 56 | ||
diff --git a/security/keys/encrypted-keys/encrypted.c b/security/keys/encrypted-keys/encrypted.c index 2d1bb8af7696..9e1e005c7596 100644 --- a/security/keys/encrypted-keys/encrypted.c +++ b/security/keys/encrypted-keys/encrypted.c | |||
| @@ -773,8 +773,8 @@ static int encrypted_init(struct encrypted_key_payload *epayload, | |||
| 773 | * | 773 | * |
| 774 | * On success, return 0. Otherwise return errno. | 774 | * On success, return 0. Otherwise return errno. |
| 775 | */ | 775 | */ |
| 776 | static int encrypted_instantiate(struct key *key, const void *data, | 776 | static int encrypted_instantiate(struct key *key, |
| 777 | size_t datalen) | 777 | struct key_preparsed_payload *prep) |
| 778 | { | 778 | { |
| 779 | struct encrypted_key_payload *epayload = NULL; | 779 | struct encrypted_key_payload *epayload = NULL; |
| 780 | char *datablob = NULL; | 780 | char *datablob = NULL; |
| @@ -782,16 +782,17 @@ static int encrypted_instantiate(struct key *key, const void *data, | |||
| 782 | char *master_desc = NULL; | 782 | char *master_desc = NULL; |
| 783 | char *decrypted_datalen = NULL; | 783 | char *decrypted_datalen = NULL; |
| 784 | char *hex_encoded_iv = NULL; | 784 | char *hex_encoded_iv = NULL; |
| 785 | size_t datalen = prep->datalen; | ||
| 785 | int ret; | 786 | int ret; |
| 786 | 787 | ||
| 787 | if (datalen <= 0 || datalen > 32767 || !data) | 788 | if (datalen <= 0 || datalen > 32767 || !prep->data) |
| 788 | return -EINVAL; | 789 | return -EINVAL; |
| 789 | 790 | ||
| 790 | datablob = kmalloc(datalen + 1, GFP_KERNEL); | 791 | datablob = kmalloc(datalen + 1, GFP_KERNEL); |
| 791 | if (!datablob) | 792 | if (!datablob) |
| 792 | return -ENOMEM; | 793 | return -ENOMEM; |
| 793 | datablob[datalen] = 0; | 794 | datablob[datalen] = 0; |
| 794 | memcpy(datablob, data, datalen); | 795 | memcpy(datablob, prep->data, datalen); |
| 795 | ret = datablob_parse(datablob, &format, &master_desc, | 796 | ret = datablob_parse(datablob, &format, &master_desc, |
| 796 | &decrypted_datalen, &hex_encoded_iv); | 797 | &decrypted_datalen, &hex_encoded_iv); |
| 797 | if (ret < 0) | 798 | if (ret < 0) |
| @@ -834,16 +835,17 @@ static void encrypted_rcu_free(struct rcu_head *rcu) | |||
| 834 | * | 835 | * |
| 835 | * On success, return 0. Otherwise return errno. | 836 | * On success, return 0. Otherwise return errno. |
| 836 | */ | 837 | */ |
| 837 | static int encrypted_update(struct key *key, const void *data, size_t datalen) | 838 | static int encrypted_update(struct key *key, struct key_preparsed_payload *prep) |
| 838 | { | 839 | { |
| 839 | struct encrypted_key_payload *epayload = key->payload.data; | 840 | struct encrypted_key_payload *epayload = key->payload.data; |
| 840 | struct encrypted_key_payload *new_epayload; | 841 | struct encrypted_key_payload *new_epayload; |
| 841 | char *buf; | 842 | char *buf; |
| 842 | char *new_master_desc = NULL; | 843 | char *new_master_desc = NULL; |
| 843 | const char *format = NULL; | 844 | const char *format = NULL; |
| 845 | size_t datalen = prep->datalen; | ||
| 844 | int ret = 0; | 846 | int ret = 0; |
| 845 | 847 | ||
| 846 | if (datalen <= 0 || datalen > 32767 || !data) | 848 | if (datalen <= 0 || datalen > 32767 || !prep->data) |
| 847 | return -EINVAL; | 849 | return -EINVAL; |
| 848 | 850 | ||
| 849 | buf = kmalloc(datalen + 1, GFP_KERNEL); | 851 | buf = kmalloc(datalen + 1, GFP_KERNEL); |
| @@ -851,7 +853,7 @@ static int encrypted_update(struct key *key, const void *data, size_t datalen) | |||
| 851 | return -ENOMEM; | 853 | return -ENOMEM; |
| 852 | 854 | ||
| 853 | buf[datalen] = 0; | 855 | buf[datalen] = 0; |
| 854 | memcpy(buf, data, datalen); | 856 | memcpy(buf, prep->data, datalen); |
| 855 | ret = datablob_parse(buf, &format, &new_master_desc, NULL, NULL); | 857 | ret = datablob_parse(buf, &format, &new_master_desc, NULL, NULL); |
| 856 | if (ret < 0) | 858 | if (ret < 0) |
| 857 | goto out; | 859 | goto out; |
diff --git a/security/keys/gc.c b/security/keys/gc.c index 61ab7c82ebb1..d67c97bb1025 100644 --- a/security/keys/gc.c +++ b/security/keys/gc.c | |||
| @@ -62,7 +62,7 @@ void key_schedule_gc(time_t gc_at) | |||
| 62 | 62 | ||
| 63 | if (gc_at <= now || test_bit(KEY_GC_REAP_KEYTYPE, &key_gc_flags)) { | 63 | if (gc_at <= now || test_bit(KEY_GC_REAP_KEYTYPE, &key_gc_flags)) { |
| 64 | kdebug("IMMEDIATE"); | 64 | kdebug("IMMEDIATE"); |
| 65 | queue_work(system_nrt_wq, &key_gc_work); | 65 | schedule_work(&key_gc_work); |
| 66 | } else if (gc_at < key_gc_next_run) { | 66 | } else if (gc_at < key_gc_next_run) { |
| 67 | kdebug("DEFERRED"); | 67 | kdebug("DEFERRED"); |
| 68 | key_gc_next_run = gc_at; | 68 | key_gc_next_run = gc_at; |
| @@ -77,7 +77,7 @@ void key_schedule_gc(time_t gc_at) | |||
| 77 | void key_schedule_gc_links(void) | 77 | void key_schedule_gc_links(void) |
| 78 | { | 78 | { |
| 79 | set_bit(KEY_GC_KEY_EXPIRED, &key_gc_flags); | 79 | set_bit(KEY_GC_KEY_EXPIRED, &key_gc_flags); |
| 80 | queue_work(system_nrt_wq, &key_gc_work); | 80 | schedule_work(&key_gc_work); |
| 81 | } | 81 | } |
| 82 | 82 | ||
| 83 | /* | 83 | /* |
| @@ -120,7 +120,7 @@ void key_gc_keytype(struct key_type *ktype) | |||
| 120 | set_bit(KEY_GC_REAP_KEYTYPE, &key_gc_flags); | 120 | set_bit(KEY_GC_REAP_KEYTYPE, &key_gc_flags); |
| 121 | 121 | ||
| 122 | kdebug("schedule"); | 122 | kdebug("schedule"); |
| 123 | queue_work(system_nrt_wq, &key_gc_work); | 123 | schedule_work(&key_gc_work); |
| 124 | 124 | ||
| 125 | kdebug("sleep"); | 125 | kdebug("sleep"); |
| 126 | wait_on_bit(&key_gc_flags, KEY_GC_REAPING_KEYTYPE, key_gc_wait_bit, | 126 | wait_on_bit(&key_gc_flags, KEY_GC_REAPING_KEYTYPE, key_gc_wait_bit, |
| @@ -369,7 +369,7 @@ maybe_resched: | |||
| 369 | } | 369 | } |
| 370 | 370 | ||
| 371 | if (gc_state & KEY_GC_REAP_AGAIN) | 371 | if (gc_state & KEY_GC_REAP_AGAIN) |
| 372 | queue_work(system_nrt_wq, &key_gc_work); | 372 | schedule_work(&key_gc_work); |
| 373 | kleave(" [end %x]", gc_state); | 373 | kleave(" [end %x]", gc_state); |
| 374 | return; | 374 | return; |
| 375 | 375 | ||
diff --git a/security/keys/internal.h b/security/keys/internal.h index 22ff05269e3d..8bbefc3b55d4 100644 --- a/security/keys/internal.h +++ b/security/keys/internal.h | |||
| @@ -52,8 +52,7 @@ struct key_user { | |||
| 52 | atomic_t usage; /* for accessing qnkeys & qnbytes */ | 52 | atomic_t usage; /* for accessing qnkeys & qnbytes */ |
| 53 | atomic_t nkeys; /* number of keys */ | 53 | atomic_t nkeys; /* number of keys */ |
| 54 | atomic_t nikeys; /* number of instantiated keys */ | 54 | atomic_t nikeys; /* number of instantiated keys */ |
| 55 | uid_t uid; | 55 | kuid_t uid; |
| 56 | struct user_namespace *user_ns; | ||
| 57 | int qnkeys; /* number of keys allocated to this user */ | 56 | int qnkeys; /* number of keys allocated to this user */ |
| 58 | int qnbytes; /* number of bytes allocated to this user */ | 57 | int qnbytes; /* number of bytes allocated to this user */ |
| 59 | }; | 58 | }; |
| @@ -62,8 +61,7 @@ extern struct rb_root key_user_tree; | |||
| 62 | extern spinlock_t key_user_lock; | 61 | extern spinlock_t key_user_lock; |
| 63 | extern struct key_user root_key_user; | 62 | extern struct key_user root_key_user; |
| 64 | 63 | ||
| 65 | extern struct key_user *key_user_lookup(uid_t uid, | 64 | extern struct key_user *key_user_lookup(kuid_t uid); |
| 66 | struct user_namespace *user_ns); | ||
| 67 | extern void key_user_put(struct key_user *user); | 65 | extern void key_user_put(struct key_user *user); |
| 68 | 66 | ||
| 69 | /* | 67 | /* |
diff --git a/security/keys/key.c b/security/keys/key.c index 50d96d4e06f2..a15c9da8f971 100644 --- a/security/keys/key.c +++ b/security/keys/key.c | |||
| @@ -18,7 +18,6 @@ | |||
| 18 | #include <linux/workqueue.h> | 18 | #include <linux/workqueue.h> |
| 19 | #include <linux/random.h> | 19 | #include <linux/random.h> |
| 20 | #include <linux/err.h> | 20 | #include <linux/err.h> |
| 21 | #include <linux/user_namespace.h> | ||
| 22 | #include "internal.h" | 21 | #include "internal.h" |
| 23 | 22 | ||
| 24 | struct kmem_cache *key_jar; | 23 | struct kmem_cache *key_jar; |
| @@ -52,7 +51,7 @@ void __key_check(const struct key *key) | |||
| 52 | * Get the key quota record for a user, allocating a new record if one doesn't | 51 | * Get the key quota record for a user, allocating a new record if one doesn't |
| 53 | * already exist. | 52 | * already exist. |
| 54 | */ | 53 | */ |
| 55 | struct key_user *key_user_lookup(uid_t uid, struct user_namespace *user_ns) | 54 | struct key_user *key_user_lookup(kuid_t uid) |
| 56 | { | 55 | { |
| 57 | struct key_user *candidate = NULL, *user; | 56 | struct key_user *candidate = NULL, *user; |
| 58 | struct rb_node *parent = NULL; | 57 | struct rb_node *parent = NULL; |
| @@ -67,13 +66,9 @@ try_again: | |||
| 67 | parent = *p; | 66 | parent = *p; |
| 68 | user = rb_entry(parent, struct key_user, node); | 67 | user = rb_entry(parent, struct key_user, node); |
| 69 | 68 | ||
| 70 | if (uid < user->uid) | 69 | if (uid_lt(uid, user->uid)) |
| 71 | p = &(*p)->rb_left; | 70 | p = &(*p)->rb_left; |
| 72 | else if (uid > user->uid) | 71 | else if (uid_gt(uid, user->uid)) |
| 73 | p = &(*p)->rb_right; | ||
| 74 | else if (user_ns < user->user_ns) | ||
| 75 | p = &(*p)->rb_left; | ||
| 76 | else if (user_ns > user->user_ns) | ||
| 77 | p = &(*p)->rb_right; | 72 | p = &(*p)->rb_right; |
| 78 | else | 73 | else |
| 79 | goto found; | 74 | goto found; |
| @@ -102,7 +97,6 @@ try_again: | |||
| 102 | atomic_set(&candidate->nkeys, 0); | 97 | atomic_set(&candidate->nkeys, 0); |
| 103 | atomic_set(&candidate->nikeys, 0); | 98 | atomic_set(&candidate->nikeys, 0); |
| 104 | candidate->uid = uid; | 99 | candidate->uid = uid; |
| 105 | candidate->user_ns = get_user_ns(user_ns); | ||
| 106 | candidate->qnkeys = 0; | 100 | candidate->qnkeys = 0; |
| 107 | candidate->qnbytes = 0; | 101 | candidate->qnbytes = 0; |
| 108 | spin_lock_init(&candidate->lock); | 102 | spin_lock_init(&candidate->lock); |
| @@ -131,7 +125,6 @@ void key_user_put(struct key_user *user) | |||
| 131 | if (atomic_dec_and_lock(&user->usage, &key_user_lock)) { | 125 | if (atomic_dec_and_lock(&user->usage, &key_user_lock)) { |
| 132 | rb_erase(&user->node, &key_user_tree); | 126 | rb_erase(&user->node, &key_user_tree); |
| 133 | spin_unlock(&key_user_lock); | 127 | spin_unlock(&key_user_lock); |
| 134 | put_user_ns(user->user_ns); | ||
| 135 | 128 | ||
| 136 | kfree(user); | 129 | kfree(user); |
| 137 | } | 130 | } |
| @@ -229,7 +222,7 @@ serial_exists: | |||
| 229 | * key_alloc() calls don't race with module unloading. | 222 | * key_alloc() calls don't race with module unloading. |
| 230 | */ | 223 | */ |
| 231 | struct key *key_alloc(struct key_type *type, const char *desc, | 224 | struct key *key_alloc(struct key_type *type, const char *desc, |
| 232 | uid_t uid, gid_t gid, const struct cred *cred, | 225 | kuid_t uid, kgid_t gid, const struct cred *cred, |
| 233 | key_perm_t perm, unsigned long flags) | 226 | key_perm_t perm, unsigned long flags) |
| 234 | { | 227 | { |
| 235 | struct key_user *user = NULL; | 228 | struct key_user *user = NULL; |
| @@ -253,16 +246,16 @@ struct key *key_alloc(struct key_type *type, const char *desc, | |||
| 253 | quotalen = desclen + type->def_datalen; | 246 | quotalen = desclen + type->def_datalen; |
| 254 | 247 | ||
| 255 | /* get hold of the key tracking for this user */ | 248 | /* get hold of the key tracking for this user */ |
| 256 | user = key_user_lookup(uid, cred->user_ns); | 249 | user = key_user_lookup(uid); |
| 257 | if (!user) | 250 | if (!user) |
| 258 | goto no_memory_1; | 251 | goto no_memory_1; |
| 259 | 252 | ||
| 260 | /* check that the user's quota permits allocation of another key and | 253 | /* check that the user's quota permits allocation of another key and |
| 261 | * its description */ | 254 | * its description */ |
| 262 | if (!(flags & KEY_ALLOC_NOT_IN_QUOTA)) { | 255 | if (!(flags & KEY_ALLOC_NOT_IN_QUOTA)) { |
| 263 | unsigned maxkeys = (uid == 0) ? | 256 | unsigned maxkeys = uid_eq(uid, GLOBAL_ROOT_UID) ? |
| 264 | key_quota_root_maxkeys : key_quota_maxkeys; | 257 | key_quota_root_maxkeys : key_quota_maxkeys; |
| 265 | unsigned maxbytes = (uid == 0) ? | 258 | unsigned maxbytes = uid_eq(uid, GLOBAL_ROOT_UID) ? |
| 266 | key_quota_root_maxbytes : key_quota_maxbytes; | 259 | key_quota_root_maxbytes : key_quota_maxbytes; |
| 267 | 260 | ||
| 268 | spin_lock(&user->lock); | 261 | spin_lock(&user->lock); |
| @@ -380,7 +373,7 @@ int key_payload_reserve(struct key *key, size_t datalen) | |||
| 380 | 373 | ||
| 381 | /* contemplate the quota adjustment */ | 374 | /* contemplate the quota adjustment */ |
| 382 | if (delta != 0 && test_bit(KEY_FLAG_IN_QUOTA, &key->flags)) { | 375 | if (delta != 0 && test_bit(KEY_FLAG_IN_QUOTA, &key->flags)) { |
| 383 | unsigned maxbytes = (key->user->uid == 0) ? | 376 | unsigned maxbytes = uid_eq(key->user->uid, GLOBAL_ROOT_UID) ? |
| 384 | key_quota_root_maxbytes : key_quota_maxbytes; | 377 | key_quota_root_maxbytes : key_quota_maxbytes; |
| 385 | 378 | ||
| 386 | spin_lock(&key->user->lock); | 379 | spin_lock(&key->user->lock); |
| @@ -412,8 +405,7 @@ EXPORT_SYMBOL(key_payload_reserve); | |||
| 412 | * key_construction_mutex. | 405 | * key_construction_mutex. |
| 413 | */ | 406 | */ |
| 414 | static int __key_instantiate_and_link(struct key *key, | 407 | static int __key_instantiate_and_link(struct key *key, |
| 415 | const void *data, | 408 | struct key_preparsed_payload *prep, |
| 416 | size_t datalen, | ||
| 417 | struct key *keyring, | 409 | struct key *keyring, |
| 418 | struct key *authkey, | 410 | struct key *authkey, |
| 419 | unsigned long *_prealloc) | 411 | unsigned long *_prealloc) |
| @@ -431,7 +423,7 @@ static int __key_instantiate_and_link(struct key *key, | |||
| 431 | /* can't instantiate twice */ | 423 | /* can't instantiate twice */ |
| 432 | if (!test_bit(KEY_FLAG_INSTANTIATED, &key->flags)) { | 424 | if (!test_bit(KEY_FLAG_INSTANTIATED, &key->flags)) { |
| 433 | /* instantiate the key */ | 425 | /* instantiate the key */ |
| 434 | ret = key->type->instantiate(key, data, datalen); | 426 | ret = key->type->instantiate(key, prep); |
| 435 | 427 | ||
| 436 | if (ret == 0) { | 428 | if (ret == 0) { |
| 437 | /* mark the key as being instantiated */ | 429 | /* mark the key as being instantiated */ |
| @@ -482,22 +474,37 @@ int key_instantiate_and_link(struct key *key, | |||
| 482 | struct key *keyring, | 474 | struct key *keyring, |
| 483 | struct key *authkey) | 475 | struct key *authkey) |
| 484 | { | 476 | { |
| 477 | struct key_preparsed_payload prep; | ||
| 485 | unsigned long prealloc; | 478 | unsigned long prealloc; |
| 486 | int ret; | 479 | int ret; |
| 487 | 480 | ||
| 481 | memset(&prep, 0, sizeof(prep)); | ||
| 482 | prep.data = data; | ||
| 483 | prep.datalen = datalen; | ||
| 484 | prep.quotalen = key->type->def_datalen; | ||
| 485 | if (key->type->preparse) { | ||
| 486 | ret = key->type->preparse(&prep); | ||
| 487 | if (ret < 0) | ||
| 488 | goto error; | ||
| 489 | } | ||
| 490 | |||
| 488 | if (keyring) { | 491 | if (keyring) { |
| 489 | ret = __key_link_begin(keyring, key->type, key->description, | 492 | ret = __key_link_begin(keyring, key->type, key->description, |
| 490 | &prealloc); | 493 | &prealloc); |
| 491 | if (ret < 0) | 494 | if (ret < 0) |
| 492 | return ret; | 495 | goto error_free_preparse; |
| 493 | } | 496 | } |
| 494 | 497 | ||
| 495 | ret = __key_instantiate_and_link(key, data, datalen, keyring, authkey, | 498 | ret = __key_instantiate_and_link(key, &prep, keyring, authkey, |
| 496 | &prealloc); | 499 | &prealloc); |
| 497 | 500 | ||
| 498 | if (keyring) | 501 | if (keyring) |
| 499 | __key_link_end(keyring, key->type, prealloc); | 502 | __key_link_end(keyring, key->type, prealloc); |
| 500 | 503 | ||
| 504 | error_free_preparse: | ||
| 505 | if (key->type->preparse) | ||
| 506 | key->type->free_preparse(&prep); | ||
| 507 | error: | ||
| 501 | return ret; | 508 | return ret; |
| 502 | } | 509 | } |
| 503 | 510 | ||
| @@ -598,7 +605,7 @@ void key_put(struct key *key) | |||
| 598 | key_check(key); | 605 | key_check(key); |
| 599 | 606 | ||
| 600 | if (atomic_dec_and_test(&key->usage)) | 607 | if (atomic_dec_and_test(&key->usage)) |
| 601 | queue_work(system_nrt_wq, &key_gc_work); | 608 | schedule_work(&key_gc_work); |
| 602 | } | 609 | } |
| 603 | } | 610 | } |
| 604 | EXPORT_SYMBOL(key_put); | 611 | EXPORT_SYMBOL(key_put); |
| @@ -706,7 +713,7 @@ void key_type_put(struct key_type *ktype) | |||
| 706 | * if we get an error. | 713 | * if we get an error. |
| 707 | */ | 714 | */ |
| 708 | static inline key_ref_t __key_update(key_ref_t key_ref, | 715 | static inline key_ref_t __key_update(key_ref_t key_ref, |
| 709 | const void *payload, size_t plen) | 716 | struct key_preparsed_payload *prep) |
| 710 | { | 717 | { |
| 711 | struct key *key = key_ref_to_ptr(key_ref); | 718 | struct key *key = key_ref_to_ptr(key_ref); |
| 712 | int ret; | 719 | int ret; |
| @@ -722,7 +729,7 @@ static inline key_ref_t __key_update(key_ref_t key_ref, | |||
| 722 | 729 | ||
| 723 | down_write(&key->sem); | 730 | down_write(&key->sem); |
| 724 | 731 | ||
| 725 | ret = key->type->update(key, payload, plen); | 732 | ret = key->type->update(key, prep); |
| 726 | if (ret == 0) | 733 | if (ret == 0) |
| 727 | /* updating a negative key instantiates it */ | 734 | /* updating a negative key instantiates it */ |
| 728 | clear_bit(KEY_FLAG_NEGATIVE, &key->flags); | 735 | clear_bit(KEY_FLAG_NEGATIVE, &key->flags); |
| @@ -774,6 +781,7 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref, | |||
| 774 | unsigned long flags) | 781 | unsigned long flags) |
| 775 | { | 782 | { |
| 776 | unsigned long prealloc; | 783 | unsigned long prealloc; |
| 784 | struct key_preparsed_payload prep; | ||
| 777 | const struct cred *cred = current_cred(); | 785 | const struct cred *cred = current_cred(); |
| 778 | struct key_type *ktype; | 786 | struct key_type *ktype; |
| 779 | struct key *keyring, *key = NULL; | 787 | struct key *keyring, *key = NULL; |
| @@ -789,8 +797,9 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref, | |||
| 789 | } | 797 | } |
| 790 | 798 | ||
| 791 | key_ref = ERR_PTR(-EINVAL); | 799 | key_ref = ERR_PTR(-EINVAL); |
| 792 | if (!ktype->match || !ktype->instantiate) | 800 | if (!ktype->match || !ktype->instantiate || |
| 793 | goto error_2; | 801 | (!description && !ktype->preparse)) |
| 802 | goto error_put_type; | ||
| 794 | 803 | ||
| 795 | keyring = key_ref_to_ptr(keyring_ref); | 804 | keyring = key_ref_to_ptr(keyring_ref); |
| 796 | 805 | ||
| @@ -798,18 +807,37 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref, | |||
| 798 | 807 | ||
| 799 | key_ref = ERR_PTR(-ENOTDIR); | 808 | key_ref = ERR_PTR(-ENOTDIR); |
| 800 | if (keyring->type != &key_type_keyring) | 809 | if (keyring->type != &key_type_keyring) |
| 801 | goto error_2; | 810 | goto error_put_type; |
| 811 | |||
| 812 | memset(&prep, 0, sizeof(prep)); | ||
| 813 | prep.data = payload; | ||
| 814 | prep.datalen = plen; | ||
| 815 | prep.quotalen = ktype->def_datalen; | ||
| 816 | if (ktype->preparse) { | ||
| 817 | ret = ktype->preparse(&prep); | ||
| 818 | if (ret < 0) { | ||
| 819 | key_ref = ERR_PTR(ret); | ||
| 820 | goto error_put_type; | ||
| 821 | } | ||
| 822 | if (!description) | ||
| 823 | description = prep.description; | ||
| 824 | key_ref = ERR_PTR(-EINVAL); | ||
| 825 | if (!description) | ||
| 826 | goto error_free_prep; | ||
| 827 | } | ||
| 802 | 828 | ||
| 803 | ret = __key_link_begin(keyring, ktype, description, &prealloc); | 829 | ret = __key_link_begin(keyring, ktype, description, &prealloc); |
| 804 | if (ret < 0) | 830 | if (ret < 0) { |
| 805 | goto error_2; | 831 | key_ref = ERR_PTR(ret); |
| 832 | goto error_free_prep; | ||
| 833 | } | ||
| 806 | 834 | ||
| 807 | /* if we're going to allocate a new key, we're going to have | 835 | /* if we're going to allocate a new key, we're going to have |
| 808 | * to modify the keyring */ | 836 | * to modify the keyring */ |
| 809 | ret = key_permission(keyring_ref, KEY_WRITE); | 837 | ret = key_permission(keyring_ref, KEY_WRITE); |
| 810 | if (ret < 0) { | 838 | if (ret < 0) { |
| 811 | key_ref = ERR_PTR(ret); | 839 | key_ref = ERR_PTR(ret); |
| 812 | goto error_3; | 840 | goto error_link_end; |
| 813 | } | 841 | } |
| 814 | 842 | ||
| 815 | /* if it's possible to update this type of key, search for an existing | 843 | /* if it's possible to update this type of key, search for an existing |
| @@ -840,25 +868,27 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref, | |||
| 840 | perm, flags); | 868 | perm, flags); |
| 841 | if (IS_ERR(key)) { | 869 | if (IS_ERR(key)) { |
| 842 | key_ref = ERR_CAST(key); | 870 | key_ref = ERR_CAST(key); |
| 843 | goto error_3; | 871 | goto error_link_end; |
| 844 | } | 872 | } |
| 845 | 873 | ||
| 846 | /* instantiate it and link it into the target keyring */ | 874 | /* instantiate it and link it into the target keyring */ |
| 847 | ret = __key_instantiate_and_link(key, payload, plen, keyring, NULL, | 875 | ret = __key_instantiate_and_link(key, &prep, keyring, NULL, &prealloc); |
| 848 | &prealloc); | ||
| 849 | if (ret < 0) { | 876 | if (ret < 0) { |
| 850 | key_put(key); | 877 | key_put(key); |
| 851 | key_ref = ERR_PTR(ret); | 878 | key_ref = ERR_PTR(ret); |
| 852 | goto error_3; | 879 | goto error_link_end; |
| 853 | } | 880 | } |
| 854 | 881 | ||
| 855 | key_ref = make_key_ref(key, is_key_possessed(keyring_ref)); | 882 | key_ref = make_key_ref(key, is_key_possessed(keyring_ref)); |
| 856 | 883 | ||
| 857 | error_3: | 884 | error_link_end: |
| 858 | __key_link_end(keyring, ktype, prealloc); | 885 | __key_link_end(keyring, ktype, prealloc); |
| 859 | error_2: | 886 | error_free_prep: |
| 887 | if (ktype->preparse) | ||
| 888 | ktype->free_preparse(&prep); | ||
| 889 | error_put_type: | ||
| 860 | key_type_put(ktype); | 890 | key_type_put(ktype); |
| 861 | error: | 891 | error: |
| 862 | return key_ref; | 892 | return key_ref; |
| 863 | 893 | ||
| 864 | found_matching_key: | 894 | found_matching_key: |
| @@ -866,10 +896,9 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref, | |||
| 866 | * - we can drop the locks first as we have the key pinned | 896 | * - we can drop the locks first as we have the key pinned |
| 867 | */ | 897 | */ |
| 868 | __key_link_end(keyring, ktype, prealloc); | 898 | __key_link_end(keyring, ktype, prealloc); |
| 869 | key_type_put(ktype); | ||
| 870 | 899 | ||
| 871 | key_ref = __key_update(key_ref, payload, plen); | 900 | key_ref = __key_update(key_ref, &prep); |
| 872 | goto error; | 901 | goto error_free_prep; |
| 873 | } | 902 | } |
| 874 | EXPORT_SYMBOL(key_create_or_update); | 903 | EXPORT_SYMBOL(key_create_or_update); |
| 875 | 904 | ||
| @@ -888,6 +917,7 @@ EXPORT_SYMBOL(key_create_or_update); | |||
| 888 | */ | 917 | */ |
| 889 | int key_update(key_ref_t key_ref, const void *payload, size_t plen) | 918 | int key_update(key_ref_t key_ref, const void *payload, size_t plen) |
| 890 | { | 919 | { |
| 920 | struct key_preparsed_payload prep; | ||
| 891 | struct key *key = key_ref_to_ptr(key_ref); | 921 | struct key *key = key_ref_to_ptr(key_ref); |
| 892 | int ret; | 922 | int ret; |
| 893 | 923 | ||
| @@ -900,18 +930,31 @@ int key_update(key_ref_t key_ref, const void *payload, size_t plen) | |||
| 900 | 930 | ||
| 901 | /* attempt to update it if supported */ | 931 | /* attempt to update it if supported */ |
| 902 | ret = -EOPNOTSUPP; | 932 | ret = -EOPNOTSUPP; |
| 903 | if (key->type->update) { | 933 | if (!key->type->update) |
| 904 | down_write(&key->sem); | 934 | goto error; |
| 905 | |||
| 906 | ret = key->type->update(key, payload, plen); | ||
| 907 | if (ret == 0) | ||
| 908 | /* updating a negative key instantiates it */ | ||
| 909 | clear_bit(KEY_FLAG_NEGATIVE, &key->flags); | ||
| 910 | 935 | ||
| 911 | up_write(&key->sem); | 936 | memset(&prep, 0, sizeof(prep)); |
| 937 | prep.data = payload; | ||
| 938 | prep.datalen = plen; | ||
| 939 | prep.quotalen = key->type->def_datalen; | ||
| 940 | if (key->type->preparse) { | ||
| 941 | ret = key->type->preparse(&prep); | ||
| 942 | if (ret < 0) | ||
| 943 | goto error; | ||
| 912 | } | 944 | } |
| 913 | 945 | ||
| 914 | error: | 946 | down_write(&key->sem); |
| 947 | |||
| 948 | ret = key->type->update(key, &prep); | ||
| 949 | if (ret == 0) | ||
| 950 | /* updating a negative key instantiates it */ | ||
| 951 | clear_bit(KEY_FLAG_NEGATIVE, &key->flags); | ||
| 952 | |||
| 953 | up_write(&key->sem); | ||
| 954 | |||
| 955 | if (key->type->preparse) | ||
| 956 | key->type->free_preparse(&prep); | ||
| 957 | error: | ||
| 915 | return ret; | 958 | return ret; |
| 916 | } | 959 | } |
| 917 | EXPORT_SYMBOL(key_update); | 960 | EXPORT_SYMBOL(key_update); |
diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c index 6cfc6478863e..5d34b4e827d6 100644 --- a/security/keys/keyctl.c +++ b/security/keys/keyctl.c | |||
| @@ -46,6 +46,9 @@ static int key_get_type_from_user(char *type, | |||
| 46 | * Extract the description of a new key from userspace and either add it as a | 46 | * Extract the description of a new key from userspace and either add it as a |
| 47 | * new key to the specified keyring or update a matching key in that keyring. | 47 | * new key to the specified keyring or update a matching key in that keyring. |
| 48 | * | 48 | * |
| 49 | * If the description is NULL or an empty string, the key type is asked to | ||
| 50 | * generate one from the payload. | ||
| 51 | * | ||
| 49 | * The keyring must be writable so that we can attach the key to it. | 52 | * The keyring must be writable so that we can attach the key to it. |
| 50 | * | 53 | * |
| 51 | * If successful, the new key's serial number is returned, otherwise an error | 54 | * If successful, the new key's serial number is returned, otherwise an error |
| @@ -72,10 +75,17 @@ SYSCALL_DEFINE5(add_key, const char __user *, _type, | |||
| 72 | if (ret < 0) | 75 | if (ret < 0) |
| 73 | goto error; | 76 | goto error; |
| 74 | 77 | ||
| 75 | description = strndup_user(_description, PAGE_SIZE); | 78 | description = NULL; |
| 76 | if (IS_ERR(description)) { | 79 | if (_description) { |
| 77 | ret = PTR_ERR(description); | 80 | description = strndup_user(_description, PAGE_SIZE); |
| 78 | goto error; | 81 | if (IS_ERR(description)) { |
| 82 | ret = PTR_ERR(description); | ||
| 83 | goto error; | ||
| 84 | } | ||
| 85 | if (!*description) { | ||
| 86 | kfree(description); | ||
| 87 | description = NULL; | ||
| 88 | } | ||
| 79 | } | 89 | } |
| 80 | 90 | ||
| 81 | /* pull the payload in if one was supplied */ | 91 | /* pull the payload in if one was supplied */ |
| @@ -569,8 +579,8 @@ okay: | |||
| 569 | ret = snprintf(tmpbuf, PAGE_SIZE - 1, | 579 | ret = snprintf(tmpbuf, PAGE_SIZE - 1, |
| 570 | "%s;%d;%d;%08x;%s", | 580 | "%s;%d;%d;%08x;%s", |
| 571 | key->type->name, | 581 | key->type->name, |
| 572 | key->uid, | 582 | from_kuid_munged(current_user_ns(), key->uid), |
| 573 | key->gid, | 583 | from_kgid_munged(current_user_ns(), key->gid), |
| 574 | key->perm, | 584 | key->perm, |
| 575 | key->description ?: ""); | 585 | key->description ?: ""); |
| 576 | 586 | ||
| @@ -766,15 +776,25 @@ error: | |||
| 766 | * | 776 | * |
| 767 | * If successful, 0 will be returned. | 777 | * If successful, 0 will be returned. |
| 768 | */ | 778 | */ |
| 769 | long keyctl_chown_key(key_serial_t id, uid_t uid, gid_t gid) | 779 | long keyctl_chown_key(key_serial_t id, uid_t user, gid_t group) |
| 770 | { | 780 | { |
| 771 | struct key_user *newowner, *zapowner = NULL; | 781 | struct key_user *newowner, *zapowner = NULL; |
| 772 | struct key *key; | 782 | struct key *key; |
| 773 | key_ref_t key_ref; | 783 | key_ref_t key_ref; |
| 774 | long ret; | 784 | long ret; |
| 785 | kuid_t uid; | ||
| 786 | kgid_t gid; | ||
| 787 | |||
| 788 | uid = make_kuid(current_user_ns(), user); | ||
| 789 | gid = make_kgid(current_user_ns(), group); | ||
| 790 | ret = -EINVAL; | ||
| 791 | if ((user != (uid_t) -1) && !uid_valid(uid)) | ||
| 792 | goto error; | ||
| 793 | if ((group != (gid_t) -1) && !gid_valid(gid)) | ||
| 794 | goto error; | ||
| 775 | 795 | ||
| 776 | ret = 0; | 796 | ret = 0; |
| 777 | if (uid == (uid_t) -1 && gid == (gid_t) -1) | 797 | if (user == (uid_t) -1 && group == (gid_t) -1) |
| 778 | goto error; | 798 | goto error; |
| 779 | 799 | ||
| 780 | key_ref = lookup_user_key(id, KEY_LOOKUP_CREATE | KEY_LOOKUP_PARTIAL, | 800 | key_ref = lookup_user_key(id, KEY_LOOKUP_CREATE | KEY_LOOKUP_PARTIAL, |
| @@ -792,27 +812,27 @@ long keyctl_chown_key(key_serial_t id, uid_t uid, gid_t gid) | |||
| 792 | 812 | ||
| 793 | if (!capable(CAP_SYS_ADMIN)) { | 813 | if (!capable(CAP_SYS_ADMIN)) { |
| 794 | /* only the sysadmin can chown a key to some other UID */ | 814 | /* only the sysadmin can chown a key to some other UID */ |
| 795 | if (uid != (uid_t) -1 && key->uid != uid) | 815 | if (user != (uid_t) -1 && !uid_eq(key->uid, uid)) |
| 796 | goto error_put; | 816 | goto error_put; |
| 797 | 817 | ||
| 798 | /* only the sysadmin can set the key's GID to a group other | 818 | /* only the sysadmin can set the key's GID to a group other |
| 799 | * than one of those that the current process subscribes to */ | 819 | * than one of those that the current process subscribes to */ |
| 800 | if (gid != (gid_t) -1 && gid != key->gid && !in_group_p(gid)) | 820 | if (group != (gid_t) -1 && !gid_eq(gid, key->gid) && !in_group_p(gid)) |
| 801 | goto error_put; | 821 | goto error_put; |
| 802 | } | 822 | } |
| 803 | 823 | ||
| 804 | /* change the UID */ | 824 | /* change the UID */ |
| 805 | if (uid != (uid_t) -1 && uid != key->uid) { | 825 | if (user != (uid_t) -1 && !uid_eq(uid, key->uid)) { |
| 806 | ret = -ENOMEM; | 826 | ret = -ENOMEM; |
| 807 | newowner = key_user_lookup(uid, current_user_ns()); | 827 | newowner = key_user_lookup(uid); |
| 808 | if (!newowner) | 828 | if (!newowner) |
| 809 | goto error_put; | 829 | goto error_put; |
| 810 | 830 | ||
| 811 | /* transfer the quota burden to the new user */ | 831 | /* transfer the quota burden to the new user */ |
| 812 | if (test_bit(KEY_FLAG_IN_QUOTA, &key->flags)) { | 832 | if (test_bit(KEY_FLAG_IN_QUOTA, &key->flags)) { |
| 813 | unsigned maxkeys = (uid == 0) ? | 833 | unsigned maxkeys = uid_eq(uid, GLOBAL_ROOT_UID) ? |
| 814 | key_quota_root_maxkeys : key_quota_maxkeys; | 834 | key_quota_root_maxkeys : key_quota_maxkeys; |
| 815 | unsigned maxbytes = (uid == 0) ? | 835 | unsigned maxbytes = uid_eq(uid, GLOBAL_ROOT_UID) ? |
| 816 | key_quota_root_maxbytes : key_quota_maxbytes; | 836 | key_quota_root_maxbytes : key_quota_maxbytes; |
| 817 | 837 | ||
| 818 | spin_lock(&newowner->lock); | 838 | spin_lock(&newowner->lock); |
| @@ -846,7 +866,7 @@ long keyctl_chown_key(key_serial_t id, uid_t uid, gid_t gid) | |||
| 846 | } | 866 | } |
| 847 | 867 | ||
| 848 | /* change the GID */ | 868 | /* change the GID */ |
| 849 | if (gid != (gid_t) -1) | 869 | if (group != (gid_t) -1) |
| 850 | key->gid = gid; | 870 | key->gid = gid; |
| 851 | 871 | ||
| 852 | ret = 0; | 872 | ret = 0; |
| @@ -897,7 +917,7 @@ long keyctl_setperm_key(key_serial_t id, key_perm_t perm) | |||
| 897 | down_write(&key->sem); | 917 | down_write(&key->sem); |
| 898 | 918 | ||
| 899 | /* if we're not the sysadmin, we can only change a key that we own */ | 919 | /* if we're not the sysadmin, we can only change a key that we own */ |
| 900 | if (capable(CAP_SYS_ADMIN) || key->uid == current_fsuid()) { | 920 | if (capable(CAP_SYS_ADMIN) || uid_eq(key->uid, current_fsuid())) { |
| 901 | key->perm = perm; | 921 | key->perm = perm; |
| 902 | ret = 0; | 922 | ret = 0; |
| 903 | } | 923 | } |
| @@ -1506,18 +1526,18 @@ long keyctl_session_to_parent(void) | |||
| 1506 | 1526 | ||
| 1507 | /* the parent must have the same effective ownership and mustn't be | 1527 | /* the parent must have the same effective ownership and mustn't be |
| 1508 | * SUID/SGID */ | 1528 | * SUID/SGID */ |
| 1509 | if (pcred->uid != mycred->euid || | 1529 | if (!uid_eq(pcred->uid, mycred->euid) || |
| 1510 | pcred->euid != mycred->euid || | 1530 | !uid_eq(pcred->euid, mycred->euid) || |
| 1511 | pcred->suid != mycred->euid || | 1531 | !uid_eq(pcred->suid, mycred->euid) || |
| 1512 | pcred->gid != mycred->egid || | 1532 | !gid_eq(pcred->gid, mycred->egid) || |
| 1513 | pcred->egid != mycred->egid || | 1533 | !gid_eq(pcred->egid, mycred->egid) || |
| 1514 | pcred->sgid != mycred->egid) | 1534 | !gid_eq(pcred->sgid, mycred->egid)) |
| 1515 | goto unlock; | 1535 | goto unlock; |
| 1516 | 1536 | ||
| 1517 | /* the keyrings must have the same UID */ | 1537 | /* the keyrings must have the same UID */ |
| 1518 | if ((pcred->tgcred->session_keyring && | 1538 | if ((pcred->tgcred->session_keyring && |
| 1519 | pcred->tgcred->session_keyring->uid != mycred->euid) || | 1539 | !uid_eq(pcred->tgcred->session_keyring->uid, mycred->euid)) || |
| 1520 | mycred->tgcred->session_keyring->uid != mycred->euid) | 1540 | !uid_eq(mycred->tgcred->session_keyring->uid, mycred->euid)) |
| 1521 | goto unlock; | 1541 | goto unlock; |
| 1522 | 1542 | ||
| 1523 | /* cancel an already pending keyring replacement */ | 1543 | /* cancel an already pending keyring replacement */ |
diff --git a/security/keys/keyring.c b/security/keys/keyring.c index 81e7852d281d..6e42df15a24c 100644 --- a/security/keys/keyring.c +++ b/security/keys/keyring.c | |||
| @@ -66,7 +66,7 @@ static inline unsigned keyring_hash(const char *desc) | |||
| 66 | * operations. | 66 | * operations. |
| 67 | */ | 67 | */ |
| 68 | static int keyring_instantiate(struct key *keyring, | 68 | static int keyring_instantiate(struct key *keyring, |
| 69 | const void *data, size_t datalen); | 69 | struct key_preparsed_payload *prep); |
| 70 | static int keyring_match(const struct key *keyring, const void *criterion); | 70 | static int keyring_match(const struct key *keyring, const void *criterion); |
| 71 | static void keyring_revoke(struct key *keyring); | 71 | static void keyring_revoke(struct key *keyring); |
| 72 | static void keyring_destroy(struct key *keyring); | 72 | static void keyring_destroy(struct key *keyring); |
| @@ -121,12 +121,12 @@ static void keyring_publish_name(struct key *keyring) | |||
| 121 | * Returns 0 on success, -EINVAL if given any data. | 121 | * Returns 0 on success, -EINVAL if given any data. |
| 122 | */ | 122 | */ |
| 123 | static int keyring_instantiate(struct key *keyring, | 123 | static int keyring_instantiate(struct key *keyring, |
| 124 | const void *data, size_t datalen) | 124 | struct key_preparsed_payload *prep) |
| 125 | { | 125 | { |
| 126 | int ret; | 126 | int ret; |
| 127 | 127 | ||
| 128 | ret = -EINVAL; | 128 | ret = -EINVAL; |
| 129 | if (datalen == 0) { | 129 | if (prep->datalen == 0) { |
| 130 | /* make the keyring available by name if it has one */ | 130 | /* make the keyring available by name if it has one */ |
| 131 | keyring_publish_name(keyring); | 131 | keyring_publish_name(keyring); |
| 132 | ret = 0; | 132 | ret = 0; |
| @@ -256,7 +256,7 @@ error: | |||
| 256 | /* | 256 | /* |
| 257 | * Allocate a keyring and link into the destination keyring. | 257 | * Allocate a keyring and link into the destination keyring. |
| 258 | */ | 258 | */ |
| 259 | struct key *keyring_alloc(const char *description, uid_t uid, gid_t gid, | 259 | struct key *keyring_alloc(const char *description, kuid_t uid, kgid_t gid, |
| 260 | const struct cred *cred, unsigned long flags, | 260 | const struct cred *cred, unsigned long flags, |
| 261 | struct key *dest) | 261 | struct key *dest) |
| 262 | { | 262 | { |
| @@ -612,7 +612,7 @@ struct key *find_keyring_by_name(const char *name, bool skip_perm_check) | |||
| 612 | &keyring_name_hash[bucket], | 612 | &keyring_name_hash[bucket], |
| 613 | type_data.link | 613 | type_data.link |
| 614 | ) { | 614 | ) { |
| 615 | if (keyring->user->user_ns != current_user_ns()) | 615 | if (!kuid_has_mapping(current_user_ns(), keyring->user->uid)) |
| 616 | continue; | 616 | continue; |
| 617 | 617 | ||
| 618 | if (test_bit(KEY_FLAG_REVOKED, &keyring->flags)) | 618 | if (test_bit(KEY_FLAG_REVOKED, &keyring->flags)) |
diff --git a/security/keys/permission.c b/security/keys/permission.c index 0b4d019e027d..efcc0c855a0d 100644 --- a/security/keys/permission.c +++ b/security/keys/permission.c | |||
| @@ -36,33 +36,27 @@ int key_task_permission(const key_ref_t key_ref, const struct cred *cred, | |||
| 36 | 36 | ||
| 37 | key = key_ref_to_ptr(key_ref); | 37 | key = key_ref_to_ptr(key_ref); |
| 38 | 38 | ||
| 39 | if (key->user->user_ns != cred->user_ns) | ||
| 40 | goto use_other_perms; | ||
| 41 | |||
| 42 | /* use the second 8-bits of permissions for keys the caller owns */ | 39 | /* use the second 8-bits of permissions for keys the caller owns */ |
| 43 | if (key->uid == cred->fsuid) { | 40 | if (uid_eq(key->uid, cred->fsuid)) { |
| 44 | kperm = key->perm >> 16; | 41 | kperm = key->perm >> 16; |
| 45 | goto use_these_perms; | 42 | goto use_these_perms; |
| 46 | } | 43 | } |
| 47 | 44 | ||
| 48 | /* use the third 8-bits of permissions for keys the caller has a group | 45 | /* use the third 8-bits of permissions for keys the caller has a group |
| 49 | * membership in common with */ | 46 | * membership in common with */ |
| 50 | if (key->gid != -1 && key->perm & KEY_GRP_ALL) { | 47 | if (gid_valid(key->gid) && key->perm & KEY_GRP_ALL) { |
| 51 | if (key->gid == cred->fsgid) { | 48 | if (gid_eq(key->gid, cred->fsgid)) { |
| 52 | kperm = key->perm >> 8; | 49 | kperm = key->perm >> 8; |
| 53 | goto use_these_perms; | 50 | goto use_these_perms; |
| 54 | } | 51 | } |
| 55 | 52 | ||
| 56 | ret = groups_search(cred->group_info, | 53 | ret = groups_search(cred->group_info, key->gid); |
| 57 | make_kgid(current_user_ns(), key->gid)); | ||
| 58 | if (ret) { | 54 | if (ret) { |
| 59 | kperm = key->perm >> 8; | 55 | kperm = key->perm >> 8; |
| 60 | goto use_these_perms; | 56 | goto use_these_perms; |
| 61 | } | 57 | } |
| 62 | } | 58 | } |
| 63 | 59 | ||
| 64 | use_other_perms: | ||
| 65 | |||
| 66 | /* otherwise use the least-significant 8-bits */ | 60 | /* otherwise use the least-significant 8-bits */ |
| 67 | kperm = key->perm; | 61 | kperm = key->perm; |
| 68 | 62 | ||
diff --git a/security/keys/proc.c b/security/keys/proc.c index 30d1ddfd9cef..217b6855e815 100644 --- a/security/keys/proc.c +++ b/security/keys/proc.c | |||
| @@ -88,14 +88,14 @@ __initcall(key_proc_init); | |||
| 88 | */ | 88 | */ |
| 89 | #ifdef CONFIG_KEYS_DEBUG_PROC_KEYS | 89 | #ifdef CONFIG_KEYS_DEBUG_PROC_KEYS |
| 90 | 90 | ||
| 91 | static struct rb_node *key_serial_next(struct rb_node *n) | 91 | static struct rb_node *key_serial_next(struct seq_file *p, struct rb_node *n) |
| 92 | { | 92 | { |
| 93 | struct user_namespace *user_ns = current_user_ns(); | 93 | struct user_namespace *user_ns = seq_user_ns(p); |
| 94 | 94 | ||
| 95 | n = rb_next(n); | 95 | n = rb_next(n); |
| 96 | while (n) { | 96 | while (n) { |
| 97 | struct key *key = rb_entry(n, struct key, serial_node); | 97 | struct key *key = rb_entry(n, struct key, serial_node); |
| 98 | if (key->user->user_ns == user_ns) | 98 | if (kuid_has_mapping(user_ns, key->user->uid)) |
| 99 | break; | 99 | break; |
| 100 | n = rb_next(n); | 100 | n = rb_next(n); |
| 101 | } | 101 | } |
| @@ -107,9 +107,9 @@ static int proc_keys_open(struct inode *inode, struct file *file) | |||
| 107 | return seq_open(file, &proc_keys_ops); | 107 | return seq_open(file, &proc_keys_ops); |
| 108 | } | 108 | } |
| 109 | 109 | ||
| 110 | static struct key *find_ge_key(key_serial_t id) | 110 | static struct key *find_ge_key(struct seq_file *p, key_serial_t id) |
| 111 | { | 111 | { |
| 112 | struct user_namespace *user_ns = current_user_ns(); | 112 | struct user_namespace *user_ns = seq_user_ns(p); |
| 113 | struct rb_node *n = key_serial_tree.rb_node; | 113 | struct rb_node *n = key_serial_tree.rb_node; |
| 114 | struct key *minkey = NULL; | 114 | struct key *minkey = NULL; |
| 115 | 115 | ||
| @@ -132,7 +132,7 @@ static struct key *find_ge_key(key_serial_t id) | |||
| 132 | return NULL; | 132 | return NULL; |
| 133 | 133 | ||
| 134 | for (;;) { | 134 | for (;;) { |
| 135 | if (minkey->user->user_ns == user_ns) | 135 | if (kuid_has_mapping(user_ns, minkey->user->uid)) |
| 136 | return minkey; | 136 | return minkey; |
| 137 | n = rb_next(&minkey->serial_node); | 137 | n = rb_next(&minkey->serial_node); |
| 138 | if (!n) | 138 | if (!n) |
| @@ -151,7 +151,7 @@ static void *proc_keys_start(struct seq_file *p, loff_t *_pos) | |||
| 151 | 151 | ||
| 152 | if (*_pos > INT_MAX) | 152 | if (*_pos > INT_MAX) |
| 153 | return NULL; | 153 | return NULL; |
| 154 | key = find_ge_key(pos); | 154 | key = find_ge_key(p, pos); |
| 155 | if (!key) | 155 | if (!key) |
| 156 | return NULL; | 156 | return NULL; |
| 157 | *_pos = key->serial; | 157 | *_pos = key->serial; |
| @@ -168,7 +168,7 @@ static void *proc_keys_next(struct seq_file *p, void *v, loff_t *_pos) | |||
| 168 | { | 168 | { |
| 169 | struct rb_node *n; | 169 | struct rb_node *n; |
| 170 | 170 | ||
| 171 | n = key_serial_next(v); | 171 | n = key_serial_next(p, v); |
| 172 | if (n) | 172 | if (n) |
| 173 | *_pos = key_node_serial(n); | 173 | *_pos = key_node_serial(n); |
| 174 | return n; | 174 | return n; |
| @@ -254,8 +254,8 @@ static int proc_keys_show(struct seq_file *m, void *v) | |||
| 254 | atomic_read(&key->usage), | 254 | atomic_read(&key->usage), |
| 255 | xbuf, | 255 | xbuf, |
| 256 | key->perm, | 256 | key->perm, |
| 257 | key->uid, | 257 | from_kuid_munged(seq_user_ns(m), key->uid), |
| 258 | key->gid, | 258 | from_kgid_munged(seq_user_ns(m), key->gid), |
| 259 | key->type->name); | 259 | key->type->name); |
| 260 | 260 | ||
| 261 | #undef showflag | 261 | #undef showflag |
| @@ -270,26 +270,26 @@ static int proc_keys_show(struct seq_file *m, void *v) | |||
| 270 | 270 | ||
| 271 | #endif /* CONFIG_KEYS_DEBUG_PROC_KEYS */ | 271 | #endif /* CONFIG_KEYS_DEBUG_PROC_KEYS */ |
| 272 | 272 | ||
| 273 | static struct rb_node *__key_user_next(struct rb_node *n) | 273 | static struct rb_node *__key_user_next(struct user_namespace *user_ns, struct rb_node *n) |
| 274 | { | 274 | { |
| 275 | while (n) { | 275 | while (n) { |
| 276 | struct key_user *user = rb_entry(n, struct key_user, node); | 276 | struct key_user *user = rb_entry(n, struct key_user, node); |
| 277 | if (user->user_ns == current_user_ns()) | 277 | if (kuid_has_mapping(user_ns, user->uid)) |
| 278 | break; | 278 | break; |
| 279 | n = rb_next(n); | 279 | n = rb_next(n); |
| 280 | } | 280 | } |
| 281 | return n; | 281 | return n; |
| 282 | } | 282 | } |
| 283 | 283 | ||
| 284 | static struct rb_node *key_user_next(struct rb_node *n) | 284 | static struct rb_node *key_user_next(struct user_namespace *user_ns, struct rb_node *n) |
| 285 | { | 285 | { |
| 286 | return __key_user_next(rb_next(n)); | 286 | return __key_user_next(user_ns, rb_next(n)); |
| 287 | } | 287 | } |
| 288 | 288 | ||
| 289 | static struct rb_node *key_user_first(struct rb_root *r) | 289 | static struct rb_node *key_user_first(struct user_namespace *user_ns, struct rb_root *r) |
| 290 | { | 290 | { |
| 291 | struct rb_node *n = rb_first(r); | 291 | struct rb_node *n = rb_first(r); |
| 292 | return __key_user_next(n); | 292 | return __key_user_next(user_ns, n); |
| 293 | } | 293 | } |
| 294 | 294 | ||
| 295 | /* | 295 | /* |
| @@ -309,10 +309,10 @@ static void *proc_key_users_start(struct seq_file *p, loff_t *_pos) | |||
| 309 | 309 | ||
| 310 | spin_lock(&key_user_lock); | 310 | spin_lock(&key_user_lock); |
| 311 | 311 | ||
| 312 | _p = key_user_first(&key_user_tree); | 312 | _p = key_user_first(seq_user_ns(p), &key_user_tree); |
| 313 | while (pos > 0 && _p) { | 313 | while (pos > 0 && _p) { |
| 314 | pos--; | 314 | pos--; |
| 315 | _p = key_user_next(_p); | 315 | _p = key_user_next(seq_user_ns(p), _p); |
| 316 | } | 316 | } |
| 317 | 317 | ||
| 318 | return _p; | 318 | return _p; |
| @@ -321,7 +321,7 @@ static void *proc_key_users_start(struct seq_file *p, loff_t *_pos) | |||
| 321 | static void *proc_key_users_next(struct seq_file *p, void *v, loff_t *_pos) | 321 | static void *proc_key_users_next(struct seq_file *p, void *v, loff_t *_pos) |
| 322 | { | 322 | { |
| 323 | (*_pos)++; | 323 | (*_pos)++; |
| 324 | return key_user_next((struct rb_node *)v); | 324 | return key_user_next(seq_user_ns(p), (struct rb_node *)v); |
| 325 | } | 325 | } |
| 326 | 326 | ||
| 327 | static void proc_key_users_stop(struct seq_file *p, void *v) | 327 | static void proc_key_users_stop(struct seq_file *p, void *v) |
| @@ -334,13 +334,13 @@ static int proc_key_users_show(struct seq_file *m, void *v) | |||
| 334 | { | 334 | { |
| 335 | struct rb_node *_p = v; | 335 | struct rb_node *_p = v; |
| 336 | struct key_user *user = rb_entry(_p, struct key_user, node); | 336 | struct key_user *user = rb_entry(_p, struct key_user, node); |
| 337 | unsigned maxkeys = (user->uid == 0) ? | 337 | unsigned maxkeys = uid_eq(user->uid, GLOBAL_ROOT_UID) ? |
| 338 | key_quota_root_maxkeys : key_quota_maxkeys; | 338 | key_quota_root_maxkeys : key_quota_maxkeys; |
| 339 | unsigned maxbytes = (user->uid == 0) ? | 339 | unsigned maxbytes = uid_eq(user->uid, GLOBAL_ROOT_UID) ? |
| 340 | key_quota_root_maxbytes : key_quota_maxbytes; | 340 | key_quota_root_maxbytes : key_quota_maxbytes; |
| 341 | 341 | ||
| 342 | seq_printf(m, "%5u: %5d %d/%d %d/%d %d/%d\n", | 342 | seq_printf(m, "%5u: %5d %d/%d %d/%d %d/%d\n", |
| 343 | user->uid, | 343 | from_kuid_munged(seq_user_ns(m), user->uid), |
| 344 | atomic_read(&user->usage), | 344 | atomic_read(&user->usage), |
| 345 | atomic_read(&user->nkeys), | 345 | atomic_read(&user->nkeys), |
| 346 | atomic_read(&user->nikeys), | 346 | atomic_read(&user->nikeys), |
diff --git a/security/keys/process_keys.c b/security/keys/process_keys.c index 54339cfd6734..a58f712605d8 100644 --- a/security/keys/process_keys.c +++ b/security/keys/process_keys.c | |||
| @@ -34,8 +34,7 @@ struct key_user root_key_user = { | |||
| 34 | .lock = __SPIN_LOCK_UNLOCKED(root_key_user.lock), | 34 | .lock = __SPIN_LOCK_UNLOCKED(root_key_user.lock), |
| 35 | .nkeys = ATOMIC_INIT(2), | 35 | .nkeys = ATOMIC_INIT(2), |
| 36 | .nikeys = ATOMIC_INIT(2), | 36 | .nikeys = ATOMIC_INIT(2), |
| 37 | .uid = 0, | 37 | .uid = GLOBAL_ROOT_UID, |
| 38 | .user_ns = &init_user_ns, | ||
| 39 | }; | 38 | }; |
| 40 | 39 | ||
| 41 | /* | 40 | /* |
| @@ -48,11 +47,13 @@ int install_user_keyrings(void) | |||
| 48 | struct key *uid_keyring, *session_keyring; | 47 | struct key *uid_keyring, *session_keyring; |
| 49 | char buf[20]; | 48 | char buf[20]; |
| 50 | int ret; | 49 | int ret; |
| 50 | uid_t uid; | ||
| 51 | 51 | ||
| 52 | cred = current_cred(); | 52 | cred = current_cred(); |
| 53 | user = cred->user; | 53 | user = cred->user; |
| 54 | uid = from_kuid(cred->user_ns, user->uid); | ||
| 54 | 55 | ||
| 55 | kenter("%p{%u}", user, user->uid); | 56 | kenter("%p{%u}", user, uid); |
| 56 | 57 | ||
| 57 | if (user->uid_keyring) { | 58 | if (user->uid_keyring) { |
| 58 | kleave(" = 0 [exist]"); | 59 | kleave(" = 0 [exist]"); |
| @@ -67,11 +68,11 @@ int install_user_keyrings(void) | |||
| 67 | * - there may be one in existence already as it may have been | 68 | * - there may be one in existence already as it may have been |
| 68 | * pinned by a session, but the user_struct pointing to it | 69 | * pinned by a session, but the user_struct pointing to it |
| 69 | * may have been destroyed by setuid */ | 70 | * may have been destroyed by setuid */ |
| 70 | sprintf(buf, "_uid.%u", user->uid); | 71 | sprintf(buf, "_uid.%u", uid); |
| 71 | 72 | ||
| 72 | uid_keyring = find_keyring_by_name(buf, true); | 73 | uid_keyring = find_keyring_by_name(buf, true); |
| 73 | if (IS_ERR(uid_keyring)) { | 74 | if (IS_ERR(uid_keyring)) { |
| 74 | uid_keyring = keyring_alloc(buf, user->uid, (gid_t) -1, | 75 | uid_keyring = keyring_alloc(buf, user->uid, INVALID_GID, |
| 75 | cred, KEY_ALLOC_IN_QUOTA, | 76 | cred, KEY_ALLOC_IN_QUOTA, |
| 76 | NULL); | 77 | NULL); |
| 77 | if (IS_ERR(uid_keyring)) { | 78 | if (IS_ERR(uid_keyring)) { |
| @@ -82,12 +83,12 @@ int install_user_keyrings(void) | |||
| 82 | 83 | ||
| 83 | /* get a default session keyring (which might also exist | 84 | /* get a default session keyring (which might also exist |
| 84 | * already) */ | 85 | * already) */ |
| 85 | sprintf(buf, "_uid_ses.%u", user->uid); | 86 | sprintf(buf, "_uid_ses.%u", uid); |
| 86 | 87 | ||
| 87 | session_keyring = find_keyring_by_name(buf, true); | 88 | session_keyring = find_keyring_by_name(buf, true); |
| 88 | if (IS_ERR(session_keyring)) { | 89 | if (IS_ERR(session_keyring)) { |
| 89 | session_keyring = | 90 | session_keyring = |
| 90 | keyring_alloc(buf, user->uid, (gid_t) -1, | 91 | keyring_alloc(buf, user->uid, INVALID_GID, |
| 91 | cred, KEY_ALLOC_IN_QUOTA, NULL); | 92 | cred, KEY_ALLOC_IN_QUOTA, NULL); |
| 92 | if (IS_ERR(session_keyring)) { | 93 | if (IS_ERR(session_keyring)) { |
| 93 | ret = PTR_ERR(session_keyring); | 94 | ret = PTR_ERR(session_keyring); |
diff --git a/security/keys/request_key.c b/security/keys/request_key.c index 000e75017520..66e21184b559 100644 --- a/security/keys/request_key.c +++ b/security/keys/request_key.c | |||
| @@ -139,8 +139,8 @@ static int call_sbin_request_key(struct key_construction *cons, | |||
| 139 | goto error_link; | 139 | goto error_link; |
| 140 | 140 | ||
| 141 | /* record the UID and GID */ | 141 | /* record the UID and GID */ |
| 142 | sprintf(uid_str, "%d", cred->fsuid); | 142 | sprintf(uid_str, "%d", from_kuid(&init_user_ns, cred->fsuid)); |
| 143 | sprintf(gid_str, "%d", cred->fsgid); | 143 | sprintf(gid_str, "%d", from_kgid(&init_user_ns, cred->fsgid)); |
| 144 | 144 | ||
| 145 | /* we say which key is under construction */ | 145 | /* we say which key is under construction */ |
| 146 | sprintf(key_str, "%d", key->serial); | 146 | sprintf(key_str, "%d", key->serial); |
| @@ -442,7 +442,7 @@ static struct key *construct_key_and_link(struct key_type *type, | |||
| 442 | 442 | ||
| 443 | kenter(""); | 443 | kenter(""); |
| 444 | 444 | ||
| 445 | user = key_user_lookup(current_fsuid(), current_user_ns()); | 445 | user = key_user_lookup(current_fsuid()); |
| 446 | if (!user) | 446 | if (!user) |
| 447 | return ERR_PTR(-ENOMEM); | 447 | return ERR_PTR(-ENOMEM); |
| 448 | 448 | ||
diff --git a/security/keys/request_key_auth.c b/security/keys/request_key_auth.c index 60d4e3f5e4bb..85730d5a5a59 100644 --- a/security/keys/request_key_auth.c +++ b/security/keys/request_key_auth.c | |||
| @@ -19,7 +19,8 @@ | |||
| 19 | #include <asm/uaccess.h> | 19 | #include <asm/uaccess.h> |
| 20 | #include "internal.h" | 20 | #include "internal.h" |
| 21 | 21 | ||
| 22 | static int request_key_auth_instantiate(struct key *, const void *, size_t); | 22 | static int request_key_auth_instantiate(struct key *, |
| 23 | struct key_preparsed_payload *); | ||
| 23 | static void request_key_auth_describe(const struct key *, struct seq_file *); | 24 | static void request_key_auth_describe(const struct key *, struct seq_file *); |
| 24 | static void request_key_auth_revoke(struct key *); | 25 | static void request_key_auth_revoke(struct key *); |
| 25 | static void request_key_auth_destroy(struct key *); | 26 | static void request_key_auth_destroy(struct key *); |
| @@ -42,10 +43,9 @@ struct key_type key_type_request_key_auth = { | |||
| 42 | * Instantiate a request-key authorisation key. | 43 | * Instantiate a request-key authorisation key. |
| 43 | */ | 44 | */ |
| 44 | static int request_key_auth_instantiate(struct key *key, | 45 | static int request_key_auth_instantiate(struct key *key, |
| 45 | const void *data, | 46 | struct key_preparsed_payload *prep) |
| 46 | size_t datalen) | ||
| 47 | { | 47 | { |
| 48 | key->payload.data = (struct request_key_auth *) data; | 48 | key->payload.data = (struct request_key_auth *)prep->data; |
| 49 | return 0; | 49 | return 0; |
| 50 | } | 50 | } |
| 51 | 51 | ||
diff --git a/security/keys/trusted.c b/security/keys/trusted.c index 2d5d041f2049..e13fcf7636f7 100644 --- a/security/keys/trusted.c +++ b/security/keys/trusted.c | |||
| @@ -369,38 +369,6 @@ static int trusted_tpm_send(const u32 chip_num, unsigned char *cmd, | |||
| 369 | } | 369 | } |
| 370 | 370 | ||
| 371 | /* | 371 | /* |
| 372 | * get a random value from TPM | ||
| 373 | */ | ||
| 374 | static int tpm_get_random(struct tpm_buf *tb, unsigned char *buf, uint32_t len) | ||
| 375 | { | ||
| 376 | int ret; | ||
| 377 | |||
| 378 | INIT_BUF(tb); | ||
| 379 | store16(tb, TPM_TAG_RQU_COMMAND); | ||
| 380 | store32(tb, TPM_GETRANDOM_SIZE); | ||
| 381 | store32(tb, TPM_ORD_GETRANDOM); | ||
| 382 | store32(tb, len); | ||
| 383 | ret = trusted_tpm_send(TPM_ANY_NUM, tb->data, sizeof tb->data); | ||
| 384 | if (!ret) | ||
| 385 | memcpy(buf, tb->data + TPM_GETRANDOM_SIZE, len); | ||
| 386 | return ret; | ||
| 387 | } | ||
| 388 | |||
| 389 | static int my_get_random(unsigned char *buf, int len) | ||
| 390 | { | ||
| 391 | struct tpm_buf *tb; | ||
| 392 | int ret; | ||
| 393 | |||
| 394 | tb = kmalloc(sizeof *tb, GFP_KERNEL); | ||
| 395 | if (!tb) | ||
| 396 | return -ENOMEM; | ||
| 397 | ret = tpm_get_random(tb, buf, len); | ||
| 398 | |||
| 399 | kfree(tb); | ||
| 400 | return ret; | ||
| 401 | } | ||
| 402 | |||
| 403 | /* | ||
| 404 | * Lock a trusted key, by extending a selected PCR. | 372 | * Lock a trusted key, by extending a selected PCR. |
| 405 | * | 373 | * |
| 406 | * Prevents a trusted key that is sealed to PCRs from being accessed. | 374 | * Prevents a trusted key that is sealed to PCRs from being accessed. |
| @@ -413,8 +381,8 @@ static int pcrlock(const int pcrnum) | |||
| 413 | 381 | ||
| 414 | if (!capable(CAP_SYS_ADMIN)) | 382 | if (!capable(CAP_SYS_ADMIN)) |
| 415 | return -EPERM; | 383 | return -EPERM; |
| 416 | ret = my_get_random(hash, SHA1_DIGEST_SIZE); | 384 | ret = tpm_get_random(TPM_ANY_NUM, hash, SHA1_DIGEST_SIZE); |
| 417 | if (ret < 0) | 385 | if (ret != SHA1_DIGEST_SIZE) |
| 418 | return ret; | 386 | return ret; |
| 419 | return tpm_pcr_extend(TPM_ANY_NUM, pcrnum, hash) ? -EINVAL : 0; | 387 | return tpm_pcr_extend(TPM_ANY_NUM, pcrnum, hash) ? -EINVAL : 0; |
| 420 | } | 388 | } |
| @@ -429,8 +397,8 @@ static int osap(struct tpm_buf *tb, struct osapsess *s, | |||
| 429 | unsigned char ononce[TPM_NONCE_SIZE]; | 397 | unsigned char ononce[TPM_NONCE_SIZE]; |
| 430 | int ret; | 398 | int ret; |
| 431 | 399 | ||
| 432 | ret = tpm_get_random(tb, ononce, TPM_NONCE_SIZE); | 400 | ret = tpm_get_random(TPM_ANY_NUM, ononce, TPM_NONCE_SIZE); |
| 433 | if (ret < 0) | 401 | if (ret != TPM_NONCE_SIZE) |
| 434 | return ret; | 402 | return ret; |
| 435 | 403 | ||
| 436 | INIT_BUF(tb); | 404 | INIT_BUF(tb); |
| @@ -524,8 +492,8 @@ static int tpm_seal(struct tpm_buf *tb, uint16_t keytype, | |||
| 524 | if (ret < 0) | 492 | if (ret < 0) |
| 525 | goto out; | 493 | goto out; |
| 526 | 494 | ||
| 527 | ret = tpm_get_random(tb, td->nonceodd, TPM_NONCE_SIZE); | 495 | ret = tpm_get_random(TPM_ANY_NUM, td->nonceodd, TPM_NONCE_SIZE); |
| 528 | if (ret < 0) | 496 | if (ret != TPM_NONCE_SIZE) |
| 529 | goto out; | 497 | goto out; |
| 530 | ordinal = htonl(TPM_ORD_SEAL); | 498 | ordinal = htonl(TPM_ORD_SEAL); |
| 531 | datsize = htonl(datalen); | 499 | datsize = htonl(datalen); |
| @@ -634,8 +602,8 @@ static int tpm_unseal(struct tpm_buf *tb, | |||
| 634 | 602 | ||
| 635 | ordinal = htonl(TPM_ORD_UNSEAL); | 603 | ordinal = htonl(TPM_ORD_UNSEAL); |
| 636 | keyhndl = htonl(SRKHANDLE); | 604 | keyhndl = htonl(SRKHANDLE); |
| 637 | ret = tpm_get_random(tb, nonceodd, TPM_NONCE_SIZE); | 605 | ret = tpm_get_random(TPM_ANY_NUM, nonceodd, TPM_NONCE_SIZE); |
| 638 | if (ret < 0) { | 606 | if (ret != TPM_NONCE_SIZE) { |
| 639 | pr_info("trusted_key: tpm_get_random failed (%d)\n", ret); | 607 | pr_info("trusted_key: tpm_get_random failed (%d)\n", ret); |
| 640 | return ret; | 608 | return ret; |
| 641 | } | 609 | } |
| @@ -927,22 +895,24 @@ static struct trusted_key_payload *trusted_payload_alloc(struct key *key) | |||
| 927 | * | 895 | * |
| 928 | * On success, return 0. Otherwise return errno. | 896 | * On success, return 0. Otherwise return errno. |
| 929 | */ | 897 | */ |
| 930 | static int trusted_instantiate(struct key *key, const void *data, | 898 | static int trusted_instantiate(struct key *key, |
| 931 | size_t datalen) | 899 | struct key_preparsed_payload *prep) |
| 932 | { | 900 | { |
| 933 | struct trusted_key_payload *payload = NULL; | 901 | struct trusted_key_payload *payload = NULL; |
| 934 | struct trusted_key_options *options = NULL; | 902 | struct trusted_key_options *options = NULL; |
| 903 | size_t datalen = prep->datalen; | ||
| 935 | char *datablob; | 904 | char *datablob; |
| 936 | int ret = 0; | 905 | int ret = 0; |
| 937 | int key_cmd; | 906 | int key_cmd; |
| 907 | size_t key_len; | ||
| 938 | 908 | ||
| 939 | if (datalen <= 0 || datalen > 32767 || !data) | 909 | if (datalen <= 0 || datalen > 32767 || !prep->data) |
| 940 | return -EINVAL; | 910 | return -EINVAL; |
| 941 | 911 | ||
| 942 | datablob = kmalloc(datalen + 1, GFP_KERNEL); | 912 | datablob = kmalloc(datalen + 1, GFP_KERNEL); |
| 943 | if (!datablob) | 913 | if (!datablob) |
| 944 | return -ENOMEM; | 914 | return -ENOMEM; |
| 945 | memcpy(datablob, data, datalen); | 915 | memcpy(datablob, prep->data, datalen); |
| 946 | datablob[datalen] = '\0'; | 916 | datablob[datalen] = '\0'; |
| 947 | 917 | ||
| 948 | options = trusted_options_alloc(); | 918 | options = trusted_options_alloc(); |
| @@ -974,8 +944,9 @@ static int trusted_instantiate(struct key *key, const void *data, | |||
| 974 | pr_info("trusted_key: key_unseal failed (%d)\n", ret); | 944 | pr_info("trusted_key: key_unseal failed (%d)\n", ret); |
| 975 | break; | 945 | break; |
| 976 | case Opt_new: | 946 | case Opt_new: |
| 977 | ret = my_get_random(payload->key, payload->key_len); | 947 | key_len = payload->key_len; |
| 978 | if (ret < 0) { | 948 | ret = tpm_get_random(TPM_ANY_NUM, payload->key, key_len); |
| 949 | if (ret != key_len) { | ||
| 979 | pr_info("trusted_key: key_create failed (%d)\n", ret); | 950 | pr_info("trusted_key: key_create failed (%d)\n", ret); |
| 980 | goto out; | 951 | goto out; |
| 981 | } | 952 | } |
| @@ -1011,17 +982,18 @@ static void trusted_rcu_free(struct rcu_head *rcu) | |||
| 1011 | /* | 982 | /* |
| 1012 | * trusted_update - reseal an existing key with new PCR values | 983 | * trusted_update - reseal an existing key with new PCR values |
| 1013 | */ | 984 | */ |
| 1014 | static int trusted_update(struct key *key, const void *data, size_t datalen) | 985 | static int trusted_update(struct key *key, struct key_preparsed_payload *prep) |
| 1015 | { | 986 | { |
| 1016 | struct trusted_key_payload *p = key->payload.data; | 987 | struct trusted_key_payload *p = key->payload.data; |
| 1017 | struct trusted_key_payload *new_p; | 988 | struct trusted_key_payload *new_p; |
| 1018 | struct trusted_key_options *new_o; | 989 | struct trusted_key_options *new_o; |
| 990 | size_t datalen = prep->datalen; | ||
| 1019 | char *datablob; | 991 | char *datablob; |
| 1020 | int ret = 0; | 992 | int ret = 0; |
| 1021 | 993 | ||
| 1022 | if (!p->migratable) | 994 | if (!p->migratable) |
| 1023 | return -EPERM; | 995 | return -EPERM; |
| 1024 | if (datalen <= 0 || datalen > 32767 || !data) | 996 | if (datalen <= 0 || datalen > 32767 || !prep->data) |
| 1025 | return -EINVAL; | 997 | return -EINVAL; |
| 1026 | 998 | ||
| 1027 | datablob = kmalloc(datalen + 1, GFP_KERNEL); | 999 | datablob = kmalloc(datalen + 1, GFP_KERNEL); |
| @@ -1038,7 +1010,7 @@ static int trusted_update(struct key *key, const void *data, size_t datalen) | |||
| 1038 | goto out; | 1010 | goto out; |
| 1039 | } | 1011 | } |
| 1040 | 1012 | ||
| 1041 | memcpy(datablob, data, datalen); | 1013 | memcpy(datablob, prep->data, datalen); |
| 1042 | datablob[datalen] = '\0'; | 1014 | datablob[datalen] = '\0'; |
| 1043 | ret = datablob_parse(datablob, new_p, new_o); | 1015 | ret = datablob_parse(datablob, new_p, new_o); |
| 1044 | if (ret != Opt_update) { | 1016 | if (ret != Opt_update) { |
diff --git a/security/keys/user_defined.c b/security/keys/user_defined.c index c7660a25a3e4..55dc88939185 100644 --- a/security/keys/user_defined.c +++ b/security/keys/user_defined.c | |||
| @@ -58,13 +58,14 @@ EXPORT_SYMBOL_GPL(key_type_logon); | |||
| 58 | /* | 58 | /* |
| 59 | * instantiate a user defined key | 59 | * instantiate a user defined key |
| 60 | */ | 60 | */ |
| 61 | int user_instantiate(struct key *key, const void *data, size_t datalen) | 61 | int user_instantiate(struct key *key, struct key_preparsed_payload *prep) |
| 62 | { | 62 | { |
| 63 | struct user_key_payload *upayload; | 63 | struct user_key_payload *upayload; |
| 64 | size_t datalen = prep->datalen; | ||
| 64 | int ret; | 65 | int ret; |
| 65 | 66 | ||
| 66 | ret = -EINVAL; | 67 | ret = -EINVAL; |
| 67 | if (datalen <= 0 || datalen > 32767 || !data) | 68 | if (datalen <= 0 || datalen > 32767 || !prep->data) |
| 68 | goto error; | 69 | goto error; |
| 69 | 70 | ||
| 70 | ret = key_payload_reserve(key, datalen); | 71 | ret = key_payload_reserve(key, datalen); |
| @@ -78,7 +79,7 @@ int user_instantiate(struct key *key, const void *data, size_t datalen) | |||
| 78 | 79 | ||
| 79 | /* attach the data */ | 80 | /* attach the data */ |
| 80 | upayload->datalen = datalen; | 81 | upayload->datalen = datalen; |
| 81 | memcpy(upayload->data, data, datalen); | 82 | memcpy(upayload->data, prep->data, datalen); |
| 82 | rcu_assign_keypointer(key, upayload); | 83 | rcu_assign_keypointer(key, upayload); |
| 83 | ret = 0; | 84 | ret = 0; |
| 84 | 85 | ||
| @@ -92,13 +93,14 @@ EXPORT_SYMBOL_GPL(user_instantiate); | |||
| 92 | * update a user defined key | 93 | * update a user defined key |
| 93 | * - the key's semaphore is write-locked | 94 | * - the key's semaphore is write-locked |
| 94 | */ | 95 | */ |
| 95 | int user_update(struct key *key, const void *data, size_t datalen) | 96 | int user_update(struct key *key, struct key_preparsed_payload *prep) |
| 96 | { | 97 | { |
| 97 | struct user_key_payload *upayload, *zap; | 98 | struct user_key_payload *upayload, *zap; |
| 99 | size_t datalen = prep->datalen; | ||
| 98 | int ret; | 100 | int ret; |
| 99 | 101 | ||
| 100 | ret = -EINVAL; | 102 | ret = -EINVAL; |
| 101 | if (datalen <= 0 || datalen > 32767 || !data) | 103 | if (datalen <= 0 || datalen > 32767 || !prep->data) |
| 102 | goto error; | 104 | goto error; |
| 103 | 105 | ||
| 104 | /* construct a replacement payload */ | 106 | /* construct a replacement payload */ |
| @@ -108,7 +110,7 @@ int user_update(struct key *key, const void *data, size_t datalen) | |||
| 108 | goto error; | 110 | goto error; |
| 109 | 111 | ||
| 110 | upayload->datalen = datalen; | 112 | upayload->datalen = datalen; |
| 111 | memcpy(upayload->data, data, datalen); | 113 | memcpy(upayload->data, prep->data, datalen); |
| 112 | 114 | ||
| 113 | /* check the quota and attach the new data */ | 115 | /* check the quota and attach the new data */ |
| 114 | zap = upayload; | 116 | zap = upayload; |
diff --git a/security/security.c b/security/security.c index 860aeb349cb3..8dcd4ae10a5f 100644 --- a/security/security.c +++ b/security/security.c | |||
| @@ -136,11 +136,23 @@ int __init register_security(struct security_operations *ops) | |||
| 136 | 136 | ||
| 137 | int security_ptrace_access_check(struct task_struct *child, unsigned int mode) | 137 | int security_ptrace_access_check(struct task_struct *child, unsigned int mode) |
| 138 | { | 138 | { |
| 139 | #ifdef CONFIG_SECURITY_YAMA_STACKED | ||
| 140 | int rc; | ||
| 141 | rc = yama_ptrace_access_check(child, mode); | ||
| 142 | if (rc) | ||
| 143 | return rc; | ||
| 144 | #endif | ||
| 139 | return security_ops->ptrace_access_check(child, mode); | 145 | return security_ops->ptrace_access_check(child, mode); |
| 140 | } | 146 | } |
| 141 | 147 | ||
| 142 | int security_ptrace_traceme(struct task_struct *parent) | 148 | int security_ptrace_traceme(struct task_struct *parent) |
| 143 | { | 149 | { |
| 150 | #ifdef CONFIG_SECURITY_YAMA_STACKED | ||
| 151 | int rc; | ||
| 152 | rc = yama_ptrace_traceme(parent); | ||
| 153 | if (rc) | ||
| 154 | return rc; | ||
| 155 | #endif | ||
| 144 | return security_ops->ptrace_traceme(parent); | 156 | return security_ops->ptrace_traceme(parent); |
| 145 | } | 157 | } |
| 146 | 158 | ||
| @@ -264,8 +276,8 @@ int security_sb_statfs(struct dentry *dentry) | |||
| 264 | return security_ops->sb_statfs(dentry); | 276 | return security_ops->sb_statfs(dentry); |
| 265 | } | 277 | } |
| 266 | 278 | ||
| 267 | int security_sb_mount(char *dev_name, struct path *path, | 279 | int security_sb_mount(const char *dev_name, struct path *path, |
| 268 | char *type, unsigned long flags, void *data) | 280 | const char *type, unsigned long flags, void *data) |
| 269 | { | 281 | { |
| 270 | return security_ops->sb_mount(dev_name, path, type, flags, data); | 282 | return security_ops->sb_mount(dev_name, path, type, flags, data); |
| 271 | } | 283 | } |
| @@ -434,7 +446,7 @@ int security_path_chmod(struct path *path, umode_t mode) | |||
| 434 | return security_ops->path_chmod(path, mode); | 446 | return security_ops->path_chmod(path, mode); |
| 435 | } | 447 | } |
| 436 | 448 | ||
| 437 | int security_path_chown(struct path *path, uid_t uid, gid_t gid) | 449 | int security_path_chown(struct path *path, kuid_t uid, kgid_t gid) |
| 438 | { | 450 | { |
| 439 | if (unlikely(IS_PRIVATE(path->dentry->d_inode))) | 451 | if (unlikely(IS_PRIVATE(path->dentry->d_inode))) |
| 440 | return 0; | 452 | return 0; |
| @@ -561,6 +573,9 @@ int security_inode_setxattr(struct dentry *dentry, const char *name, | |||
| 561 | ret = security_ops->inode_setxattr(dentry, name, value, size, flags); | 573 | ret = security_ops->inode_setxattr(dentry, name, value, size, flags); |
| 562 | if (ret) | 574 | if (ret) |
| 563 | return ret; | 575 | return ret; |
| 576 | ret = ima_inode_setxattr(dentry, name, value, size); | ||
| 577 | if (ret) | ||
| 578 | return ret; | ||
| 564 | return evm_inode_setxattr(dentry, name, value, size); | 579 | return evm_inode_setxattr(dentry, name, value, size); |
| 565 | } | 580 | } |
| 566 | 581 | ||
| @@ -596,6 +611,9 @@ int security_inode_removexattr(struct dentry *dentry, const char *name) | |||
| 596 | ret = security_ops->inode_removexattr(dentry, name); | 611 | ret = security_ops->inode_removexattr(dentry, name); |
| 597 | if (ret) | 612 | if (ret) |
| 598 | return ret; | 613 | return ret; |
| 614 | ret = ima_inode_removexattr(dentry, name); | ||
| 615 | if (ret) | ||
| 616 | return ret; | ||
| 599 | return evm_inode_removexattr(dentry, name); | 617 | return evm_inode_removexattr(dentry, name); |
| 600 | } | 618 | } |
| 601 | 619 | ||
| @@ -761,6 +779,9 @@ int security_task_create(unsigned long clone_flags) | |||
| 761 | 779 | ||
| 762 | void security_task_free(struct task_struct *task) | 780 | void security_task_free(struct task_struct *task) |
| 763 | { | 781 | { |
| 782 | #ifdef CONFIG_SECURITY_YAMA_STACKED | ||
| 783 | yama_task_free(task); | ||
| 784 | #endif | ||
| 764 | security_ops->task_free(task); | 785 | security_ops->task_free(task); |
| 765 | } | 786 | } |
| 766 | 787 | ||
| @@ -876,6 +897,12 @@ int security_task_wait(struct task_struct *p) | |||
| 876 | int security_task_prctl(int option, unsigned long arg2, unsigned long arg3, | 897 | int security_task_prctl(int option, unsigned long arg2, unsigned long arg3, |
| 877 | unsigned long arg4, unsigned long arg5) | 898 | unsigned long arg4, unsigned long arg5) |
| 878 | { | 899 | { |
| 900 | #ifdef CONFIG_SECURITY_YAMA_STACKED | ||
| 901 | int rc; | ||
| 902 | rc = yama_task_prctl(option, arg2, arg3, arg4, arg5); | ||
| 903 | if (rc != -ENOSYS) | ||
| 904 | return rc; | ||
| 905 | #endif | ||
| 879 | return security_ops->task_prctl(option, arg2, arg3, arg4, arg5); | 906 | return security_ops->task_prctl(option, arg2, arg3, arg4, arg5); |
| 880 | } | 907 | } |
| 881 | 908 | ||
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 6c77f63c7591..61a53367d029 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c | |||
| @@ -2088,15 +2088,19 @@ static int selinux_bprm_secureexec(struct linux_binprm *bprm) | |||
| 2088 | return (atsecure || cap_bprm_secureexec(bprm)); | 2088 | return (atsecure || cap_bprm_secureexec(bprm)); |
| 2089 | } | 2089 | } |
| 2090 | 2090 | ||
| 2091 | static int match_file(const void *p, struct file *file, unsigned fd) | ||
| 2092 | { | ||
| 2093 | return file_has_perm(p, file, file_to_av(file)) ? fd + 1 : 0; | ||
| 2094 | } | ||
| 2095 | |||
| 2091 | /* Derived from fs/exec.c:flush_old_files. */ | 2096 | /* Derived from fs/exec.c:flush_old_files. */ |
| 2092 | static inline void flush_unauthorized_files(const struct cred *cred, | 2097 | static inline void flush_unauthorized_files(const struct cred *cred, |
| 2093 | struct files_struct *files) | 2098 | struct files_struct *files) |
| 2094 | { | 2099 | { |
| 2095 | struct file *file, *devnull = NULL; | 2100 | struct file *file, *devnull = NULL; |
| 2096 | struct tty_struct *tty; | 2101 | struct tty_struct *tty; |
| 2097 | struct fdtable *fdt; | ||
| 2098 | long j = -1; | ||
| 2099 | int drop_tty = 0; | 2102 | int drop_tty = 0; |
| 2103 | unsigned n; | ||
| 2100 | 2104 | ||
| 2101 | tty = get_current_tty(); | 2105 | tty = get_current_tty(); |
| 2102 | if (tty) { | 2106 | if (tty) { |
| @@ -2123,58 +2127,19 @@ static inline void flush_unauthorized_files(const struct cred *cred, | |||
| 2123 | no_tty(); | 2127 | no_tty(); |
| 2124 | 2128 | ||
| 2125 | /* Revalidate access to inherited open files. */ | 2129 | /* Revalidate access to inherited open files. */ |
| 2126 | spin_lock(&files->file_lock); | 2130 | n = iterate_fd(files, 0, match_file, cred); |
| 2127 | for (;;) { | 2131 | if (!n) /* none found? */ |
| 2128 | unsigned long set, i; | 2132 | return; |
| 2129 | int fd; | ||
| 2130 | |||
| 2131 | j++; | ||
| 2132 | i = j * BITS_PER_LONG; | ||
| 2133 | fdt = files_fdtable(files); | ||
| 2134 | if (i >= fdt->max_fds) | ||
| 2135 | break; | ||
| 2136 | set = fdt->open_fds[j]; | ||
| 2137 | if (!set) | ||
| 2138 | continue; | ||
| 2139 | spin_unlock(&files->file_lock); | ||
| 2140 | for ( ; set ; i++, set >>= 1) { | ||
| 2141 | if (set & 1) { | ||
| 2142 | file = fget(i); | ||
| 2143 | if (!file) | ||
| 2144 | continue; | ||
| 2145 | if (file_has_perm(cred, | ||
| 2146 | file, | ||
| 2147 | file_to_av(file))) { | ||
| 2148 | sys_close(i); | ||
| 2149 | fd = get_unused_fd(); | ||
| 2150 | if (fd != i) { | ||
| 2151 | if (fd >= 0) | ||
| 2152 | put_unused_fd(fd); | ||
| 2153 | fput(file); | ||
| 2154 | continue; | ||
| 2155 | } | ||
| 2156 | if (devnull) { | ||
| 2157 | get_file(devnull); | ||
| 2158 | } else { | ||
| 2159 | devnull = dentry_open( | ||
| 2160 | &selinux_null, | ||
| 2161 | O_RDWR, cred); | ||
| 2162 | if (IS_ERR(devnull)) { | ||
| 2163 | devnull = NULL; | ||
| 2164 | put_unused_fd(fd); | ||
| 2165 | fput(file); | ||
| 2166 | continue; | ||
| 2167 | } | ||
| 2168 | } | ||
| 2169 | fd_install(fd, devnull); | ||
| 2170 | } | ||
| 2171 | fput(file); | ||
| 2172 | } | ||
| 2173 | } | ||
| 2174 | spin_lock(&files->file_lock); | ||
| 2175 | 2133 | ||
| 2176 | } | 2134 | devnull = dentry_open(&selinux_null, O_RDWR, cred); |
| 2177 | spin_unlock(&files->file_lock); | 2135 | if (IS_ERR(devnull)) |
| 2136 | devnull = NULL; | ||
| 2137 | /* replace all the matching ones with this */ | ||
| 2138 | do { | ||
| 2139 | replace_fd(n - 1, devnull, 0); | ||
| 2140 | } while ((n = iterate_fd(files, n, match_file, cred)) != 0); | ||
| 2141 | if (devnull) | ||
| 2142 | fput(devnull); | ||
| 2178 | } | 2143 | } |
| 2179 | 2144 | ||
| 2180 | /* | 2145 | /* |
| @@ -2483,9 +2448,9 @@ static int selinux_sb_statfs(struct dentry *dentry) | |||
| 2483 | return superblock_has_perm(cred, dentry->d_sb, FILESYSTEM__GETATTR, &ad); | 2448 | return superblock_has_perm(cred, dentry->d_sb, FILESYSTEM__GETATTR, &ad); |
| 2484 | } | 2449 | } |
| 2485 | 2450 | ||
| 2486 | static int selinux_mount(char *dev_name, | 2451 | static int selinux_mount(const char *dev_name, |
| 2487 | struct path *path, | 2452 | struct path *path, |
| 2488 | char *type, | 2453 | const char *type, |
| 2489 | unsigned long flags, | 2454 | unsigned long flags, |
| 2490 | void *data) | 2455 | void *data) |
| 2491 | { | 2456 | { |
diff --git a/security/selinux/netlink.c b/security/selinux/netlink.c index 8a77725423e0..14d810ead420 100644 --- a/security/selinux/netlink.c +++ b/security/selinux/netlink.c | |||
| @@ -113,13 +113,12 @@ static int __init selnl_init(void) | |||
| 113 | { | 113 | { |
| 114 | struct netlink_kernel_cfg cfg = { | 114 | struct netlink_kernel_cfg cfg = { |
| 115 | .groups = SELNLGRP_MAX, | 115 | .groups = SELNLGRP_MAX, |
| 116 | .flags = NL_CFG_F_NONROOT_RECV, | ||
| 116 | }; | 117 | }; |
| 117 | 118 | ||
| 118 | selnl = netlink_kernel_create(&init_net, NETLINK_SELINUX, | 119 | selnl = netlink_kernel_create(&init_net, NETLINK_SELINUX, &cfg); |
| 119 | THIS_MODULE, &cfg); | ||
| 120 | if (selnl == NULL) | 120 | if (selnl == NULL) |
| 121 | panic("SELinux: Cannot create netlink socket."); | 121 | panic("SELinux: Cannot create netlink socket."); |
| 122 | netlink_set_nonroot(NETLINK_SELINUX, NL_NONROOT_RECV); | ||
| 123 | return 0; | 122 | return 0; |
| 124 | } | 123 | } |
| 125 | 124 | ||
diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c index 298e695d6822..3a6e8731646c 100644 --- a/security/selinux/selinuxfs.c +++ b/security/selinux/selinuxfs.c | |||
| @@ -174,7 +174,7 @@ static ssize_t sel_write_enforce(struct file *file, const char __user *buf, | |||
| 174 | audit_log(current->audit_context, GFP_KERNEL, AUDIT_MAC_STATUS, | 174 | audit_log(current->audit_context, GFP_KERNEL, AUDIT_MAC_STATUS, |
| 175 | "enforcing=%d old_enforcing=%d auid=%u ses=%u", | 175 | "enforcing=%d old_enforcing=%d auid=%u ses=%u", |
| 176 | new_value, selinux_enforcing, | 176 | new_value, selinux_enforcing, |
| 177 | audit_get_loginuid(current), | 177 | from_kuid(&init_user_ns, audit_get_loginuid(current)), |
| 178 | audit_get_sessionid(current)); | 178 | audit_get_sessionid(current)); |
| 179 | selinux_enforcing = new_value; | 179 | selinux_enforcing = new_value; |
| 180 | if (selinux_enforcing) | 180 | if (selinux_enforcing) |
| @@ -305,7 +305,7 @@ static ssize_t sel_write_disable(struct file *file, const char __user *buf, | |||
| 305 | goto out; | 305 | goto out; |
| 306 | audit_log(current->audit_context, GFP_KERNEL, AUDIT_MAC_STATUS, | 306 | audit_log(current->audit_context, GFP_KERNEL, AUDIT_MAC_STATUS, |
| 307 | "selinux=0 auid=%u ses=%u", | 307 | "selinux=0 auid=%u ses=%u", |
| 308 | audit_get_loginuid(current), | 308 | from_kuid(&init_user_ns, audit_get_loginuid(current)), |
| 309 | audit_get_sessionid(current)); | 309 | audit_get_sessionid(current)); |
| 310 | } | 310 | } |
| 311 | 311 | ||
| @@ -485,7 +485,7 @@ static int sel_mmap_policy(struct file *filp, struct vm_area_struct *vma) | |||
| 485 | return -EACCES; | 485 | return -EACCES; |
| 486 | } | 486 | } |
| 487 | 487 | ||
| 488 | vma->vm_flags |= VM_RESERVED; | 488 | vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP; |
| 489 | vma->vm_ops = &sel_mmap_policy_ops; | 489 | vma->vm_ops = &sel_mmap_policy_ops; |
| 490 | 490 | ||
| 491 | return 0; | 491 | return 0; |
| @@ -551,7 +551,7 @@ static ssize_t sel_write_load(struct file *file, const char __user *buf, | |||
| 551 | out1: | 551 | out1: |
| 552 | audit_log(current->audit_context, GFP_KERNEL, AUDIT_MAC_POLICY_LOAD, | 552 | audit_log(current->audit_context, GFP_KERNEL, AUDIT_MAC_POLICY_LOAD, |
| 553 | "policy loaded auid=%u ses=%u", | 553 | "policy loaded auid=%u ses=%u", |
| 554 | audit_get_loginuid(current), | 554 | from_kuid(&init_user_ns, audit_get_loginuid(current)), |
| 555 | audit_get_sessionid(current)); | 555 | audit_get_sessionid(current)); |
| 556 | out: | 556 | out: |
| 557 | mutex_unlock(&sel_mutex); | 557 | mutex_unlock(&sel_mutex); |
diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index 4321b8fc8863..b4feecc3fe01 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c | |||
| @@ -2440,7 +2440,7 @@ int security_set_bools(int len, int *values) | |||
| 2440 | sym_name(&policydb, SYM_BOOLS, i), | 2440 | sym_name(&policydb, SYM_BOOLS, i), |
| 2441 | !!values[i], | 2441 | !!values[i], |
| 2442 | policydb.bool_val_to_struct[i]->state, | 2442 | policydb.bool_val_to_struct[i]->state, |
| 2443 | audit_get_loginuid(current), | 2443 | from_kuid(&init_user_ns, audit_get_loginuid(current)), |
| 2444 | audit_get_sessionid(current)); | 2444 | audit_get_sessionid(current)); |
| 2445 | } | 2445 | } |
| 2446 | if (values[i]) | 2446 | if (values[i]) |
diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index 8221514cc997..38be92ce901e 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c | |||
| @@ -408,8 +408,8 @@ static int smack_sb_statfs(struct dentry *dentry) | |||
| 408 | * Returns 0 if current can write the floor of the filesystem | 408 | * Returns 0 if current can write the floor of the filesystem |
| 409 | * being mounted on, an error code otherwise. | 409 | * being mounted on, an error code otherwise. |
| 410 | */ | 410 | */ |
| 411 | static int smack_sb_mount(char *dev_name, struct path *path, | 411 | static int smack_sb_mount(const char *dev_name, struct path *path, |
| 412 | char *type, unsigned long flags, void *data) | 412 | const char *type, unsigned long flags, void *data) |
| 413 | { | 413 | { |
| 414 | struct superblock_smack *sbp = path->dentry->d_sb->s_security; | 414 | struct superblock_smack *sbp = path->dentry->d_sb->s_security; |
| 415 | struct smk_audit_info ad; | 415 | struct smk_audit_info ad; |
| @@ -1691,40 +1691,19 @@ static int smack_task_kill(struct task_struct *p, struct siginfo *info, | |||
| 1691 | * smack_task_wait - Smack access check for waiting | 1691 | * smack_task_wait - Smack access check for waiting |
| 1692 | * @p: task to wait for | 1692 | * @p: task to wait for |
| 1693 | * | 1693 | * |
| 1694 | * Returns 0 if current can wait for p, error code otherwise | 1694 | * Returns 0 |
| 1695 | */ | 1695 | */ |
| 1696 | static int smack_task_wait(struct task_struct *p) | 1696 | static int smack_task_wait(struct task_struct *p) |
| 1697 | { | 1697 | { |
| 1698 | struct smk_audit_info ad; | ||
| 1699 | char *sp = smk_of_current(); | ||
| 1700 | char *tsp = smk_of_forked(task_security(p)); | ||
| 1701 | int rc; | ||
| 1702 | |||
| 1703 | /* we don't log here, we can be overriden */ | ||
| 1704 | rc = smk_access(tsp, sp, MAY_WRITE, NULL); | ||
| 1705 | if (rc == 0) | ||
| 1706 | goto out_log; | ||
| 1707 | |||
| 1708 | /* | 1698 | /* |
| 1709 | * Allow the operation to succeed if either task | 1699 | * Allow the operation to succeed. |
| 1710 | * has privilege to perform operations that might | 1700 | * Zombies are bad. |
| 1711 | * account for the smack labels having gotten to | 1701 | * In userless environments (e.g. phones) programs |
| 1712 | * be different in the first place. | 1702 | * get marked with SMACK64EXEC and even if the parent |
| 1713 | * | 1703 | * and child shouldn't be talking the parent still |
| 1714 | * This breaks the strict subject/object access | 1704 | * may expect to know when the child exits. |
| 1715 | * control ideal, taking the object's privilege | ||
| 1716 | * state into account in the decision as well as | ||
| 1717 | * the smack value. | ||
| 1718 | */ | 1705 | */ |
| 1719 | if (smack_privileged(CAP_MAC_OVERRIDE) || | 1706 | return 0; |
| 1720 | has_capability(p, CAP_MAC_OVERRIDE)) | ||
| 1721 | rc = 0; | ||
| 1722 | /* we log only if we didn't get overriden */ | ||
| 1723 | out_log: | ||
| 1724 | smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_TASK); | ||
| 1725 | smk_ad_setfield_u_tsk(&ad, p); | ||
| 1726 | smack_log(tsp, sp, MAY_WRITE, rc, &ad); | ||
| 1727 | return rc; | ||
| 1728 | } | 1707 | } |
| 1729 | 1708 | ||
| 1730 | /** | 1709 | /** |
| @@ -2705,9 +2684,7 @@ static int smack_getprocattr(struct task_struct *p, char *name, char **value) | |||
| 2705 | static int smack_setprocattr(struct task_struct *p, char *name, | 2684 | static int smack_setprocattr(struct task_struct *p, char *name, |
| 2706 | void *value, size_t size) | 2685 | void *value, size_t size) |
| 2707 | { | 2686 | { |
| 2708 | int rc; | ||
| 2709 | struct task_smack *tsp; | 2687 | struct task_smack *tsp; |
| 2710 | struct task_smack *oldtsp; | ||
| 2711 | struct cred *new; | 2688 | struct cred *new; |
| 2712 | char *newsmack; | 2689 | char *newsmack; |
| 2713 | 2690 | ||
| @@ -2737,21 +2714,13 @@ static int smack_setprocattr(struct task_struct *p, char *name, | |||
| 2737 | if (newsmack == smack_known_web.smk_known) | 2714 | if (newsmack == smack_known_web.smk_known) |
| 2738 | return -EPERM; | 2715 | return -EPERM; |
| 2739 | 2716 | ||
| 2740 | oldtsp = p->cred->security; | ||
| 2741 | new = prepare_creds(); | 2717 | new = prepare_creds(); |
| 2742 | if (new == NULL) | 2718 | if (new == NULL) |
| 2743 | return -ENOMEM; | 2719 | return -ENOMEM; |
| 2744 | 2720 | ||
| 2745 | tsp = new_task_smack(newsmack, oldtsp->smk_forked, GFP_KERNEL); | 2721 | tsp = new->security; |
| 2746 | if (tsp == NULL) { | 2722 | tsp->smk_task = newsmack; |
| 2747 | kfree(new); | ||
| 2748 | return -ENOMEM; | ||
| 2749 | } | ||
| 2750 | rc = smk_copy_rules(&tsp->smk_rules, &oldtsp->smk_rules, GFP_KERNEL); | ||
| 2751 | if (rc != 0) | ||
| 2752 | return rc; | ||
| 2753 | 2723 | ||
| 2754 | new->security = tsp; | ||
| 2755 | commit_creds(new); | 2724 | commit_creds(new); |
| 2756 | return size; | 2725 | return size; |
| 2757 | } | 2726 | } |
diff --git a/security/smack/smackfs.c b/security/smack/smackfs.c index b1b768e4049a..99929a50093a 100644 --- a/security/smack/smackfs.c +++ b/security/smack/smackfs.c | |||
| @@ -49,6 +49,7 @@ enum smk_inos { | |||
| 49 | SMK_LOAD_SELF2 = 15, /* load task specific rules with long labels */ | 49 | SMK_LOAD_SELF2 = 15, /* load task specific rules with long labels */ |
| 50 | SMK_ACCESS2 = 16, /* make an access check with long labels */ | 50 | SMK_ACCESS2 = 16, /* make an access check with long labels */ |
| 51 | SMK_CIPSO2 = 17, /* load long label -> CIPSO mapping */ | 51 | SMK_CIPSO2 = 17, /* load long label -> CIPSO mapping */ |
| 52 | SMK_REVOKE_SUBJ = 18, /* set rules with subject label to '-' */ | ||
| 52 | }; | 53 | }; |
| 53 | 54 | ||
| 54 | /* | 55 | /* |
| @@ -1992,6 +1993,77 @@ static const struct file_operations smk_access2_ops = { | |||
| 1992 | }; | 1993 | }; |
| 1993 | 1994 | ||
| 1994 | /** | 1995 | /** |
| 1996 | * smk_write_revoke_subj - write() for /smack/revoke-subject | ||
| 1997 | * @file: file pointer | ||
| 1998 | * @buf: data from user space | ||
| 1999 | * @count: bytes sent | ||
| 2000 | * @ppos: where to start - must be 0 | ||
| 2001 | */ | ||
| 2002 | static ssize_t smk_write_revoke_subj(struct file *file, const char __user *buf, | ||
| 2003 | size_t count, loff_t *ppos) | ||
| 2004 | { | ||
| 2005 | char *data = NULL; | ||
| 2006 | const char *cp = NULL; | ||
| 2007 | struct smack_known *skp; | ||
| 2008 | struct smack_rule *sp; | ||
| 2009 | struct list_head *rule_list; | ||
| 2010 | struct mutex *rule_lock; | ||
| 2011 | int rc = count; | ||
| 2012 | |||
| 2013 | if (*ppos != 0) | ||
| 2014 | return -EINVAL; | ||
| 2015 | |||
| 2016 | if (!smack_privileged(CAP_MAC_ADMIN)) | ||
| 2017 | return -EPERM; | ||
| 2018 | |||
| 2019 | if (count == 0 || count > SMK_LONGLABEL) | ||
| 2020 | return -EINVAL; | ||
| 2021 | |||
| 2022 | data = kzalloc(count, GFP_KERNEL); | ||
| 2023 | if (data == NULL) | ||
| 2024 | return -ENOMEM; | ||
| 2025 | |||
| 2026 | if (copy_from_user(data, buf, count) != 0) { | ||
| 2027 | rc = -EFAULT; | ||
| 2028 | goto free_out; | ||
| 2029 | } | ||
| 2030 | |||
| 2031 | cp = smk_parse_smack(data, count); | ||
| 2032 | if (cp == NULL) { | ||
| 2033 | rc = -EINVAL; | ||
| 2034 | goto free_out; | ||
| 2035 | } | ||
| 2036 | |||
| 2037 | skp = smk_find_entry(cp); | ||
| 2038 | if (skp == NULL) { | ||
| 2039 | rc = -EINVAL; | ||
| 2040 | goto free_out; | ||
| 2041 | } | ||
| 2042 | |||
| 2043 | rule_list = &skp->smk_rules; | ||
| 2044 | rule_lock = &skp->smk_rules_lock; | ||
| 2045 | |||
| 2046 | mutex_lock(rule_lock); | ||
| 2047 | |||
| 2048 | list_for_each_entry_rcu(sp, rule_list, list) | ||
| 2049 | sp->smk_access = 0; | ||
| 2050 | |||
| 2051 | mutex_unlock(rule_lock); | ||
| 2052 | |||
| 2053 | free_out: | ||
| 2054 | kfree(data); | ||
| 2055 | kfree(cp); | ||
| 2056 | return rc; | ||
| 2057 | } | ||
| 2058 | |||
| 2059 | static const struct file_operations smk_revoke_subj_ops = { | ||
| 2060 | .write = smk_write_revoke_subj, | ||
| 2061 | .read = simple_transaction_read, | ||
| 2062 | .release = simple_transaction_release, | ||
| 2063 | .llseek = generic_file_llseek, | ||
| 2064 | }; | ||
| 2065 | |||
| 2066 | /** | ||
| 1995 | * smk_fill_super - fill the /smackfs superblock | 2067 | * smk_fill_super - fill the /smackfs superblock |
| 1996 | * @sb: the empty superblock | 2068 | * @sb: the empty superblock |
| 1997 | * @data: unused | 2069 | * @data: unused |
| @@ -2037,6 +2109,9 @@ static int smk_fill_super(struct super_block *sb, void *data, int silent) | |||
| 2037 | "access2", &smk_access2_ops, S_IRUGO|S_IWUGO}, | 2109 | "access2", &smk_access2_ops, S_IRUGO|S_IWUGO}, |
| 2038 | [SMK_CIPSO2] = { | 2110 | [SMK_CIPSO2] = { |
| 2039 | "cipso2", &smk_cipso2_ops, S_IRUGO|S_IWUSR}, | 2111 | "cipso2", &smk_cipso2_ops, S_IRUGO|S_IWUSR}, |
| 2112 | [SMK_REVOKE_SUBJ] = { | ||
| 2113 | "revoke-subject", &smk_revoke_subj_ops, | ||
| 2114 | S_IRUGO|S_IWUSR}, | ||
| 2040 | /* last one */ | 2115 | /* last one */ |
| 2041 | {""} | 2116 | {""} |
| 2042 | }; | 2117 | }; |
diff --git a/security/tomoyo/audit.c b/security/tomoyo/audit.c index 7ef9fa3e37e0..c1b00375c9ad 100644 --- a/security/tomoyo/audit.c +++ b/security/tomoyo/audit.c | |||
| @@ -168,9 +168,14 @@ static char *tomoyo_print_header(struct tomoyo_request_info *r) | |||
| 168 | stamp.day, stamp.hour, stamp.min, stamp.sec, r->profile, | 168 | stamp.day, stamp.hour, stamp.min, stamp.sec, r->profile, |
| 169 | tomoyo_mode[r->mode], tomoyo_yesno(r->granted), gpid, | 169 | tomoyo_mode[r->mode], tomoyo_yesno(r->granted), gpid, |
| 170 | tomoyo_sys_getpid(), tomoyo_sys_getppid(), | 170 | tomoyo_sys_getpid(), tomoyo_sys_getppid(), |
| 171 | current_uid(), current_gid(), current_euid(), | 171 | from_kuid(&init_user_ns, current_uid()), |
| 172 | current_egid(), current_suid(), current_sgid(), | 172 | from_kgid(&init_user_ns, current_gid()), |
| 173 | current_fsuid(), current_fsgid()); | 173 | from_kuid(&init_user_ns, current_euid()), |
| 174 | from_kgid(&init_user_ns, current_egid()), | ||
| 175 | from_kuid(&init_user_ns, current_suid()), | ||
| 176 | from_kgid(&init_user_ns, current_sgid()), | ||
| 177 | from_kuid(&init_user_ns, current_fsuid()), | ||
| 178 | from_kgid(&init_user_ns, current_fsgid())); | ||
| 174 | if (!obj) | 179 | if (!obj) |
| 175 | goto no_obj_info; | 180 | goto no_obj_info; |
| 176 | if (!obj->validate_done) { | 181 | if (!obj->validate_done) { |
| @@ -191,15 +196,19 @@ static char *tomoyo_print_header(struct tomoyo_request_info *r) | |||
| 191 | tomoyo_buffer_len - 1 - pos, | 196 | tomoyo_buffer_len - 1 - pos, |
| 192 | " path%u.parent={ uid=%u gid=%u " | 197 | " path%u.parent={ uid=%u gid=%u " |
| 193 | "ino=%lu perm=0%o }", (i >> 1) + 1, | 198 | "ino=%lu perm=0%o }", (i >> 1) + 1, |
| 194 | stat->uid, stat->gid, (unsigned long) | 199 | from_kuid(&init_user_ns, stat->uid), |
| 195 | stat->ino, stat->mode & S_IALLUGO); | 200 | from_kgid(&init_user_ns, stat->gid), |
| 201 | (unsigned long)stat->ino, | ||
| 202 | stat->mode & S_IALLUGO); | ||
| 196 | continue; | 203 | continue; |
| 197 | } | 204 | } |
| 198 | pos += snprintf(buffer + pos, tomoyo_buffer_len - 1 - pos, | 205 | pos += snprintf(buffer + pos, tomoyo_buffer_len - 1 - pos, |
| 199 | " path%u={ uid=%u gid=%u ino=%lu major=%u" | 206 | " path%u={ uid=%u gid=%u ino=%lu major=%u" |
| 200 | " minor=%u perm=0%o type=%s", (i >> 1) + 1, | 207 | " minor=%u perm=0%o type=%s", (i >> 1) + 1, |
| 201 | stat->uid, stat->gid, (unsigned long) | 208 | from_kuid(&init_user_ns, stat->uid), |
| 202 | stat->ino, MAJOR(dev), MINOR(dev), | 209 | from_kgid(&init_user_ns, stat->gid), |
| 210 | (unsigned long)stat->ino, | ||
| 211 | MAJOR(dev), MINOR(dev), | ||
| 203 | mode & S_IALLUGO, tomoyo_filetype(mode)); | 212 | mode & S_IALLUGO, tomoyo_filetype(mode)); |
| 204 | if (S_ISCHR(mode) || S_ISBLK(mode)) { | 213 | if (S_ISCHR(mode) || S_ISBLK(mode)) { |
| 205 | dev = stat->rdev; | 214 | dev = stat->rdev; |
diff --git a/security/tomoyo/common.c b/security/tomoyo/common.c index 2e0f12c62938..f89a0333b813 100644 --- a/security/tomoyo/common.c +++ b/security/tomoyo/common.c | |||
| @@ -925,7 +925,9 @@ static bool tomoyo_manager(void) | |||
| 925 | 925 | ||
| 926 | if (!tomoyo_policy_loaded) | 926 | if (!tomoyo_policy_loaded) |
| 927 | return true; | 927 | return true; |
| 928 | if (!tomoyo_manage_by_non_root && (task->cred->uid || task->cred->euid)) | 928 | if (!tomoyo_manage_by_non_root && |
| 929 | (!uid_eq(task->cred->uid, GLOBAL_ROOT_UID) || | ||
| 930 | !uid_eq(task->cred->euid, GLOBAL_ROOT_UID))) | ||
| 929 | return false; | 931 | return false; |
| 930 | exe = tomoyo_get_exe(); | 932 | exe = tomoyo_get_exe(); |
| 931 | if (!exe) | 933 | if (!exe) |
diff --git a/security/tomoyo/common.h b/security/tomoyo/common.h index 75e4dc1c02a0..d4f166bc3508 100644 --- a/security/tomoyo/common.h +++ b/security/tomoyo/common.h | |||
| @@ -561,8 +561,8 @@ struct tomoyo_address_group { | |||
| 561 | 561 | ||
| 562 | /* Subset of "struct stat". Used by conditional ACL and audit logs. */ | 562 | /* Subset of "struct stat". Used by conditional ACL and audit logs. */ |
| 563 | struct tomoyo_mini_stat { | 563 | struct tomoyo_mini_stat { |
| 564 | uid_t uid; | 564 | kuid_t uid; |
| 565 | gid_t gid; | 565 | kgid_t gid; |
| 566 | ino_t ino; | 566 | ino_t ino; |
| 567 | umode_t mode; | 567 | umode_t mode; |
| 568 | dev_t dev; | 568 | dev_t dev; |
| @@ -970,7 +970,7 @@ int tomoyo_init_request_info(struct tomoyo_request_info *r, | |||
| 970 | const u8 index); | 970 | const u8 index); |
| 971 | int tomoyo_mkdev_perm(const u8 operation, struct path *path, | 971 | int tomoyo_mkdev_perm(const u8 operation, struct path *path, |
| 972 | const unsigned int mode, unsigned int dev); | 972 | const unsigned int mode, unsigned int dev); |
| 973 | int tomoyo_mount_permission(char *dev_name, struct path *path, | 973 | int tomoyo_mount_permission(const char *dev_name, struct path *path, |
| 974 | const char *type, unsigned long flags, | 974 | const char *type, unsigned long flags, |
| 975 | void *data_page); | 975 | void *data_page); |
| 976 | int tomoyo_open_control(const u8 type, struct file *file); | 976 | int tomoyo_open_control(const u8 type, struct file *file); |
diff --git a/security/tomoyo/condition.c b/security/tomoyo/condition.c index 986330b8c73e..63681e8be628 100644 --- a/security/tomoyo/condition.c +++ b/security/tomoyo/condition.c | |||
| @@ -813,28 +813,28 @@ bool tomoyo_condition(struct tomoyo_request_info *r, | |||
| 813 | unsigned long value = 0; | 813 | unsigned long value = 0; |
| 814 | switch (index) { | 814 | switch (index) { |
| 815 | case TOMOYO_TASK_UID: | 815 | case TOMOYO_TASK_UID: |
| 816 | value = current_uid(); | 816 | value = from_kuid(&init_user_ns, current_uid()); |
| 817 | break; | 817 | break; |
| 818 | case TOMOYO_TASK_EUID: | 818 | case TOMOYO_TASK_EUID: |
| 819 | value = current_euid(); | 819 | value = from_kuid(&init_user_ns, current_euid()); |
| 820 | break; | 820 | break; |
| 821 | case TOMOYO_TASK_SUID: | 821 | case TOMOYO_TASK_SUID: |
| 822 | value = current_suid(); | 822 | value = from_kuid(&init_user_ns, current_suid()); |
| 823 | break; | 823 | break; |
| 824 | case TOMOYO_TASK_FSUID: | 824 | case TOMOYO_TASK_FSUID: |
| 825 | value = current_fsuid(); | 825 | value = from_kuid(&init_user_ns, current_fsuid()); |
| 826 | break; | 826 | break; |
| 827 | case TOMOYO_TASK_GID: | 827 | case TOMOYO_TASK_GID: |
| 828 | value = current_gid(); | 828 | value = from_kgid(&init_user_ns, current_gid()); |
| 829 | break; | 829 | break; |
| 830 | case TOMOYO_TASK_EGID: | 830 | case TOMOYO_TASK_EGID: |
| 831 | value = current_egid(); | 831 | value = from_kgid(&init_user_ns, current_egid()); |
| 832 | break; | 832 | break; |
| 833 | case TOMOYO_TASK_SGID: | 833 | case TOMOYO_TASK_SGID: |
| 834 | value = current_sgid(); | 834 | value = from_kgid(&init_user_ns, current_sgid()); |
| 835 | break; | 835 | break; |
| 836 | case TOMOYO_TASK_FSGID: | 836 | case TOMOYO_TASK_FSGID: |
| 837 | value = current_fsgid(); | 837 | value = from_kgid(&init_user_ns, current_fsgid()); |
| 838 | break; | 838 | break; |
| 839 | case TOMOYO_TASK_PID: | 839 | case TOMOYO_TASK_PID: |
| 840 | value = tomoyo_sys_getpid(); | 840 | value = tomoyo_sys_getpid(); |
| @@ -970,13 +970,13 @@ bool tomoyo_condition(struct tomoyo_request_info *r, | |||
| 970 | case TOMOYO_PATH2_UID: | 970 | case TOMOYO_PATH2_UID: |
| 971 | case TOMOYO_PATH1_PARENT_UID: | 971 | case TOMOYO_PATH1_PARENT_UID: |
| 972 | case TOMOYO_PATH2_PARENT_UID: | 972 | case TOMOYO_PATH2_PARENT_UID: |
| 973 | value = stat->uid; | 973 | value = from_kuid(&init_user_ns, stat->uid); |
| 974 | break; | 974 | break; |
| 975 | case TOMOYO_PATH1_GID: | 975 | case TOMOYO_PATH1_GID: |
| 976 | case TOMOYO_PATH2_GID: | 976 | case TOMOYO_PATH2_GID: |
| 977 | case TOMOYO_PATH1_PARENT_GID: | 977 | case TOMOYO_PATH1_PARENT_GID: |
| 978 | case TOMOYO_PATH2_PARENT_GID: | 978 | case TOMOYO_PATH2_PARENT_GID: |
| 979 | value = stat->gid; | 979 | value = from_kgid(&init_user_ns, stat->gid); |
| 980 | break; | 980 | break; |
| 981 | case TOMOYO_PATH1_INO: | 981 | case TOMOYO_PATH1_INO: |
| 982 | case TOMOYO_PATH2_INO: | 982 | case TOMOYO_PATH2_INO: |
diff --git a/security/tomoyo/mount.c b/security/tomoyo/mount.c index fe00cdfd0267..390c646013cb 100644 --- a/security/tomoyo/mount.c +++ b/security/tomoyo/mount.c | |||
| @@ -71,7 +71,8 @@ static bool tomoyo_check_mount_acl(struct tomoyo_request_info *r, | |||
| 71 | * | 71 | * |
| 72 | * Caller holds tomoyo_read_lock(). | 72 | * Caller holds tomoyo_read_lock(). |
| 73 | */ | 73 | */ |
| 74 | static int tomoyo_mount_acl(struct tomoyo_request_info *r, char *dev_name, | 74 | static int tomoyo_mount_acl(struct tomoyo_request_info *r, |
| 75 | const char *dev_name, | ||
| 75 | struct path *dir, const char *type, | 76 | struct path *dir, const char *type, |
| 76 | unsigned long flags) | 77 | unsigned long flags) |
| 77 | { | 78 | { |
| @@ -183,7 +184,7 @@ static int tomoyo_mount_acl(struct tomoyo_request_info *r, char *dev_name, | |||
| 183 | * | 184 | * |
| 184 | * Returns 0 on success, negative value otherwise. | 185 | * Returns 0 on success, negative value otherwise. |
| 185 | */ | 186 | */ |
| 186 | int tomoyo_mount_permission(char *dev_name, struct path *path, | 187 | int tomoyo_mount_permission(const char *dev_name, struct path *path, |
| 187 | const char *type, unsigned long flags, | 188 | const char *type, unsigned long flags, |
| 188 | void *data_page) | 189 | void *data_page) |
| 189 | { | 190 | { |
diff --git a/security/tomoyo/tomoyo.c b/security/tomoyo/tomoyo.c index c2d04a50f76a..a2ee362546ab 100644 --- a/security/tomoyo/tomoyo.c +++ b/security/tomoyo/tomoyo.c | |||
| @@ -373,13 +373,15 @@ static int tomoyo_path_chmod(struct path *path, umode_t mode) | |||
| 373 | * | 373 | * |
| 374 | * Returns 0 on success, negative value otherwise. | 374 | * Returns 0 on success, negative value otherwise. |
| 375 | */ | 375 | */ |
| 376 | static int tomoyo_path_chown(struct path *path, uid_t uid, gid_t gid) | 376 | static int tomoyo_path_chown(struct path *path, kuid_t uid, kgid_t gid) |
| 377 | { | 377 | { |
| 378 | int error = 0; | 378 | int error = 0; |
| 379 | if (uid != (uid_t) -1) | 379 | if (uid_valid(uid)) |
| 380 | error = tomoyo_path_number_perm(TOMOYO_TYPE_CHOWN, path, uid); | 380 | error = tomoyo_path_number_perm(TOMOYO_TYPE_CHOWN, path, |
| 381 | if (!error && gid != (gid_t) -1) | 381 | from_kuid(&init_user_ns, uid)); |
| 382 | error = tomoyo_path_number_perm(TOMOYO_TYPE_CHGRP, path, gid); | 382 | if (!error && gid_valid(gid)) |
| 383 | error = tomoyo_path_number_perm(TOMOYO_TYPE_CHGRP, path, | ||
| 384 | from_kgid(&init_user_ns, gid)); | ||
| 383 | return error; | 385 | return error; |
| 384 | } | 386 | } |
| 385 | 387 | ||
| @@ -406,8 +408,8 @@ static int tomoyo_path_chroot(struct path *path) | |||
| 406 | * | 408 | * |
| 407 | * Returns 0 on success, negative value otherwise. | 409 | * Returns 0 on success, negative value otherwise. |
| 408 | */ | 410 | */ |
| 409 | static int tomoyo_sb_mount(char *dev_name, struct path *path, | 411 | static int tomoyo_sb_mount(const char *dev_name, struct path *path, |
| 410 | char *type, unsigned long flags, void *data) | 412 | const char *type, unsigned long flags, void *data) |
| 411 | { | 413 | { |
| 412 | return tomoyo_mount_permission(dev_name, path, type, flags, data); | 414 | return tomoyo_mount_permission(dev_name, path, type, flags, data); |
| 413 | } | 415 | } |
diff --git a/security/tomoyo/util.c b/security/tomoyo/util.c index 867558c98334..2952ba576fb9 100644 --- a/security/tomoyo/util.c +++ b/security/tomoyo/util.c | |||
| @@ -949,18 +949,13 @@ bool tomoyo_path_matches_pattern(const struct tomoyo_path_info *filename, | |||
| 949 | const char *tomoyo_get_exe(void) | 949 | const char *tomoyo_get_exe(void) |
| 950 | { | 950 | { |
| 951 | struct mm_struct *mm = current->mm; | 951 | struct mm_struct *mm = current->mm; |
| 952 | struct vm_area_struct *vma; | ||
| 953 | const char *cp = NULL; | 952 | const char *cp = NULL; |
| 954 | 953 | ||
| 955 | if (!mm) | 954 | if (!mm) |
| 956 | return NULL; | 955 | return NULL; |
| 957 | down_read(&mm->mmap_sem); | 956 | down_read(&mm->mmap_sem); |
| 958 | for (vma = mm->mmap; vma; vma = vma->vm_next) { | 957 | if (mm->exe_file) |
| 959 | if ((vma->vm_flags & VM_EXECUTABLE) && vma->vm_file) { | 958 | cp = tomoyo_realpath_from_path(&mm->exe_file->f_path); |
| 960 | cp = tomoyo_realpath_from_path(&vma->vm_file->f_path); | ||
| 961 | break; | ||
| 962 | } | ||
| 963 | } | ||
| 964 | up_read(&mm->mmap_sem); | 959 | up_read(&mm->mmap_sem); |
| 965 | return cp; | 960 | return cp; |
| 966 | } | 961 | } |
diff --git a/security/yama/Kconfig b/security/yama/Kconfig index 51d6709d8bbd..20ef5143c0c0 100644 --- a/security/yama/Kconfig +++ b/security/yama/Kconfig | |||
| @@ -11,3 +11,11 @@ config SECURITY_YAMA | |||
| 11 | Further information can be found in Documentation/security/Yama.txt. | 11 | Further information can be found in Documentation/security/Yama.txt. |
| 12 | 12 | ||
| 13 | If you are unsure how to answer this question, answer N. | 13 | If you are unsure how to answer this question, answer N. |
| 14 | |||
| 15 | config SECURITY_YAMA_STACKED | ||
| 16 | bool "Yama stacked with other LSMs" | ||
| 17 | depends on SECURITY_YAMA | ||
| 18 | default n | ||
| 19 | help | ||
| 20 | When Yama is built into the kernel, force it to stack with the | ||
| 21 | selected primary LSM. | ||
diff --git a/security/yama/yama_lsm.c b/security/yama/yama_lsm.c index 0cc99a3ea42d..b4c29848b49d 100644 --- a/security/yama/yama_lsm.c +++ b/security/yama/yama_lsm.c | |||
| @@ -100,7 +100,7 @@ static void yama_ptracer_del(struct task_struct *tracer, | |||
| 100 | * yama_task_free - check for task_pid to remove from exception list | 100 | * yama_task_free - check for task_pid to remove from exception list |
| 101 | * @task: task being removed | 101 | * @task: task being removed |
| 102 | */ | 102 | */ |
| 103 | static void yama_task_free(struct task_struct *task) | 103 | void yama_task_free(struct task_struct *task) |
| 104 | { | 104 | { |
| 105 | yama_ptracer_del(task, task); | 105 | yama_ptracer_del(task, task); |
| 106 | } | 106 | } |
| @@ -116,7 +116,7 @@ static void yama_task_free(struct task_struct *task) | |||
| 116 | * Return 0 on success, -ve on error. -ENOSYS is returned when Yama | 116 | * Return 0 on success, -ve on error. -ENOSYS is returned when Yama |
| 117 | * does not handle the given option. | 117 | * does not handle the given option. |
| 118 | */ | 118 | */ |
| 119 | static int yama_task_prctl(int option, unsigned long arg2, unsigned long arg3, | 119 | int yama_task_prctl(int option, unsigned long arg2, unsigned long arg3, |
| 120 | unsigned long arg4, unsigned long arg5) | 120 | unsigned long arg4, unsigned long arg5) |
| 121 | { | 121 | { |
| 122 | int rc; | 122 | int rc; |
| @@ -143,7 +143,7 @@ static int yama_task_prctl(int option, unsigned long arg2, unsigned long arg3, | |||
| 143 | if (arg2 == 0) { | 143 | if (arg2 == 0) { |
| 144 | yama_ptracer_del(NULL, myself); | 144 | yama_ptracer_del(NULL, myself); |
| 145 | rc = 0; | 145 | rc = 0; |
| 146 | } else if (arg2 == PR_SET_PTRACER_ANY) { | 146 | } else if (arg2 == PR_SET_PTRACER_ANY || (int)arg2 == -1) { |
| 147 | rc = yama_ptracer_add(NULL, myself); | 147 | rc = yama_ptracer_add(NULL, myself); |
| 148 | } else { | 148 | } else { |
| 149 | struct task_struct *tracer; | 149 | struct task_struct *tracer; |
| @@ -243,7 +243,7 @@ static int ptracer_exception_found(struct task_struct *tracer, | |||
| 243 | * | 243 | * |
| 244 | * Returns 0 if following the ptrace is allowed, -ve on error. | 244 | * Returns 0 if following the ptrace is allowed, -ve on error. |
| 245 | */ | 245 | */ |
| 246 | static int yama_ptrace_access_check(struct task_struct *child, | 246 | int yama_ptrace_access_check(struct task_struct *child, |
| 247 | unsigned int mode) | 247 | unsigned int mode) |
| 248 | { | 248 | { |
| 249 | int rc; | 249 | int rc; |
| @@ -293,7 +293,7 @@ static int yama_ptrace_access_check(struct task_struct *child, | |||
| 293 | * | 293 | * |
| 294 | * Returns 0 if following the ptrace is allowed, -ve on error. | 294 | * Returns 0 if following the ptrace is allowed, -ve on error. |
| 295 | */ | 295 | */ |
| 296 | static int yama_ptrace_traceme(struct task_struct *parent) | 296 | int yama_ptrace_traceme(struct task_struct *parent) |
| 297 | { | 297 | { |
| 298 | int rc; | 298 | int rc; |
| 299 | 299 | ||
| @@ -324,6 +324,7 @@ static int yama_ptrace_traceme(struct task_struct *parent) | |||
| 324 | return rc; | 324 | return rc; |
| 325 | } | 325 | } |
| 326 | 326 | ||
| 327 | #ifndef CONFIG_SECURITY_YAMA_STACKED | ||
| 327 | static struct security_operations yama_ops = { | 328 | static struct security_operations yama_ops = { |
| 328 | .name = "yama", | 329 | .name = "yama", |
| 329 | 330 | ||
| @@ -332,6 +333,7 @@ static struct security_operations yama_ops = { | |||
| 332 | .task_prctl = yama_task_prctl, | 333 | .task_prctl = yama_task_prctl, |
| 333 | .task_free = yama_task_free, | 334 | .task_free = yama_task_free, |
| 334 | }; | 335 | }; |
| 336 | #endif | ||
| 335 | 337 | ||
| 336 | #ifdef CONFIG_SYSCTL | 338 | #ifdef CONFIG_SYSCTL |
| 337 | static int yama_dointvec_minmax(struct ctl_table *table, int write, | 339 | static int yama_dointvec_minmax(struct ctl_table *table, int write, |
| @@ -378,13 +380,17 @@ static struct ctl_table yama_sysctl_table[] = { | |||
| 378 | 380 | ||
| 379 | static __init int yama_init(void) | 381 | static __init int yama_init(void) |
| 380 | { | 382 | { |
| 383 | #ifndef CONFIG_SECURITY_YAMA_STACKED | ||
| 381 | if (!security_module_enable(&yama_ops)) | 384 | if (!security_module_enable(&yama_ops)) |
| 382 | return 0; | 385 | return 0; |
| 386 | #endif | ||
| 383 | 387 | ||
| 384 | printk(KERN_INFO "Yama: becoming mindful.\n"); | 388 | printk(KERN_INFO "Yama: becoming mindful.\n"); |
| 385 | 389 | ||
| 390 | #ifndef CONFIG_SECURITY_YAMA_STACKED | ||
| 386 | if (register_security(&yama_ops)) | 391 | if (register_security(&yama_ops)) |
| 387 | panic("Yama: kernel registration failed.\n"); | 392 | panic("Yama: kernel registration failed.\n"); |
| 393 | #endif | ||
| 388 | 394 | ||
| 389 | #ifdef CONFIG_SYSCTL | 395 | #ifdef CONFIG_SYSCTL |
| 390 | if (!register_sysctl_paths(yama_sysctl_path, yama_sysctl_table)) | 396 | if (!register_sysctl_paths(yama_sysctl_path, yama_sysctl_table)) |
