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
22static DEFINE_MUTEX(uid_policy_update_lock);
23static 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 */
31static 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
70static 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
84static void release_ruleset(struct setid_ruleset *pol){
85	call_rcu(&pol->rcu, __release_ruleset);
86}
87
88static 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
98static 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
139static 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	pol = kmalloc(sizeof(struct setid_ruleset), GFP_KERNEL);
147	if (!pol)
148		return -ENOMEM;
149	pol->policy_str = NULL;
150	pol->type = policy_type;
151	hash_init(pol->rules);
152
153	p = buf = memdup_user_nul(ubuf, len);
154	if (IS_ERR(buf)) {
155		err = PTR_ERR(buf);
156		goto out_free_pol;
157	}
158	pol->policy_str = kstrdup(buf, GFP_KERNEL);
159	if (pol->policy_str == NULL) {
160		err = -ENOMEM;
161		goto out_free_buf;
162	}
163
164	/* policy lines, including the last one, end with \n */
165	while (*p != '\0') {
166		struct setid_rule *rule;
167
168		end = strchr(p, '\n');
169		if (end == NULL) {
170			err = -EINVAL;
171			goto out_free_buf;
172		}
173		*end = '\0';
174
175		rule = kmalloc(sizeof(struct setid_rule), GFP_KERNEL);
176		if (!rule) {
177			err = -ENOMEM;
178			goto out_free_buf;
179		}
180
181		rule->type = policy_type;
182		err = parse_policy_line(file, p, rule);
183		if (err)
184			goto out_free_rule;
185
186		if (_setid_policy_lookup(pol, rule->src_id, rule->dst_id) == SIDPOL_ALLOWED) {
187			pr_warn("bad policy: duplicate entry\n");
188			err = -EEXIST;
189			goto out_free_rule;
190		}
191
192		insert_rule(pol, rule);
193		p = end + 1;
194		continue;
195
196out_free_rule:
197		kfree(rule);
198		goto out_free_buf;
199	}
200
201	err = verify_ruleset(pol);
202	/* bogus policy falls through after fixing it up */
203	if (err && err != -EINVAL)
204		goto out_free_buf;
205
206	/*
207	 * Everything looks good, apply the policy and release the old one.
208	 * What we really want here is an xchg() wrapper for RCU, but since that
209	 * doesn't currently exist, just use a spinlock for now.
210	 */
211	if (policy_type == UID) {
212		mutex_lock(&uid_policy_update_lock);
213		pol = rcu_replace_pointer(safesetid_setuid_rules, pol,
214					  lockdep_is_held(&uid_policy_update_lock));
215		mutex_unlock(&uid_policy_update_lock);
216	} else if (policy_type == GID) {
217		mutex_lock(&gid_policy_update_lock);
218		pol = rcu_replace_pointer(safesetid_setgid_rules, pol,
219					  lockdep_is_held(&gid_policy_update_lock));
220		mutex_unlock(&gid_policy_update_lock);
221	} else {
222		/* Error, policy type is neither UID or GID */
223		pr_warn("error: bad policy type");
224	}
225	err = len;
226
227out_free_buf:
228	kfree(buf);
229out_free_pol:
230	if (pol)
231		release_ruleset(pol);
232	return err;
233}
234
235static ssize_t safesetid_uid_file_write(struct file *file,
236				    const char __user *buf,
237				    size_t len,
238				    loff_t *ppos)
239{
240	if (!file_ns_capable(file, &init_user_ns, CAP_MAC_ADMIN))
241		return -EPERM;
242
243	if (*ppos != 0)
244		return -EINVAL;
245
246	return handle_policy_update(file, buf, len, UID);
247}
248
249static ssize_t safesetid_gid_file_write(struct file *file,
250				    const char __user *buf,
251				    size_t len,
252				    loff_t *ppos)
253{
254	if (!file_ns_capable(file, &init_user_ns, CAP_MAC_ADMIN))
255		return -EPERM;
256
257	if (*ppos != 0)
258		return -EINVAL;
259
260	return handle_policy_update(file, buf, len, GID);
261}
262
263static ssize_t safesetid_file_read(struct file *file, char __user *buf,
264				   size_t len, loff_t *ppos, struct mutex *policy_update_lock, struct __rcu setid_ruleset* ruleset)
265{
266	ssize_t res = 0;
267	struct setid_ruleset *pol;
268	const char *kbuf;
269
270	mutex_lock(policy_update_lock);
271	pol = rcu_dereference_protected(ruleset, lockdep_is_held(policy_update_lock));
272	if (pol) {
273		kbuf = pol->policy_str;
274		res = simple_read_from_buffer(buf, len, ppos,
275					      kbuf, strlen(kbuf));
276	}
277	mutex_unlock(policy_update_lock);
278
279	return res;
280}
281
282static ssize_t safesetid_uid_file_read(struct file *file, char __user *buf,
283				   size_t len, loff_t *ppos)
284{
285	return safesetid_file_read(file, buf, len, ppos,
286				   &uid_policy_update_lock, safesetid_setuid_rules);
287}
288
289static ssize_t safesetid_gid_file_read(struct file *file, char __user *buf,
290				   size_t len, loff_t *ppos)
291{
292	return safesetid_file_read(file, buf, len, ppos,
293				   &gid_policy_update_lock, safesetid_setgid_rules);
294}
295
296
297
298static const struct file_operations safesetid_uid_file_fops = {
299	.read = safesetid_uid_file_read,
300	.write = safesetid_uid_file_write,
301};
302
303static const struct file_operations safesetid_gid_file_fops = {
304	.read = safesetid_gid_file_read,
305	.write = safesetid_gid_file_write,
306};
307
308static int __init safesetid_init_securityfs(void)
309{
310	int ret;
311	struct dentry *policy_dir;
312	struct dentry *uid_policy_file;
313	struct dentry *gid_policy_file;
314
315	if (!safesetid_initialized)
316		return 0;
317
318	policy_dir = securityfs_create_dir("safesetid", NULL);
319	if (IS_ERR(policy_dir)) {
320		ret = PTR_ERR(policy_dir);
321		goto error;
322	}
323
324	uid_policy_file = securityfs_create_file("uid_allowlist_policy", 0600,
325			policy_dir, NULL, &safesetid_uid_file_fops);
326	if (IS_ERR(uid_policy_file)) {
327		ret = PTR_ERR(uid_policy_file);
328		goto error;
329	}
330
331	gid_policy_file = securityfs_create_file("gid_allowlist_policy", 0600,
332			policy_dir, NULL, &safesetid_gid_file_fops);
333	if (IS_ERR(gid_policy_file)) {
334		ret = PTR_ERR(gid_policy_file);
335		goto error;
336	}
337
338
339	return 0;
340
341error:
342	securityfs_remove(policy_dir);
343	return ret;
344}
345fs_initcall(safesetid_init_securityfs);
346