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

Merge tag 'safesetid-5.3' of git://github.com/micah-morton/linux

Pull safesetid updates from Micah Morton:
"These changes from Jann Horn fix a couple issues in the recently added
SafeSetID LSM:

- There was a simple logic bug in one of the hooks for the LSM where
the code was incorrectly returning early in some cases before all
security checks had been passed.

- There was a more high level issue with how this LSM gets configured
that could allow for a program to bypass the security restrictions
by switching to an allowed UID and then again to any other UID on
the system if the target UID of the first transition is
unconstrained on the system. Luckily this is an easy fix that we
now enforce at the time the LSM gets configured.

There are also some changes from Jann that make policy updates for
this LSM atomic. Kees Cook, Jann and myself have reviewed these
changes and they look good from our point of view"

* tag 'safesetid-5.3' of git://github.com/micah-morton/linux:
LSM: SafeSetID: fix use of literal -1 in capable hook
LSM: SafeSetID: verify transitive constrainedness
LSM: SafeSetID: add read handler
LSM: SafeSetID: rewrite userspace API to atomic updates
LSM: SafeSetID: fix userns handling in securityfs
LSM: SafeSetID: refactor policy parsing
LSM: SafeSetID: refactor safesetid_security_capable()
LSM: SafeSetID: refactor policy hash table
LSM: SafeSetID: fix check for setresuid(new1, new2, new3)
LSM: SafeSetID: fix pr_warn() to include newline

+310 -333
+84 -190
security/safesetid/lsm.c
··· 14 14 15 15 #define pr_fmt(fmt) "SafeSetID: " fmt 16 16 17 - #include <linux/hashtable.h> 18 17 #include <linux/lsm_hooks.h> 19 18 #include <linux/module.h> 20 19 #include <linux/ptrace.h> 21 20 #include <linux/sched/task_stack.h> 22 21 #include <linux/security.h> 22 + #include "lsm.h" 23 23 24 24 /* Flag indicating whether initialization completed */ 25 25 int safesetid_initialized; 26 26 27 - #define NUM_BITS 8 /* 128 buckets in hash table */ 27 + struct setuid_ruleset __rcu *safesetid_setuid_rules; 28 28 29 - static DEFINE_HASHTABLE(safesetid_whitelist_hashtable, NUM_BITS); 30 - 31 - /* 32 - * Hash table entry to store safesetid policy signifying that 'parent' user 33 - * can setid to 'child' user. 34 - */ 35 - struct entry { 36 - struct hlist_node next; 37 - struct hlist_node dlist; /* for deletion cleanup */ 38 - uint64_t parent_kuid; 39 - uint64_t child_kuid; 40 - }; 41 - 42 - static DEFINE_SPINLOCK(safesetid_whitelist_hashtable_spinlock); 43 - 44 - static bool check_setuid_policy_hashtable_key(kuid_t parent) 29 + /* Compute a decision for a transition from @src to @dst under @policy. */ 30 + enum sid_policy_type _setuid_policy_lookup(struct setuid_ruleset *policy, 31 + kuid_t src, kuid_t dst) 45 32 { 46 - struct entry *entry; 33 + struct setuid_rule *rule; 34 + enum sid_policy_type result = SIDPOL_DEFAULT; 47 35 48 - rcu_read_lock(); 49 - hash_for_each_possible_rcu(safesetid_whitelist_hashtable, 50 - entry, next, __kuid_val(parent)) { 51 - if (entry->parent_kuid == __kuid_val(parent)) { 52 - rcu_read_unlock(); 53 - return true; 54 - } 36 + hash_for_each_possible(policy->rules, rule, next, __kuid_val(src)) { 37 + if (!uid_eq(rule->src_uid, src)) 38 + continue; 39 + if (uid_eq(rule->dst_uid, dst)) 40 + return SIDPOL_ALLOWED; 41 + result = SIDPOL_CONSTRAINED; 55 42 } 56 - rcu_read_unlock(); 57 - 58 - return false; 43 + return result; 59 44 } 60 45 61 - static bool check_setuid_policy_hashtable_key_value(kuid_t parent, 62 - kuid_t child) 46 + /* 47 + * Compute a decision for a transition from @src to @dst under the active 48 + * policy. 49 + */ 50 + static enum sid_policy_type setuid_policy_lookup(kuid_t src, kuid_t dst) 63 51 { 64 - struct entry *entry; 52 + enum sid_policy_type result = SIDPOL_DEFAULT; 53 + struct setuid_ruleset *pol; 65 54 66 55 rcu_read_lock(); 67 - hash_for_each_possible_rcu(safesetid_whitelist_hashtable, 68 - entry, next, __kuid_val(parent)) { 69 - if (entry->parent_kuid == __kuid_val(parent) && 70 - entry->child_kuid == __kuid_val(child)) { 71 - rcu_read_unlock(); 72 - return true; 73 - } 74 - } 56 + pol = rcu_dereference(safesetid_setuid_rules); 57 + if (pol) 58 + result = _setuid_policy_lookup(pol, src, dst); 75 59 rcu_read_unlock(); 76 - 77 - return false; 60 + return result; 78 61 } 79 62 80 63 static int safesetid_security_capable(const struct cred *cred, ··· 65 82 int cap, 66 83 unsigned int opts) 67 84 { 68 - if (cap == CAP_SETUID && 69 - check_setuid_policy_hashtable_key(cred->uid)) { 70 - if (!(opts & CAP_OPT_INSETID)) { 71 - /* 72 - * Deny if we're not in a set*uid() syscall to avoid 73 - * giving powers gated by CAP_SETUID that are related 74 - * to functionality other than calling set*uid() (e.g. 75 - * allowing user to set up userns uid mappings). 76 - */ 77 - pr_warn("Operation requires CAP_SETUID, which is not available to UID %u for operations besides approved set*uid transitions", 78 - __kuid_val(cred->uid)); 79 - return -1; 80 - } 81 - } 82 - return 0; 85 + /* We're only interested in CAP_SETUID. */ 86 + if (cap != CAP_SETUID) 87 + return 0; 88 + 89 + /* 90 + * If CAP_SETUID is currently used for a set*uid() syscall, we want to 91 + * let it go through here; the real security check happens later, in the 92 + * task_fix_setuid hook. 93 + */ 94 + if ((opts & CAP_OPT_INSETID) != 0) 95 + return 0; 96 + 97 + /* 98 + * If no policy applies to this task, allow the use of CAP_SETUID for 99 + * other purposes. 100 + */ 101 + if (setuid_policy_lookup(cred->uid, INVALID_UID) == SIDPOL_DEFAULT) 102 + return 0; 103 + 104 + /* 105 + * Reject use of CAP_SETUID for functionality other than calling 106 + * set*uid() (e.g. setting up userns uid mappings). 107 + */ 108 + pr_warn("Operation requires CAP_SETUID, which is not available to UID %u for operations besides approved set*uid transitions\n", 109 + __kuid_val(cred->uid)); 110 + return -EPERM; 83 111 } 84 112 85 - static int check_uid_transition(kuid_t parent, kuid_t child) 113 + /* 114 + * Check whether a caller with old credentials @old is allowed to switch to 115 + * credentials that contain @new_uid. 116 + */ 117 + static bool uid_permitted_for_cred(const struct cred *old, kuid_t new_uid) 86 118 { 87 - if (check_setuid_policy_hashtable_key_value(parent, child)) 88 - return 0; 89 - pr_warn("UID transition (%d -> %d) blocked", 90 - __kuid_val(parent), 91 - __kuid_val(child)); 119 + bool permitted; 120 + 121 + /* If our old creds already had this UID in it, it's fine. */ 122 + if (uid_eq(new_uid, old->uid) || uid_eq(new_uid, old->euid) || 123 + uid_eq(new_uid, old->suid)) 124 + return true; 125 + 92 126 /* 93 - * Kill this process to avoid potential security vulnerabilities 94 - * that could arise from a missing whitelist entry preventing a 95 - * privileged process from dropping to a lesser-privileged one. 127 + * Transitions to new UIDs require a check against the policy of the old 128 + * RUID. 96 129 */ 97 - force_sig(SIGKILL); 98 - return -EACCES; 130 + permitted = 131 + setuid_policy_lookup(old->uid, new_uid) != SIDPOL_CONSTRAINED; 132 + if (!permitted) { 133 + pr_warn("UID transition ((%d,%d,%d) -> %d) blocked\n", 134 + __kuid_val(old->uid), __kuid_val(old->euid), 135 + __kuid_val(old->suid), __kuid_val(new_uid)); 136 + } 137 + return permitted; 99 138 } 100 139 101 140 /* ··· 130 125 int flags) 131 126 { 132 127 133 - /* Do nothing if there are no setuid restrictions for this UID. */ 134 - if (!check_setuid_policy_hashtable_key(old->uid)) 128 + /* Do nothing if there are no setuid restrictions for our old RUID. */ 129 + if (setuid_policy_lookup(old->uid, INVALID_UID) == SIDPOL_DEFAULT) 135 130 return 0; 136 131 137 - switch (flags) { 138 - case LSM_SETID_RE: 139 - /* 140 - * Users for which setuid restrictions exist can only set the 141 - * real UID to the real UID or the effective UID, unless an 142 - * explicit whitelist policy allows the transition. 143 - */ 144 - if (!uid_eq(old->uid, new->uid) && 145 - !uid_eq(old->euid, new->uid)) { 146 - return check_uid_transition(old->uid, new->uid); 147 - } 148 - /* 149 - * Users for which setuid restrictions exist can only set the 150 - * effective UID to the real UID, the effective UID, or the 151 - * saved set-UID, unless an explicit whitelist policy allows 152 - * the transition. 153 - */ 154 - if (!uid_eq(old->uid, new->euid) && 155 - !uid_eq(old->euid, new->euid) && 156 - !uid_eq(old->suid, new->euid)) { 157 - return check_uid_transition(old->euid, new->euid); 158 - } 159 - break; 160 - case LSM_SETID_ID: 161 - /* 162 - * Users for which setuid restrictions exist cannot change the 163 - * real UID or saved set-UID unless an explicit whitelist 164 - * policy allows the transition. 165 - */ 166 - if (!uid_eq(old->uid, new->uid)) 167 - return check_uid_transition(old->uid, new->uid); 168 - if (!uid_eq(old->suid, new->suid)) 169 - return check_uid_transition(old->suid, new->suid); 170 - break; 171 - case LSM_SETID_RES: 172 - /* 173 - * Users for which setuid restrictions exist cannot change the 174 - * real UID, effective UID, or saved set-UID to anything but 175 - * one of: the current real UID, the current effective UID or 176 - * the current saved set-user-ID unless an explicit whitelist 177 - * policy allows the transition. 178 - */ 179 - if (!uid_eq(new->uid, old->uid) && 180 - !uid_eq(new->uid, old->euid) && 181 - !uid_eq(new->uid, old->suid)) { 182 - return check_uid_transition(old->uid, new->uid); 183 - } 184 - if (!uid_eq(new->euid, old->uid) && 185 - !uid_eq(new->euid, old->euid) && 186 - !uid_eq(new->euid, old->suid)) { 187 - return check_uid_transition(old->euid, new->euid); 188 - } 189 - if (!uid_eq(new->suid, old->uid) && 190 - !uid_eq(new->suid, old->euid) && 191 - !uid_eq(new->suid, old->suid)) { 192 - return check_uid_transition(old->suid, new->suid); 193 - } 194 - break; 195 - case LSM_SETID_FS: 196 - /* 197 - * Users for which setuid restrictions exist cannot change the 198 - * filesystem UID to anything but one of: the current real UID, 199 - * the current effective UID or the current saved set-UID 200 - * unless an explicit whitelist policy allows the transition. 201 - */ 202 - if (!uid_eq(new->fsuid, old->uid) && 203 - !uid_eq(new->fsuid, old->euid) && 204 - !uid_eq(new->fsuid, old->suid) && 205 - !uid_eq(new->fsuid, old->fsuid)) { 206 - return check_uid_transition(old->fsuid, new->fsuid); 207 - } 208 - break; 209 - default: 210 - pr_warn("Unknown setid state %d\n", flags); 211 - force_sig(SIGKILL); 212 - return -EINVAL; 213 - } 214 - return 0; 215 - } 216 - 217 - int add_safesetid_whitelist_entry(kuid_t parent, kuid_t child) 218 - { 219 - struct entry *new; 220 - 221 - /* Return if entry already exists */ 222 - if (check_setuid_policy_hashtable_key_value(parent, child)) 132 + if (uid_permitted_for_cred(old, new->uid) && 133 + uid_permitted_for_cred(old, new->euid) && 134 + uid_permitted_for_cred(old, new->suid) && 135 + uid_permitted_for_cred(old, new->fsuid)) 223 136 return 0; 224 - 225 - new = kzalloc(sizeof(struct entry), GFP_KERNEL); 226 - if (!new) 227 - return -ENOMEM; 228 - new->parent_kuid = __kuid_val(parent); 229 - new->child_kuid = __kuid_val(child); 230 - spin_lock(&safesetid_whitelist_hashtable_spinlock); 231 - hash_add_rcu(safesetid_whitelist_hashtable, 232 - &new->next, 233 - __kuid_val(parent)); 234 - spin_unlock(&safesetid_whitelist_hashtable_spinlock); 235 - return 0; 236 - } 237 - 238 - void flush_safesetid_whitelist_entries(void) 239 - { 240 - struct entry *entry; 241 - struct hlist_node *hlist_node; 242 - unsigned int bkt_loop_cursor; 243 - HLIST_HEAD(free_list); 244 137 245 138 /* 246 - * Could probably use hash_for_each_rcu here instead, but this should 247 - * be fine as well. 139 + * Kill this process to avoid potential security vulnerabilities 140 + * that could arise from a missing whitelist entry preventing a 141 + * privileged process from dropping to a lesser-privileged one. 248 142 */ 249 - spin_lock(&safesetid_whitelist_hashtable_spinlock); 250 - hash_for_each_safe(safesetid_whitelist_hashtable, bkt_loop_cursor, 251 - hlist_node, entry, next) { 252 - hash_del_rcu(&entry->next); 253 - hlist_add_head(&entry->dlist, &free_list); 254 - } 255 - spin_unlock(&safesetid_whitelist_hashtable_spinlock); 256 - synchronize_rcu(); 257 - hlist_for_each_entry_safe(entry, hlist_node, &free_list, dlist) { 258 - hlist_del(&entry->dlist); 259 - kfree(entry); 260 - } 143 + force_sig(SIGKILL); 144 + return -EACCES; 261 145 } 262 146 263 147 static struct security_hook_list safesetid_security_hooks[] = {
+27 -7
security/safesetid/lsm.h
··· 15 15 #define _SAFESETID_H 16 16 17 17 #include <linux/types.h> 18 + #include <linux/uidgid.h> 19 + #include <linux/hashtable.h> 18 20 19 21 /* Flag indicating whether initialization completed */ 20 22 extern int safesetid_initialized; 21 23 22 - /* Function type. */ 23 - enum safesetid_whitelist_file_write_type { 24 - SAFESETID_WHITELIST_ADD, /* Add whitelist policy. */ 25 - SAFESETID_WHITELIST_FLUSH, /* Flush whitelist policies. */ 24 + enum sid_policy_type { 25 + SIDPOL_DEFAULT, /* source ID is unaffected by policy */ 26 + SIDPOL_CONSTRAINED, /* source ID is affected by policy */ 27 + SIDPOL_ALLOWED /* target ID explicitly allowed */ 26 28 }; 27 29 28 - /* Add entry to safesetid whitelist to allow 'parent' to setid to 'child'. */ 29 - int add_safesetid_whitelist_entry(kuid_t parent, kuid_t child); 30 + /* 31 + * Hash table entry to store safesetid policy signifying that 'src_uid' 32 + * can setuid to 'dst_uid'. 33 + */ 34 + struct setuid_rule { 35 + struct hlist_node next; 36 + kuid_t src_uid; 37 + kuid_t dst_uid; 38 + }; 30 39 31 - void flush_safesetid_whitelist_entries(void); 40 + #define SETID_HASH_BITS 8 /* 256 buckets in hash table */ 41 + 42 + struct setuid_ruleset { 43 + DECLARE_HASHTABLE(rules, SETID_HASH_BITS); 44 + char *policy_str; 45 + struct rcu_head rcu; 46 + }; 47 + 48 + enum sid_policy_type _setuid_policy_lookup(struct setuid_ruleset *policy, 49 + kuid_t src, kuid_t dst); 50 + 51 + extern struct setuid_ruleset __rcu *safesetid_setuid_rules; 32 52 33 53 #endif /* _SAFESETID_H */
+192 -125
security/safesetid/securityfs.c
··· 11 11 * published by the Free Software Foundation. 12 12 * 13 13 */ 14 + 15 + #define pr_fmt(fmt) "SafeSetID: " fmt 16 + 14 17 #include <linux/security.h> 15 18 #include <linux/cred.h> 16 19 17 20 #include "lsm.h" 18 21 19 - static struct dentry *safesetid_policy_dir; 20 - 21 - struct safesetid_file_entry { 22 - const char *name; 23 - enum safesetid_whitelist_file_write_type type; 24 - struct dentry *dentry; 25 - }; 26 - 27 - static struct safesetid_file_entry safesetid_files[] = { 28 - {.name = "add_whitelist_policy", 29 - .type = SAFESETID_WHITELIST_ADD}, 30 - {.name = "flush_whitelist_policies", 31 - .type = SAFESETID_WHITELIST_FLUSH}, 32 - }; 22 + static DEFINE_MUTEX(policy_update_lock); 33 23 34 24 /* 35 25 * In the case the input buffer contains one or more invalid UIDs, the kuid_t 36 - * variables pointed to by 'parent' and 'child' will get updated but this 26 + * variables pointed to by @parent and @child will get updated but this 37 27 * function will return an error. 28 + * Contents of @buf may be modified. 38 29 */ 39 - static int parse_safesetid_whitelist_policy(const char __user *buf, 40 - size_t len, 41 - kuid_t *parent, 42 - kuid_t *child) 30 + static int parse_policy_line(struct file *file, char *buf, 31 + struct setuid_rule *rule) 43 32 { 44 - char *kern_buf; 45 - char *parent_buf; 46 - char *child_buf; 47 - const char separator[] = ":"; 33 + char *child_str; 48 34 int ret; 49 - size_t first_substring_length; 50 - long parsed_parent; 51 - long parsed_child; 35 + u32 parsed_parent, parsed_child; 52 36 53 - /* Duplicate string from user memory and NULL-terminate */ 54 - kern_buf = memdup_user_nul(buf, len); 55 - if (IS_ERR(kern_buf)) 56 - return PTR_ERR(kern_buf); 37 + /* Format of |buf| string should be <UID>:<UID>. */ 38 + child_str = strchr(buf, ':'); 39 + if (child_str == NULL) 40 + return -EINVAL; 41 + *child_str = '\0'; 42 + child_str++; 43 + 44 + ret = kstrtou32(buf, 0, &parsed_parent); 45 + if (ret) 46 + return ret; 47 + 48 + ret = kstrtou32(child_str, 0, &parsed_child); 49 + if (ret) 50 + return ret; 51 + 52 + rule->src_uid = make_kuid(file->f_cred->user_ns, parsed_parent); 53 + rule->dst_uid = make_kuid(file->f_cred->user_ns, parsed_child); 54 + if (!uid_valid(rule->src_uid) || !uid_valid(rule->dst_uid)) 55 + return -EINVAL; 56 + 57 + return 0; 58 + } 59 + 60 + static void __release_ruleset(struct rcu_head *rcu) 61 + { 62 + struct setuid_ruleset *pol = 63 + container_of(rcu, struct setuid_ruleset, rcu); 64 + int bucket; 65 + struct setuid_rule *rule; 66 + struct hlist_node *tmp; 67 + 68 + hash_for_each_safe(pol->rules, bucket, tmp, rule, next) 69 + kfree(rule); 70 + kfree(pol->policy_str); 71 + kfree(pol); 72 + } 73 + 74 + static void release_ruleset(struct setuid_ruleset *pol) 75 + { 76 + call_rcu(&pol->rcu, __release_ruleset); 77 + } 78 + 79 + static void insert_rule(struct setuid_ruleset *pol, struct setuid_rule *rule) 80 + { 81 + hash_add(pol->rules, &rule->next, __kuid_val(rule->src_uid)); 82 + } 83 + 84 + static int verify_ruleset(struct setuid_ruleset *pol) 85 + { 86 + int bucket; 87 + struct setuid_rule *rule, *nrule; 88 + int res = 0; 89 + 90 + hash_for_each(pol->rules, bucket, rule, next) { 91 + if (_setuid_policy_lookup(pol, rule->dst_uid, INVALID_UID) == 92 + SIDPOL_DEFAULT) { 93 + pr_warn("insecure policy detected: uid %d is constrained but transitively unconstrained through uid %d\n", 94 + __kuid_val(rule->src_uid), 95 + __kuid_val(rule->dst_uid)); 96 + res = -EINVAL; 97 + 98 + /* fix it up */ 99 + nrule = kmalloc(sizeof(struct setuid_rule), GFP_KERNEL); 100 + if (!nrule) 101 + return -ENOMEM; 102 + nrule->src_uid = rule->dst_uid; 103 + nrule->dst_uid = rule->dst_uid; 104 + insert_rule(pol, nrule); 105 + } 106 + } 107 + return res; 108 + } 109 + 110 + static ssize_t handle_policy_update(struct file *file, 111 + const char __user *ubuf, size_t len) 112 + { 113 + struct setuid_ruleset *pol; 114 + char *buf, *p, *end; 115 + int err; 116 + 117 + pol = kmalloc(sizeof(struct setuid_ruleset), GFP_KERNEL); 118 + if (!pol) 119 + return -ENOMEM; 120 + pol->policy_str = NULL; 121 + hash_init(pol->rules); 122 + 123 + p = buf = memdup_user_nul(ubuf, len); 124 + if (IS_ERR(buf)) { 125 + err = PTR_ERR(buf); 126 + goto out_free_pol; 127 + } 128 + pol->policy_str = kstrdup(buf, GFP_KERNEL); 129 + if (pol->policy_str == NULL) { 130 + err = -ENOMEM; 131 + goto out_free_buf; 132 + } 133 + 134 + /* policy lines, including the last one, end with \n */ 135 + while (*p != '\0') { 136 + struct setuid_rule *rule; 137 + 138 + end = strchr(p, '\n'); 139 + if (end == NULL) { 140 + err = -EINVAL; 141 + goto out_free_buf; 142 + } 143 + *end = '\0'; 144 + 145 + rule = kmalloc(sizeof(struct setuid_rule), GFP_KERNEL); 146 + if (!rule) { 147 + err = -ENOMEM; 148 + goto out_free_buf; 149 + } 150 + 151 + err = parse_policy_line(file, p, rule); 152 + if (err) 153 + goto out_free_rule; 154 + 155 + if (_setuid_policy_lookup(pol, rule->src_uid, rule->dst_uid) == 156 + SIDPOL_ALLOWED) { 157 + pr_warn("bad policy: duplicate entry\n"); 158 + err = -EEXIST; 159 + goto out_free_rule; 160 + } 161 + 162 + insert_rule(pol, rule); 163 + p = end + 1; 164 + continue; 165 + 166 + out_free_rule: 167 + kfree(rule); 168 + goto out_free_buf; 169 + } 170 + 171 + err = verify_ruleset(pol); 172 + /* bogus policy falls through after fixing it up */ 173 + if (err && err != -EINVAL) 174 + goto out_free_buf; 57 175 58 176 /* 59 - * Format of |buf| string should be <UID>:<UID>. 60 - * Find location of ":" in kern_buf (copied from |buf|). 177 + * Everything looks good, apply the policy and release the old one. 178 + * What we really want here is an xchg() wrapper for RCU, but since that 179 + * doesn't currently exist, just use a spinlock for now. 61 180 */ 62 - first_substring_length = strcspn(kern_buf, separator); 63 - if (first_substring_length == 0 || first_substring_length == len) { 64 - ret = -EINVAL; 65 - goto free_kern; 66 - } 181 + mutex_lock(&policy_update_lock); 182 + rcu_swap_protected(safesetid_setuid_rules, pol, 183 + lockdep_is_held(&policy_update_lock)); 184 + mutex_unlock(&policy_update_lock); 185 + err = len; 67 186 68 - parent_buf = kmemdup_nul(kern_buf, first_substring_length, GFP_KERNEL); 69 - if (!parent_buf) { 70 - ret = -ENOMEM; 71 - goto free_kern; 72 - } 73 - 74 - ret = kstrtol(parent_buf, 0, &parsed_parent); 75 - if (ret) 76 - goto free_both; 77 - 78 - child_buf = kern_buf + first_substring_length + 1; 79 - ret = kstrtol(child_buf, 0, &parsed_child); 80 - if (ret) 81 - goto free_both; 82 - 83 - *parent = make_kuid(current_user_ns(), parsed_parent); 84 - if (!uid_valid(*parent)) { 85 - ret = -EINVAL; 86 - goto free_both; 87 - } 88 - 89 - *child = make_kuid(current_user_ns(), parsed_child); 90 - if (!uid_valid(*child)) { 91 - ret = -EINVAL; 92 - goto free_both; 93 - } 94 - 95 - free_both: 96 - kfree(parent_buf); 97 - free_kern: 98 - kfree(kern_buf); 99 - return ret; 187 + out_free_buf: 188 + kfree(buf); 189 + out_free_pol: 190 + release_ruleset(pol); 191 + return err; 100 192 } 101 193 102 194 static ssize_t safesetid_file_write(struct file *file, ··· 196 104 size_t len, 197 105 loff_t *ppos) 198 106 { 199 - struct safesetid_file_entry *file_entry = 200 - file->f_inode->i_private; 201 - kuid_t parent; 202 - kuid_t child; 203 - int ret; 204 - 205 - if (!ns_capable(current_user_ns(), CAP_MAC_ADMIN)) 107 + if (!file_ns_capable(file, &init_user_ns, CAP_MAC_ADMIN)) 206 108 return -EPERM; 207 109 208 110 if (*ppos != 0) 209 111 return -EINVAL; 210 112 211 - switch (file_entry->type) { 212 - case SAFESETID_WHITELIST_FLUSH: 213 - flush_safesetid_whitelist_entries(); 214 - break; 215 - case SAFESETID_WHITELIST_ADD: 216 - ret = parse_safesetid_whitelist_policy(buf, len, &parent, 217 - &child); 218 - if (ret) 219 - return ret; 113 + return handle_policy_update(file, buf, len); 114 + } 220 115 221 - ret = add_safesetid_whitelist_entry(parent, child); 222 - if (ret) 223 - return ret; 224 - break; 225 - default: 226 - pr_warn("Unknown securityfs file %d\n", file_entry->type); 227 - break; 116 + static ssize_t safesetid_file_read(struct file *file, char __user *buf, 117 + size_t len, loff_t *ppos) 118 + { 119 + ssize_t res = 0; 120 + struct setuid_ruleset *pol; 121 + const char *kbuf; 122 + 123 + mutex_lock(&policy_update_lock); 124 + pol = rcu_dereference_protected(safesetid_setuid_rules, 125 + lockdep_is_held(&policy_update_lock)); 126 + if (pol) { 127 + kbuf = pol->policy_str; 128 + res = simple_read_from_buffer(buf, len, ppos, 129 + kbuf, strlen(kbuf)); 228 130 } 229 - 230 - /* Return len on success so caller won't keep trying to write */ 231 - return len; 131 + mutex_unlock(&policy_update_lock); 132 + return res; 232 133 } 233 134 234 135 static const struct file_operations safesetid_file_fops = { 136 + .read = safesetid_file_read, 235 137 .write = safesetid_file_write, 236 138 }; 237 139 238 - static void safesetid_shutdown_securityfs(void) 239 - { 240 - int i; 241 - 242 - for (i = 0; i < ARRAY_SIZE(safesetid_files); ++i) { 243 - struct safesetid_file_entry *entry = 244 - &safesetid_files[i]; 245 - securityfs_remove(entry->dentry); 246 - entry->dentry = NULL; 247 - } 248 - 249 - securityfs_remove(safesetid_policy_dir); 250 - safesetid_policy_dir = NULL; 251 - } 252 - 253 140 static int __init safesetid_init_securityfs(void) 254 141 { 255 - int i; 256 142 int ret; 143 + struct dentry *policy_dir; 144 + struct dentry *policy_file; 257 145 258 146 if (!safesetid_initialized) 259 147 return 0; 260 148 261 - safesetid_policy_dir = securityfs_create_dir("safesetid", NULL); 262 - if (IS_ERR(safesetid_policy_dir)) { 263 - ret = PTR_ERR(safesetid_policy_dir); 149 + policy_dir = securityfs_create_dir("safesetid", NULL); 150 + if (IS_ERR(policy_dir)) { 151 + ret = PTR_ERR(policy_dir); 264 152 goto error; 265 153 } 266 154 267 - for (i = 0; i < ARRAY_SIZE(safesetid_files); ++i) { 268 - struct safesetid_file_entry *entry = 269 - &safesetid_files[i]; 270 - entry->dentry = securityfs_create_file( 271 - entry->name, 0200, safesetid_policy_dir, 272 - entry, &safesetid_file_fops); 273 - if (IS_ERR(entry->dentry)) { 274 - ret = PTR_ERR(entry->dentry); 275 - goto error; 276 - } 155 + policy_file = securityfs_create_file("whitelist_policy", 0600, 156 + policy_dir, NULL, &safesetid_file_fops); 157 + if (IS_ERR(policy_file)) { 158 + ret = PTR_ERR(policy_file); 159 + goto error; 277 160 } 278 161 279 162 return 0; 280 163 281 164 error: 282 - safesetid_shutdown_securityfs(); 165 + securityfs_remove(policy_dir); 283 166 return ret; 284 167 } 285 168 fs_initcall(safesetid_init_securityfs);
+7 -11
tools/testing/selftests/safesetid/safesetid-test.c
··· 142 142 143 143 static void write_policies(void) 144 144 { 145 + static char *policy_str = 146 + "1:2\n" 147 + "1:3\n" 148 + "2:2\n" 149 + "3:3\n"; 145 150 ssize_t written; 146 151 int fd; 147 152 148 153 fd = open(add_whitelist_policy_file, O_WRONLY); 149 154 if (fd < 0) 150 155 die("cant open add_whitelist_policy file\n"); 151 - written = write(fd, "1:2", strlen("1:2")); 152 - if (written != strlen("1:2")) { 153 - if (written >= 0) { 154 - die("short write to %s\n", add_whitelist_policy_file); 155 - } else { 156 - die("write to %s failed: %s\n", 157 - add_whitelist_policy_file, strerror(errno)); 158 - } 159 - } 160 - written = write(fd, "1:3", strlen("1:3")); 161 - if (written != strlen("1:3")) { 156 + written = write(fd, policy_str, strlen(policy_str)); 157 + if (written != strlen(policy_str)) { 162 158 if (written >= 0) { 163 159 die("short write to %s\n", add_whitelist_policy_file); 164 160 } else {