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 <asm/syscall.h> 18 #include <linux/hashtable.h> 19 #include <linux/lsm_hooks.h> 20 #include <linux/module.h> 21 #include <linux/ptrace.h> 22 #include <linux/sched/task_stack.h> 23 #include <linux/security.h> 24 25 /* Flag indicating whether initialization completed */ 26 int safesetid_initialized; 27 28 #define NUM_BITS 8 /* 128 buckets in hash table */ 29 30 static DEFINE_HASHTABLE(safesetid_whitelist_hashtable, NUM_BITS); 31 32 /* 33 * Hash table entry to store safesetid policy signifying that 'parent' user 34 * can setid to 'child' user. 35 */ 36 struct entry { 37 struct hlist_node next; 38 struct hlist_node dlist; /* for deletion cleanup */ 39 uint64_t parent_kuid; 40 uint64_t child_kuid; 41 }; 42 43 static DEFINE_SPINLOCK(safesetid_whitelist_hashtable_spinlock); 44 45 static bool check_setuid_policy_hashtable_key(kuid_t parent) 46 { 47 struct entry *entry; 48 49 rcu_read_lock(); 50 hash_for_each_possible_rcu(safesetid_whitelist_hashtable, 51 entry, next, __kuid_val(parent)) { 52 if (entry->parent_kuid == __kuid_val(parent)) { 53 rcu_read_unlock(); 54 return true; 55 } 56 } 57 rcu_read_unlock(); 58 59 return false; 60 } 61 62 static bool check_setuid_policy_hashtable_key_value(kuid_t parent, 63 kuid_t child) 64 { 65 struct entry *entry; 66 67 rcu_read_lock(); 68 hash_for_each_possible_rcu(safesetid_whitelist_hashtable, 69 entry, next, __kuid_val(parent)) { 70 if (entry->parent_kuid == __kuid_val(parent) && 71 entry->child_kuid == __kuid_val(child)) { 72 rcu_read_unlock(); 73 return true; 74 } 75 } 76 rcu_read_unlock(); 77 78 return false; 79 } 80 81 static int safesetid_security_capable(const struct cred *cred, 82 struct user_namespace *ns, 83 int cap, 84 unsigned int opts) 85 { 86 if (cap == CAP_SETUID && 87 check_setuid_policy_hashtable_key(cred->uid)) { 88 if (!(opts & CAP_OPT_INSETID)) { 89 /* 90 * Deny if we're not in a set*uid() syscall to avoid 91 * giving powers gated by CAP_SETUID that are related 92 * to functionality other than calling set*uid() (e.g. 93 * allowing user to set up userns uid mappings). 94 */ 95 pr_warn("Operation requires CAP_SETUID, which is not available to UID %u for operations besides approved set*uid transitions", 96 __kuid_val(cred->uid)); 97 return -1; 98 } 99 } 100 return 0; 101 } 102 103 static int check_uid_transition(kuid_t parent, kuid_t child) 104 { 105 if (check_setuid_policy_hashtable_key_value(parent, child)) 106 return 0; 107 pr_warn("UID transition (%d -> %d) blocked", 108 __kuid_val(parent), 109 __kuid_val(child)); 110 /* 111 * Kill this process to avoid potential security vulnerabilities 112 * that could arise from a missing whitelist entry preventing a 113 * privileged process from dropping to a lesser-privileged one. 114 */ 115 force_sig(SIGKILL, current); 116 return -EACCES; 117 } 118 119 /* 120 * Check whether there is either an exception for user under old cred struct to 121 * set*uid to user under new cred struct, or the UID transition is allowed (by 122 * Linux set*uid rules) even without CAP_SETUID. 123 */ 124 static int safesetid_task_fix_setuid(struct cred *new, 125 const struct cred *old, 126 int flags) 127 { 128 129 /* Do nothing if there are no setuid restrictions for this UID. */ 130 if (!check_setuid_policy_hashtable_key(old->uid)) 131 return 0; 132 133 switch (flags) { 134 case LSM_SETID_RE: 135 /* 136 * Users for which setuid restrictions exist can only set the 137 * real UID to the real UID or the effective UID, unless an 138 * explicit whitelist policy allows the transition. 139 */ 140 if (!uid_eq(old->uid, new->uid) && 141 !uid_eq(old->euid, new->uid)) { 142 return check_uid_transition(old->uid, new->uid); 143 } 144 /* 145 * Users for which setuid restrictions exist can only set the 146 * effective UID to the real UID, the effective UID, or the 147 * saved set-UID, unless an explicit whitelist policy allows 148 * the transition. 149 */ 150 if (!uid_eq(old->uid, new->euid) && 151 !uid_eq(old->euid, new->euid) && 152 !uid_eq(old->suid, new->euid)) { 153 return check_uid_transition(old->euid, new->euid); 154 } 155 break; 156 case LSM_SETID_ID: 157 /* 158 * Users for which setuid restrictions exist cannot change the 159 * real UID or saved set-UID unless an explicit whitelist 160 * policy allows the transition. 161 */ 162 if (!uid_eq(old->uid, new->uid)) 163 return check_uid_transition(old->uid, new->uid); 164 if (!uid_eq(old->suid, new->suid)) 165 return check_uid_transition(old->suid, new->suid); 166 break; 167 case LSM_SETID_RES: 168 /* 169 * Users for which setuid restrictions exist cannot change the 170 * real UID, effective UID, or saved set-UID to anything but 171 * one of: the current real UID, the current effective UID or 172 * the current saved set-user-ID unless an explicit whitelist 173 * policy allows the transition. 174 */ 175 if (!uid_eq(new->uid, old->uid) && 176 !uid_eq(new->uid, old->euid) && 177 !uid_eq(new->uid, old->suid)) { 178 return check_uid_transition(old->uid, new->uid); 179 } 180 if (!uid_eq(new->euid, old->uid) && 181 !uid_eq(new->euid, old->euid) && 182 !uid_eq(new->euid, old->suid)) { 183 return check_uid_transition(old->euid, new->euid); 184 } 185 if (!uid_eq(new->suid, old->uid) && 186 !uid_eq(new->suid, old->euid) && 187 !uid_eq(new->suid, old->suid)) { 188 return check_uid_transition(old->suid, new->suid); 189 } 190 break; 191 case LSM_SETID_FS: 192 /* 193 * Users for which setuid restrictions exist cannot change the 194 * filesystem UID to anything but one of: the current real UID, 195 * the current effective UID or the current saved set-UID 196 * unless an explicit whitelist policy allows the transition. 197 */ 198 if (!uid_eq(new->fsuid, old->uid) && 199 !uid_eq(new->fsuid, old->euid) && 200 !uid_eq(new->fsuid, old->suid) && 201 !uid_eq(new->fsuid, old->fsuid)) { 202 return check_uid_transition(old->fsuid, new->fsuid); 203 } 204 break; 205 default: 206 pr_warn("Unknown setid state %d\n", flags); 207 force_sig(SIGKILL, current); 208 return -EINVAL; 209 } 210 return 0; 211 } 212 213 int add_safesetid_whitelist_entry(kuid_t parent, kuid_t child) 214 { 215 struct entry *new; 216 217 /* Return if entry already exists */ 218 if (check_setuid_policy_hashtable_key_value(parent, child)) 219 return 0; 220 221 new = kzalloc(sizeof(struct entry), GFP_KERNEL); 222 if (!new) 223 return -ENOMEM; 224 new->parent_kuid = __kuid_val(parent); 225 new->child_kuid = __kuid_val(child); 226 spin_lock(&safesetid_whitelist_hashtable_spinlock); 227 hash_add_rcu(safesetid_whitelist_hashtable, 228 &new->next, 229 __kuid_val(parent)); 230 spin_unlock(&safesetid_whitelist_hashtable_spinlock); 231 return 0; 232 } 233 234 void flush_safesetid_whitelist_entries(void) 235 { 236 struct entry *entry; 237 struct hlist_node *hlist_node; 238 unsigned int bkt_loop_cursor; 239 HLIST_HEAD(free_list); 240 241 /* 242 * Could probably use hash_for_each_rcu here instead, but this should 243 * be fine as well. 244 */ 245 spin_lock(&safesetid_whitelist_hashtable_spinlock); 246 hash_for_each_safe(safesetid_whitelist_hashtable, bkt_loop_cursor, 247 hlist_node, entry, next) { 248 hash_del_rcu(&entry->next); 249 hlist_add_head(&entry->dlist, &free_list); 250 } 251 spin_unlock(&safesetid_whitelist_hashtable_spinlock); 252 synchronize_rcu(); 253 hlist_for_each_entry_safe(entry, hlist_node, &free_list, dlist) { 254 hlist_del(&entry->dlist); 255 kfree(entry); 256 } 257 } 258 259 static struct security_hook_list safesetid_security_hooks[] = { 260 LSM_HOOK_INIT(task_fix_setuid, safesetid_task_fix_setuid), 261 LSM_HOOK_INIT(capable, safesetid_security_capable) 262 }; 263 264 static int __init safesetid_security_init(void) 265 { 266 security_add_hooks(safesetid_security_hooks, 267 ARRAY_SIZE(safesetid_security_hooks), "safesetid"); 268 269 /* Report that SafeSetID successfully initialized */ 270 safesetid_initialized = 1; 271 272 return 0; 273 } 274 275 DEFINE_LSM(safesetid_security_init) = { 276 .init = safesetid_security_init, 277 }; 278