xref: /openbmc/linux/security/safesetid/lsm.c (revision be709d48)
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/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>
23 
24 /* Flag indicating whether initialization completed */
25 int safesetid_initialized;
26 
27 #define NUM_BITS 8 /* 128 buckets in hash table */
28 
29 static DEFINE_HASHTABLE(safesetid_whitelist_hashtable, NUM_BITS);
30 
31 /*
32  * Hash table entry to store safesetid policy signifying that 'parent' user
33  * can setid to 'child' user.
34  */
35 struct entry {
36 	struct hlist_node next;
37 	struct hlist_node dlist; /* for deletion cleanup */
38 	uint64_t parent_kuid;
39 	uint64_t child_kuid;
40 };
41 
42 static DEFINE_SPINLOCK(safesetid_whitelist_hashtable_spinlock);
43 
44 static bool check_setuid_policy_hashtable_key(kuid_t parent)
45 {
46 	struct entry *entry;
47 
48 	rcu_read_lock();
49 	hash_for_each_possible_rcu(safesetid_whitelist_hashtable,
50 				   entry, next, __kuid_val(parent)) {
51 		if (entry->parent_kuid == __kuid_val(parent)) {
52 			rcu_read_unlock();
53 			return true;
54 		}
55 	}
56 	rcu_read_unlock();
57 
58 	return false;
59 }
60 
61 static bool check_setuid_policy_hashtable_key_value(kuid_t parent,
62 						    kuid_t child)
63 {
64 	struct entry *entry;
65 
66 	rcu_read_lock();
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)) {
71 			rcu_read_unlock();
72 			return true;
73 		}
74 	}
75 	rcu_read_unlock();
76 
77 	return false;
78 }
79 
80 static int safesetid_security_capable(const struct cred *cred,
81 				      struct user_namespace *ns,
82 				      int cap,
83 				      unsigned int opts)
84 {
85 	if (cap == CAP_SETUID &&
86 	    check_setuid_policy_hashtable_key(cred->uid)) {
87 		if (!(opts & CAP_OPT_INSETID)) {
88 			/*
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).
93 			 */
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));
96 			return -1;
97 		}
98 	}
99 	return 0;
100 }
101 
102 static int check_uid_transition(kuid_t parent, kuid_t child)
103 {
104 	if (check_setuid_policy_hashtable_key_value(parent, child))
105 		return 0;
106 	pr_warn("UID transition (%d -> %d) blocked",
107 		__kuid_val(parent),
108 		__kuid_val(child));
109 	/*
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.
113 	 */
114 	force_sig(SIGKILL, current);
115 	return -EACCES;
116 }
117 
118 /*
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.
122  */
123 static int safesetid_task_fix_setuid(struct cred *new,
124 				     const struct cred *old,
125 				     int flags)
126 {
127 
128 	/* Do nothing if there are no setuid restrictions for this UID. */
129 	if (!check_setuid_policy_hashtable_key(old->uid))
130 		return 0;
131 
132 	switch (flags) {
133 	case LSM_SETID_RE:
134 		/*
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.
138 		 */
139 		if (!uid_eq(old->uid, new->uid) &&
140 			!uid_eq(old->euid, new->uid)) {
141 			return check_uid_transition(old->uid, new->uid);
142 		}
143 		/*
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
147 		 * the transition.
148 		 */
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);
153 		}
154 		break;
155 	case LSM_SETID_ID:
156 		/*
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.
160 		 */
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);
165 		break;
166 	case LSM_SETID_RES:
167 		/*
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.
173 		 */
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);
178 		}
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);
183 		}
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);
188 		}
189 		break;
190 	case LSM_SETID_FS:
191 		/*
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.
196 		 */
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);
202 		}
203 		break;
204 	default:
205 		pr_warn("Unknown setid state %d\n", flags);
206 		force_sig(SIGKILL, current);
207 		return -EINVAL;
208 	}
209 	return 0;
210 }
211 
212 int add_safesetid_whitelist_entry(kuid_t parent, kuid_t child)
213 {
214 	struct entry *new;
215 
216 	/* Return if entry already exists */
217 	if (check_setuid_policy_hashtable_key_value(parent, child))
218 		return 0;
219 
220 	new = kzalloc(sizeof(struct entry), GFP_KERNEL);
221 	if (!new)
222 		return -ENOMEM;
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,
227 		     &new->next,
228 		     __kuid_val(parent));
229 	spin_unlock(&safesetid_whitelist_hashtable_spinlock);
230 	return 0;
231 }
232 
233 void flush_safesetid_whitelist_entries(void)
234 {
235 	struct entry *entry;
236 	struct hlist_node *hlist_node;
237 	unsigned int bkt_loop_cursor;
238 	HLIST_HEAD(free_list);
239 
240 	/*
241 	 * Could probably use hash_for_each_rcu here instead, but this should
242 	 * be fine as well.
243 	 */
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);
249 	}
250 	spin_unlock(&safesetid_whitelist_hashtable_spinlock);
251 	synchronize_rcu();
252 	hlist_for_each_entry_safe(entry, hlist_node, &free_list, dlist) {
253 		hlist_del(&entry->dlist);
254 		kfree(entry);
255 	}
256 }
257 
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)
261 };
262 
263 static int __init safesetid_security_init(void)
264 {
265 	security_add_hooks(safesetid_security_hooks,
266 			   ARRAY_SIZE(safesetid_security_hooks), "safesetid");
267 
268 	/* Report that SafeSetID successfully initialized */
269 	safesetid_initialized = 1;
270 
271 	return 0;
272 }
273 
274 DEFINE_LSM(safesetid_security_init) = {
275 	.init = safesetid_security_init,
276 	.name = "safesetid",
277 };
278