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/security.h>
18 #include <linux/cred.h>
19
20 #include "lsm.h"
21
22 static DEFINE_MUTEX(uid_policy_update_lock);
23 static DEFINE_MUTEX(gid_policy_update_lock);
24
25 /*
26 * In the case the input buffer contains one or more invalid IDs, the kid_t
27 * variables pointed to by @parent and @child will get updated but this
28 * function will return an error.
29 * Contents of @buf may be modified.
30 */
parse_policy_line(struct file * file,char * buf,struct setid_rule * rule)31 static int parse_policy_line(struct file *file, char *buf,
32 struct setid_rule *rule)
33 {
34 char *child_str;
35 int ret;
36 u32 parsed_parent, parsed_child;
37
38 /* Format of |buf| string should be <UID>:<UID> or <GID>:<GID> */
39 child_str = strchr(buf, ':');
40 if (child_str == NULL)
41 return -EINVAL;
42 *child_str = '\0';
43 child_str++;
44
45 ret = kstrtou32(buf, 0, &parsed_parent);
46 if (ret)
47 return ret;
48
49 ret = kstrtou32(child_str, 0, &parsed_child);
50 if (ret)
51 return ret;
52
53 if (rule->type == UID){
54 rule->src_id.uid = make_kuid(file->f_cred->user_ns, parsed_parent);
55 rule->dst_id.uid = make_kuid(file->f_cred->user_ns, parsed_child);
56 if (!uid_valid(rule->src_id.uid) || !uid_valid(rule->dst_id.uid))
57 return -EINVAL;
58 } else if (rule->type == GID){
59 rule->src_id.gid = make_kgid(file->f_cred->user_ns, parsed_parent);
60 rule->dst_id.gid = make_kgid(file->f_cred->user_ns, parsed_child);
61 if (!gid_valid(rule->src_id.gid) || !gid_valid(rule->dst_id.gid))
62 return -EINVAL;
63 } else {
64 /* Error, rule->type is an invalid type */
65 return -EINVAL;
66 }
67 return 0;
68 }
69
__release_ruleset(struct rcu_head * rcu)70 static void __release_ruleset(struct rcu_head *rcu)
71 {
72 struct setid_ruleset *pol =
73 container_of(rcu, struct setid_ruleset, rcu);
74 int bucket;
75 struct setid_rule *rule;
76 struct hlist_node *tmp;
77
78 hash_for_each_safe(pol->rules, bucket, tmp, rule, next)
79 kfree(rule);
80 kfree(pol->policy_str);
81 kfree(pol);
82 }
83
release_ruleset(struct setid_ruleset * pol)84 static void release_ruleset(struct setid_ruleset *pol){
85 call_rcu(&pol->rcu, __release_ruleset);
86 }
87
insert_rule(struct setid_ruleset * pol,struct setid_rule * rule)88 static void insert_rule(struct setid_ruleset *pol, struct setid_rule *rule)
89 {
90 if (pol->type == UID)
91 hash_add(pol->rules, &rule->next, __kuid_val(rule->src_id.uid));
92 else if (pol->type == GID)
93 hash_add(pol->rules, &rule->next, __kgid_val(rule->src_id.gid));
94 else /* Error, pol->type is neither UID or GID */
95 return;
96 }
97
verify_ruleset(struct setid_ruleset * pol)98 static int verify_ruleset(struct setid_ruleset *pol)
99 {
100 int bucket;
101 struct setid_rule *rule, *nrule;
102 int res = 0;
103
104 hash_for_each(pol->rules, bucket, rule, next) {
105 if (_setid_policy_lookup(pol, rule->dst_id, INVALID_ID) == SIDPOL_DEFAULT) {
106 if (pol->type == UID) {
107 pr_warn("insecure policy detected: uid %d is constrained but transitively unconstrained through uid %d\n",
108 __kuid_val(rule->src_id.uid),
109 __kuid_val(rule->dst_id.uid));
110 } else if (pol->type == GID) {
111 pr_warn("insecure policy detected: gid %d is constrained but transitively unconstrained through gid %d\n",
112 __kgid_val(rule->src_id.gid),
113 __kgid_val(rule->dst_id.gid));
114 } else { /* pol->type is an invalid type */
115 res = -EINVAL;
116 return res;
117 }
118 res = -EINVAL;
119
120 /* fix it up */
121 nrule = kmalloc(sizeof(struct setid_rule), GFP_KERNEL);
122 if (!nrule)
123 return -ENOMEM;
124 if (pol->type == UID){
125 nrule->src_id.uid = rule->dst_id.uid;
126 nrule->dst_id.uid = rule->dst_id.uid;
127 nrule->type = UID;
128 } else { /* pol->type must be GID if we've made it to here */
129 nrule->src_id.gid = rule->dst_id.gid;
130 nrule->dst_id.gid = rule->dst_id.gid;
131 nrule->type = GID;
132 }
133 insert_rule(pol, nrule);
134 }
135 }
136 return res;
137 }
138
handle_policy_update(struct file * file,const char __user * ubuf,size_t len,enum setid_type policy_type)139 static ssize_t handle_policy_update(struct file *file,
140 const char __user *ubuf, size_t len, enum setid_type policy_type)
141 {
142 struct setid_ruleset *pol;
143 char *buf, *p, *end;
144 int err;
145
146 if (len >= KMALLOC_MAX_SIZE)
147 return -EINVAL;
148
149 pol = kmalloc(sizeof(struct setid_ruleset), GFP_KERNEL);
150 if (!pol)
151 return -ENOMEM;
152 pol->policy_str = NULL;
153 pol->type = policy_type;
154 hash_init(pol->rules);
155
156 p = buf = memdup_user_nul(ubuf, len);
157 if (IS_ERR(buf)) {
158 err = PTR_ERR(buf);
159 goto out_free_pol;
160 }
161 pol->policy_str = kstrdup(buf, GFP_KERNEL);
162 if (pol->policy_str == NULL) {
163 err = -ENOMEM;
164 goto out_free_buf;
165 }
166
167 /* policy lines, including the last one, end with \n */
168 while (*p != '\0') {
169 struct setid_rule *rule;
170
171 end = strchr(p, '\n');
172 if (end == NULL) {
173 err = -EINVAL;
174 goto out_free_buf;
175 }
176 *end = '\0';
177
178 rule = kmalloc(sizeof(struct setid_rule), GFP_KERNEL);
179 if (!rule) {
180 err = -ENOMEM;
181 goto out_free_buf;
182 }
183
184 rule->type = policy_type;
185 err = parse_policy_line(file, p, rule);
186 if (err)
187 goto out_free_rule;
188
189 if (_setid_policy_lookup(pol, rule->src_id, rule->dst_id) == SIDPOL_ALLOWED) {
190 pr_warn("bad policy: duplicate entry\n");
191 err = -EEXIST;
192 goto out_free_rule;
193 }
194
195 insert_rule(pol, rule);
196 p = end + 1;
197 continue;
198
199 out_free_rule:
200 kfree(rule);
201 goto out_free_buf;
202 }
203
204 err = verify_ruleset(pol);
205 /* bogus policy falls through after fixing it up */
206 if (err && err != -EINVAL)
207 goto out_free_buf;
208
209 /*
210 * Everything looks good, apply the policy and release the old one.
211 * What we really want here is an xchg() wrapper for RCU, but since that
212 * doesn't currently exist, just use a spinlock for now.
213 */
214 if (policy_type == UID) {
215 mutex_lock(&uid_policy_update_lock);
216 pol = rcu_replace_pointer(safesetid_setuid_rules, pol,
217 lockdep_is_held(&uid_policy_update_lock));
218 mutex_unlock(&uid_policy_update_lock);
219 } else if (policy_type == GID) {
220 mutex_lock(&gid_policy_update_lock);
221 pol = rcu_replace_pointer(safesetid_setgid_rules, pol,
222 lockdep_is_held(&gid_policy_update_lock));
223 mutex_unlock(&gid_policy_update_lock);
224 } else {
225 /* Error, policy type is neither UID or GID */
226 pr_warn("error: bad policy type");
227 }
228 err = len;
229
230 out_free_buf:
231 kfree(buf);
232 out_free_pol:
233 if (pol)
234 release_ruleset(pol);
235 return err;
236 }
237
safesetid_uid_file_write(struct file * file,const char __user * buf,size_t len,loff_t * ppos)238 static ssize_t safesetid_uid_file_write(struct file *file,
239 const char __user *buf,
240 size_t len,
241 loff_t *ppos)
242 {
243 if (!file_ns_capable(file, &init_user_ns, CAP_MAC_ADMIN))
244 return -EPERM;
245
246 if (*ppos != 0)
247 return -EINVAL;
248
249 return handle_policy_update(file, buf, len, UID);
250 }
251
safesetid_gid_file_write(struct file * file,const char __user * buf,size_t len,loff_t * ppos)252 static ssize_t safesetid_gid_file_write(struct file *file,
253 const char __user *buf,
254 size_t len,
255 loff_t *ppos)
256 {
257 if (!file_ns_capable(file, &init_user_ns, CAP_MAC_ADMIN))
258 return -EPERM;
259
260 if (*ppos != 0)
261 return -EINVAL;
262
263 return handle_policy_update(file, buf, len, GID);
264 }
265
safesetid_file_read(struct file * file,char __user * buf,size_t len,loff_t * ppos,struct mutex * policy_update_lock,struct __rcu setid_ruleset * ruleset)266 static ssize_t safesetid_file_read(struct file *file, char __user *buf,
267 size_t len, loff_t *ppos, struct mutex *policy_update_lock, struct __rcu setid_ruleset* ruleset)
268 {
269 ssize_t res = 0;
270 struct setid_ruleset *pol;
271 const char *kbuf;
272
273 mutex_lock(policy_update_lock);
274 pol = rcu_dereference_protected(ruleset, lockdep_is_held(policy_update_lock));
275 if (pol) {
276 kbuf = pol->policy_str;
277 res = simple_read_from_buffer(buf, len, ppos,
278 kbuf, strlen(kbuf));
279 }
280 mutex_unlock(policy_update_lock);
281
282 return res;
283 }
284
safesetid_uid_file_read(struct file * file,char __user * buf,size_t len,loff_t * ppos)285 static ssize_t safesetid_uid_file_read(struct file *file, char __user *buf,
286 size_t len, loff_t *ppos)
287 {
288 return safesetid_file_read(file, buf, len, ppos,
289 &uid_policy_update_lock, safesetid_setuid_rules);
290 }
291
safesetid_gid_file_read(struct file * file,char __user * buf,size_t len,loff_t * ppos)292 static ssize_t safesetid_gid_file_read(struct file *file, char __user *buf,
293 size_t len, loff_t *ppos)
294 {
295 return safesetid_file_read(file, buf, len, ppos,
296 &gid_policy_update_lock, safesetid_setgid_rules);
297 }
298
299
300
301 static const struct file_operations safesetid_uid_file_fops = {
302 .read = safesetid_uid_file_read,
303 .write = safesetid_uid_file_write,
304 };
305
306 static const struct file_operations safesetid_gid_file_fops = {
307 .read = safesetid_gid_file_read,
308 .write = safesetid_gid_file_write,
309 };
310
safesetid_init_securityfs(void)311 static int __init safesetid_init_securityfs(void)
312 {
313 int ret;
314 struct dentry *policy_dir;
315 struct dentry *uid_policy_file;
316 struct dentry *gid_policy_file;
317
318 if (!safesetid_initialized)
319 return 0;
320
321 policy_dir = securityfs_create_dir("safesetid", NULL);
322 if (IS_ERR(policy_dir)) {
323 ret = PTR_ERR(policy_dir);
324 goto error;
325 }
326
327 uid_policy_file = securityfs_create_file("uid_allowlist_policy", 0600,
328 policy_dir, NULL, &safesetid_uid_file_fops);
329 if (IS_ERR(uid_policy_file)) {
330 ret = PTR_ERR(uid_policy_file);
331 goto error;
332 }
333
334 gid_policy_file = securityfs_create_file("gid_allowlist_policy", 0600,
335 policy_dir, NULL, &safesetid_gid_file_fops);
336 if (IS_ERR(gid_policy_file)) {
337 ret = PTR_ERR(gid_policy_file);
338 goto error;
339 }
340
341
342 return 0;
343
344 error:
345 securityfs_remove(policy_dir);
346 return ret;
347 }
348 fs_initcall(safesetid_init_securityfs);
349