xref: /linux/security/safesetid/lsm.c (revision aeca4e2ca65c1aeacfbe520684e6421719d99417)
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