1 // SPDX-License-Identifier: GPL-2.0
3 * SafeSetID Linux Security Module
5 * Author: Micah Morton <mortonm@chromium.org>
7 * Copyright (C) 2018 The Chromium OS Authors.
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.
15 #define pr_fmt(fmt) "SafeSetID: " fmt
17 #include <linux/hashtable.h>
18 #include <linux/lsm_hooks.h>
19 #include <linux/module.h>
20 #include <linux/ptrace.h>
21 #include <linux/sched/task_stack.h>
22 #include <linux/security.h>
24 /* Flag indicating whether initialization completed */
25 int safesetid_initialized;
27 #define NUM_BITS 8 /* 128 buckets in hash table */
29 static DEFINE_HASHTABLE(safesetid_whitelist_hashtable, NUM_BITS);
32 * Hash table entry to store safesetid policy signifying that 'parent' user
33 * can setid to 'child' user.
36 struct hlist_node next;
37 struct hlist_node dlist; /* for deletion cleanup */
42 static DEFINE_SPINLOCK(safesetid_whitelist_hashtable_spinlock);
44 static bool check_setuid_policy_hashtable_key(kuid_t parent)
49 hash_for_each_possible_rcu(safesetid_whitelist_hashtable,
50 entry, next, __kuid_val(parent)) {
51 if (entry->parent_kuid == __kuid_val(parent)) {
61 static bool check_setuid_policy_hashtable_key_value(kuid_t parent,
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)) {
80 static int safesetid_security_capable(const struct cred *cred,
81 struct user_namespace *ns,
85 if (cap == CAP_SETUID &&
86 check_setuid_policy_hashtable_key(cred->uid)) {
87 if (!(opts & CAP_OPT_INSETID)) {
89 * Deny if we're not in a set*uid() syscall to avoid
90 * giving powers gated by CAP_SETUID that are related
91 * to functionality other than calling set*uid() (e.g.
92 * allowing user to set up userns uid mappings).
94 pr_warn("Operation requires CAP_SETUID, which is not available to UID %u for operations besides approved set*uid transitions",
95 __kuid_val(cred->uid));
102 static int check_uid_transition(kuid_t parent, kuid_t child)
104 if (check_setuid_policy_hashtable_key_value(parent, child))
106 pr_warn("UID transition (%d -> %d) blocked",
110 * Kill this process to avoid potential security vulnerabilities
111 * that could arise from a missing whitelist entry preventing a
112 * privileged process from dropping to a lesser-privileged one.
119 * Check whether there is either an exception for user under old cred struct to
120 * set*uid to user under new cred struct, or the UID transition is allowed (by
121 * Linux set*uid rules) even without CAP_SETUID.
123 static int safesetid_task_fix_setuid(struct cred *new,
124 const struct cred *old,
128 /* Do nothing if there are no setuid restrictions for this UID. */
129 if (!check_setuid_policy_hashtable_key(old->uid))
135 * Users for which setuid restrictions exist can only set the
136 * real UID to the real UID or the effective UID, unless an
137 * explicit whitelist policy allows the transition.
139 if (!uid_eq(old->uid, new->uid) &&
140 !uid_eq(old->euid, new->uid)) {
141 return check_uid_transition(old->uid, new->uid);
144 * Users for which setuid restrictions exist can only set the
145 * effective UID to the real UID, the effective UID, or the
146 * saved set-UID, unless an explicit whitelist policy allows
149 if (!uid_eq(old->uid, new->euid) &&
150 !uid_eq(old->euid, new->euid) &&
151 !uid_eq(old->suid, new->euid)) {
152 return check_uid_transition(old->euid, new->euid);
157 * Users for which setuid restrictions exist cannot change the
158 * real UID or saved set-UID unless an explicit whitelist
159 * policy allows the transition.
161 if (!uid_eq(old->uid, new->uid))
162 return check_uid_transition(old->uid, new->uid);
163 if (!uid_eq(old->suid, new->suid))
164 return check_uid_transition(old->suid, new->suid);
168 * Users for which setuid restrictions exist cannot change the
169 * real UID, effective UID, or saved set-UID to anything but
170 * one of: the current real UID, the current effective UID or
171 * the current saved set-user-ID unless an explicit whitelist
172 * policy allows the transition.
174 if (!uid_eq(new->uid, old->uid) &&
175 !uid_eq(new->uid, old->euid) &&
176 !uid_eq(new->uid, old->suid)) {
177 return check_uid_transition(old->uid, new->uid);
179 if (!uid_eq(new->euid, old->uid) &&
180 !uid_eq(new->euid, old->euid) &&
181 !uid_eq(new->euid, old->suid)) {
182 return check_uid_transition(old->euid, new->euid);
184 if (!uid_eq(new->suid, old->uid) &&
185 !uid_eq(new->suid, old->euid) &&
186 !uid_eq(new->suid, old->suid)) {
187 return check_uid_transition(old->suid, new->suid);
192 * Users for which setuid restrictions exist cannot change the
193 * filesystem UID to anything but one of: the current real UID,
194 * the current effective UID or the current saved set-UID
195 * unless an explicit whitelist policy allows the transition.
197 if (!uid_eq(new->fsuid, old->uid) &&
198 !uid_eq(new->fsuid, old->euid) &&
199 !uid_eq(new->fsuid, old->suid) &&
200 !uid_eq(new->fsuid, old->fsuid)) {
201 return check_uid_transition(old->fsuid, new->fsuid);
205 pr_warn("Unknown setid state %d\n", flags);
212 int add_safesetid_whitelist_entry(kuid_t parent, kuid_t child)
216 /* Return if entry already exists */
217 if (check_setuid_policy_hashtable_key_value(parent, child))
220 new = kzalloc(sizeof(struct entry), GFP_KERNEL);
223 new->parent_kuid = __kuid_val(parent);
224 new->child_kuid = __kuid_val(child);
225 spin_lock(&safesetid_whitelist_hashtable_spinlock);
226 hash_add_rcu(safesetid_whitelist_hashtable,
229 spin_unlock(&safesetid_whitelist_hashtable_spinlock);
233 void flush_safesetid_whitelist_entries(void)
236 struct hlist_node *hlist_node;
237 unsigned int bkt_loop_cursor;
238 HLIST_HEAD(free_list);
241 * Could probably use hash_for_each_rcu here instead, but this should
244 spin_lock(&safesetid_whitelist_hashtable_spinlock);
245 hash_for_each_safe(safesetid_whitelist_hashtable, bkt_loop_cursor,
246 hlist_node, entry, next) {
247 hash_del_rcu(&entry->next);
248 hlist_add_head(&entry->dlist, &free_list);
250 spin_unlock(&safesetid_whitelist_hashtable_spinlock);
252 hlist_for_each_entry_safe(entry, hlist_node, &free_list, dlist) {
253 hlist_del(&entry->dlist);
258 static struct security_hook_list safesetid_security_hooks[] = {
259 LSM_HOOK_INIT(task_fix_setuid, safesetid_task_fix_setuid),
260 LSM_HOOK_INIT(capable, safesetid_security_capable)
263 static int __init safesetid_security_init(void)
265 security_add_hooks(safesetid_security_hooks,
266 ARRAY_SIZE(safesetid_security_hooks), "safesetid");
268 /* Report that SafeSetID successfully initialized */
269 safesetid_initialized = 1;
274 DEFINE_LSM(safesetid_security_init) = {
275 .init = safesetid_security_init,