1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * SafeSetID Linux Security Module 4 * 5 * Author: Micah Morton <mortonm@chromium.org> 6 * 7 * Copyright (C) 2018 The Chromium OS Authors. 8 * 9 * This program is free software; you can redistribute it and/or modify 10 * it under the terms of the GNU General Public License version 2, as 11 * published by the Free Software Foundation. 12 * 13 */ 14 15 #define pr_fmt(fmt) "SafeSetID: " fmt 16 17 #include <linux/lsm_hooks.h> 18 #include <linux/module.h> 19 #include <linux/ptrace.h> 20 #include <linux/sched/task_stack.h> 21 #include <linux/security.h> 22 #include <uapi/linux/lsm.h> 23 #include "lsm.h" 24 25 /* Flag indicating whether initialization completed */ 26 int safesetid_initialized __initdata; 27 28 struct setid_ruleset __rcu *safesetid_setuid_rules; 29 struct setid_ruleset __rcu *safesetid_setgid_rules; 30 31 32 /* Compute a decision for a transition from @src to @dst under @policy. */ 33 enum sid_policy_type _setid_policy_lookup(struct setid_ruleset *policy, 34 kid_t src, kid_t dst) 35 { 36 struct setid_rule *rule; 37 enum sid_policy_type result = SIDPOL_DEFAULT; 38 39 if (policy->type == UID) { 40 hash_for_each_possible(policy->rules, rule, next, __kuid_val(src.uid)) { 41 if (!uid_eq(rule->src_id.uid, src.uid)) 42 continue; 43 if (uid_eq(rule->dst_id.uid, dst.uid)) 44 return SIDPOL_ALLOWED; 45 result = SIDPOL_CONSTRAINED; 46 } 47 } else if (policy->type == GID) { 48 hash_for_each_possible(policy->rules, rule, next, __kgid_val(src.gid)) { 49 if (!gid_eq(rule->src_id.gid, src.gid)) 50 continue; 51 if (gid_eq(rule->dst_id.gid, dst.gid)){ 52 return SIDPOL_ALLOWED; 53 } 54 result = SIDPOL_CONSTRAINED; 55 } 56 } else { 57 /* Should not reach here, report the ID as contrainsted */ 58 result = SIDPOL_CONSTRAINED; 59 } 60 return result; 61 } 62 63 /* 64 * Compute a decision for a transition from @src to @dst under the active 65 * policy. 66 */ 67 static enum sid_policy_type setid_policy_lookup(kid_t src, kid_t dst, enum setid_type new_type) 68 { 69 enum sid_policy_type result = SIDPOL_DEFAULT; 70 struct setid_ruleset *pol; 71 72 rcu_read_lock(); 73 if (new_type == UID) 74 pol = rcu_dereference(safesetid_setuid_rules); 75 else if (new_type == GID) 76 pol = rcu_dereference(safesetid_setgid_rules); 77 else { /* Should not reach here */ 78 result = SIDPOL_CONSTRAINED; 79 rcu_read_unlock(); 80 return result; 81 } 82 83 if (pol) { 84 pol->type = new_type; 85 result = _setid_policy_lookup(pol, src, dst); 86 } 87 rcu_read_unlock(); 88 return result; 89 } 90 91 static int safesetid_security_capable(const struct cred *cred, 92 struct user_namespace *ns, 93 int cap, 94 unsigned int opts) 95 { 96 /* We're only interested in CAP_SETUID and CAP_SETGID. */ 97 if (cap != CAP_SETUID && cap != CAP_SETGID) 98 return 0; 99 100 /* 101 * If CAP_SET{U/G}ID is currently used for a setid or setgroups syscall, we 102 * want to let it go through here; the real security check happens later, in 103 * the task_fix_set{u/g}id or task_fix_setgroups hooks. 104 */ 105 if ((opts & CAP_OPT_INSETID) != 0) 106 return 0; 107 108 switch (cap) { 109 case CAP_SETUID: 110 /* 111 * If no policy applies to this task, allow the use of CAP_SETUID for 112 * other purposes. 113 */ 114 if (setid_policy_lookup((kid_t){.uid = cred->uid}, INVALID_ID, UID) == SIDPOL_DEFAULT) 115 return 0; 116 /* 117 * Reject use of CAP_SETUID for functionality other than calling 118 * set*uid() (e.g. setting up userns uid mappings). 119 */ 120 pr_warn("Operation requires CAP_SETUID, which is not available to UID %u for operations besides approved set*uid transitions\n", 121 __kuid_val(cred->uid)); 122 return -EPERM; 123 case CAP_SETGID: 124 /* 125 * If no policy applies to this task, allow the use of CAP_SETGID for 126 * other purposes. 127 */ 128 if (setid_policy_lookup((kid_t){.gid = cred->gid}, INVALID_ID, GID) == SIDPOL_DEFAULT) 129 return 0; 130 /* 131 * Reject use of CAP_SETUID for functionality other than calling 132 * set*gid() (e.g. setting up userns gid mappings). 133 */ 134 pr_warn("Operation requires CAP_SETGID, which is not available to GID %u for operations besides approved set*gid transitions\n", 135 __kgid_val(cred->gid)); 136 return -EPERM; 137 default: 138 /* Error, the only capabilities were checking for is CAP_SETUID/GID */ 139 return 0; 140 } 141 return 0; 142 } 143 144 /* 145 * Check whether a caller with old credentials @old is allowed to switch to 146 * credentials that contain @new_id. 147 */ 148 static bool id_permitted_for_cred(const struct cred *old, kid_t new_id, enum setid_type new_type) 149 { 150 bool permitted; 151 152 /* If our old creds already had this ID in it, it's fine. */ 153 if (new_type == UID) { 154 if (uid_eq(new_id.uid, old->uid) || uid_eq(new_id.uid, old->euid) || 155 uid_eq(new_id.uid, old->suid)) 156 return true; 157 } else if (new_type == GID){ 158 if (gid_eq(new_id.gid, old->gid) || gid_eq(new_id.gid, old->egid) || 159 gid_eq(new_id.gid, old->sgid)) 160 return true; 161 } else /* Error, new_type is an invalid type */ 162 return false; 163 164 /* 165 * Transitions to new UIDs require a check against the policy of the old 166 * RUID. 167 */ 168 permitted = 169 setid_policy_lookup((kid_t){.uid = old->uid}, new_id, new_type) != SIDPOL_CONSTRAINED; 170 171 if (!permitted) { 172 if (new_type == UID) { 173 pr_warn("UID transition ((%d,%d,%d) -> %d) blocked\n", 174 __kuid_val(old->uid), __kuid_val(old->euid), 175 __kuid_val(old->suid), __kuid_val(new_id.uid)); 176 } else if (new_type == GID) { 177 pr_warn("GID transition ((%d,%d,%d) -> %d) blocked\n", 178 __kgid_val(old->gid), __kgid_val(old->egid), 179 __kgid_val(old->sgid), __kgid_val(new_id.gid)); 180 } else /* Error, new_type is an invalid type */ 181 return false; 182 } 183 return permitted; 184 } 185 186 /* 187 * Check whether there is either an exception for user under old cred struct to 188 * set*uid to user under new cred struct, or the UID transition is allowed (by 189 * Linux set*uid rules) even without CAP_SETUID. 190 */ 191 static int safesetid_task_fix_setuid(struct cred *new, 192 const struct cred *old, 193 int flags) 194 { 195 196 /* Do nothing if there are no setuid restrictions for our old RUID. */ 197 if (setid_policy_lookup((kid_t){.uid = old->uid}, INVALID_ID, UID) == SIDPOL_DEFAULT) 198 return 0; 199 200 if (id_permitted_for_cred(old, (kid_t){.uid = new->uid}, UID) && 201 id_permitted_for_cred(old, (kid_t){.uid = new->euid}, UID) && 202 id_permitted_for_cred(old, (kid_t){.uid = new->suid}, UID) && 203 id_permitted_for_cred(old, (kid_t){.uid = new->fsuid}, UID)) 204 return 0; 205 206 /* 207 * Kill this process to avoid potential security vulnerabilities 208 * that could arise from a missing allowlist entry preventing a 209 * privileged process from dropping to a lesser-privileged one. 210 */ 211 force_sig(SIGKILL); 212 return -EACCES; 213 } 214 215 static int safesetid_task_fix_setgid(struct cred *new, 216 const struct cred *old, 217 int flags) 218 { 219 220 /* Do nothing if there are no setgid restrictions for our old RGID. */ 221 if (setid_policy_lookup((kid_t){.gid = old->gid}, INVALID_ID, GID) == SIDPOL_DEFAULT) 222 return 0; 223 224 if (id_permitted_for_cred(old, (kid_t){.gid = new->gid}, GID) && 225 id_permitted_for_cred(old, (kid_t){.gid = new->egid}, GID) && 226 id_permitted_for_cred(old, (kid_t){.gid = new->sgid}, GID) && 227 id_permitted_for_cred(old, (kid_t){.gid = new->fsgid}, GID)) 228 return 0; 229 230 /* 231 * Kill this process to avoid potential security vulnerabilities 232 * that could arise from a missing allowlist entry preventing a 233 * privileged process from dropping to a lesser-privileged one. 234 */ 235 force_sig(SIGKILL); 236 return -EACCES; 237 } 238 239 static int safesetid_task_fix_setgroups(struct cred *new, const struct cred *old) 240 { 241 int i; 242 243 /* Do nothing if there are no setgid restrictions for our old RGID. */ 244 if (setid_policy_lookup((kid_t){.gid = old->gid}, INVALID_ID, GID) == SIDPOL_DEFAULT) 245 return 0; 246 247 get_group_info(new->group_info); 248 for (i = 0; i < new->group_info->ngroups; i++) { 249 if (!id_permitted_for_cred(old, (kid_t){.gid = new->group_info->gid[i]}, GID)) { 250 put_group_info(new->group_info); 251 /* 252 * Kill this process to avoid potential security vulnerabilities 253 * that could arise from a missing allowlist entry preventing a 254 * privileged process from dropping to a lesser-privileged one. 255 */ 256 force_sig(SIGKILL); 257 return -EACCES; 258 } 259 } 260 261 put_group_info(new->group_info); 262 return 0; 263 } 264 265 static const struct lsm_id safesetid_lsmid = { 266 .name = "safesetid", 267 .id = LSM_ID_SAFESETID, 268 }; 269 270 static struct security_hook_list safesetid_security_hooks[] = { 271 LSM_HOOK_INIT(task_fix_setuid, safesetid_task_fix_setuid), 272 LSM_HOOK_INIT(task_fix_setgid, safesetid_task_fix_setgid), 273 LSM_HOOK_INIT(task_fix_setgroups, safesetid_task_fix_setgroups), 274 LSM_HOOK_INIT(capable, safesetid_security_capable) 275 }; 276 277 static int __init safesetid_security_init(void) 278 { 279 security_add_hooks(safesetid_security_hooks, 280 ARRAY_SIZE(safesetid_security_hooks), 281 &safesetid_lsmid); 282 283 /* Report that SafeSetID successfully initialized */ 284 safesetid_initialized = 1; 285 286 return 0; 287 } 288 289 DEFINE_LSM(safesetid_security_init) = { 290 .init = safesetid_security_init, 291 .name = "safesetid", 292 }; 293
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.