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
,