cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

securityfs.c (8821B)


      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
     22static DEFINE_MUTEX(uid_policy_update_lock);
     23static 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 */
     31static 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
     70static 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
     84static void release_ruleset(struct setid_ruleset *pol){
     85	call_rcu(&pol->rcu, __release_ruleset);
     86}
     87
     88static 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
     98static 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
    139static 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
    196out_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
    227out_free_buf:
    228	kfree(buf);
    229out_free_pol:
    230	if (pol)
    231		release_ruleset(pol);
    232	return err;
    233}
    234
    235static 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
    249static 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
    263static 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
    282static 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
    289static 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
    298static const struct file_operations safesetid_uid_file_fops = {
    299	.read = safesetid_uid_file_read,
    300	.write = safesetid_uid_file_write,
    301};
    302
    303static const struct file_operations safesetid_gid_file_fops = {
    304	.read = safesetid_gid_file_read,
    305	.write = safesetid_gid_file_write,
    306};
    307
    308static 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
    341error:
    342	securityfs_remove(policy_dir);
    343	return ret;
    344}
    345fs_initcall(safesetid_init_securityfs);