Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * SafeSetID Linux Security Module
0004  *
0005  * Author: Micah Morton <mortonm@chromium.org>
0006  *
0007  * Copyright (C) 2018 The Chromium OS Authors.
0008  *
0009  * This program is free software; you can redistribute it and/or modify
0010  * it under the terms of the GNU General Public License version 2, as
0011  * published by the Free Software Foundation.
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  * In the case the input buffer contains one or more invalid IDs, the kid_t
0027  * variables pointed to by @parent and @child will get updated but this
0028  * function will return an error.
0029  * Contents of @buf may be modified.
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     /* Format of |buf| string should be <UID>:<UID> or <GID>:<GID> */
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         /* Error, rule->type is an invalid type */
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 /* Error, pol->type is neither UID or GID */
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 { /* pol->type is an invalid type */
0115                 res = -EINVAL;
0116                 return res;
0117             }
0118             res = -EINVAL;
0119 
0120             /* fix it up */
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 { /* pol->type must be GID if we've made it to here */
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     /* policy lines, including the last one, end with \n */
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     /* bogus policy falls through after fixing it up */
0203     if (err && err != -EINVAL)
0204         goto out_free_buf;
0205 
0206     /*
0207      * Everything looks good, apply the policy and release the old one.
0208      * What we really want here is an xchg() wrapper for RCU, but since that
0209      * doesn't currently exist, just use a spinlock for now.
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         /* Error, policy type is neither UID or GID */
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);