X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=security%2Fselinux%2Fselinuxfs.c;h=66adccd1c789db103a41fc8e22a8e676ff6ed8de;hb=6a77f38946aaee1cd85eeec6cf4229b204c15071;hp=57c2c8eaf060f720870ba234a37df637aa4d332e;hpb=87fc8d1bb10cd459024a742c6a10961fefcef18f;p=linux-2.6.git diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c index 57c2c8eaf..66adccd1c 100644 --- a/security/selinux/selinuxfs.c +++ b/security/selinux/selinuxfs.c @@ -3,6 +3,7 @@ * Added conditional policy language extensions * * Copyright (C) 2003 - 2004 Tresys Technology, LLC + * Copyright (C) 2004 Red Hat, Inc., James Morris * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 2. @@ -18,6 +19,8 @@ #include #include #include +#include +#include #include #include @@ -51,7 +54,7 @@ int task_has_security(struct task_struct *tsk, return -EACCES; return avc_has_perm(tsec->sid, SECINITSID_SECURITY, - SECCLASS_SECURITY, perms, NULL, NULL); + SECCLASS_SECURITY, perms, NULL); } enum sel_inos { @@ -66,7 +69,9 @@ enum sel_inos { SEL_POLICYVERS, /* return policy version for this kernel */ SEL_COMMIT_BOOLS, /* commit new boolean values */ SEL_MLS, /* return if MLS policy is enabled */ - SEL_DISABLE /* disable SELinux until next reboot */ + SEL_DISABLE, /* disable SELinux until next reboot */ + SEL_AVC, /* AVC management directory */ + SEL_MEMBER, /* compute polyinstantiation membership decision */ }; #define TMPBUFLEN 12 @@ -303,12 +308,14 @@ static ssize_t sel_write_access(struct file * file, char *buf, size_t size); static ssize_t sel_write_create(struct file * file, char *buf, size_t size); static ssize_t sel_write_relabel(struct file * file, char *buf, size_t size); static ssize_t sel_write_user(struct file * file, char *buf, size_t size); +static ssize_t sel_write_member(struct file * file, char *buf, size_t size); static ssize_t (*write_op[])(struct file *, char *, size_t) = { [SEL_ACCESS] = sel_write_access, [SEL_CREATE] = sel_write_create, [SEL_RELABEL] = sel_write_relabel, [SEL_USER] = sel_write_user, + [SEL_MEMBER] = sel_write_member, }; static ssize_t selinux_transaction_write(struct file *file, const char __user *buf, size_t size, loff_t *pos) @@ -578,6 +585,67 @@ out: return length; } +static ssize_t sel_write_member(struct file * file, char *buf, size_t size) +{ + char *scon, *tcon; + u32 ssid, tsid, newsid; + u16 tclass; + ssize_t length; + char *newcon; + u32 len; + + length = task_has_security(current, SECURITY__COMPUTE_MEMBER); + if (length) + return length; + + length = -ENOMEM; + scon = kmalloc(size+1, GFP_KERNEL); + if (!scon) + return length; + memset(scon, 0, size+1); + + tcon = kmalloc(size+1, GFP_KERNEL); + if (!tcon) + goto out; + memset(tcon, 0, size+1); + + length = -EINVAL; + if (sscanf(buf, "%s %s %hu", scon, tcon, &tclass) != 3) + goto out2; + + length = security_context_to_sid(scon, strlen(scon)+1, &ssid); + if (length < 0) + goto out2; + length = security_context_to_sid(tcon, strlen(tcon)+1, &tsid); + if (length < 0) + goto out2; + + length = security_member_sid(ssid, tsid, tclass, &newsid); + if (length < 0) + goto out2; + + length = security_sid_to_context(newsid, &newcon, &len); + if (length < 0) + goto out2; + + if (len > SIMPLE_TRANSACTION_LIMIT) { + printk(KERN_ERR "%s: context size (%u) exceeds payload " + "max\n", __FUNCTION__, len); + length = -ERANGE; + goto out3; + } + + memcpy(buf, newcon, len); + length = len; +out3: + kfree(newcon); +out2: + kfree(tcon); +out: + kfree(scon); + return length; +} + static struct inode *sel_make_inode(struct super_block *sb, int mode) { struct inode *ret = new_inode(sb); @@ -818,7 +886,6 @@ static int sel_make_bools(void) struct dentry *dir = bool_dir; struct inode *inode = NULL; struct inode_security_struct *isec; - struct qstr qname; char **names = NULL, *page; int num; int *values = NULL; @@ -838,10 +905,7 @@ static int sel_make_bools(void) goto out; for (i = 0; i < num; i++) { - qname.name = names[i]; - qname.len = strlen(qname.name); - qname.hash = full_name_hash(qname.name, qname.len); - dentry = d_alloc(dir, &qname); + dentry = d_alloc_name(dir, names[i]); if (!dentry) { ret = -ENOMEM; goto err; @@ -891,12 +955,218 @@ err: struct dentry *selinux_null = NULL; +static ssize_t sel_read_avc_cache_threshold(struct file *filp, char __user *buf, + size_t count, loff_t *ppos) +{ + char tmpbuf[TMPBUFLEN]; + ssize_t length; + + length = scnprintf(tmpbuf, TMPBUFLEN, "%u", avc_cache_threshold); + return simple_read_from_buffer(buf, count, ppos, tmpbuf, length); +} + +static ssize_t sel_write_avc_cache_threshold(struct file * file, + const char __user * buf, + size_t count, loff_t *ppos) + +{ + char *page; + ssize_t ret; + int new_value; + + if (count < 0 || count >= PAGE_SIZE) { + ret = -ENOMEM; + goto out; + } + + if (*ppos != 0) { + /* No partial writes. */ + ret = -EINVAL; + goto out; + } + + page = (char*)get_zeroed_page(GFP_KERNEL); + if (!page) { + ret = -ENOMEM; + goto out; + } + + if (copy_from_user(page, buf, count)) { + ret = -EFAULT; + goto out_free; + } + + if (sscanf(page, "%u", &new_value) != 1) { + ret = -EINVAL; + goto out; + } + + if (new_value != avc_cache_threshold) { + ret = task_has_security(current, SECURITY__SETSECPARAM); + if (ret) + goto out_free; + avc_cache_threshold = new_value; + } + ret = count; +out_free: + free_page((unsigned long)page); +out: + return ret; +} + +static ssize_t sel_read_avc_hash_stats(struct file *filp, char __user *buf, + size_t count, loff_t *ppos) +{ + char *page; + ssize_t ret = 0; + + page = (char *)__get_free_page(GFP_KERNEL); + if (!page) { + ret = -ENOMEM; + goto out; + } + ret = avc_get_hash_stats(page); + if (ret >= 0) + ret = simple_read_from_buffer(buf, count, ppos, page, ret); + free_page((unsigned long)page); +out: + return ret; +} + +static struct file_operations sel_avc_cache_threshold_ops = { + .read = sel_read_avc_cache_threshold, + .write = sel_write_avc_cache_threshold, +}; + +static struct file_operations sel_avc_hash_stats_ops = { + .read = sel_read_avc_hash_stats, +}; + +#ifdef CONFIG_SECURITY_SELINUX_AVC_STATS +static struct avc_cache_stats *sel_avc_get_stat_idx(loff_t *idx) +{ + int cpu; + + for (cpu = *idx; cpu < NR_CPUS; ++cpu) { + if (!cpu_possible(cpu)) + continue; + *idx = cpu + 1; + return &per_cpu(avc_cache_stats, cpu); + } + return NULL; +} + +static void *sel_avc_stats_seq_start(struct seq_file *seq, loff_t *pos) +{ + loff_t n = *pos - 1; + + if (*pos == 0) + return SEQ_START_TOKEN; + + return sel_avc_get_stat_idx(&n); +} + +static void *sel_avc_stats_seq_next(struct seq_file *seq, void *v, loff_t *pos) +{ + return sel_avc_get_stat_idx(pos); +} + +static int sel_avc_stats_seq_show(struct seq_file *seq, void *v) +{ + struct avc_cache_stats *st = v; + + if (v == SEQ_START_TOKEN) + seq_printf(seq, "lookups hits misses allocations reclaims " + "frees\n"); + else + seq_printf(seq, "%u %u %u %u %u %u\n", st->lookups, + st->hits, st->misses, st->allocations, + st->reclaims, st->frees); + return 0; +} + +static void sel_avc_stats_seq_stop(struct seq_file *seq, void *v) +{ } + +static struct seq_operations sel_avc_cache_stats_seq_ops = { + .start = sel_avc_stats_seq_start, + .next = sel_avc_stats_seq_next, + .show = sel_avc_stats_seq_show, + .stop = sel_avc_stats_seq_stop, +}; + +static int sel_open_avc_cache_stats(struct inode *inode, struct file *file) +{ + return seq_open(file, &sel_avc_cache_stats_seq_ops); +} + +static struct file_operations sel_avc_cache_stats_ops = { + .open = sel_open_avc_cache_stats, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; +#endif + +static int sel_make_avc_files(struct dentry *dir) +{ + int i, ret = 0; + static struct tree_descr files[] = { + { "cache_threshold", + &sel_avc_cache_threshold_ops, S_IRUGO|S_IWUSR }, + { "hash_stats", &sel_avc_hash_stats_ops, S_IRUGO }, +#ifdef CONFIG_SECURITY_SELINUX_AVC_STATS + { "cache_stats", &sel_avc_cache_stats_ops, S_IRUGO }, +#endif + }; + + for (i = 0; i < sizeof (files) / sizeof (files[0]); i++) { + struct inode *inode; + struct dentry *dentry; + + dentry = d_alloc_name(dir, files[i].name); + if (!dentry) { + ret = -ENOMEM; + goto err; + } + + inode = sel_make_inode(dir->d_sb, S_IFREG|files[i].mode); + if (!inode) { + ret = -ENOMEM; + goto err; + } + inode->i_fop = files[i].ops; + d_add(dentry, inode); + } +out: + return ret; +err: + d_genocide(dir); + goto out; +} + +static int sel_make_dir(struct super_block *sb, struct dentry *dentry) +{ + int ret = 0; + struct inode *inode; + + inode = sel_make_inode(sb, S_IFDIR | S_IRUGO | S_IXUGO); + if (!inode) { + ret = -ENOMEM; + goto out; + } + inode->i_op = &simple_dir_inode_operations; + inode->i_fop = &simple_dir_operations; + d_add(dentry, inode); +out: + return ret; +} + static int sel_fill_super(struct super_block * sb, void * data, int silent) { int ret; struct dentry *dentry; struct inode *inode; - struct qstr qname; struct inode_security_struct *isec; static struct tree_descr selinux_files[] = { @@ -911,16 +1181,14 @@ static int sel_fill_super(struct super_block * sb, void * data, int silent) [SEL_COMMIT_BOOLS] = {"commit_pending_bools", &sel_commit_bools_ops, S_IWUSR}, [SEL_MLS] = {"mls", &sel_mls_ops, S_IRUGO}, [SEL_DISABLE] = {"disable", &sel_disable_ops, S_IWUSR}, + [SEL_MEMBER] = {"member", &transaction_ops, S_IRUGO|S_IWUGO}, /* last one */ {""} }; ret = simple_fill_super(sb, SELINUX_MAGIC, selinux_files); if (ret) return ret; - qname.name = BOOL_DIR_NAME; - qname.len = strlen(qname.name); - qname.hash = full_name_hash(qname.name, qname.len); - dentry = d_alloc(sb->s_root, &qname); + dentry = d_alloc_name(sb->s_root, BOOL_DIR_NAME); if (!dentry) return -ENOMEM; @@ -935,10 +1203,7 @@ static int sel_fill_super(struct super_block * sb, void * data, int silent) if (ret) goto out; - qname.name = NULL_FILE_NAME; - qname.len = strlen(qname.name); - qname.hash = full_name_hash(qname.name, qname.len); - dentry = d_alloc(sb->s_root, &qname); + dentry = d_alloc_name(sb->s_root, NULL_FILE_NAME); if (!dentry) return -ENOMEM; @@ -954,6 +1219,18 @@ static int sel_fill_super(struct super_block * sb, void * data, int silent) d_add(dentry, inode); selinux_null = dentry; + dentry = d_alloc_name(sb->s_root, "avc"); + if (!dentry) + return -ENOMEM; + + ret = sel_make_dir(sb, dentry); + if (ret) + goto out; + + ret = sel_make_avc_files(dentry); + if (ret) + goto out; + return 0; out: dput(dentry);