| From: =?UTF-8?q?Ernesto=20A=2E=20Fern=C3=A1ndez?= |
| <ernesto.mnd.fernandez@gmail.com> |
| Date: Sun, 30 Jul 2017 22:43:41 -0400 |
| Subject: ext4: preserve i_mode if __ext4_set_acl() fails |
| MIME-Version: 1.0 |
| Content-Type: text/plain; charset=UTF-8 |
| Content-Transfer-Encoding: 8bit |
| |
| commit 397e434176bb62bc6068d2210af1d876c6212a7e upstream. |
| |
| When changing a file's acl mask, __ext4_set_acl() will first set the group |
| bits of i_mode to the value of the mask, and only then set the actual |
| extended attribute representing the new acl. |
| |
| If the second part fails (due to lack of space, for example) and the file |
| had no acl attribute to begin with, the system will from now on assume |
| that the mask permission bits are actual group permission bits, potentially |
| granting access to the wrong users. |
| |
| Prevent this by only changing the inode mode after the acl has been set. |
| |
| Signed-off-by: Ernesto A. Fernández <ernesto.mnd.fernandez@gmail.com> |
| Signed-off-by: Theodore Ts'o <tytso@mit.edu> |
| Reviewed-by: Jan Kara <jack@suse.cz> |
| [bwh: Backported to 3.16: keep using ext4_current_time()] |
| Signed-off-by: Ben Hutchings <ben@decadent.org.uk> |
| --- |
| fs/ext4/acl.c | 15 +++++++++++---- |
| 1 file changed, 11 insertions(+), 4 deletions(-) |
| |
| --- a/fs/ext4/acl.c |
| +++ b/fs/ext4/acl.c |
| @@ -196,16 +196,17 @@ __ext4_set_acl(handle_t *handle, struct |
| void *value = NULL; |
| size_t size = 0; |
| int error; |
| + int update_mode = 0; |
| + umode_t mode = inode->i_mode; |
| |
| switch (type) { |
| case ACL_TYPE_ACCESS: |
| name_index = EXT4_XATTR_INDEX_POSIX_ACL_ACCESS; |
| if (acl) { |
| - error = posix_acl_update_mode(inode, &inode->i_mode, &acl); |
| + error = posix_acl_update_mode(inode, &mode, &acl); |
| if (error) |
| return error; |
| - inode->i_ctime = ext4_current_time(inode); |
| - ext4_mark_inode_dirty(handle, inode); |
| + update_mode = 1; |
| } |
| break; |
| |
| @@ -228,8 +229,14 @@ __ext4_set_acl(handle_t *handle, struct |
| value, size, 0); |
| |
| kfree(value); |
| - if (!error) |
| + if (!error) { |
| set_cached_acl(inode, type, acl); |
| + if (update_mode) { |
| + inode->i_mode = mode; |
| + inode->i_ctime = ext4_current_time(inode); |
| + ext4_mark_inode_dirty(handle, inode); |
| + } |
| + } |
| |
| return error; |
| } |