Merge master.kernel.org:/pub/scm/linux/kernel/git/gregkh/aoe-2.6
[sfrench/cifs-2.6.git] / security / selinux / selinuxfs.c
index fdc3823897207284fafe407546b40cdc7edd7899..f5d78365488fc2728782342cdaa5d49fb39d1302 100644 (file)
@@ -15,6 +15,7 @@
 #include <linux/slab.h>
 #include <linux/vmalloc.h>
 #include <linux/fs.h>
+#include <linux/mutex.h>
 #include <linux/init.h>
 #include <linux/string.h>
 #include <linux/security.h>
@@ -44,7 +45,7 @@ static int __init checkreqprot_setup(char *str)
 __setup("checkreqprot=", checkreqprot_setup);
 
 
-static DECLARE_MUTEX(sel_sem);
+static DEFINE_MUTEX(sel_mutex);
 
 /* global data for booleans */
 static struct dentry *bool_dir = NULL;
@@ -230,7 +231,7 @@ static ssize_t sel_write_load(struct file * file, const char __user * buf,
        ssize_t length;
        void *data = NULL;
 
-       down(&sel_sem);
+       mutex_lock(&sel_mutex);
 
        length = task_has_security(current, SECURITY__LOAD_POLICY);
        if (length)
@@ -262,7 +263,7 @@ static ssize_t sel_write_load(struct file * file, const char __user * buf,
        else
                length = count;
 out:
-       up(&sel_sem);
+       mutex_unlock(&sel_mutex);
        vfree(data);
        return length;
 }
@@ -271,46 +272,38 @@ static struct file_operations sel_load_ops = {
        .write          = sel_write_load,
 };
 
-
-static ssize_t sel_write_context(struct file * file, const char __user * buf,
-                                size_t count, loff_t *ppos)
-
+static ssize_t sel_write_context(struct file * file, char *buf, size_t size)
 {
-       char *page;
-       u32 sid;
+       char *canon;
+       u32 sid, len;
        ssize_t length;
 
        length = task_has_security(current, SECURITY__CHECK_CONTEXT);
        if (length)
                return length;
 
-       if (count >= PAGE_SIZE)
-               return -ENOMEM;
-       if (*ppos != 0) {
-               /* No partial writes. */
-               return -EINVAL;
-       }
-       page = (char*)get_zeroed_page(GFP_KERNEL);
-       if (!page)
-               return -ENOMEM;
-       length = -EFAULT;
-       if (copy_from_user(page, buf, count))
-               goto out;
+       length = security_context_to_sid(buf, size, &sid);
+       if (length < 0)
+               return length;
 
-       length = security_context_to_sid(page, count, &sid);
+       length = security_sid_to_context(sid, &canon, &len);
        if (length < 0)
+               return length;
+
+       if (len > SIMPLE_TRANSACTION_LIMIT) {
+               printk(KERN_ERR "%s:  context size (%u) exceeds payload "
+                      "max\n", __FUNCTION__, len);
+               length = -ERANGE;
                goto out;
+       }
 
-       length = count;
+       memcpy(buf, canon, len);
+       length = len;
 out:
-       free_page((unsigned long) page);
+       kfree(canon);
        return length;
 }
 
-static struct file_operations sel_context_ops = {
-       .write          = sel_write_context,
-};
-
 static ssize_t sel_read_checkreqprot(struct file *filp, char __user *buf,
                                     size_t count, loff_t *ppos)
 {
@@ -375,6 +368,7 @@ static ssize_t (*write_op[])(struct file *, char *, size_t) = {
        [SEL_RELABEL] = sel_write_relabel,
        [SEL_USER] = sel_write_user,
        [SEL_MEMBER] = sel_write_member,
+       [SEL_CONTEXT] = sel_write_context,
 };
 
 static ssize_t selinux_transaction_write(struct file *file, const char __user *buf, size_t size, loff_t *pos)
@@ -383,7 +377,7 @@ static ssize_t selinux_transaction_write(struct file *file, const char __user *b
        char *data;
        ssize_t rv;
 
-       if (ino >= sizeof(write_op)/sizeof(write_op[0]) || !write_op[ino])
+       if (ino >= ARRAY_SIZE(write_op) || !write_op[ino])
                return -EINVAL;
 
        data = simple_transaction_get(file, buf, size);
@@ -716,12 +710,11 @@ static ssize_t sel_read_bool(struct file *filep, char __user *buf,
 {
        char *page = NULL;
        ssize_t length;
-       ssize_t end;
        ssize_t ret;
        int cur_enforcing;
        struct inode *inode;
 
-       down(&sel_sem);
+       mutex_lock(&sel_mutex);
 
        ret = -EFAULT;
 
@@ -747,26 +740,9 @@ static ssize_t sel_read_bool(struct file *filep, char __user *buf,
 
        length = scnprintf(page, PAGE_SIZE, "%d %d", cur_enforcing,
                          bool_pending_values[inode->i_ino - BOOL_INO_OFFSET]);
-       if (length < 0) {
-               ret = length;
-               goto out;
-       }
-
-       if (*ppos >= length) {
-               ret = 0;
-               goto out;
-       }
-       if (count + *ppos > length)
-               count = length - *ppos;
-       end = count + *ppos;
-       if (copy_to_user(buf, (char *) page + *ppos, count)) {
-               ret = -EFAULT;
-               goto out;
-       }
-       *ppos = end;
-       ret = count;
+       ret = simple_read_from_buffer(buf, count, ppos, page, length);
 out:
-       up(&sel_sem);
+       mutex_unlock(&sel_mutex);
        if (page)
                free_page((unsigned long)page);
        return ret;
@@ -780,7 +756,7 @@ static ssize_t sel_write_bool(struct file *filep, const char __user *buf,
        int new_value;
        struct inode *inode;
 
-       down(&sel_sem);
+       mutex_lock(&sel_mutex);
 
        length = task_has_security(current, SECURITY__SETBOOL);
        if (length)
@@ -819,7 +795,7 @@ static ssize_t sel_write_bool(struct file *filep, const char __user *buf,
        length = count;
 
 out:
-       up(&sel_sem);
+       mutex_unlock(&sel_mutex);
        if (page)
                free_page((unsigned long) page);
        return length;
@@ -838,7 +814,7 @@ static ssize_t sel_commit_bools_write(struct file *filep,
        ssize_t length = -EFAULT;
        int new_value;
 
-       down(&sel_sem);
+       mutex_lock(&sel_mutex);
 
        length = task_has_security(current, SECURITY__SETBOOL);
        if (length)
@@ -876,7 +852,7 @@ static ssize_t sel_commit_bools_write(struct file *filep,
        length = count;
 
 out:
-       up(&sel_sem);
+       mutex_unlock(&sel_mutex);
        if (page)
                free_page((unsigned long) page);
        return length;
@@ -896,7 +872,7 @@ static void sel_remove_bools(struct dentry *de)
        spin_lock(&dcache_lock);
        node = de->d_subdirs.next;
        while (node != &de->d_subdirs) {
-               struct dentry *d = list_entry(node, struct dentry, d_child);
+               struct dentry *d = list_entry(node, struct dentry, d_u.d_child);
                list_del_init(node);
 
                if (d->d_inode) {
@@ -994,7 +970,7 @@ out:
        return ret;
 err:
        kfree(values);
-       d_genocide(dir);
+       sel_remove_bools(dir);
        ret = -ENOMEM;
        goto out;
 }
@@ -1168,44 +1144,45 @@ static int sel_make_avc_files(struct dentry *dir)
 #endif
        };
 
-       for (i = 0; i < sizeof (files) / sizeof (files[0]); i++) {
+       for (i = 0; i < ARRAY_SIZE(files); i++) {
                struct inode *inode;
                struct dentry *dentry;
 
                dentry = d_alloc_name(dir, files[i].name);
                if (!dentry) {
                        ret = -ENOMEM;
-                       goto err;
+                       goto out;
                }
 
                inode = sel_make_inode(dir->d_sb, S_IFREG|files[i].mode);
                if (!inode) {
                        ret = -ENOMEM;
-                       goto err;
+                       goto out;
                }
                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)
+static int sel_make_dir(struct inode *dir, struct dentry *dentry)
 {
        int ret = 0;
        struct inode *inode;
 
-       inode = sel_make_inode(sb, S_IFDIR | S_IRUGO | S_IXUGO);
+       inode = sel_make_inode(dir->i_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;
+       /* directory inodes start off with i_nlink == 2 (for "." entry) */
+       inode->i_nlink++;
        d_add(dentry, inode);
+       /* bump link count on parent directory, too */
+       dir->i_nlink++;
 out:
        return ret;
 }
@@ -1214,13 +1191,13 @@ static int sel_fill_super(struct super_block * sb, void * data, int silent)
 {
        int ret;
        struct dentry *dentry;
-       struct inode *inode;
+       struct inode *inode, *root_inode;
        struct inode_security_struct *isec;
 
        static struct tree_descr selinux_files[] = {
                [SEL_LOAD] = {"load", &sel_load_ops, S_IRUSR|S_IWUSR},
                [SEL_ENFORCE] = {"enforce", &sel_enforce_ops, S_IRUGO|S_IWUSR},
-               [SEL_CONTEXT] = {"context", &sel_context_ops, S_IRUGO|S_IWUGO},
+               [SEL_CONTEXT] = {"context", &transaction_ops, S_IRUGO|S_IWUGO},
                [SEL_ACCESS] = {"access", &transaction_ops, S_IRUGO|S_IWUGO},
                [SEL_CREATE] = {"create", &transaction_ops, S_IRUGO|S_IWUGO},
                [SEL_RELABEL] = {"relabel", &transaction_ops, S_IRUGO|S_IWUGO},
@@ -1235,30 +1212,33 @@ static int sel_fill_super(struct super_block * sb, void * data, int silent)
        };
        ret = simple_fill_super(sb, SELINUX_MAGIC, selinux_files);
        if (ret)
-               return ret;
+               goto err;
+
+       root_inode = sb->s_root->d_inode;
 
        dentry = d_alloc_name(sb->s_root, BOOL_DIR_NAME);
-       if (!dentry)
-               return -ENOMEM;
+       if (!dentry) {
+               ret = -ENOMEM;
+               goto err;
+       }
 
-       inode = sel_make_inode(sb, S_IFDIR | S_IRUGO | S_IXUGO);
-       if (!inode)
-               goto out;
-       inode->i_op = &simple_dir_inode_operations;
-       inode->i_fop = &simple_dir_operations;
-       d_add(dentry, inode);
-       bool_dir = dentry;
-       ret = sel_make_bools();
+       ret = sel_make_dir(root_inode, dentry);
        if (ret)
-               goto out;
+               goto err;
+
+       bool_dir = dentry;
 
        dentry = d_alloc_name(sb->s_root, NULL_FILE_NAME);
-       if (!dentry)
-               return -ENOMEM;
+       if (!dentry) {
+               ret = -ENOMEM;
+               goto err;
+       }
 
        inode = sel_make_inode(sb, S_IFCHR | S_IRUGO | S_IWUGO);
-       if (!inode)
-               goto out;
+       if (!inode) {
+               ret = -ENOMEM;
+               goto err;
+       }
        isec = (struct inode_security_struct*)inode->i_security;
        isec->sid = SECINITSID_DEVNULL;
        isec->sclass = SECCLASS_CHR_FILE;
@@ -1269,22 +1249,23 @@ static int sel_fill_super(struct super_block * sb, void * data, int silent)
        selinux_null = dentry;
 
        dentry = d_alloc_name(sb->s_root, "avc");
-       if (!dentry)
-               return -ENOMEM;
+       if (!dentry) {
+               ret = -ENOMEM;
+               goto err;
+       }
 
-       ret = sel_make_dir(sb, dentry);
+       ret = sel_make_dir(root_inode, dentry);
        if (ret)
-               goto out;
+               goto err;
 
        ret = sel_make_avc_files(dentry);
        if (ret)
-               goto out;
-
-       return 0;
+               goto err;
 out:
-       dput(dentry);
+       return ret;
+err:
        printk(KERN_ERR "%s:  failed while creating inodes\n", __FUNCTION__);
-       return -ENOMEM;
+       goto out;
 }
 
 static struct super_block *sel_get_sb(struct file_system_type *fs_type,