GNU Linux-libre 6.6.34-gnu
[releases.git] / security / safesetid / securityfs.c
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  */
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
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
84 static void release_ruleset(struct setid_ruleset *pol){
85         call_rcu(&pol->rcu, __release_ruleset);
86 }
87
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
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
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         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
196 out_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
227 out_free_buf:
228         kfree(buf);
229 out_free_pol:
230         if (pol)
231                 release_ruleset(pol);
232         return err;
233 }
234
235 static 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
249 static 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
263 static 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
282 static 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
289 static 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
298 static const struct file_operations safesetid_uid_file_fops = {
299         .read = safesetid_uid_file_read,
300         .write = safesetid_uid_file_write,
301 };
302
303 static const struct file_operations safesetid_gid_file_fops = {
304         .read = safesetid_gid_file_read,
305         .write = safesetid_gid_file_write,
306 };
307
308 static 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
341 error:
342         securityfs_remove(policy_dir);
343         return ret;
344 }
345 fs_initcall(safesetid_init_securityfs);