summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJan Kara <jack@suse.cz>2016-09-19 11:39:09 -0400
committerJan Kara <jack@suse.cz>2016-09-22 04:55:32 -0400
commit073931017b49d9458aa351605b43a7e34598caef (patch)
tree8c4374e82e5cad92bdb589e5be417f1a94870399
parent5d3ddd84eaefffd23c028bce5610dac8726f71c1 (diff)
posix_acl: Clear SGID bit when setting file permissions
When file permissions are modified via chmod(2) and the user is not in the owning group or capable of CAP_FSETID, the setgid bit is cleared in inode_change_ok(). Setting a POSIX ACL via setxattr(2) sets the file permissions as well as the new ACL, but doesn't clear the setgid bit in a similar way; this allows to bypass the check in chmod(2). Fix that. References: CVE-2016-7097 Reviewed-by: Christoph Hellwig <hch@lst.de> Reviewed-by: Jeff Layton <jlayton@redhat.com> Signed-off-by: Jan Kara <jack@suse.cz> Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
-rw-r--r--fs/9p/acl.c40
-rw-r--r--fs/btrfs/acl.c6
-rw-r--r--fs/ceph/acl.c6
-rw-r--r--fs/ext2/acl.c12
-rw-r--r--fs/ext4/acl.c12
-rw-r--r--fs/f2fs/acl.c6
-rw-r--r--fs/gfs2/acl.c12
-rw-r--r--fs/hfsplus/posix_acl.c4
-rw-r--r--fs/jffs2/acl.c9
-rw-r--r--fs/jfs/acl.c6
-rw-r--r--fs/ocfs2/acl.c10
-rw-r--r--fs/orangefs/acl.c15
-rw-r--r--fs/posix_acl.c31
-rw-r--r--fs/reiserfs/xattr_acl.c8
-rw-r--r--fs/xfs/xfs_acl.c13
-rw-r--r--include/linux/posix_acl.h1
16 files changed, 89 insertions, 102 deletions
diff --git a/fs/9p/acl.c b/fs/9p/acl.c
index 5b6a1743ea17..b3c2cc79c20d 100644
--- a/fs/9p/acl.c
+++ b/fs/9p/acl.c
@@ -276,32 +276,26 @@ static int v9fs_xattr_set_acl(const struct xattr_handler *handler,
276 switch (handler->flags) { 276 switch (handler->flags) {
277 case ACL_TYPE_ACCESS: 277 case ACL_TYPE_ACCESS:
278 if (acl) { 278 if (acl) {
279 umode_t mode = inode->i_mode; 279 struct iattr iattr;
280 retval = posix_acl_equiv_mode(acl, &mode); 280
281 if (retval < 0) 281 retval = posix_acl_update_mode(inode, &iattr.ia_mode, &acl);
282 if (retval)
282 goto err_out; 283 goto err_out;
283 else { 284 if (!acl) {
284 struct iattr iattr; 285 /*
285 if (retval == 0) { 286 * ACL can be represented
286 /* 287 * by the mode bits. So don't
287 * ACL can be represented 288 * update ACL.
288 * by the mode bits. So don't
289 * update ACL.
290 */
291 acl = NULL;
292 value = NULL;
293 size = 0;
294 }
295 /* Updte the mode bits */
296 iattr.ia_mode = ((mode & S_IALLUGO) |
297 (inode->i_mode & ~S_IALLUGO));
298 iattr.ia_valid = ATTR_MODE;
299 /* FIXME should we update ctime ?
300 * What is the following setxattr update the
301 * mode ?
302 */ 289 */
303 v9fs_vfs_setattr_dotl(dentry, &iattr); 290 value = NULL;
291 size = 0;
304 } 292 }
293 iattr.ia_valid = ATTR_MODE;
294 /* FIXME should we update ctime ?
295 * What is the following setxattr update the
296 * mode ?
297 */
298 v9fs_vfs_setattr_dotl(dentry, &iattr);
305 } 299 }
306 break; 300 break;
307 case ACL_TYPE_DEFAULT: 301 case ACL_TYPE_DEFAULT:
diff --git a/fs/btrfs/acl.c b/fs/btrfs/acl.c
index 53bb7af4e5f0..247b8dfaf6e5 100644
--- a/fs/btrfs/acl.c
+++ b/fs/btrfs/acl.c
@@ -79,11 +79,9 @@ static int __btrfs_set_acl(struct btrfs_trans_handle *trans,
79 case ACL_TYPE_ACCESS: 79 case ACL_TYPE_ACCESS:
80 name = XATTR_NAME_POSIX_ACL_ACCESS; 80 name = XATTR_NAME_POSIX_ACL_ACCESS;
81 if (acl) { 81 if (acl) {
82 ret = posix_acl_equiv_mode(acl, &inode->i_mode); 82 ret = posix_acl_update_mode(inode, &inode->i_mode, &acl);
83 if (ret < 0) 83 if (ret)
84 return ret; 84 return ret;
85 if (ret == 0)
86 acl = NULL;
87 } 85 }
88 ret = 0; 86 ret = 0;
89 break; 87 break;
diff --git a/fs/ceph/acl.c b/fs/ceph/acl.c
index 4f67227f69a5..d0b6b342dff9 100644
--- a/fs/ceph/acl.c
+++ b/fs/ceph/acl.c
@@ -95,11 +95,9 @@ int ceph_set_acl(struct inode *inode, struct posix_acl *acl, int type)
95 case ACL_TYPE_ACCESS: 95 case ACL_TYPE_ACCESS:
96 name = XATTR_NAME_POSIX_ACL_ACCESS; 96 name = XATTR_NAME_POSIX_ACL_ACCESS;
97 if (acl) { 97 if (acl) {
98 ret = posix_acl_equiv_mode(acl, &new_mode); 98 ret = posix_acl_update_mode(inode, &new_mode, &acl);
99 if (ret < 0) 99 if (ret)
100 goto out; 100 goto out;
101 if (ret == 0)
102 acl = NULL;
103 } 101 }
104 break; 102 break;
105 case ACL_TYPE_DEFAULT: 103 case ACL_TYPE_DEFAULT:
diff --git a/fs/ext2/acl.c b/fs/ext2/acl.c
index 42f1d1814083..e725aa0890e0 100644
--- a/fs/ext2/acl.c
+++ b/fs/ext2/acl.c
@@ -190,15 +190,11 @@ ext2_set_acl(struct inode *inode, struct posix_acl *acl, int type)
190 case ACL_TYPE_ACCESS: 190 case ACL_TYPE_ACCESS:
191 name_index = EXT2_XATTR_INDEX_POSIX_ACL_ACCESS; 191 name_index = EXT2_XATTR_INDEX_POSIX_ACL_ACCESS;
192 if (acl) { 192 if (acl) {
193 error = posix_acl_equiv_mode(acl, &inode->i_mode); 193 error = posix_acl_update_mode(inode, &inode->i_mode, &acl);
194 if (error < 0) 194 if (error)
195 return error; 195 return error;
196 else { 196 inode->i_ctime = CURRENT_TIME_SEC;
197 inode->i_ctime = CURRENT_TIME_SEC; 197 mark_inode_dirty(inode);
198 mark_inode_dirty(inode);
199 if (error == 0)
200 acl = NULL;
201 }
202 } 198 }
203 break; 199 break;
204 200
diff --git a/fs/ext4/acl.c b/fs/ext4/acl.c
index c6601a476c02..dfa519979038 100644
--- a/fs/ext4/acl.c
+++ b/fs/ext4/acl.c
@@ -193,15 +193,11 @@ __ext4_set_acl(handle_t *handle, struct inode *inode, int type,
193 case ACL_TYPE_ACCESS: 193 case ACL_TYPE_ACCESS:
194 name_index = EXT4_XATTR_INDEX_POSIX_ACL_ACCESS; 194 name_index = EXT4_XATTR_INDEX_POSIX_ACL_ACCESS;
195 if (acl) { 195 if (acl) {
196 error = posix_acl_equiv_mode(acl, &inode->i_mode); 196 error = posix_acl_update_mode(inode, &inode->i_mode, &acl);
197 if (error < 0) 197 if (error)
198 return error; 198 return error;
199 else { 199 inode->i_ctime = ext4_current_time(inode);
200 inode->i_ctime = ext4_current_time(inode); 200 ext4_mark_inode_dirty(handle, inode);
201 ext4_mark_inode_dirty(handle, inode);
202 if (error == 0)
203 acl = NULL;
204 }
205 } 201 }
206 break; 202 break;
207 203
diff --git a/fs/f2fs/acl.c b/fs/f2fs/acl.c
index 4dcc9e28dc5c..31344247ce89 100644
--- a/fs/f2fs/acl.c
+++ b/fs/f2fs/acl.c
@@ -210,12 +210,10 @@ static int __f2fs_set_acl(struct inode *inode, int type,
210 case ACL_TYPE_ACCESS: 210 case ACL_TYPE_ACCESS:
211 name_index = F2FS_XATTR_INDEX_POSIX_ACL_ACCESS; 211 name_index = F2FS_XATTR_INDEX_POSIX_ACL_ACCESS;
212 if (acl) { 212 if (acl) {
213 error = posix_acl_equiv_mode(acl, &inode->i_mode); 213 error = posix_acl_update_mode(inode, &inode->i_mode, &acl);
214 if (error < 0) 214 if (error)
215 return error; 215 return error;
216 set_acl_inode(inode, inode->i_mode); 216 set_acl_inode(inode, inode->i_mode);
217 if (error == 0)
218 acl = NULL;
219 } 217 }
220 break; 218 break;
221 219
diff --git a/fs/gfs2/acl.c b/fs/gfs2/acl.c
index 363ba9e9d8d0..2524807ee070 100644
--- a/fs/gfs2/acl.c
+++ b/fs/gfs2/acl.c
@@ -92,17 +92,11 @@ int __gfs2_set_acl(struct inode *inode, struct posix_acl *acl, int type)
92 if (type == ACL_TYPE_ACCESS) { 92 if (type == ACL_TYPE_ACCESS) {
93 umode_t mode = inode->i_mode; 93 umode_t mode = inode->i_mode;
94 94
95 error = posix_acl_equiv_mode(acl, &mode); 95 error = posix_acl_update_mode(inode, &inode->i_mode, &acl);
96 if (error < 0) 96 if (error)
97 return error; 97 return error;
98 98 if (mode != inode->i_mode)
99 if (error == 0)
100 acl = NULL;
101
102 if (mode != inode->i_mode) {
103 inode->i_mode = mode;
104 mark_inode_dirty(inode); 99 mark_inode_dirty(inode);
105 }
106 } 100 }
107 101
108 if (acl) { 102 if (acl) {
diff --git a/fs/hfsplus/posix_acl.c b/fs/hfsplus/posix_acl.c
index ab7ea2506b4d..9b92058a1240 100644
--- a/fs/hfsplus/posix_acl.c
+++ b/fs/hfsplus/posix_acl.c
@@ -65,8 +65,8 @@ int hfsplus_set_posix_acl(struct inode *inode, struct posix_acl *acl,
65 case ACL_TYPE_ACCESS: 65 case ACL_TYPE_ACCESS:
66 xattr_name = XATTR_NAME_POSIX_ACL_ACCESS; 66 xattr_name = XATTR_NAME_POSIX_ACL_ACCESS;
67 if (acl) { 67 if (acl) {
68 err = posix_acl_equiv_mode(acl, &inode->i_mode); 68 err = posix_acl_update_mode(inode, &inode->i_mode, &acl);
69 if (err < 0) 69 if (err)
70 return err; 70 return err;
71 } 71 }
72 err = 0; 72 err = 0;
diff --git a/fs/jffs2/acl.c b/fs/jffs2/acl.c
index bc2693d56298..2a0f2a1044c1 100644
--- a/fs/jffs2/acl.c
+++ b/fs/jffs2/acl.c
@@ -233,9 +233,10 @@ int jffs2_set_acl(struct inode *inode, struct posix_acl *acl, int type)
233 case ACL_TYPE_ACCESS: 233 case ACL_TYPE_ACCESS:
234 xprefix = JFFS2_XPREFIX_ACL_ACCESS; 234 xprefix = JFFS2_XPREFIX_ACL_ACCESS;
235 if (acl) { 235 if (acl) {
236 umode_t mode = inode->i_mode; 236 umode_t mode;
237 rc = posix_acl_equiv_mode(acl, &mode); 237
238 if (rc < 0) 238 rc = posix_acl_update_mode(inode, &mode, &acl);
239 if (rc)
239 return rc; 240 return rc;
240 if (inode->i_mode != mode) { 241 if (inode->i_mode != mode) {
241 struct iattr attr; 242 struct iattr attr;
@@ -247,8 +248,6 @@ int jffs2_set_acl(struct inode *inode, struct posix_acl *acl, int type)
247 if (rc < 0) 248 if (rc < 0)
248 return rc; 249 return rc;
249 } 250 }
250 if (rc == 0)
251 acl = NULL;
252 } 251 }
253 break; 252 break;
254 case ACL_TYPE_DEFAULT: 253 case ACL_TYPE_DEFAULT:
diff --git a/fs/jfs/acl.c b/fs/jfs/acl.c
index 21fa92ba2c19..3a1e1554a4e3 100644
--- a/fs/jfs/acl.c
+++ b/fs/jfs/acl.c
@@ -78,13 +78,11 @@ static int __jfs_set_acl(tid_t tid, struct inode *inode, int type,
78 case ACL_TYPE_ACCESS: 78 case ACL_TYPE_ACCESS:
79 ea_name = XATTR_NAME_POSIX_ACL_ACCESS; 79 ea_name = XATTR_NAME_POSIX_ACL_ACCESS;
80 if (acl) { 80 if (acl) {
81 rc = posix_acl_equiv_mode(acl, &inode->i_mode); 81 rc = posix_acl_update_mode(inode, &inode->i_mode, &acl);
82 if (rc < 0) 82 if (rc)
83 return rc; 83 return rc;
84 inode->i_ctime = CURRENT_TIME; 84 inode->i_ctime = CURRENT_TIME;
85 mark_inode_dirty(inode); 85 mark_inode_dirty(inode);
86 if (rc == 0)
87 acl = NULL;
88 } 86 }
89 break; 87 break;
90 case ACL_TYPE_DEFAULT: 88 case ACL_TYPE_DEFAULT:
diff --git a/fs/ocfs2/acl.c b/fs/ocfs2/acl.c
index 2162434728c0..164307b99405 100644
--- a/fs/ocfs2/acl.c
+++ b/fs/ocfs2/acl.c
@@ -241,13 +241,11 @@ int ocfs2_set_acl(handle_t *handle,
241 case ACL_TYPE_ACCESS: 241 case ACL_TYPE_ACCESS:
242 name_index = OCFS2_XATTR_INDEX_POSIX_ACL_ACCESS; 242 name_index = OCFS2_XATTR_INDEX_POSIX_ACL_ACCESS;
243 if (acl) { 243 if (acl) {
244 umode_t mode = inode->i_mode; 244 umode_t mode;
245 ret = posix_acl_equiv_mode(acl, &mode);
246 if (ret < 0)
247 return ret;
248 245
249 if (ret == 0) 246 ret = posix_acl_update_mode(inode, &mode, &acl);
250 acl = NULL; 247 if (ret)
248 return ret;
251 249
252 ret = ocfs2_acl_set_mode(inode, di_bh, 250 ret = ocfs2_acl_set_mode(inode, di_bh,
253 handle, mode); 251 handle, mode);
diff --git a/fs/orangefs/acl.c b/fs/orangefs/acl.c
index 28f2195cd798..7a3754488312 100644
--- a/fs/orangefs/acl.c
+++ b/fs/orangefs/acl.c
@@ -73,14 +73,11 @@ int orangefs_set_acl(struct inode *inode, struct posix_acl *acl, int type)
73 case ACL_TYPE_ACCESS: 73 case ACL_TYPE_ACCESS:
74 name = XATTR_NAME_POSIX_ACL_ACCESS; 74 name = XATTR_NAME_POSIX_ACL_ACCESS;
75 if (acl) { 75 if (acl) {
76 umode_t mode = inode->i_mode; 76 umode_t mode;
77 /* 77
78 * can we represent this with the traditional file 78 error = posix_acl_update_mode(inode, &mode, &acl);
79 * mode permission bits? 79 if (error) {
80 */ 80 gossip_err("%s: posix_acl_update_mode err: %d\n",
81 error = posix_acl_equiv_mode(acl, &mode);
82 if (error < 0) {
83 gossip_err("%s: posix_acl_equiv_mode err: %d\n",
84 __func__, 81 __func__,
85 error); 82 error);
86 return error; 83 return error;
@@ -90,8 +87,6 @@ int orangefs_set_acl(struct inode *inode, struct posix_acl *acl, int type)
90 SetModeFlag(orangefs_inode); 87 SetModeFlag(orangefs_inode);
91 inode->i_mode = mode; 88 inode->i_mode = mode;
92 mark_inode_dirty_sync(inode); 89 mark_inode_dirty_sync(inode);
93 if (error == 0)
94 acl = NULL;
95 } 90 }
96 break; 91 break;
97 case ACL_TYPE_DEFAULT: 92 case ACL_TYPE_DEFAULT:
diff --git a/fs/posix_acl.c b/fs/posix_acl.c
index 59d47ab0791a..bfc3ec388322 100644
--- a/fs/posix_acl.c
+++ b/fs/posix_acl.c
@@ -626,6 +626,37 @@ no_mem:
626} 626}
627EXPORT_SYMBOL_GPL(posix_acl_create); 627EXPORT_SYMBOL_GPL(posix_acl_create);
628 628
629/**
630 * posix_acl_update_mode - update mode in set_acl
631 *
632 * Update the file mode when setting an ACL: compute the new file permission
633 * bits based on the ACL. In addition, if the ACL is equivalent to the new
634 * file mode, set *acl to NULL to indicate that no ACL should be set.
635 *
636 * As with chmod, clear the setgit bit if the caller is not in the owning group
637 * or capable of CAP_FSETID (see inode_change_ok).
638 *
639 * Called from set_acl inode operations.
640 */
641int posix_acl_update_mode(struct inode *inode, umode_t *mode_p,
642 struct posix_acl **acl)
643{
644 umode_t mode = inode->i_mode;
645 int error;
646
647 error = posix_acl_equiv_mode(*acl, &mode);
648 if (error < 0)
649 return error;
650 if (error == 0)
651 *acl = NULL;
652 if (!in_group_p(inode->i_gid) &&
653 !capable_wrt_inode_uidgid(inode, CAP_FSETID))
654 mode &= ~S_ISGID;
655 *mode_p = mode;
656 return 0;
657}
658EXPORT_SYMBOL(posix_acl_update_mode);
659
629/* 660/*
630 * Fix up the uids and gids in posix acl extended attributes in place. 661 * Fix up the uids and gids in posix acl extended attributes in place.
631 */ 662 */
diff --git a/fs/reiserfs/xattr_acl.c b/fs/reiserfs/xattr_acl.c
index dbed42f755e0..27376681c640 100644
--- a/fs/reiserfs/xattr_acl.c
+++ b/fs/reiserfs/xattr_acl.c
@@ -242,13 +242,9 @@ __reiserfs_set_acl(struct reiserfs_transaction_handle *th, struct inode *inode,
242 case ACL_TYPE_ACCESS: 242 case ACL_TYPE_ACCESS:
243 name = XATTR_NAME_POSIX_ACL_ACCESS; 243 name = XATTR_NAME_POSIX_ACL_ACCESS;
244 if (acl) { 244 if (acl) {
245 error = posix_acl_equiv_mode(acl, &inode->i_mode); 245 error = posix_acl_update_mode(inode, &inode->i_mode, &acl);
246 if (error < 0) 246 if (error)
247 return error; 247 return error;
248 else {
249 if (error == 0)
250 acl = NULL;
251 }
252 } 248 }
253 break; 249 break;
254 case ACL_TYPE_DEFAULT: 250 case ACL_TYPE_DEFAULT:
diff --git a/fs/xfs/xfs_acl.c b/fs/xfs/xfs_acl.c
index b6e527b8eccb..8a0dec89ca56 100644
--- a/fs/xfs/xfs_acl.c
+++ b/fs/xfs/xfs_acl.c
@@ -257,16 +257,11 @@ xfs_set_acl(struct inode *inode, struct posix_acl *acl, int type)
257 return error; 257 return error;
258 258
259 if (type == ACL_TYPE_ACCESS) { 259 if (type == ACL_TYPE_ACCESS) {
260 umode_t mode = inode->i_mode; 260 umode_t mode;
261 error = posix_acl_equiv_mode(acl, &mode);
262
263 if (error <= 0) {
264 acl = NULL;
265
266 if (error < 0)
267 return error;
268 }
269 261
262 error = posix_acl_update_mode(inode, &mode, &acl);
263 if (error)
264 return error;
270 error = xfs_set_mode(inode, mode); 265 error = xfs_set_mode(inode, mode);
271 if (error) 266 if (error)
272 return error; 267 return error;
diff --git a/include/linux/posix_acl.h b/include/linux/posix_acl.h
index d5d3d741f028..bf1046d0397b 100644
--- a/include/linux/posix_acl.h
+++ b/include/linux/posix_acl.h
@@ -93,6 +93,7 @@ extern int set_posix_acl(struct inode *, int, struct posix_acl *);
93extern int posix_acl_chmod(struct inode *, umode_t); 93extern int posix_acl_chmod(struct inode *, umode_t);
94extern int posix_acl_create(struct inode *, umode_t *, struct posix_acl **, 94extern int posix_acl_create(struct inode *, umode_t *, struct posix_acl **,
95 struct posix_acl **); 95 struct posix_acl **);
96extern int posix_acl_update_mode(struct inode *, umode_t *, struct posix_acl **);
96 97
97extern int simple_set_acl(struct inode *, struct posix_acl *, int); 98extern int simple_set_acl(struct inode *, struct posix_acl *, int);
98extern int simple_acl_create(struct inode *, struct inode *); 99extern int simple_acl_create(struct inode *, struct inode *);