sysfs: Add labeling support for sysfs
authorDavid P. Quigley <dpquigl@tycho.nsa.gov>
Wed, 9 Sep 2009 18:25:37 +0000 (14:25 -0400)
committerJames Morris <jmorris@namei.org>
Thu, 10 Sep 2009 00:11:29 +0000 (10:11 +1000)
This patch adds a setxattr handler to the file, directory, and symlink
inode_operations structures for sysfs. The patch uses hooks introduced in the
previous patch to handle the getting and setting of security information for
the sysfs inodes. As was suggested by Eric Biederman the struct iattr in the
sysfs_dirent structure has been replaced by a structure which contains the
iattr, secdata and secdata length to allow the changes to persist in the event
that the inode representing the sysfs_dirent is evicted. Because sysfs only
stores this information when a change is made all the optional data is moved
into one dynamically allocated field.

This patch addresses an issue where SELinux was denying virtd access to the PCI
configuration entries in sysfs. The lack of setxattr handlers for sysfs
required that a single label be assigned to all entries in sysfs. Granting virtd
access to every entry in sysfs is not an acceptable solution so fine grained
labeling of sysfs is required such that individual entries can be labeled
appropriately.

[sds:  Fixed compile-time warnings, coding style, and setting of inode security init flags.]

Signed-off-by: David P. Quigley <dpquigl@tycho.nsa.gov>
Signed-off-by: Stephen D. Smalley <sds@tycho.nsa.gov>
Signed-off-by: James Morris <jmorris@namei.org>
fs/sysfs/dir.c
fs/sysfs/inode.c
fs/sysfs/symlink.c
fs/sysfs/sysfs.h
security/selinux/hooks.c
security/smack/smack_lsm.c

index 14f2d71ea3ce8b7077b0da8774bc554b912cd0fb..0050fc40e8c9f373a3f7c61855870ad7d4a1952c 100644 (file)
@@ -760,6 +760,7 @@ static struct dentry * sysfs_lookup(struct inode *dir, struct dentry *dentry,
 const struct inode_operations sysfs_dir_inode_operations = {
        .lookup         = sysfs_lookup,
        .setattr        = sysfs_setattr,
+       .setxattr       = sysfs_setxattr,
 };
 
 static void remove_dir(struct sysfs_dirent *sd)
index 555f0ff988df1379638d0372ebced2349db25848..2b6a8d9de73dfd0ddba64ba0113a23f6f01fc929 100644 (file)
@@ -18,6 +18,8 @@
 #include <linux/capability.h>
 #include <linux/errno.h>
 #include <linux/sched.h>
+#include <linux/xattr.h>
+#include <linux/security.h>
 #include "sysfs.h"
 
 extern struct super_block * sysfs_sb;
@@ -35,6 +37,7 @@ static struct backing_dev_info sysfs_backing_dev_info = {
 
 static const struct inode_operations sysfs_inode_operations ={
        .setattr        = sysfs_setattr,
+       .setxattr       = sysfs_setxattr,
 };
 
 int __init sysfs_inode_init(void)
@@ -42,18 +45,37 @@ int __init sysfs_inode_init(void)
        return bdi_init(&sysfs_backing_dev_info);
 }
 
+struct sysfs_inode_attrs *sysfs_init_inode_attrs(struct sysfs_dirent *sd)
+{
+       struct sysfs_inode_attrs *attrs;
+       struct iattr *iattrs;
+
+       attrs = kzalloc(sizeof(struct sysfs_inode_attrs), GFP_KERNEL);
+       if (!attrs)
+               return NULL;
+       iattrs = &attrs->ia_iattr;
+
+       /* assign default attributes */
+       iattrs->ia_mode = sd->s_mode;
+       iattrs->ia_uid = 0;
+       iattrs->ia_gid = 0;
+       iattrs->ia_atime = iattrs->ia_mtime = iattrs->ia_ctime = CURRENT_TIME;
+
+       return attrs;
+}
 int sysfs_setattr(struct dentry * dentry, struct iattr * iattr)
 {
        struct inode * inode = dentry->d_inode;
        struct sysfs_dirent * sd = dentry->d_fsdata;
-       struct iattr * sd_iattr;
+       struct sysfs_inode_attrs *sd_attrs;
+       struct iattr *iattrs;
        unsigned int ia_valid = iattr->ia_valid;
        int error;
 
        if (!sd)
                return -EINVAL;
 
-       sd_iattr = sd->s_iattr;
+       sd_attrs = sd->s_iattr;
 
        error = inode_change_ok(inode, iattr);
        if (error)
@@ -65,42 +87,77 @@ int sysfs_setattr(struct dentry * dentry, struct iattr * iattr)
        if (error)
                return error;
 
-       if (!sd_iattr) {
+       if (!sd_attrs) {
                /* setting attributes for the first time, allocate now */
-               sd_iattr = kzalloc(sizeof(struct iattr), GFP_KERNEL);
-               if (!sd_iattr)
+               sd_attrs = sysfs_init_inode_attrs(sd);
+               if (!sd_attrs)
                        return -ENOMEM;
-               /* assign default attributes */
-               sd_iattr->ia_mode = sd->s_mode;
-               sd_iattr->ia_uid = 0;
-               sd_iattr->ia_gid = 0;
-               sd_iattr->ia_atime = sd_iattr->ia_mtime = sd_iattr->ia_ctime = CURRENT_TIME;
-               sd->s_iattr = sd_iattr;
+               sd->s_iattr = sd_attrs;
+       } else {
+               /* attributes were changed at least once in past */
+               iattrs = &sd_attrs->ia_iattr;
+
+               if (ia_valid & ATTR_UID)
+                       iattrs->ia_uid = iattr->ia_uid;
+               if (ia_valid & ATTR_GID)
+                       iattrs->ia_gid = iattr->ia_gid;
+               if (ia_valid & ATTR_ATIME)
+                       iattrs->ia_atime = timespec_trunc(iattr->ia_atime,
+                                       inode->i_sb->s_time_gran);
+               if (ia_valid & ATTR_MTIME)
+                       iattrs->ia_mtime = timespec_trunc(iattr->ia_mtime,
+                                       inode->i_sb->s_time_gran);
+               if (ia_valid & ATTR_CTIME)
+                       iattrs->ia_ctime = timespec_trunc(iattr->ia_ctime,
+                                       inode->i_sb->s_time_gran);
+               if (ia_valid & ATTR_MODE) {
+                       umode_t mode = iattr->ia_mode;
+
+                       if (!in_group_p(inode->i_gid) && !capable(CAP_FSETID))
+                               mode &= ~S_ISGID;
+                       iattrs->ia_mode = sd->s_mode = mode;
+               }
        }
+       return error;
+}
 
-       /* attributes were changed atleast once in past */
-
-       if (ia_valid & ATTR_UID)
-               sd_iattr->ia_uid = iattr->ia_uid;
-       if (ia_valid & ATTR_GID)
-               sd_iattr->ia_gid = iattr->ia_gid;
-       if (ia_valid & ATTR_ATIME)
-               sd_iattr->ia_atime = timespec_trunc(iattr->ia_atime,
-                                               inode->i_sb->s_time_gran);
-       if (ia_valid & ATTR_MTIME)
-               sd_iattr->ia_mtime = timespec_trunc(iattr->ia_mtime,
-                                               inode->i_sb->s_time_gran);
-       if (ia_valid & ATTR_CTIME)
-               sd_iattr->ia_ctime = timespec_trunc(iattr->ia_ctime,
-                                               inode->i_sb->s_time_gran);
-       if (ia_valid & ATTR_MODE) {
-               umode_t mode = iattr->ia_mode;
-
-               if (!in_group_p(inode->i_gid) && !capable(CAP_FSETID))
-                       mode &= ~S_ISGID;
-               sd_iattr->ia_mode = sd->s_mode = mode;
-       }
+int sysfs_setxattr(struct dentry *dentry, const char *name, const void *value,
+               size_t size, int flags)
+{
+       struct sysfs_dirent *sd = dentry->d_fsdata;
+       struct sysfs_inode_attrs *iattrs;
+       void *secdata;
+       int error;
+       u32 secdata_len = 0;
+
+       if (!sd)
+               return -EINVAL;
+       if (!sd->s_iattr)
+               sd->s_iattr = sysfs_init_inode_attrs(sd);
+       if (!sd->s_iattr)
+               return -ENOMEM;
+
+       iattrs = sd->s_iattr;
+
+       if (!strncmp(name, XATTR_SECURITY_PREFIX, XATTR_SECURITY_PREFIX_LEN)) {
+               const char *suffix = name + XATTR_SECURITY_PREFIX_LEN;
+               error = security_inode_setsecurity(dentry->d_inode, suffix,
+                                               value, size, flags);
+               if (error)
+                       goto out;
+               error = security_inode_getsecctx(dentry->d_inode,
+                                               &secdata, &secdata_len);
+               if (error)
+                       goto out;
+               if (iattrs->ia_secdata)
+                       security_release_secctx(iattrs->ia_secdata,
+                                               iattrs->ia_secdata_len);
+               iattrs->ia_secdata = secdata;
+               iattrs->ia_secdata_len = secdata_len;
 
+       } else
+               return -EINVAL;
+out:
        return error;
 }
 
@@ -146,6 +203,7 @@ static int sysfs_count_nlink(struct sysfs_dirent *sd)
 static void sysfs_init_inode(struct sysfs_dirent *sd, struct inode *inode)
 {
        struct bin_attribute *bin_attr;
+       struct sysfs_inode_attrs *iattrs;
 
        inode->i_private = sysfs_get(sd);
        inode->i_mapping->a_ops = &sysfs_aops;
@@ -154,16 +212,20 @@ static void sysfs_init_inode(struct sysfs_dirent *sd, struct inode *inode)
        inode->i_ino = sd->s_ino;
        lockdep_set_class(&inode->i_mutex, &sysfs_inode_imutex_key);
 
-       if (sd->s_iattr) {
+       iattrs = sd->s_iattr;
+       if (iattrs) {
                /* sysfs_dirent has non-default attributes
                 * get them for the new inode from persistent copy
                 * in sysfs_dirent
                 */
-               set_inode_attr(inode, sd->s_iattr);
+               set_inode_attr(inode, &iattrs->ia_iattr);
+               if (iattrs->ia_secdata)
+                       security_inode_notifysecctx(inode,
+                                               iattrs->ia_secdata,
+                                               iattrs->ia_secdata_len);
        } else
                set_default_inode_attr(inode, sd->s_mode);
 
-
        /* initialize inode according to type */
        switch (sysfs_type(sd)) {
        case SYSFS_DIR:
index 1d897ad808e0b1d234c302bc745a3c858848ce6b..c5081ad77026e95689e58fda616dc0784b958044 100644 (file)
@@ -16,6 +16,7 @@
 #include <linux/kobject.h>
 #include <linux/namei.h>
 #include <linux/mutex.h>
+#include <linux/security.h>
 
 #include "sysfs.h"
 
@@ -209,6 +210,7 @@ static void sysfs_put_link(struct dentry *dentry, struct nameidata *nd, void *co
 }
 
 const struct inode_operations sysfs_symlink_inode_operations = {
+       .setxattr = sysfs_setxattr,
        .readlink = generic_readlink,
        .follow_link = sysfs_follow_link,
        .put_link = sysfs_put_link,
index 3fa0d98481e2253bd444c1656a50f96d35a150a7..af4c4e7482ac80cd41d9072fcf1a9150a626694c 100644 (file)
@@ -8,6 +8,8 @@
  * This file is released under the GPLv2.
  */
 
+#include <linux/fs.h>
+
 struct sysfs_open_dirent;
 
 /* type-specific structures for sysfs_dirent->s_* union members */
@@ -31,6 +33,12 @@ struct sysfs_elem_bin_attr {
        struct hlist_head       buffers;
 };
 
+struct sysfs_inode_attrs {
+       struct iattr    ia_iattr;
+       void            *ia_secdata;
+       u32             ia_secdata_len;
+};
+
 /*
  * sysfs_dirent - the building block of sysfs hierarchy.  Each and
  * every sysfs node is represented by single sysfs_dirent.
@@ -56,7 +64,7 @@ struct sysfs_dirent {
        unsigned int            s_flags;
        ino_t                   s_ino;
        umode_t                 s_mode;
-       struct iattr            *s_iattr;
+       struct sysfs_inode_attrs *s_iattr;
 };
 
 #define SD_DEACTIVATED_BIAS            INT_MIN
@@ -148,6 +156,8 @@ static inline void __sysfs_put(struct sysfs_dirent *sd)
 struct inode *sysfs_get_inode(struct sysfs_dirent *sd);
 void sysfs_delete_inode(struct inode *inode);
 int sysfs_setattr(struct dentry *dentry, struct iattr *iattr);
+int sysfs_setxattr(struct dentry *dentry, const char *name, const void *value,
+               size_t size, int flags);
 int sysfs_hash_and_remove(struct sysfs_dirent *dir_sd, const char *name);
 int sysfs_inode_init(void);
 
index 7118be2a74a5ab55fb9e52aa658f9c850bd48991..417f7c9945229175f79842c5a9637af6fd48fc1d 100644 (file)
@@ -448,6 +448,10 @@ static int sb_finish_set_opts(struct super_block *sb)
            sbsec->behavior > ARRAY_SIZE(labeling_behaviors))
                sbsec->flags &= ~SE_SBLABELSUPP;
 
+       /* Special handling for sysfs. Is genfs but also has setxattr handler*/
+       if (strncmp(sb->s_type->name, "sysfs", sizeof("sysfs")) == 0)
+               sbsec->flags |= SE_SBLABELSUPP;
+
        /* Initialize the root inode. */
        rc = inode_doinit_with_dentry(root_inode, root);
 
@@ -2923,6 +2927,7 @@ static int selinux_inode_setsecurity(struct inode *inode, const char *name,
                return rc;
 
        isec->sid = newsid;
+       isec->initialized = 1;
        return 0;
 }
 
index 0b3bb646f90e5e236b6a3c290b23d78a69291cf1..acae7ef4092dd02872b54d7861ad233f8a74978e 100644 (file)
@@ -1666,6 +1666,7 @@ static int smack_inode_setsecurity(struct inode *inode, const char *name,
 
        if (strcmp(name, XATTR_SMACK_SUFFIX) == 0) {
                nsp->smk_inode = sp;
+               nsp->smk_flags |= SMK_INODE_INSTANT;
                return 0;
        }
        /*