Merge tag 'for-5.5-rc6-tag' of git://git.kernel.org/pub/scm/linux/kernel/git/kdave...
[platform/kernel/linux-rpi.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(policy_update_lock);
23
24 /*
25  * In the case the input buffer contains one or more invalid UIDs, the kuid_t
26  * variables pointed to by @parent and @child will get updated but this
27  * function will return an error.
28  * Contents of @buf may be modified.
29  */
30 static int parse_policy_line(struct file *file, char *buf,
31         struct setuid_rule *rule)
32 {
33         char *child_str;
34         int ret;
35         u32 parsed_parent, parsed_child;
36
37         /* Format of |buf| string should be <UID>:<UID>. */
38         child_str = strchr(buf, ':');
39         if (child_str == NULL)
40                 return -EINVAL;
41         *child_str = '\0';
42         child_str++;
43
44         ret = kstrtou32(buf, 0, &parsed_parent);
45         if (ret)
46                 return ret;
47
48         ret = kstrtou32(child_str, 0, &parsed_child);
49         if (ret)
50                 return ret;
51
52         rule->src_uid = make_kuid(file->f_cred->user_ns, parsed_parent);
53         rule->dst_uid = make_kuid(file->f_cred->user_ns, parsed_child);
54         if (!uid_valid(rule->src_uid) || !uid_valid(rule->dst_uid))
55                 return -EINVAL;
56
57         return 0;
58 }
59
60 static void __release_ruleset(struct rcu_head *rcu)
61 {
62         struct setuid_ruleset *pol =
63                 container_of(rcu, struct setuid_ruleset, rcu);
64         int bucket;
65         struct setuid_rule *rule;
66         struct hlist_node *tmp;
67
68         hash_for_each_safe(pol->rules, bucket, tmp, rule, next)
69                 kfree(rule);
70         kfree(pol->policy_str);
71         kfree(pol);
72 }
73
74 static void release_ruleset(struct setuid_ruleset *pol)
75 {
76         call_rcu(&pol->rcu, __release_ruleset);
77 }
78
79 static void insert_rule(struct setuid_ruleset *pol, struct setuid_rule *rule)
80 {
81         hash_add(pol->rules, &rule->next, __kuid_val(rule->src_uid));
82 }
83
84 static int verify_ruleset(struct setuid_ruleset *pol)
85 {
86         int bucket;
87         struct setuid_rule *rule, *nrule;
88         int res = 0;
89
90         hash_for_each(pol->rules, bucket, rule, next) {
91                 if (_setuid_policy_lookup(pol, rule->dst_uid, INVALID_UID) ==
92                     SIDPOL_DEFAULT) {
93                         pr_warn("insecure policy detected: uid %d is constrained but transitively unconstrained through uid %d\n",
94                                 __kuid_val(rule->src_uid),
95                                 __kuid_val(rule->dst_uid));
96                         res = -EINVAL;
97
98                         /* fix it up */
99                         nrule = kmalloc(sizeof(struct setuid_rule), GFP_KERNEL);
100                         if (!nrule)
101                                 return -ENOMEM;
102                         nrule->src_uid = rule->dst_uid;
103                         nrule->dst_uid = rule->dst_uid;
104                         insert_rule(pol, nrule);
105                 }
106         }
107         return res;
108 }
109
110 static ssize_t handle_policy_update(struct file *file,
111                                     const char __user *ubuf, size_t len)
112 {
113         struct setuid_ruleset *pol;
114         char *buf, *p, *end;
115         int err;
116
117         pol = kmalloc(sizeof(struct setuid_ruleset), GFP_KERNEL);
118         if (!pol)
119                 return -ENOMEM;
120         pol->policy_str = NULL;
121         hash_init(pol->rules);
122
123         p = buf = memdup_user_nul(ubuf, len);
124         if (IS_ERR(buf)) {
125                 err = PTR_ERR(buf);
126                 goto out_free_pol;
127         }
128         pol->policy_str = kstrdup(buf, GFP_KERNEL);
129         if (pol->policy_str == NULL) {
130                 err = -ENOMEM;
131                 goto out_free_buf;
132         }
133
134         /* policy lines, including the last one, end with \n */
135         while (*p != '\0') {
136                 struct setuid_rule *rule;
137
138                 end = strchr(p, '\n');
139                 if (end == NULL) {
140                         err = -EINVAL;
141                         goto out_free_buf;
142                 }
143                 *end = '\0';
144
145                 rule = kmalloc(sizeof(struct setuid_rule), GFP_KERNEL);
146                 if (!rule) {
147                         err = -ENOMEM;
148                         goto out_free_buf;
149                 }
150
151                 err = parse_policy_line(file, p, rule);
152                 if (err)
153                         goto out_free_rule;
154
155                 if (_setuid_policy_lookup(pol, rule->src_uid, rule->dst_uid) ==
156                     SIDPOL_ALLOWED) {
157                         pr_warn("bad policy: duplicate entry\n");
158                         err = -EEXIST;
159                         goto out_free_rule;
160                 }
161
162                 insert_rule(pol, rule);
163                 p = end + 1;
164                 continue;
165
166 out_free_rule:
167                 kfree(rule);
168                 goto out_free_buf;
169         }
170
171         err = verify_ruleset(pol);
172         /* bogus policy falls through after fixing it up */
173         if (err && err != -EINVAL)
174                 goto out_free_buf;
175
176         /*
177          * Everything looks good, apply the policy and release the old one.
178          * What we really want here is an xchg() wrapper for RCU, but since that
179          * doesn't currently exist, just use a spinlock for now.
180          */
181         mutex_lock(&policy_update_lock);
182         pol = rcu_replace_pointer(safesetid_setuid_rules, pol,
183                                   lockdep_is_held(&policy_update_lock));
184         mutex_unlock(&policy_update_lock);
185         err = len;
186
187 out_free_buf:
188         kfree(buf);
189 out_free_pol:
190         if (pol)
191                 release_ruleset(pol);
192         return err;
193 }
194
195 static ssize_t safesetid_file_write(struct file *file,
196                                     const char __user *buf,
197                                     size_t len,
198                                     loff_t *ppos)
199 {
200         if (!file_ns_capable(file, &init_user_ns, CAP_MAC_ADMIN))
201                 return -EPERM;
202
203         if (*ppos != 0)
204                 return -EINVAL;
205
206         return handle_policy_update(file, buf, len);
207 }
208
209 static ssize_t safesetid_file_read(struct file *file, char __user *buf,
210                                    size_t len, loff_t *ppos)
211 {
212         ssize_t res = 0;
213         struct setuid_ruleset *pol;
214         const char *kbuf;
215
216         mutex_lock(&policy_update_lock);
217         pol = rcu_dereference_protected(safesetid_setuid_rules,
218                                         lockdep_is_held(&policy_update_lock));
219         if (pol) {
220                 kbuf = pol->policy_str;
221                 res = simple_read_from_buffer(buf, len, ppos,
222                                               kbuf, strlen(kbuf));
223         }
224         mutex_unlock(&policy_update_lock);
225         return res;
226 }
227
228 static const struct file_operations safesetid_file_fops = {
229         .read = safesetid_file_read,
230         .write = safesetid_file_write,
231 };
232
233 static int __init safesetid_init_securityfs(void)
234 {
235         int ret;
236         struct dentry *policy_dir;
237         struct dentry *policy_file;
238
239         if (!safesetid_initialized)
240                 return 0;
241
242         policy_dir = securityfs_create_dir("safesetid", NULL);
243         if (IS_ERR(policy_dir)) {
244                 ret = PTR_ERR(policy_dir);
245                 goto error;
246         }
247
248         policy_file = securityfs_create_file("whitelist_policy", 0600,
249                         policy_dir, NULL, &safesetid_file_fops);
250         if (IS_ERR(policy_file)) {
251                 ret = PTR_ERR(policy_file);
252                 goto error;
253         }
254
255         return 0;
256
257 error:
258         securityfs_remove(policy_dir);
259         return ret;
260 }
261 fs_initcall(safesetid_init_securityfs);