0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015 #define pr_fmt(fmt) "SafeSetID: " fmt
0016
0017 #include <linux/security.h>
0018 #include <linux/cred.h>
0019
0020 #include "lsm.h"
0021
0022 static DEFINE_MUTEX(uid_policy_update_lock);
0023 static DEFINE_MUTEX(gid_policy_update_lock);
0024
0025
0026
0027
0028
0029
0030
0031 static int parse_policy_line(struct file *file, char *buf,
0032 struct setid_rule *rule)
0033 {
0034 char *child_str;
0035 int ret;
0036 u32 parsed_parent, parsed_child;
0037
0038
0039 child_str = strchr(buf, ':');
0040 if (child_str == NULL)
0041 return -EINVAL;
0042 *child_str = '\0';
0043 child_str++;
0044
0045 ret = kstrtou32(buf, 0, &parsed_parent);
0046 if (ret)
0047 return ret;
0048
0049 ret = kstrtou32(child_str, 0, &parsed_child);
0050 if (ret)
0051 return ret;
0052
0053 if (rule->type == UID){
0054 rule->src_id.uid = make_kuid(file->f_cred->user_ns, parsed_parent);
0055 rule->dst_id.uid = make_kuid(file->f_cred->user_ns, parsed_child);
0056 if (!uid_valid(rule->src_id.uid) || !uid_valid(rule->dst_id.uid))
0057 return -EINVAL;
0058 } else if (rule->type == GID){
0059 rule->src_id.gid = make_kgid(file->f_cred->user_ns, parsed_parent);
0060 rule->dst_id.gid = make_kgid(file->f_cred->user_ns, parsed_child);
0061 if (!gid_valid(rule->src_id.gid) || !gid_valid(rule->dst_id.gid))
0062 return -EINVAL;
0063 } else {
0064
0065 return -EINVAL;
0066 }
0067 return 0;
0068 }
0069
0070 static void __release_ruleset(struct rcu_head *rcu)
0071 {
0072 struct setid_ruleset *pol =
0073 container_of(rcu, struct setid_ruleset, rcu);
0074 int bucket;
0075 struct setid_rule *rule;
0076 struct hlist_node *tmp;
0077
0078 hash_for_each_safe(pol->rules, bucket, tmp, rule, next)
0079 kfree(rule);
0080 kfree(pol->policy_str);
0081 kfree(pol);
0082 }
0083
0084 static void release_ruleset(struct setid_ruleset *pol){
0085 call_rcu(&pol->rcu, __release_ruleset);
0086 }
0087
0088 static void insert_rule(struct setid_ruleset *pol, struct setid_rule *rule)
0089 {
0090 if (pol->type == UID)
0091 hash_add(pol->rules, &rule->next, __kuid_val(rule->src_id.uid));
0092 else if (pol->type == GID)
0093 hash_add(pol->rules, &rule->next, __kgid_val(rule->src_id.gid));
0094 else
0095 return;
0096 }
0097
0098 static int verify_ruleset(struct setid_ruleset *pol)
0099 {
0100 int bucket;
0101 struct setid_rule *rule, *nrule;
0102 int res = 0;
0103
0104 hash_for_each(pol->rules, bucket, rule, next) {
0105 if (_setid_policy_lookup(pol, rule->dst_id, INVALID_ID) == SIDPOL_DEFAULT) {
0106 if (pol->type == UID) {
0107 pr_warn("insecure policy detected: uid %d is constrained but transitively unconstrained through uid %d\n",
0108 __kuid_val(rule->src_id.uid),
0109 __kuid_val(rule->dst_id.uid));
0110 } else if (pol->type == GID) {
0111 pr_warn("insecure policy detected: gid %d is constrained but transitively unconstrained through gid %d\n",
0112 __kgid_val(rule->src_id.gid),
0113 __kgid_val(rule->dst_id.gid));
0114 } else {
0115 res = -EINVAL;
0116 return res;
0117 }
0118 res = -EINVAL;
0119
0120
0121 nrule = kmalloc(sizeof(struct setid_rule), GFP_KERNEL);
0122 if (!nrule)
0123 return -ENOMEM;
0124 if (pol->type == UID){
0125 nrule->src_id.uid = rule->dst_id.uid;
0126 nrule->dst_id.uid = rule->dst_id.uid;
0127 nrule->type = UID;
0128 } else {
0129 nrule->src_id.gid = rule->dst_id.gid;
0130 nrule->dst_id.gid = rule->dst_id.gid;
0131 nrule->type = GID;
0132 }
0133 insert_rule(pol, nrule);
0134 }
0135 }
0136 return res;
0137 }
0138
0139 static ssize_t handle_policy_update(struct file *file,
0140 const char __user *ubuf, size_t len, enum setid_type policy_type)
0141 {
0142 struct setid_ruleset *pol;
0143 char *buf, *p, *end;
0144 int err;
0145
0146 pol = kmalloc(sizeof(struct setid_ruleset), GFP_KERNEL);
0147 if (!pol)
0148 return -ENOMEM;
0149 pol->policy_str = NULL;
0150 pol->type = policy_type;
0151 hash_init(pol->rules);
0152
0153 p = buf = memdup_user_nul(ubuf, len);
0154 if (IS_ERR(buf)) {
0155 err = PTR_ERR(buf);
0156 goto out_free_pol;
0157 }
0158 pol->policy_str = kstrdup(buf, GFP_KERNEL);
0159 if (pol->policy_str == NULL) {
0160 err = -ENOMEM;
0161 goto out_free_buf;
0162 }
0163
0164
0165 while (*p != '\0') {
0166 struct setid_rule *rule;
0167
0168 end = strchr(p, '\n');
0169 if (end == NULL) {
0170 err = -EINVAL;
0171 goto out_free_buf;
0172 }
0173 *end = '\0';
0174
0175 rule = kmalloc(sizeof(struct setid_rule), GFP_KERNEL);
0176 if (!rule) {
0177 err = -ENOMEM;
0178 goto out_free_buf;
0179 }
0180
0181 rule->type = policy_type;
0182 err = parse_policy_line(file, p, rule);
0183 if (err)
0184 goto out_free_rule;
0185
0186 if (_setid_policy_lookup(pol, rule->src_id, rule->dst_id) == SIDPOL_ALLOWED) {
0187 pr_warn("bad policy: duplicate entry\n");
0188 err = -EEXIST;
0189 goto out_free_rule;
0190 }
0191
0192 insert_rule(pol, rule);
0193 p = end + 1;
0194 continue;
0195
0196 out_free_rule:
0197 kfree(rule);
0198 goto out_free_buf;
0199 }
0200
0201 err = verify_ruleset(pol);
0202
0203 if (err && err != -EINVAL)
0204 goto out_free_buf;
0205
0206
0207
0208
0209
0210
0211 if (policy_type == UID) {
0212 mutex_lock(&uid_policy_update_lock);
0213 pol = rcu_replace_pointer(safesetid_setuid_rules, pol,
0214 lockdep_is_held(&uid_policy_update_lock));
0215 mutex_unlock(&uid_policy_update_lock);
0216 } else if (policy_type == GID) {
0217 mutex_lock(&gid_policy_update_lock);
0218 pol = rcu_replace_pointer(safesetid_setgid_rules, pol,
0219 lockdep_is_held(&gid_policy_update_lock));
0220 mutex_unlock(&gid_policy_update_lock);
0221 } else {
0222
0223 pr_warn("error: bad policy type");
0224 }
0225 err = len;
0226
0227 out_free_buf:
0228 kfree(buf);
0229 out_free_pol:
0230 if (pol)
0231 release_ruleset(pol);
0232 return err;
0233 }
0234
0235 static ssize_t safesetid_uid_file_write(struct file *file,
0236 const char __user *buf,
0237 size_t len,
0238 loff_t *ppos)
0239 {
0240 if (!file_ns_capable(file, &init_user_ns, CAP_MAC_ADMIN))
0241 return -EPERM;
0242
0243 if (*ppos != 0)
0244 return -EINVAL;
0245
0246 return handle_policy_update(file, buf, len, UID);
0247 }
0248
0249 static ssize_t safesetid_gid_file_write(struct file *file,
0250 const char __user *buf,
0251 size_t len,
0252 loff_t *ppos)
0253 {
0254 if (!file_ns_capable(file, &init_user_ns, CAP_MAC_ADMIN))
0255 return -EPERM;
0256
0257 if (*ppos != 0)
0258 return -EINVAL;
0259
0260 return handle_policy_update(file, buf, len, GID);
0261 }
0262
0263 static ssize_t safesetid_file_read(struct file *file, char __user *buf,
0264 size_t len, loff_t *ppos, struct mutex *policy_update_lock, struct __rcu setid_ruleset* ruleset)
0265 {
0266 ssize_t res = 0;
0267 struct setid_ruleset *pol;
0268 const char *kbuf;
0269
0270 mutex_lock(policy_update_lock);
0271 pol = rcu_dereference_protected(ruleset, lockdep_is_held(policy_update_lock));
0272 if (pol) {
0273 kbuf = pol->policy_str;
0274 res = simple_read_from_buffer(buf, len, ppos,
0275 kbuf, strlen(kbuf));
0276 }
0277 mutex_unlock(policy_update_lock);
0278
0279 return res;
0280 }
0281
0282 static ssize_t safesetid_uid_file_read(struct file *file, char __user *buf,
0283 size_t len, loff_t *ppos)
0284 {
0285 return safesetid_file_read(file, buf, len, ppos,
0286 &uid_policy_update_lock, safesetid_setuid_rules);
0287 }
0288
0289 static ssize_t safesetid_gid_file_read(struct file *file, char __user *buf,
0290 size_t len, loff_t *ppos)
0291 {
0292 return safesetid_file_read(file, buf, len, ppos,
0293 &gid_policy_update_lock, safesetid_setgid_rules);
0294 }
0295
0296
0297
0298 static const struct file_operations safesetid_uid_file_fops = {
0299 .read = safesetid_uid_file_read,
0300 .write = safesetid_uid_file_write,
0301 };
0302
0303 static const struct file_operations safesetid_gid_file_fops = {
0304 .read = safesetid_gid_file_read,
0305 .write = safesetid_gid_file_write,
0306 };
0307
0308 static int __init safesetid_init_securityfs(void)
0309 {
0310 int ret;
0311 struct dentry *policy_dir;
0312 struct dentry *uid_policy_file;
0313 struct dentry *gid_policy_file;
0314
0315 if (!safesetid_initialized)
0316 return 0;
0317
0318 policy_dir = securityfs_create_dir("safesetid", NULL);
0319 if (IS_ERR(policy_dir)) {
0320 ret = PTR_ERR(policy_dir);
0321 goto error;
0322 }
0323
0324 uid_policy_file = securityfs_create_file("uid_allowlist_policy", 0600,
0325 policy_dir, NULL, &safesetid_uid_file_fops);
0326 if (IS_ERR(uid_policy_file)) {
0327 ret = PTR_ERR(uid_policy_file);
0328 goto error;
0329 }
0330
0331 gid_policy_file = securityfs_create_file("gid_allowlist_policy", 0600,
0332 policy_dir, NULL, &safesetid_gid_file_fops);
0333 if (IS_ERR(gid_policy_file)) {
0334 ret = PTR_ERR(gid_policy_file);
0335 goto error;
0336 }
0337
0338
0339 return 0;
0340
0341 error:
0342 securityfs_remove(policy_dir);
0343 return ret;
0344 }
0345 fs_initcall(safesetid_init_securityfs);