diff options
Diffstat (limited to 'fs/attr.c')
-rw-r--r-- | fs/attr.c | 35 |
1 files changed, 23 insertions, 12 deletions
@@ -17,19 +17,22 @@ | |||
17 | #include <linux/ima.h> | 17 | #include <linux/ima.h> |
18 | 18 | ||
19 | /** | 19 | /** |
20 | * inode_change_ok - check if attribute changes to an inode are allowed | 20 | * setattr_prepare - check if attribute changes to a dentry are allowed |
21 | * @inode: inode to check | 21 | * @dentry: dentry to check |
22 | * @attr: attributes to change | 22 | * @attr: attributes to change |
23 | * | 23 | * |
24 | * Check if we are allowed to change the attributes contained in @attr | 24 | * Check if we are allowed to change the attributes contained in @attr |
25 | * in the given inode. This includes the normal unix access permission | 25 | * in the given dentry. This includes the normal unix access permission |
26 | * checks, as well as checks for rlimits and others. | 26 | * checks, as well as checks for rlimits and others. The function also clears |
27 | * SGID bit from mode if user is not allowed to set it. Also file capabilities | ||
28 | * and IMA extended attributes are cleared if ATTR_KILL_PRIV is set. | ||
27 | * | 29 | * |
28 | * Should be called as the first thing in ->setattr implementations, | 30 | * Should be called as the first thing in ->setattr implementations, |
29 | * possibly after taking additional locks. | 31 | * possibly after taking additional locks. |
30 | */ | 32 | */ |
31 | int inode_change_ok(const struct inode *inode, struct iattr *attr) | 33 | int setattr_prepare(struct dentry *dentry, struct iattr *attr) |
32 | { | 34 | { |
35 | struct inode *inode = d_inode(dentry); | ||
33 | unsigned int ia_valid = attr->ia_valid; | 36 | unsigned int ia_valid = attr->ia_valid; |
34 | 37 | ||
35 | /* | 38 | /* |
@@ -44,7 +47,7 @@ int inode_change_ok(const struct inode *inode, struct iattr *attr) | |||
44 | 47 | ||
45 | /* If force is set do it anyway. */ | 48 | /* If force is set do it anyway. */ |
46 | if (ia_valid & ATTR_FORCE) | 49 | if (ia_valid & ATTR_FORCE) |
47 | return 0; | 50 | goto kill_priv; |
48 | 51 | ||
49 | /* Make sure a caller can chown. */ | 52 | /* Make sure a caller can chown. */ |
50 | if ((ia_valid & ATTR_UID) && | 53 | if ((ia_valid & ATTR_UID) && |
@@ -77,9 +80,19 @@ int inode_change_ok(const struct inode *inode, struct iattr *attr) | |||
77 | return -EPERM; | 80 | return -EPERM; |
78 | } | 81 | } |
79 | 82 | ||
83 | kill_priv: | ||
84 | /* User has permission for the change */ | ||
85 | if (ia_valid & ATTR_KILL_PRIV) { | ||
86 | int error; | ||
87 | |||
88 | error = security_inode_killpriv(dentry); | ||
89 | if (error) | ||
90 | return error; | ||
91 | } | ||
92 | |||
80 | return 0; | 93 | return 0; |
81 | } | 94 | } |
82 | EXPORT_SYMBOL(inode_change_ok); | 95 | EXPORT_SYMBOL(setattr_prepare); |
83 | 96 | ||
84 | /** | 97 | /** |
85 | * inode_newsize_ok - may this inode be truncated to a given size | 98 | * inode_newsize_ok - may this inode be truncated to a given size |
@@ -217,13 +230,11 @@ int notify_change(struct dentry * dentry, struct iattr * attr, struct inode **de | |||
217 | if (!(ia_valid & ATTR_MTIME_SET)) | 230 | if (!(ia_valid & ATTR_MTIME_SET)) |
218 | attr->ia_mtime = now; | 231 | attr->ia_mtime = now; |
219 | if (ia_valid & ATTR_KILL_PRIV) { | 232 | if (ia_valid & ATTR_KILL_PRIV) { |
220 | attr->ia_valid &= ~ATTR_KILL_PRIV; | ||
221 | ia_valid &= ~ATTR_KILL_PRIV; | ||
222 | error = security_inode_need_killpriv(dentry); | 233 | error = security_inode_need_killpriv(dentry); |
223 | if (error > 0) | 234 | if (error < 0) |
224 | error = security_inode_killpriv(dentry); | ||
225 | if (error) | ||
226 | return error; | 235 | return error; |
236 | if (error == 0) | ||
237 | ia_valid = attr->ia_valid &= ~ATTR_KILL_PRIV; | ||
227 | } | 238 | } |
228 | 239 | ||
229 | /* | 240 | /* |