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

Configure Feed

Select the types of activity you want to include in your feed.

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

Pull SafeSetID updates from Micah Morton:
"The changes are mostly contained to within the SafeSetID LSM, with the
exception of a few 1-line changes to change some ns_capable() calls to
ns_capable_setid() -- causing a flag (CAP_OPT_INSETID) to be set that
is examined by SafeSetID code and nothing else in the kernel.

The changes to SafeSetID internally allow for setting up GID
transition security policies, as already existed for UIDs"

* tag 'safesetid-5.10' of git://github.com/micah-morton/linux:
LSM: SafeSetID: Fix warnings reported by test bot
LSM: SafeSetID: Add GID security policy handling
LSM: Signal to SafeSetID when setting group IDs

+336 -125
+20 -9
Documentation/admin-guide/LSM/SafeSetID.rst
··· 3 3 ========= 4 4 SafeSetID is an LSM module that gates the setid family of syscalls to restrict 5 5 UID/GID transitions from a given UID/GID to only those approved by a 6 - system-wide whitelist. These restrictions also prohibit the given UIDs/GIDs 6 + system-wide allowlist. These restrictions also prohibit the given UIDs/GIDs 7 7 from obtaining auxiliary privileges associated with CAP_SET{U/G}ID, such as 8 - allowing a user to set up user namespace UID mappings. 8 + allowing a user to set up user namespace UID/GID mappings. 9 9 10 10 11 11 Background ··· 98 98 ================== 99 99 This LSM hooks the setid syscalls to make sure transitions are allowed if an 100 100 applicable restriction policy is in place. Policies are configured through 101 - securityfs by writing to the safesetid/add_whitelist_policy and 102 - safesetid/flush_whitelist_policies files at the location where securityfs is 103 - mounted. The format for adding a policy is '<UID>:<UID>', using literal 104 - numbers, such as '123:456'. To flush the policies, any write to the file is 105 - sufficient. Again, configuring a policy for a UID will prevent that UID from 106 - obtaining auxiliary setid privileges, such as allowing a user to set up user 107 - namespace UID mappings. 101 + securityfs by writing to the safesetid/uid_allowlist_policy and 102 + safesetid/gid_allowlist_policy files at the location where securityfs is 103 + mounted. The format for adding a policy is '<UID>:<UID>' or '<GID>:<GID>', 104 + using literal numbers, and ending with a newline character such as '123:456\n'. 105 + Writing an empty string "" will flush the policy. Again, configuring a policy 106 + for a UID/GID will prevent that UID/GID from obtaining auxiliary setid 107 + privileges, such as allowing a user to set up user namespace UID/GID mappings. 108 + 109 + Note on GID policies and setgroups() 110 + ================== 111 + In v5.9 we are adding support for limiting CAP_SETGID privileges as was done 112 + previously for CAP_SETUID. However, for compatibility with common sandboxing 113 + related code conventions in userspace, we currently allow arbitrary 114 + setgroups() calls for processes with CAP_SETGID restrictions. Until we add 115 + support in a future release for restricting setgroups() calls, these GID 116 + policies add no meaningful security. setgroups() restrictions will be enforced 117 + once we have the policy checking code in place, which will rely on GID policy 118 + configuration code added in v5.9.
+1 -1
kernel/capability.c
··· 418 418 /** 419 419 * ns_capable_setid - Determine if the current task has a superior capability 420 420 * in effect, while signalling that this check is being done from within a 421 - * setid syscall. 421 + * setid or setgroups syscall. 422 422 * @ns: The usernamespace we want the capability in 423 423 * @cap: The capability to be tested for 424 424 *
+1 -1
kernel/groups.c
··· 178 178 { 179 179 struct user_namespace *user_ns = current_user_ns(); 180 180 181 - return ns_capable(user_ns, CAP_SETGID) && 181 + return ns_capable_setid(user_ns, CAP_SETGID) && 182 182 userns_may_setgroups(user_ns); 183 183 } 184 184
+5 -5
kernel/sys.c
··· 373 373 if (rgid != (gid_t) -1) { 374 374 if (gid_eq(old->gid, krgid) || 375 375 gid_eq(old->egid, krgid) || 376 - ns_capable(old->user_ns, CAP_SETGID)) 376 + ns_capable_setid(old->user_ns, CAP_SETGID)) 377 377 new->gid = krgid; 378 378 else 379 379 goto error; ··· 382 382 if (gid_eq(old->gid, kegid) || 383 383 gid_eq(old->egid, kegid) || 384 384 gid_eq(old->sgid, kegid) || 385 - ns_capable(old->user_ns, CAP_SETGID)) 385 + ns_capable_setid(old->user_ns, CAP_SETGID)) 386 386 new->egid = kegid; 387 387 else 388 388 goto error; ··· 432 432 old = current_cred(); 433 433 434 434 retval = -EPERM; 435 - if (ns_capable(old->user_ns, CAP_SETGID)) 435 + if (ns_capable_setid(old->user_ns, CAP_SETGID)) 436 436 new->gid = new->egid = new->sgid = new->fsgid = kgid; 437 437 else if (gid_eq(kgid, old->gid) || gid_eq(kgid, old->sgid)) 438 438 new->egid = new->fsgid = kgid; ··· 744 744 old = current_cred(); 745 745 746 746 retval = -EPERM; 747 - if (!ns_capable(old->user_ns, CAP_SETGID)) { 747 + if (!ns_capable_setid(old->user_ns, CAP_SETGID)) { 748 748 if (rgid != (gid_t) -1 && !gid_eq(krgid, old->gid) && 749 749 !gid_eq(krgid, old->egid) && !gid_eq(krgid, old->sgid)) 750 750 goto error; ··· 871 871 872 872 if (gid_eq(kgid, old->gid) || gid_eq(kgid, old->egid) || 873 873 gid_eq(kgid, old->sgid) || gid_eq(kgid, old->fsgid) || 874 - ns_capable(old->user_ns, CAP_SETGID)) { 874 + ns_capable_setid(old->user_ns, CAP_SETGID)) { 875 875 if (!gid_eq(kgid, old->fsgid)) { 876 876 new->fsgid = kgid; 877 877 if (security_task_fix_setgid(new,old,LSM_SETID_FS) == 0)
+143 -47
security/safesetid/lsm.c
··· 24 24 /* Flag indicating whether initialization completed */ 25 25 int safesetid_initialized; 26 26 27 - struct setuid_ruleset __rcu *safesetid_setuid_rules; 27 + struct setid_ruleset __rcu *safesetid_setuid_rules; 28 + struct setid_ruleset __rcu *safesetid_setgid_rules; 29 + 28 30 29 31 /* 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) 32 + enum sid_policy_type _setid_policy_lookup(struct setid_ruleset *policy, 33 + kid_t src, kid_t dst) 32 34 { 33 - struct setuid_rule *rule; 35 + struct setid_rule *rule; 34 36 enum sid_policy_type result = SIDPOL_DEFAULT; 35 37 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; 38 + if (policy->type == UID) { 39 + hash_for_each_possible(policy->rules, rule, next, __kuid_val(src.uid)) { 40 + if (!uid_eq(rule->src_id.uid, src.uid)) 41 + continue; 42 + if (uid_eq(rule->dst_id.uid, dst.uid)) 43 + return SIDPOL_ALLOWED; 44 + result = SIDPOL_CONSTRAINED; 45 + } 46 + } else if (policy->type == GID) { 47 + hash_for_each_possible(policy->rules, rule, next, __kgid_val(src.gid)) { 48 + if (!gid_eq(rule->src_id.gid, src.gid)) 49 + continue; 50 + if (gid_eq(rule->dst_id.gid, dst.gid)){ 51 + return SIDPOL_ALLOWED; 52 + } 53 + result = SIDPOL_CONSTRAINED; 54 + } 55 + } else { 56 + /* Should not reach here, report the ID as contrainsted */ 41 57 result = SIDPOL_CONSTRAINED; 42 58 } 43 59 return result; ··· 63 47 * Compute a decision for a transition from @src to @dst under the active 64 48 * policy. 65 49 */ 66 - static enum sid_policy_type setuid_policy_lookup(kuid_t src, kuid_t dst) 50 + static enum sid_policy_type setid_policy_lookup(kid_t src, kid_t dst, enum setid_type new_type) 67 51 { 68 52 enum sid_policy_type result = SIDPOL_DEFAULT; 69 - struct setuid_ruleset *pol; 53 + struct setid_ruleset *pol; 70 54 71 55 rcu_read_lock(); 72 - pol = rcu_dereference(safesetid_setuid_rules); 73 - if (pol) 74 - result = _setuid_policy_lookup(pol, src, dst); 56 + if (new_type == UID) 57 + pol = rcu_dereference(safesetid_setuid_rules); 58 + else if (new_type == GID) 59 + pol = rcu_dereference(safesetid_setgid_rules); 60 + else { /* Should not reach here */ 61 + result = SIDPOL_CONSTRAINED; 62 + rcu_read_unlock(); 63 + return result; 64 + } 65 + 66 + if (pol) { 67 + pol->type = new_type; 68 + result = _setid_policy_lookup(pol, src, dst); 69 + } 75 70 rcu_read_unlock(); 76 71 return result; 77 72 } ··· 92 65 int cap, 93 66 unsigned int opts) 94 67 { 95 - /* We're only interested in CAP_SETUID. */ 96 - if (cap != CAP_SETUID) 68 + /* We're only interested in CAP_SETUID and CAP_SETGID. */ 69 + if (cap != CAP_SETUID && cap != CAP_SETGID) 97 70 return 0; 98 71 99 72 /* 100 - * If CAP_SETUID is currently used for a set*uid() syscall, we want to 73 + * If CAP_SET{U/G}ID is currently used for a setid() syscall, we want to 101 74 * let it go through here; the real security check happens later, in the 102 - * task_fix_setuid hook. 75 + * task_fix_set{u/g}id hook. 76 + * 77 + * NOTE: 78 + * Until we add support for restricting setgroups() calls, GID security 79 + * policies offer no meaningful security since we always return 0 here 80 + * when called from within the setgroups() syscall and there is no 81 + * additional hook later on to enforce security policies for setgroups(). 103 82 */ 104 83 if ((opts & CAP_OPT_INSETID) != 0) 105 84 return 0; 106 85 107 - /* 108 - * If no policy applies to this task, allow the use of CAP_SETUID for 109 - * other purposes. 110 - */ 111 - if (setuid_policy_lookup(cred->uid, INVALID_UID) == SIDPOL_DEFAULT) 86 + switch (cap) { 87 + case CAP_SETUID: 88 + /* 89 + * If no policy applies to this task, allow the use of CAP_SETUID for 90 + * other purposes. 91 + */ 92 + if (setid_policy_lookup((kid_t){.uid = cred->uid}, INVALID_ID, UID) == SIDPOL_DEFAULT) 93 + return 0; 94 + /* 95 + * Reject use of CAP_SETUID for functionality other than calling 96 + * set*uid() (e.g. setting up userns uid mappings). 97 + */ 98 + pr_warn("Operation requires CAP_SETUID, which is not available to UID %u for operations besides approved set*uid transitions\n", 99 + __kuid_val(cred->uid)); 100 + return -EPERM; 101 + break; 102 + case CAP_SETGID: 103 + /* 104 + * If no policy applies to this task, allow the use of CAP_SETGID for 105 + * other purposes. 106 + */ 107 + if (setid_policy_lookup((kid_t){.gid = cred->gid}, INVALID_ID, GID) == SIDPOL_DEFAULT) 108 + return 0; 109 + /* 110 + * Reject use of CAP_SETUID for functionality other than calling 111 + * set*gid() (e.g. setting up userns gid mappings). 112 + */ 113 + pr_warn("Operation requires CAP_SETGID, which is not available to GID %u for operations besides approved set*gid transitions\n", 114 + __kuid_val(cred->uid)); 115 + return -EPERM; 116 + break; 117 + default: 118 + /* Error, the only capabilities were checking for is CAP_SETUID/GID */ 112 119 return 0; 113 - 114 - /* 115 - * Reject use of CAP_SETUID for functionality other than calling 116 - * set*uid() (e.g. setting up userns uid mappings). 117 - */ 118 - pr_warn("Operation requires CAP_SETUID, which is not available to UID %u for operations besides approved set*uid transitions\n", 119 - __kuid_val(cred->uid)); 120 - return -EPERM; 120 + break; 121 + } 122 + return 0; 121 123 } 122 124 123 125 /* 124 126 * Check whether a caller with old credentials @old is allowed to switch to 125 - * credentials that contain @new_uid. 127 + * credentials that contain @new_id. 126 128 */ 127 - static bool uid_permitted_for_cred(const struct cred *old, kuid_t new_uid) 129 + static bool id_permitted_for_cred(const struct cred *old, kid_t new_id, enum setid_type new_type) 128 130 { 129 131 bool permitted; 130 132 131 - /* If our old creds already had this UID in it, it's fine. */ 132 - if (uid_eq(new_uid, old->uid) || uid_eq(new_uid, old->euid) || 133 - uid_eq(new_uid, old->suid)) 134 - return true; 133 + /* If our old creds already had this ID in it, it's fine. */ 134 + if (new_type == UID) { 135 + if (uid_eq(new_id.uid, old->uid) || uid_eq(new_id.uid, old->euid) || 136 + uid_eq(new_id.uid, old->suid)) 137 + return true; 138 + } else if (new_type == GID){ 139 + if (gid_eq(new_id.gid, old->gid) || gid_eq(new_id.gid, old->egid) || 140 + gid_eq(new_id.gid, old->sgid)) 141 + return true; 142 + } else /* Error, new_type is an invalid type */ 143 + return false; 135 144 136 145 /* 137 146 * Transitions to new UIDs require a check against the policy of the old 138 147 * RUID. 139 148 */ 140 149 permitted = 141 - setuid_policy_lookup(old->uid, new_uid) != SIDPOL_CONSTRAINED; 150 + setid_policy_lookup((kid_t){.uid = old->uid}, new_id, new_type) != SIDPOL_CONSTRAINED; 151 + 142 152 if (!permitted) { 143 - pr_warn("UID transition ((%d,%d,%d) -> %d) blocked\n", 144 - __kuid_val(old->uid), __kuid_val(old->euid), 145 - __kuid_val(old->suid), __kuid_val(new_uid)); 153 + if (new_type == UID) { 154 + pr_warn("UID transition ((%d,%d,%d) -> %d) blocked\n", 155 + __kuid_val(old->uid), __kuid_val(old->euid), 156 + __kuid_val(old->suid), __kuid_val(new_id.uid)); 157 + } else if (new_type == GID) { 158 + pr_warn("GID transition ((%d,%d,%d) -> %d) blocked\n", 159 + __kgid_val(old->gid), __kgid_val(old->egid), 160 + __kgid_val(old->sgid), __kgid_val(new_id.gid)); 161 + } else /* Error, new_type is an invalid type */ 162 + return false; 146 163 } 147 164 return permitted; 148 165 } ··· 202 131 { 203 132 204 133 /* Do nothing if there are no setuid restrictions for our old RUID. */ 205 - if (setuid_policy_lookup(old->uid, INVALID_UID) == SIDPOL_DEFAULT) 134 + if (setid_policy_lookup((kid_t){.uid = old->uid}, INVALID_ID, UID) == SIDPOL_DEFAULT) 206 135 return 0; 207 136 208 - if (uid_permitted_for_cred(old, new->uid) && 209 - uid_permitted_for_cred(old, new->euid) && 210 - uid_permitted_for_cred(old, new->suid) && 211 - uid_permitted_for_cred(old, new->fsuid)) 137 + if (id_permitted_for_cred(old, (kid_t){.uid = new->uid}, UID) && 138 + id_permitted_for_cred(old, (kid_t){.uid = new->euid}, UID) && 139 + id_permitted_for_cred(old, (kid_t){.uid = new->suid}, UID) && 140 + id_permitted_for_cred(old, (kid_t){.uid = new->fsuid}, UID)) 212 141 return 0; 213 142 214 143 /* 215 144 * Kill this process to avoid potential security vulnerabilities 216 - * that could arise from a missing whitelist entry preventing a 145 + * that could arise from a missing allowlist entry preventing a 146 + * privileged process from dropping to a lesser-privileged one. 147 + */ 148 + force_sig(SIGKILL); 149 + return -EACCES; 150 + } 151 + 152 + static int safesetid_task_fix_setgid(struct cred *new, 153 + const struct cred *old, 154 + int flags) 155 + { 156 + 157 + /* Do nothing if there are no setgid restrictions for our old RGID. */ 158 + if (setid_policy_lookup((kid_t){.gid = old->gid}, INVALID_ID, GID) == SIDPOL_DEFAULT) 159 + return 0; 160 + 161 + if (id_permitted_for_cred(old, (kid_t){.gid = new->gid}, GID) && 162 + id_permitted_for_cred(old, (kid_t){.gid = new->egid}, GID) && 163 + id_permitted_for_cred(old, (kid_t){.gid = new->sgid}, GID) && 164 + id_permitted_for_cred(old, (kid_t){.gid = new->fsgid}, GID)) 165 + return 0; 166 + 167 + /* 168 + * Kill this process to avoid potential security vulnerabilities 169 + * that could arise from a missing allowlist entry preventing a 217 170 * privileged process from dropping to a lesser-privileged one. 218 171 */ 219 172 force_sig(SIGKILL); ··· 246 151 247 152 static struct security_hook_list safesetid_security_hooks[] = { 248 153 LSM_HOOK_INIT(task_fix_setuid, safesetid_task_fix_setuid), 154 + LSM_HOOK_INIT(task_fix_setgid, safesetid_task_fix_setgid), 249 155 LSM_HOOK_INIT(capable, safesetid_security_capable) 250 156 }; 251 157
+29 -9
security/safesetid/lsm.h
··· 27 27 SIDPOL_ALLOWED /* target ID explicitly allowed */ 28 28 }; 29 29 30 + typedef union { 31 + kuid_t uid; 32 + kgid_t gid; 33 + } kid_t; 34 + 35 + enum setid_type { 36 + UID, 37 + GID 38 + }; 39 + 30 40 /* 31 - * Hash table entry to store safesetid policy signifying that 'src_uid' 32 - * can setuid to 'dst_uid'. 41 + * Hash table entry to store safesetid policy signifying that 'src_id' 42 + * can set*id to 'dst_id'. 33 43 */ 34 - struct setuid_rule { 44 + struct setid_rule { 35 45 struct hlist_node next; 36 - kuid_t src_uid; 37 - kuid_t dst_uid; 46 + kid_t src_id; 47 + kid_t dst_id; 48 + 49 + /* Flag to signal if rule is for UID's or GID's */ 50 + enum setid_type type; 38 51 }; 39 52 40 53 #define SETID_HASH_BITS 8 /* 256 buckets in hash table */ 41 54 42 - struct setuid_ruleset { 55 + /* Extension of INVALID_UID/INVALID_GID for kid_t type */ 56 + #define INVALID_ID (kid_t){.uid = INVALID_UID} 57 + 58 + struct setid_ruleset { 43 59 DECLARE_HASHTABLE(rules, SETID_HASH_BITS); 44 60 char *policy_str; 45 61 struct rcu_head rcu; 62 + 63 + //Flag to signal if ruleset is for UID's or GID's 64 + enum setid_type type; 46 65 }; 47 66 48 - enum sid_policy_type _setuid_policy_lookup(struct setuid_ruleset *policy, 49 - kuid_t src, kuid_t dst); 67 + enum sid_policy_type _setid_policy_lookup(struct setid_ruleset *policy, 68 + kid_t src, kid_t dst); 50 69 51 - extern struct setuid_ruleset __rcu *safesetid_setuid_rules; 70 + extern struct setid_ruleset __rcu *safesetid_setuid_rules; 71 + extern struct setid_ruleset __rcu *safesetid_setgid_rules; 52 72 53 73 #endif /* _SAFESETID_H */
+137 -53
security/safesetid/securityfs.c
··· 19 19 20 20 #include "lsm.h" 21 21 22 - static DEFINE_MUTEX(policy_update_lock); 22 + static DEFINE_MUTEX(uid_policy_update_lock); 23 + static DEFINE_MUTEX(gid_policy_update_lock); 23 24 24 25 /* 25 - * In the case the input buffer contains one or more invalid UIDs, the kuid_t 26 + * In the case the input buffer contains one or more invalid IDs, the kid_t 26 27 * variables pointed to by @parent and @child will get updated but this 27 28 * function will return an error. 28 29 * Contents of @buf may be modified. 29 30 */ 30 31 static int parse_policy_line(struct file *file, char *buf, 31 - struct setuid_rule *rule) 32 + struct setid_rule *rule) 32 33 { 33 34 char *child_str; 34 35 int ret; 35 36 u32 parsed_parent, parsed_child; 36 37 37 - /* Format of |buf| string should be <UID>:<UID>. */ 38 + /* Format of |buf| string should be <UID>:<UID> or <GID>:<GID> */ 38 39 child_str = strchr(buf, ':'); 39 40 if (child_str == NULL) 40 41 return -EINVAL; ··· 50 49 if (ret) 51 50 return ret; 52 51 53 - rule->src_uid = make_kuid(file->f_cred->user_ns, parsed_parent); 54 - rule->dst_uid = make_kuid(file->f_cred->user_ns, parsed_child); 55 - if (!uid_valid(rule->src_uid) || !uid_valid(rule->dst_uid)) 52 + if (rule->type == UID){ 53 + rule->src_id.uid = make_kuid(file->f_cred->user_ns, parsed_parent); 54 + rule->dst_id.uid = make_kuid(file->f_cred->user_ns, parsed_child); 55 + if (!uid_valid(rule->src_id.uid) || !uid_valid(rule->dst_id.uid)) 56 + return -EINVAL; 57 + } else if (rule->type == GID){ 58 + rule->src_id.gid = make_kgid(file->f_cred->user_ns, parsed_parent); 59 + rule->dst_id.gid = make_kgid(file->f_cred->user_ns, parsed_child); 60 + if (!gid_valid(rule->src_id.gid) || !gid_valid(rule->dst_id.gid)) 61 + return -EINVAL; 62 + } else { 63 + /* Error, rule->type is an invalid type */ 56 64 return -EINVAL; 57 - 65 + } 58 66 return 0; 59 67 } 60 68 61 69 static void __release_ruleset(struct rcu_head *rcu) 62 70 { 63 - struct setuid_ruleset *pol = 64 - container_of(rcu, struct setuid_ruleset, rcu); 71 + struct setid_ruleset *pol = 72 + container_of(rcu, struct setid_ruleset, rcu); 65 73 int bucket; 66 - struct setuid_rule *rule; 74 + struct setid_rule *rule; 67 75 struct hlist_node *tmp; 68 76 69 77 hash_for_each_safe(pol->rules, bucket, tmp, rule, next) ··· 81 71 kfree(pol); 82 72 } 83 73 84 - static void release_ruleset(struct setuid_ruleset *pol) 85 - { 74 + static void release_ruleset(struct setid_ruleset *pol){ 86 75 call_rcu(&pol->rcu, __release_ruleset); 87 76 } 88 77 89 - static void insert_rule(struct setuid_ruleset *pol, struct setuid_rule *rule) 78 + static void insert_rule(struct setid_ruleset *pol, struct setid_rule *rule) 90 79 { 91 - hash_add(pol->rules, &rule->next, __kuid_val(rule->src_uid)); 80 + if (pol->type == UID) 81 + hash_add(pol->rules, &rule->next, __kuid_val(rule->src_id.uid)); 82 + else if (pol->type == GID) 83 + hash_add(pol->rules, &rule->next, __kgid_val(rule->src_id.gid)); 84 + else /* Error, pol->type is neither UID or GID */ 85 + return; 92 86 } 93 87 94 - static int verify_ruleset(struct setuid_ruleset *pol) 88 + static int verify_ruleset(struct setid_ruleset *pol) 95 89 { 96 90 int bucket; 97 - struct setuid_rule *rule, *nrule; 91 + struct setid_rule *rule, *nrule; 98 92 int res = 0; 99 93 100 94 hash_for_each(pol->rules, bucket, rule, next) { 101 - if (_setuid_policy_lookup(pol, rule->dst_uid, INVALID_UID) == 102 - SIDPOL_DEFAULT) { 103 - pr_warn("insecure policy detected: uid %d is constrained but transitively unconstrained through uid %d\n", 104 - __kuid_val(rule->src_uid), 105 - __kuid_val(rule->dst_uid)); 95 + if (_setid_policy_lookup(pol, rule->dst_id, INVALID_ID) == SIDPOL_DEFAULT) { 96 + if (pol->type == UID) { 97 + pr_warn("insecure policy detected: uid %d is constrained but transitively unconstrained through uid %d\n", 98 + __kuid_val(rule->src_id.uid), 99 + __kuid_val(rule->dst_id.uid)); 100 + } else if (pol->type == GID) { 101 + pr_warn("insecure policy detected: gid %d is constrained but transitively unconstrained through gid %d\n", 102 + __kgid_val(rule->src_id.gid), 103 + __kgid_val(rule->dst_id.gid)); 104 + } else { /* pol->type is an invalid type */ 105 + res = -EINVAL; 106 + return res; 107 + } 106 108 res = -EINVAL; 107 109 108 110 /* fix it up */ 109 - nrule = kmalloc(sizeof(struct setuid_rule), GFP_KERNEL); 111 + nrule = kmalloc(sizeof(struct setid_rule), GFP_KERNEL); 110 112 if (!nrule) 111 113 return -ENOMEM; 112 - nrule->src_uid = rule->dst_uid; 113 - nrule->dst_uid = rule->dst_uid; 114 + if (pol->type == UID){ 115 + nrule->src_id.uid = rule->dst_id.uid; 116 + nrule->dst_id.uid = rule->dst_id.uid; 117 + nrule->type = UID; 118 + } else { /* pol->type must be GID if we've made it to here */ 119 + nrule->src_id.gid = rule->dst_id.gid; 120 + nrule->dst_id.gid = rule->dst_id.gid; 121 + nrule->type = GID; 122 + } 114 123 insert_rule(pol, nrule); 115 124 } 116 125 } ··· 137 108 } 138 109 139 110 static ssize_t handle_policy_update(struct file *file, 140 - const char __user *ubuf, size_t len) 111 + const char __user *ubuf, size_t len, enum setid_type policy_type) 141 112 { 142 - struct setuid_ruleset *pol; 113 + struct setid_ruleset *pol; 143 114 char *buf, *p, *end; 144 115 int err; 145 116 146 - pol = kmalloc(sizeof(struct setuid_ruleset), GFP_KERNEL); 117 + pol = kmalloc(sizeof(struct setid_ruleset), GFP_KERNEL); 147 118 if (!pol) 148 119 return -ENOMEM; 149 120 pol->policy_str = NULL; 121 + pol->type = policy_type; 150 122 hash_init(pol->rules); 151 123 152 124 p = buf = memdup_user_nul(ubuf, len); ··· 163 133 164 134 /* policy lines, including the last one, end with \n */ 165 135 while (*p != '\0') { 166 - struct setuid_rule *rule; 136 + struct setid_rule *rule; 167 137 168 138 end = strchr(p, '\n'); 169 139 if (end == NULL) { ··· 172 142 } 173 143 *end = '\0'; 174 144 175 - rule = kmalloc(sizeof(struct setuid_rule), GFP_KERNEL); 145 + rule = kmalloc(sizeof(struct setid_rule), GFP_KERNEL); 176 146 if (!rule) { 177 147 err = -ENOMEM; 178 148 goto out_free_buf; 179 149 } 180 150 151 + rule->type = policy_type; 181 152 err = parse_policy_line(file, p, rule); 182 153 if (err) 183 154 goto out_free_rule; 184 155 185 - if (_setuid_policy_lookup(pol, rule->src_uid, rule->dst_uid) == 186 - SIDPOL_ALLOWED) { 156 + if (_setid_policy_lookup(pol, rule->src_id, rule->dst_id) == SIDPOL_ALLOWED) { 187 157 pr_warn("bad policy: duplicate entry\n"); 188 158 err = -EEXIST; 189 159 goto out_free_rule; ··· 208 178 * What we really want here is an xchg() wrapper for RCU, but since that 209 179 * doesn't currently exist, just use a spinlock for now. 210 180 */ 211 - mutex_lock(&policy_update_lock); 212 - pol = rcu_replace_pointer(safesetid_setuid_rules, pol, 213 - lockdep_is_held(&policy_update_lock)); 214 - mutex_unlock(&policy_update_lock); 181 + if (policy_type == UID) { 182 + mutex_lock(&uid_policy_update_lock); 183 + pol = rcu_replace_pointer(safesetid_setuid_rules, pol, 184 + lockdep_is_held(&uid_policy_update_lock)); 185 + mutex_unlock(&uid_policy_update_lock); 186 + } else if (policy_type == GID) { 187 + mutex_lock(&gid_policy_update_lock); 188 + pol = rcu_replace_pointer(safesetid_setgid_rules, pol, 189 + lockdep_is_held(&gid_policy_update_lock)); 190 + mutex_unlock(&gid_policy_update_lock); 191 + } else { 192 + /* Error, policy type is neither UID or GID */ 193 + pr_warn("error: bad policy type"); 194 + } 215 195 err = len; 216 196 217 197 out_free_buf: 218 198 kfree(buf); 219 199 out_free_pol: 220 200 if (pol) 221 - release_ruleset(pol); 201 + release_ruleset(pol); 222 202 return err; 223 203 } 224 204 225 - static ssize_t safesetid_file_write(struct file *file, 205 + static ssize_t safesetid_uid_file_write(struct file *file, 226 206 const char __user *buf, 227 207 size_t len, 228 208 loff_t *ppos) ··· 243 203 if (*ppos != 0) 244 204 return -EINVAL; 245 205 246 - return handle_policy_update(file, buf, len); 206 + return handle_policy_update(file, buf, len, UID); 207 + } 208 + 209 + static ssize_t safesetid_gid_file_write(struct file *file, 210 + const char __user *buf, 211 + size_t len, 212 + loff_t *ppos) 213 + { 214 + if (!file_ns_capable(file, &init_user_ns, CAP_MAC_ADMIN)) 215 + return -EPERM; 216 + 217 + if (*ppos != 0) 218 + return -EINVAL; 219 + 220 + return handle_policy_update(file, buf, len, GID); 247 221 } 248 222 249 223 static ssize_t safesetid_file_read(struct file *file, char __user *buf, 250 - size_t len, loff_t *ppos) 224 + size_t len, loff_t *ppos, struct mutex *policy_update_lock, struct __rcu setid_ruleset* ruleset) 251 225 { 252 226 ssize_t res = 0; 253 - struct setuid_ruleset *pol; 227 + struct setid_ruleset *pol; 254 228 const char *kbuf; 255 229 256 - mutex_lock(&policy_update_lock); 257 - pol = rcu_dereference_protected(safesetid_setuid_rules, 258 - lockdep_is_held(&policy_update_lock)); 230 + mutex_lock(policy_update_lock); 231 + pol = rcu_dereference_protected(ruleset, lockdep_is_held(policy_update_lock)); 259 232 if (pol) { 260 233 kbuf = pol->policy_str; 261 234 res = simple_read_from_buffer(buf, len, ppos, 262 235 kbuf, strlen(kbuf)); 263 236 } 264 - mutex_unlock(&policy_update_lock); 237 + mutex_unlock(policy_update_lock); 238 + 265 239 return res; 266 240 } 267 241 268 - static const struct file_operations safesetid_file_fops = { 269 - .read = safesetid_file_read, 270 - .write = safesetid_file_write, 242 + static ssize_t safesetid_uid_file_read(struct file *file, char __user *buf, 243 + size_t len, loff_t *ppos) 244 + { 245 + return safesetid_file_read(file, buf, len, ppos, 246 + &uid_policy_update_lock, safesetid_setuid_rules); 247 + } 248 + 249 + static ssize_t safesetid_gid_file_read(struct file *file, char __user *buf, 250 + size_t len, loff_t *ppos) 251 + { 252 + return safesetid_file_read(file, buf, len, ppos, 253 + &gid_policy_update_lock, safesetid_setgid_rules); 254 + } 255 + 256 + 257 + 258 + static const struct file_operations safesetid_uid_file_fops = { 259 + .read = safesetid_uid_file_read, 260 + .write = safesetid_uid_file_write, 261 + }; 262 + 263 + static const struct file_operations safesetid_gid_file_fops = { 264 + .read = safesetid_gid_file_read, 265 + .write = safesetid_gid_file_write, 271 266 }; 272 267 273 268 static int __init safesetid_init_securityfs(void) 274 269 { 275 270 int ret; 276 271 struct dentry *policy_dir; 277 - struct dentry *policy_file; 272 + struct dentry *uid_policy_file; 273 + struct dentry *gid_policy_file; 278 274 279 275 if (!safesetid_initialized) 280 276 return 0; ··· 321 245 goto error; 322 246 } 323 247 324 - policy_file = securityfs_create_file("whitelist_policy", 0600, 325 - policy_dir, NULL, &safesetid_file_fops); 326 - if (IS_ERR(policy_file)) { 327 - ret = PTR_ERR(policy_file); 248 + uid_policy_file = securityfs_create_file("uid_allowlist_policy", 0600, 249 + policy_dir, NULL, &safesetid_uid_file_fops); 250 + if (IS_ERR(uid_policy_file)) { 251 + ret = PTR_ERR(uid_policy_file); 328 252 goto error; 329 253 } 254 + 255 + gid_policy_file = securityfs_create_file("gid_allowlist_policy", 0600, 256 + policy_dir, NULL, &safesetid_gid_file_fops); 257 + if (IS_ERR(gid_policy_file)) { 258 + ret = PTR_ERR(gid_policy_file); 259 + goto error; 260 + } 261 + 330 262 331 263 return 0; 332 264