1 // SPDX-License-Identifier: GPL-2.0
3 * SafeSetID Linux Security Module
5 * Author: Micah Morton <mortonm@chromium.org>
7 * Copyright (C) 2018 The Chromium OS Authors.
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.
15 #define pr_fmt(fmt) "SafeSetID: " fmt
17 #include <linux/security.h>
18 #include <linux/cred.h>
22 static DEFINE_MUTEX(policy_update_lock);
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.
30 static int parse_policy_line(struct file *file, char *buf,
31 struct setuid_rule *rule)
35 u32 parsed_parent, parsed_child;
37 /* Format of |buf| string should be <UID>:<UID>. */
38 child_str = strchr(buf, ':');
39 if (child_str == NULL)
44 ret = kstrtou32(buf, 0, &parsed_parent);
48 ret = kstrtou32(child_str, 0, &parsed_child);
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))
60 static void __release_ruleset(struct rcu_head *rcu)
62 struct setuid_ruleset *pol =
63 container_of(rcu, struct setuid_ruleset, rcu);
65 struct setuid_rule *rule;
66 struct hlist_node *tmp;
68 hash_for_each_safe(pol->rules, bucket, tmp, rule, next)
70 kfree(pol->policy_str);
74 static void release_ruleset(struct setuid_ruleset *pol)
76 call_rcu(&pol->rcu, __release_ruleset);
79 static void insert_rule(struct setuid_ruleset *pol, struct setuid_rule *rule)
81 hash_add(pol->rules, &rule->next, __kuid_val(rule->src_uid));
84 static int verify_ruleset(struct setuid_ruleset *pol)
87 struct setuid_rule *rule, *nrule;
90 hash_for_each(pol->rules, bucket, rule, next) {
91 if (_setuid_policy_lookup(pol, rule->dst_uid, INVALID_UID) ==
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));
99 nrule = kmalloc(sizeof(struct setuid_rule), GFP_KERNEL);
102 nrule->src_uid = rule->dst_uid;
103 nrule->dst_uid = rule->dst_uid;
104 insert_rule(pol, nrule);
110 static ssize_t handle_policy_update(struct file *file,
111 const char __user *ubuf, size_t len)
113 struct setuid_ruleset *pol;
117 pol = kmalloc(sizeof(struct setuid_ruleset), GFP_KERNEL);
120 pol->policy_str = NULL;
121 hash_init(pol->rules);
123 p = buf = memdup_user_nul(ubuf, len);
128 pol->policy_str = kstrdup(buf, GFP_KERNEL);
129 if (pol->policy_str == NULL) {
134 /* policy lines, including the last one, end with \n */
136 struct setuid_rule *rule;
138 end = strchr(p, '\n');
145 rule = kmalloc(sizeof(struct setuid_rule), GFP_KERNEL);
151 err = parse_policy_line(file, p, rule);
155 if (_setuid_policy_lookup(pol, rule->src_uid, rule->dst_uid) ==
157 pr_warn("bad policy: duplicate entry\n");
162 insert_rule(pol, rule);
171 err = verify_ruleset(pol);
172 /* bogus policy falls through after fixing it up */
173 if (err && err != -EINVAL)
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.
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);
191 release_ruleset(pol);
195 static ssize_t safesetid_file_write(struct file *file,
196 const char __user *buf,
200 if (!file_ns_capable(file, &init_user_ns, CAP_MAC_ADMIN))
206 return handle_policy_update(file, buf, len);
209 static ssize_t safesetid_file_read(struct file *file, char __user *buf,
210 size_t len, loff_t *ppos)
213 struct setuid_ruleset *pol;
216 mutex_lock(&policy_update_lock);
217 pol = rcu_dereference_protected(safesetid_setuid_rules,
218 lockdep_is_held(&policy_update_lock));
220 kbuf = pol->policy_str;
221 res = simple_read_from_buffer(buf, len, ppos,
224 mutex_unlock(&policy_update_lock);
228 static const struct file_operations safesetid_file_fops = {
229 .read = safesetid_file_read,
230 .write = safesetid_file_write,
233 static int __init safesetid_init_securityfs(void)
236 struct dentry *policy_dir;
237 struct dentry *policy_file;
239 if (!safesetid_initialized)
242 policy_dir = securityfs_create_dir("safesetid", NULL);
243 if (IS_ERR(policy_dir)) {
244 ret = PTR_ERR(policy_dir);
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);
258 securityfs_remove(policy_dir);
261 fs_initcall(safesetid_init_securityfs);