aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid P. Quigley <dpquigl@tycho.nsa.gov>2009-09-09 14:25:37 -0400
committerJames Morris <jmorris@namei.org>2009-09-09 20:11:29 -0400
commitddd29ec6597125c830f7badb608a86c98b936b64 (patch)
treee6df1ef9a635179de78650d006ecb4cd1453ebb1
parent1ee65e37e904b959c24404139f5752edc66319d5 (diff)
sysfs: Add labeling support for sysfs
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>
-rw-r--r--fs/sysfs/dir.c1
-rw-r--r--fs/sysfs/inode.c134
-rw-r--r--fs/sysfs/symlink.c2
-rw-r--r--fs/sysfs/sysfs.h12
-rw-r--r--security/selinux/hooks.c5
-rw-r--r--security/smack/smack_lsm.c1
6 files changed, 118 insertions, 37 deletions
diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c
index 14f2d71ea3ce..0050fc40e8c9 100644
--- a/fs/sysfs/dir.c
+++ b/fs/sysfs/dir.c
@@ -760,6 +760,7 @@ static struct dentry * sysfs_lookup(struct inode *dir, struct dentry *dentry,
760const struct inode_operations sysfs_dir_inode_operations = { 760const struct inode_operations sysfs_dir_inode_operations = {
761 .lookup = sysfs_lookup, 761 .lookup = sysfs_lookup,
762 .setattr = sysfs_setattr, 762 .setattr = sysfs_setattr,
763 .setxattr = sysfs_setxattr,
763}; 764};
764 765
765static void remove_dir(struct sysfs_dirent *sd) 766static void remove_dir(struct sysfs_dirent *sd)
diff --git a/fs/sysfs/inode.c b/fs/sysfs/inode.c
index 555f0ff988df..2b6a8d9de73d 100644
--- a/fs/sysfs/inode.c
+++ b/fs/sysfs/inode.c
@@ -18,6 +18,8 @@
18#include <linux/capability.h> 18#include <linux/capability.h>
19#include <linux/errno.h> 19#include <linux/errno.h>
20#include <linux/sched.h> 20#include <linux/sched.h>
21#include <linux/xattr.h>
22#include <linux/security.h>
21#include "sysfs.h" 23#include "sysfs.h"
22 24
23extern struct super_block * sysfs_sb; 25extern struct super_block * sysfs_sb;
@@ -35,6 +37,7 @@ static struct backing_dev_info sysfs_backing_dev_info = {
35 37
36static const struct inode_operations sysfs_inode_operations ={ 38static const struct inode_operations sysfs_inode_operations ={
37 .setattr = sysfs_setattr, 39 .setattr = sysfs_setattr,
40 .setxattr = sysfs_setxattr,
38}; 41};
39 42
40int __init sysfs_inode_init(void) 43int __init sysfs_inode_init(void)
@@ -42,18 +45,37 @@ int __init sysfs_inode_init(void)
42 return bdi_init(&sysfs_backing_dev_info); 45 return bdi_init(&sysfs_backing_dev_info);
43} 46}
44 47
48struct sysfs_inode_attrs *sysfs_init_inode_attrs(struct sysfs_dirent *sd)
49{
50 struct sysfs_inode_attrs *attrs;
51 struct iattr *iattrs;
52
53 attrs = kzalloc(sizeof(struct sysfs_inode_attrs), GFP_KERNEL);
54 if (!attrs)
55 return NULL;
56 iattrs = &attrs->ia_iattr;
57
58 /* assign default attributes */
59 iattrs->ia_mode = sd->s_mode;
60 iattrs->ia_uid = 0;
61 iattrs->ia_gid = 0;
62 iattrs->ia_atime = iattrs->ia_mtime = iattrs->ia_ctime = CURRENT_TIME;
63
64 return attrs;
65}
45int sysfs_setattr(struct dentry * dentry, struct iattr * iattr) 66int sysfs_setattr(struct dentry * dentry, struct iattr * iattr)
46{ 67{
47 struct inode * inode = dentry->d_inode; 68 struct inode * inode = dentry->d_inode;
48 struct sysfs_dirent * sd = dentry->d_fsdata; 69 struct sysfs_dirent * sd = dentry->d_fsdata;
49 struct iattr * sd_iattr; 70 struct sysfs_inode_attrs *sd_attrs;
71 struct iattr *iattrs;
50 unsigned int ia_valid = iattr->ia_valid; 72 unsigned int ia_valid = iattr->ia_valid;
51 int error; 73 int error;
52 74
53 if (!sd) 75 if (!sd)
54 return -EINVAL; 76 return -EINVAL;
55 77
56 sd_iattr = sd->s_iattr; 78 sd_attrs = sd->s_iattr;
57 79
58 error = inode_change_ok(inode, iattr); 80 error = inode_change_ok(inode, iattr);
59 if (error) 81 if (error)
@@ -65,42 +87,77 @@ int sysfs_setattr(struct dentry * dentry, struct iattr * iattr)
65 if (error) 87 if (error)
66 return error; 88 return error;
67 89
68 if (!sd_iattr) { 90 if (!sd_attrs) {
69 /* setting attributes for the first time, allocate now */ 91 /* setting attributes for the first time, allocate now */
70 sd_iattr = kzalloc(sizeof(struct iattr), GFP_KERNEL); 92 sd_attrs = sysfs_init_inode_attrs(sd);
71 if (!sd_iattr) 93 if (!sd_attrs)
72 return -ENOMEM; 94 return -ENOMEM;
73 /* assign default attributes */ 95 sd->s_iattr = sd_attrs;
74 sd_iattr->ia_mode = sd->s_mode; 96 } else {
75 sd_iattr->ia_uid = 0; 97 /* attributes were changed at least once in past */
76 sd_iattr->ia_gid = 0; 98 iattrs = &sd_attrs->ia_iattr;
77 sd_iattr->ia_atime = sd_iattr->ia_mtime = sd_iattr->ia_ctime = CURRENT_TIME; 99
78 sd->s_iattr = sd_iattr; 100 if (ia_valid & ATTR_UID)
101 iattrs->ia_uid = iattr->ia_uid;
102 if (ia_valid & ATTR_GID)
103 iattrs->ia_gid = iattr->ia_gid;
104 if (ia_valid & ATTR_ATIME)
105 iattrs->ia_atime = timespec_trunc(iattr->ia_atime,
106 inode->i_sb->s_time_gran);
107 if (ia_valid & ATTR_MTIME)
108 iattrs->ia_mtime = timespec_trunc(iattr->ia_mtime,
109 inode->i_sb->s_time_gran);
110 if (ia_valid & ATTR_CTIME)
111 iattrs->ia_ctime = timespec_trunc(iattr->ia_ctime,
112 inode->i_sb->s_time_gran);
113 if (ia_valid & ATTR_MODE) {
114 umode_t mode = iattr->ia_mode;
115
116 if (!in_group_p(inode->i_gid) && !capable(CAP_FSETID))
117 mode &= ~S_ISGID;
118 iattrs->ia_mode = sd->s_mode = mode;
119 }
79 } 120 }
121 return error;
122}
80 123
81 /* attributes were changed atleast once in past */ 124int sysfs_setxattr(struct dentry *dentry, const char *name, const void *value,
82 125 size_t size, int flags)
83 if (ia_valid & ATTR_UID) 126{
84 sd_iattr->ia_uid = iattr->ia_uid; 127 struct sysfs_dirent *sd = dentry->d_fsdata;
85 if (ia_valid & ATTR_GID) 128 struct sysfs_inode_attrs *iattrs;
86 sd_iattr->ia_gid = iattr->ia_gid; 129 void *secdata;
87 if (ia_valid & ATTR_ATIME) 130 int error;
88 sd_iattr->ia_atime = timespec_trunc(iattr->ia_atime, 131 u32 secdata_len = 0;
89 inode->i_sb->s_time_gran); 132
90 if (ia_valid & ATTR_MTIME) 133 if (!sd)
91 sd_iattr->ia_mtime = timespec_trunc(iattr->ia_mtime, 134 return -EINVAL;
92 inode->i_sb->s_time_gran); 135 if (!sd->s_iattr)
93 if (ia_valid & ATTR_CTIME) 136 sd->s_iattr = sysfs_init_inode_attrs(sd);
94 sd_iattr->ia_ctime = timespec_trunc(iattr->ia_ctime, 137 if (!sd->s_iattr)
95 inode->i_sb->s_time_gran); 138 return -ENOMEM;
96 if (ia_valid & ATTR_MODE) { 139
97 umode_t mode = iattr->ia_mode; 140 iattrs = sd->s_iattr;
98 141
99 if (!in_group_p(inode->i_gid) && !capable(CAP_FSETID)) 142 if (!strncmp(name, XATTR_SECURITY_PREFIX, XATTR_SECURITY_PREFIX_LEN)) {
100 mode &= ~S_ISGID; 143 const char *suffix = name + XATTR_SECURITY_PREFIX_LEN;
101 sd_iattr->ia_mode = sd->s_mode = mode; 144 error = security_inode_setsecurity(dentry->d_inode, suffix,
102 } 145 value, size, flags);
146 if (error)
147 goto out;
148 error = security_inode_getsecctx(dentry->d_inode,
149 &secdata, &secdata_len);
150 if (error)
151 goto out;
152 if (iattrs->ia_secdata)
153 security_release_secctx(iattrs->ia_secdata,
154 iattrs->ia_secdata_len);
155 iattrs->ia_secdata = secdata;
156 iattrs->ia_secdata_len = secdata_len;
103 157
158 } else
159 return -EINVAL;
160out:
104 return error; 161 return error;
105} 162}
106 163
@@ -146,6 +203,7 @@ static int sysfs_count_nlink(struct sysfs_dirent *sd)
146static void sysfs_init_inode(struct sysfs_dirent *sd, struct inode *inode) 203static void sysfs_init_inode(struct sysfs_dirent *sd, struct inode *inode)
147{ 204{
148 struct bin_attribute *bin_attr; 205 struct bin_attribute *bin_attr;
206 struct sysfs_inode_attrs *iattrs;
149 207
150 inode->i_private = sysfs_get(sd); 208 inode->i_private = sysfs_get(sd);
151 inode->i_mapping->a_ops = &sysfs_aops; 209 inode->i_mapping->a_ops = &sysfs_aops;
@@ -154,16 +212,20 @@ static void sysfs_init_inode(struct sysfs_dirent *sd, struct inode *inode)
154 inode->i_ino = sd->s_ino; 212 inode->i_ino = sd->s_ino;
155 lockdep_set_class(&inode->i_mutex, &sysfs_inode_imutex_key); 213 lockdep_set_class(&inode->i_mutex, &sysfs_inode_imutex_key);
156 214
157 if (sd->s_iattr) { 215 iattrs = sd->s_iattr;
216 if (iattrs) {
158 /* sysfs_dirent has non-default attributes 217 /* sysfs_dirent has non-default attributes
159 * get them for the new inode from persistent copy 218 * get them for the new inode from persistent copy
160 * in sysfs_dirent 219 * in sysfs_dirent
161 */ 220 */
162 set_inode_attr(inode, sd->s_iattr); 221 set_inode_attr(inode, &iattrs->ia_iattr);
222 if (iattrs->ia_secdata)
223 security_inode_notifysecctx(inode,
224 iattrs->ia_secdata,
225 iattrs->ia_secdata_len);
163 } else 226 } else
164 set_default_inode_attr(inode, sd->s_mode); 227 set_default_inode_attr(inode, sd->s_mode);
165 228
166
167 /* initialize inode according to type */ 229 /* initialize inode according to type */
168 switch (sysfs_type(sd)) { 230 switch (sysfs_type(sd)) {
169 case SYSFS_DIR: 231 case SYSFS_DIR:
diff --git a/fs/sysfs/symlink.c b/fs/sysfs/symlink.c
index 1d897ad808e0..c5081ad77026 100644
--- a/fs/sysfs/symlink.c
+++ b/fs/sysfs/symlink.c
@@ -16,6 +16,7 @@
16#include <linux/kobject.h> 16#include <linux/kobject.h>
17#include <linux/namei.h> 17#include <linux/namei.h>
18#include <linux/mutex.h> 18#include <linux/mutex.h>
19#include <linux/security.h>
19 20
20#include "sysfs.h" 21#include "sysfs.h"
21 22
@@ -209,6 +210,7 @@ static void sysfs_put_link(struct dentry *dentry, struct nameidata *nd, void *co
209} 210}
210 211
211const struct inode_operations sysfs_symlink_inode_operations = { 212const struct inode_operations sysfs_symlink_inode_operations = {
213 .setxattr = sysfs_setxattr,
212 .readlink = generic_readlink, 214 .readlink = generic_readlink,
213 .follow_link = sysfs_follow_link, 215 .follow_link = sysfs_follow_link,
214 .put_link = sysfs_put_link, 216 .put_link = sysfs_put_link,
diff --git a/fs/sysfs/sysfs.h b/fs/sysfs/sysfs.h
index 3fa0d98481e2..af4c4e7482ac 100644
--- a/fs/sysfs/sysfs.h
+++ b/fs/sysfs/sysfs.h
@@ -8,6 +8,8 @@
8 * This file is released under the GPLv2. 8 * This file is released under the GPLv2.
9 */ 9 */
10 10
11#include <linux/fs.h>
12
11struct sysfs_open_dirent; 13struct sysfs_open_dirent;
12 14
13/* type-specific structures for sysfs_dirent->s_* union members */ 15/* type-specific structures for sysfs_dirent->s_* union members */
@@ -31,6 +33,12 @@ struct sysfs_elem_bin_attr {
31 struct hlist_head buffers; 33 struct hlist_head buffers;
32}; 34};
33 35
36struct sysfs_inode_attrs {
37 struct iattr ia_iattr;
38 void *ia_secdata;
39 u32 ia_secdata_len;
40};
41
34/* 42/*
35 * sysfs_dirent - the building block of sysfs hierarchy. Each and 43 * sysfs_dirent - the building block of sysfs hierarchy. Each and
36 * every sysfs node is represented by single sysfs_dirent. 44 * every sysfs node is represented by single sysfs_dirent.
@@ -56,7 +64,7 @@ struct sysfs_dirent {
56 unsigned int s_flags; 64 unsigned int s_flags;
57 ino_t s_ino; 65 ino_t s_ino;
58 umode_t s_mode; 66 umode_t s_mode;
59 struct iattr *s_iattr; 67 struct sysfs_inode_attrs *s_iattr;
60}; 68};
61 69
62#define SD_DEACTIVATED_BIAS INT_MIN 70#define SD_DEACTIVATED_BIAS INT_MIN
@@ -148,6 +156,8 @@ static inline void __sysfs_put(struct sysfs_dirent *sd)
148struct inode *sysfs_get_inode(struct sysfs_dirent *sd); 156struct inode *sysfs_get_inode(struct sysfs_dirent *sd);
149void sysfs_delete_inode(struct inode *inode); 157void sysfs_delete_inode(struct inode *inode);
150int sysfs_setattr(struct dentry *dentry, struct iattr *iattr); 158int sysfs_setattr(struct dentry *dentry, struct iattr *iattr);
159int sysfs_setxattr(struct dentry *dentry, const char *name, const void *value,
160 size_t size, int flags);
151int sysfs_hash_and_remove(struct sysfs_dirent *dir_sd, const char *name); 161int sysfs_hash_and_remove(struct sysfs_dirent *dir_sd, const char *name);
152int sysfs_inode_init(void); 162int sysfs_inode_init(void);
153 163
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 7118be2a74a5..417f7c994522 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -448,6 +448,10 @@ static int sb_finish_set_opts(struct super_block *sb)
448 sbsec->behavior > ARRAY_SIZE(labeling_behaviors)) 448 sbsec->behavior > ARRAY_SIZE(labeling_behaviors))
449 sbsec->flags &= ~SE_SBLABELSUPP; 449 sbsec->flags &= ~SE_SBLABELSUPP;
450 450
451 /* Special handling for sysfs. Is genfs but also has setxattr handler*/
452 if (strncmp(sb->s_type->name, "sysfs", sizeof("sysfs")) == 0)
453 sbsec->flags |= SE_SBLABELSUPP;
454
451 /* Initialize the root inode. */ 455 /* Initialize the root inode. */
452 rc = inode_doinit_with_dentry(root_inode, root); 456 rc = inode_doinit_with_dentry(root_inode, root);
453 457
@@ -2923,6 +2927,7 @@ static int selinux_inode_setsecurity(struct inode *inode, const char *name,
2923 return rc; 2927 return rc;
2924 2928
2925 isec->sid = newsid; 2929 isec->sid = newsid;
2930 isec->initialized = 1;
2926 return 0; 2931 return 0;
2927} 2932}
2928 2933
diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
index 0b3bb646f90e..acae7ef4092d 100644
--- a/security/smack/smack_lsm.c
+++ b/security/smack/smack_lsm.c
@@ -1666,6 +1666,7 @@ static int smack_inode_setsecurity(struct inode *inode, const char *name,
1666 1666
1667 if (strcmp(name, XATTR_SMACK_SUFFIX) == 0) { 1667 if (strcmp(name, XATTR_SMACK_SUFFIX) == 0) {
1668 nsp->smk_inode = sp; 1668 nsp->smk_inode = sp;
1669 nsp->smk_flags |= SMK_INODE_INSTANT;
1669 return 0; 1670 return 0;
1670 } 1671 }
1671 /* 1672 /*