diff options
-rw-r--r-- | fs/xattr.c | 14 | ||||
-rw-r--r-- | include/linux/security.h | 11 | ||||
-rw-r--r-- | security/dummy.c | 2 | ||||
-rw-r--r-- | security/selinux/hooks.c | 46 |
4 files changed, 49 insertions, 24 deletions
diff --git a/fs/xattr.c b/fs/xattr.c index 3f9c64bea151..f6e00c0e114f 100644 --- a/fs/xattr.c +++ b/fs/xattr.c | |||
@@ -143,7 +143,7 @@ getxattr(struct dentry *d, char __user *name, void __user *value, size_t size) | |||
143 | if (size) { | 143 | if (size) { |
144 | if (size > XATTR_SIZE_MAX) | 144 | if (size > XATTR_SIZE_MAX) |
145 | size = XATTR_SIZE_MAX; | 145 | size = XATTR_SIZE_MAX; |
146 | kvalue = kmalloc(size, GFP_KERNEL); | 146 | kvalue = kzalloc(size, GFP_KERNEL); |
147 | if (!kvalue) | 147 | if (!kvalue) |
148 | return -ENOMEM; | 148 | return -ENOMEM; |
149 | } | 149 | } |
@@ -154,11 +154,15 @@ getxattr(struct dentry *d, char __user *name, void __user *value, size_t size) | |||
154 | error = -EOPNOTSUPP; | 154 | error = -EOPNOTSUPP; |
155 | if (d->d_inode->i_op && d->d_inode->i_op->getxattr) | 155 | if (d->d_inode->i_op && d->d_inode->i_op->getxattr) |
156 | error = d->d_inode->i_op->getxattr(d, kname, kvalue, size); | 156 | error = d->d_inode->i_op->getxattr(d, kname, kvalue, size); |
157 | else if (!strncmp(kname, XATTR_SECURITY_PREFIX, | 157 | |
158 | sizeof XATTR_SECURITY_PREFIX - 1)) { | 158 | if (!strncmp(kname, XATTR_SECURITY_PREFIX, |
159 | sizeof XATTR_SECURITY_PREFIX - 1)) { | ||
159 | const char *suffix = kname + sizeof XATTR_SECURITY_PREFIX - 1; | 160 | const char *suffix = kname + sizeof XATTR_SECURITY_PREFIX - 1; |
160 | error = security_inode_getsecurity(d->d_inode, suffix, kvalue, | 161 | int rv = security_inode_getsecurity(d->d_inode, suffix, kvalue, |
161 | size); | 162 | size, error); |
163 | /* Security module active: overwrite error value */ | ||
164 | if (rv != -EOPNOTSUPP) | ||
165 | error = rv; | ||
162 | } | 166 | } |
163 | if (error > 0) { | 167 | if (error > 0) { |
164 | if (size && copy_to_user(value, kvalue, error)) | 168 | if (size && copy_to_user(value, kvalue, error)) |
diff --git a/include/linux/security.h b/include/linux/security.h index dac956ed98f0..607ee209ea3b 100644 --- a/include/linux/security.h +++ b/include/linux/security.h | |||
@@ -385,6 +385,9 @@ struct swap_info_struct; | |||
385 | * NULL to request the size of the buffer required. @size indicates | 385 | * NULL to request the size of the buffer required. @size indicates |
386 | * the size of @buffer in bytes. Note that @name is the remainder | 386 | * the size of @buffer in bytes. Note that @name is the remainder |
387 | * of the attribute name after the security. prefix has been removed. | 387 | * of the attribute name after the security. prefix has been removed. |
388 | * @err is the return value from the preceding fs getxattr call, | ||
389 | * and can be used by the security module to determine whether it | ||
390 | * should try and canonicalize the attribute value. | ||
388 | * Return number of bytes used/required on success. | 391 | * Return number of bytes used/required on success. |
389 | * @inode_setsecurity: | 392 | * @inode_setsecurity: |
390 | * Set the security label associated with @name for @inode from the | 393 | * Set the security label associated with @name for @inode from the |
@@ -1091,7 +1094,7 @@ struct security_operations { | |||
1091 | int (*inode_getxattr) (struct dentry *dentry, char *name); | 1094 | int (*inode_getxattr) (struct dentry *dentry, char *name); |
1092 | int (*inode_listxattr) (struct dentry *dentry); | 1095 | int (*inode_listxattr) (struct dentry *dentry); |
1093 | int (*inode_removexattr) (struct dentry *dentry, char *name); | 1096 | int (*inode_removexattr) (struct dentry *dentry, char *name); |
1094 | int (*inode_getsecurity)(struct inode *inode, const char *name, void *buffer, size_t size); | 1097 | int (*inode_getsecurity)(struct inode *inode, const char *name, void *buffer, size_t size, int err); |
1095 | int (*inode_setsecurity)(struct inode *inode, const char *name, const void *value, size_t size, int flags); | 1098 | int (*inode_setsecurity)(struct inode *inode, const char *name, const void *value, size_t size, int flags); |
1096 | int (*inode_listsecurity)(struct inode *inode, char *buffer, size_t buffer_size); | 1099 | int (*inode_listsecurity)(struct inode *inode, char *buffer, size_t buffer_size); |
1097 | 1100 | ||
@@ -1580,11 +1583,11 @@ static inline int security_inode_removexattr (struct dentry *dentry, char *name) | |||
1580 | return security_ops->inode_removexattr (dentry, name); | 1583 | return security_ops->inode_removexattr (dentry, name); |
1581 | } | 1584 | } |
1582 | 1585 | ||
1583 | static inline int security_inode_getsecurity(struct inode *inode, const char *name, void *buffer, size_t size) | 1586 | static inline int security_inode_getsecurity(struct inode *inode, const char *name, void *buffer, size_t size, int err) |
1584 | { | 1587 | { |
1585 | if (unlikely (IS_PRIVATE (inode))) | 1588 | if (unlikely (IS_PRIVATE (inode))) |
1586 | return 0; | 1589 | return 0; |
1587 | return security_ops->inode_getsecurity(inode, name, buffer, size); | 1590 | return security_ops->inode_getsecurity(inode, name, buffer, size, err); |
1588 | } | 1591 | } |
1589 | 1592 | ||
1590 | static inline int security_inode_setsecurity(struct inode *inode, const char *name, const void *value, size_t size, int flags) | 1593 | static inline int security_inode_setsecurity(struct inode *inode, const char *name, const void *value, size_t size, int flags) |
@@ -2222,7 +2225,7 @@ static inline int security_inode_removexattr (struct dentry *dentry, char *name) | |||
2222 | return cap_inode_removexattr(dentry, name); | 2225 | return cap_inode_removexattr(dentry, name); |
2223 | } | 2226 | } |
2224 | 2227 | ||
2225 | static inline int security_inode_getsecurity(struct inode *inode, const char *name, void *buffer, size_t size) | 2228 | static inline int security_inode_getsecurity(struct inode *inode, const char *name, void *buffer, size_t size, int err) |
2226 | { | 2229 | { |
2227 | return -EOPNOTSUPP; | 2230 | return -EOPNOTSUPP; |
2228 | } | 2231 | } |
diff --git a/security/dummy.c b/security/dummy.c index 3d34f3de7e82..2a0337a52d32 100644 --- a/security/dummy.c +++ b/security/dummy.c | |||
@@ -377,7 +377,7 @@ static int dummy_inode_removexattr (struct dentry *dentry, char *name) | |||
377 | return 0; | 377 | return 0; |
378 | } | 378 | } |
379 | 379 | ||
380 | static int dummy_inode_getsecurity(struct inode *inode, const char *name, void *buffer, size_t size) | 380 | static int dummy_inode_getsecurity(struct inode *inode, const char *name, void *buffer, size_t size, int err) |
381 | { | 381 | { |
382 | return -EOPNOTSUPP; | 382 | return -EOPNOTSUPP; |
383 | } | 383 | } |
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 8cd33b2cd865..d9ec85292e1c 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c | |||
@@ -2208,9 +2208,6 @@ static int selinux_inode_getxattr (struct dentry *dentry, char *name) | |||
2208 | struct inode *inode = dentry->d_inode; | 2208 | struct inode *inode = dentry->d_inode; |
2209 | struct superblock_security_struct *sbsec = inode->i_sb->s_security; | 2209 | struct superblock_security_struct *sbsec = inode->i_sb->s_security; |
2210 | 2210 | ||
2211 | if (sbsec->behavior == SECURITY_FS_USE_MNTPOINT) | ||
2212 | return -EOPNOTSUPP; | ||
2213 | |||
2214 | return dentry_has_perm(current, NULL, dentry, FILE__GETATTR); | 2211 | return dentry_has_perm(current, NULL, dentry, FILE__GETATTR); |
2215 | } | 2212 | } |
2216 | 2213 | ||
@@ -2241,33 +2238,54 @@ static int selinux_inode_removexattr (struct dentry *dentry, char *name) | |||
2241 | return -EACCES; | 2238 | return -EACCES; |
2242 | } | 2239 | } |
2243 | 2240 | ||
2244 | static int selinux_inode_getsecurity(struct inode *inode, const char *name, void *buffer, size_t size) | 2241 | /* |
2242 | * Copy the in-core inode security context value to the user. If the | ||
2243 | * getxattr() prior to this succeeded, check to see if we need to | ||
2244 | * canonicalize the value to be finally returned to the user. | ||
2245 | * | ||
2246 | * Permission check is handled by selinux_inode_getxattr hook. | ||
2247 | */ | ||
2248 | static int selinux_inode_getsecurity(struct inode *inode, const char *name, void *buffer, size_t size, int err) | ||
2245 | { | 2249 | { |
2246 | struct inode_security_struct *isec = inode->i_security; | 2250 | struct inode_security_struct *isec = inode->i_security; |
2247 | char *context; | 2251 | char *context; |
2248 | unsigned len; | 2252 | unsigned len; |
2249 | int rc; | 2253 | int rc; |
2250 | 2254 | ||
2251 | /* Permission check handled by selinux_inode_getxattr hook.*/ | 2255 | if (strcmp(name, XATTR_SELINUX_SUFFIX)) { |
2252 | 2256 | rc = -EOPNOTSUPP; | |
2253 | if (strcmp(name, XATTR_SELINUX_SUFFIX)) | 2257 | goto out; |
2254 | return -EOPNOTSUPP; | 2258 | } |
2255 | 2259 | ||
2256 | rc = security_sid_to_context(isec->sid, &context, &len); | 2260 | rc = security_sid_to_context(isec->sid, &context, &len); |
2257 | if (rc) | 2261 | if (rc) |
2258 | return rc; | 2262 | goto out; |
2259 | 2263 | ||
2264 | /* Probe for required buffer size */ | ||
2260 | if (!buffer || !size) { | 2265 | if (!buffer || !size) { |
2261 | kfree(context); | 2266 | rc = len; |
2262 | return len; | 2267 | goto out_free; |
2263 | } | 2268 | } |
2269 | |||
2264 | if (size < len) { | 2270 | if (size < len) { |
2265 | kfree(context); | 2271 | rc = -ERANGE; |
2266 | return -ERANGE; | 2272 | goto out_free; |
2273 | } | ||
2274 | |||
2275 | if (err > 0) { | ||
2276 | if ((len == err) && !(memcmp(context, buffer, len))) { | ||
2277 | /* Don't need to canonicalize value */ | ||
2278 | rc = err; | ||
2279 | goto out_free; | ||
2280 | } | ||
2281 | memset(buffer, 0, size); | ||
2267 | } | 2282 | } |
2268 | memcpy(buffer, context, len); | 2283 | memcpy(buffer, context, len); |
2284 | rc = len; | ||
2285 | out_free: | ||
2269 | kfree(context); | 2286 | kfree(context); |
2270 | return len; | 2287 | out: |
2288 | return rc; | ||
2271 | } | 2289 | } |
2272 | 2290 | ||
2273 | static int selinux_inode_setsecurity(struct inode *inode, const char *name, | 2291 | static int selinux_inode_setsecurity(struct inode *inode, const char *name, |