]>
Commit | Line | Data |
---|---|---|
4227f6b8 GKH |
1 | From 073931017b49d9458aa351605b43a7e34598caef Mon Sep 17 00:00:00 2001 |
2 | From: Jan Kara <jack@suse.cz> | |
3 | Date: Mon, 19 Sep 2016 17:39:09 +0200 | |
4 | Subject: posix_acl: Clear SGID bit when setting file permissions | |
5 | ||
6 | From: Jan Kara <jack@suse.cz> | |
7 | ||
8 | commit 073931017b49d9458aa351605b43a7e34598caef upstream. | |
9 | ||
10 | When file permissions are modified via chmod(2) and the user is not in | |
11 | the owning group or capable of CAP_FSETID, the setgid bit is cleared in | |
12 | inode_change_ok(). Setting a POSIX ACL via setxattr(2) sets the file | |
13 | permissions as well as the new ACL, but doesn't clear the setgid bit in | |
14 | a similar way; this allows to bypass the check in chmod(2). Fix that. | |
15 | ||
16 | References: CVE-2016-7097 | |
17 | Reviewed-by: Christoph Hellwig <hch@lst.de> | |
18 | Reviewed-by: Jeff Layton <jlayton@redhat.com> | |
19 | Signed-off-by: Jan Kara <jack@suse.cz> | |
20 | Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com> | |
21 | Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> | |
22 | ||
23 | --- | |
24 | fs/9p/acl.c | 40 +++++++++++++++++----------------------- | |
25 | fs/btrfs/acl.c | 6 ++---- | |
26 | fs/ceph/acl.c | 6 ++---- | |
27 | fs/ext2/acl.c | 12 ++++-------- | |
28 | fs/ext4/acl.c | 12 ++++-------- | |
29 | fs/f2fs/acl.c | 6 ++---- | |
30 | fs/gfs2/acl.c | 12 +++--------- | |
31 | fs/hfsplus/posix_acl.c | 4 ++-- | |
32 | fs/jffs2/acl.c | 9 ++++----- | |
33 | fs/jfs/acl.c | 6 ++---- | |
34 | fs/ocfs2/acl.c | 10 ++++------ | |
35 | fs/posix_acl.c | 31 +++++++++++++++++++++++++++++++ | |
36 | fs/reiserfs/xattr_acl.c | 8 ++------ | |
37 | fs/xfs/xfs_acl.c | 13 ++++--------- | |
38 | include/linux/posix_acl.h | 1 + | |
39 | 15 files changed, 84 insertions(+), 92 deletions(-) | |
40 | ||
41 | --- a/fs/9p/acl.c | |
42 | +++ b/fs/9p/acl.c | |
43 | @@ -282,32 +282,26 @@ static int v9fs_xattr_set_acl(const stru | |
44 | switch (handler->flags) { | |
45 | case ACL_TYPE_ACCESS: | |
46 | if (acl) { | |
47 | - umode_t mode = inode->i_mode; | |
48 | - retval = posix_acl_equiv_mode(acl, &mode); | |
49 | - if (retval < 0) | |
50 | + struct iattr iattr; | |
51 | + | |
52 | + retval = posix_acl_update_mode(inode, &iattr.ia_mode, &acl); | |
53 | + if (retval) | |
54 | goto err_out; | |
55 | - else { | |
56 | - struct iattr iattr; | |
57 | - if (retval == 0) { | |
58 | - /* | |
59 | - * ACL can be represented | |
60 | - * by the mode bits. So don't | |
61 | - * update ACL. | |
62 | - */ | |
63 | - acl = NULL; | |
64 | - value = NULL; | |
65 | - size = 0; | |
66 | - } | |
67 | - /* Updte the mode bits */ | |
68 | - iattr.ia_mode = ((mode & S_IALLUGO) | | |
69 | - (inode->i_mode & ~S_IALLUGO)); | |
70 | - iattr.ia_valid = ATTR_MODE; | |
71 | - /* FIXME should we update ctime ? | |
72 | - * What is the following setxattr update the | |
73 | - * mode ? | |
74 | + if (!acl) { | |
75 | + /* | |
76 | + * ACL can be represented | |
77 | + * by the mode bits. So don't | |
78 | + * update ACL. | |
79 | */ | |
80 | - v9fs_vfs_setattr_dotl(dentry, &iattr); | |
81 | + value = NULL; | |
82 | + size = 0; | |
83 | } | |
84 | + iattr.ia_valid = ATTR_MODE; | |
85 | + /* FIXME should we update ctime ? | |
86 | + * What is the following setxattr update the | |
87 | + * mode ? | |
88 | + */ | |
89 | + v9fs_vfs_setattr_dotl(dentry, &iattr); | |
90 | } | |
91 | break; | |
92 | case ACL_TYPE_DEFAULT: | |
93 | --- a/fs/btrfs/acl.c | |
94 | +++ b/fs/btrfs/acl.c | |
95 | @@ -83,11 +83,9 @@ static int __btrfs_set_acl(struct btrfs_ | |
96 | case ACL_TYPE_ACCESS: | |
97 | name = POSIX_ACL_XATTR_ACCESS; | |
98 | if (acl) { | |
99 | - ret = posix_acl_equiv_mode(acl, &inode->i_mode); | |
100 | - if (ret < 0) | |
101 | + ret = posix_acl_update_mode(inode, &inode->i_mode, &acl); | |
102 | + if (ret) | |
103 | return ret; | |
104 | - if (ret == 0) | |
105 | - acl = NULL; | |
106 | } | |
107 | ret = 0; | |
108 | break; | |
109 | --- a/fs/ceph/acl.c | |
110 | +++ b/fs/ceph/acl.c | |
111 | @@ -94,11 +94,9 @@ int ceph_set_acl(struct inode *inode, st | |
112 | case ACL_TYPE_ACCESS: | |
113 | name = POSIX_ACL_XATTR_ACCESS; | |
114 | if (acl) { | |
115 | - ret = posix_acl_equiv_mode(acl, &new_mode); | |
116 | - if (ret < 0) | |
117 | + ret = posix_acl_update_mode(inode, &new_mode, &acl); | |
118 | + if (ret) | |
119 | goto out; | |
120 | - if (ret == 0) | |
121 | - acl = NULL; | |
122 | } | |
123 | break; | |
124 | case ACL_TYPE_DEFAULT: | |
125 | --- a/fs/ext2/acl.c | |
126 | +++ b/fs/ext2/acl.c | |
127 | @@ -193,15 +193,11 @@ ext2_set_acl(struct inode *inode, struct | |
128 | case ACL_TYPE_ACCESS: | |
129 | name_index = EXT2_XATTR_INDEX_POSIX_ACL_ACCESS; | |
130 | if (acl) { | |
131 | - error = posix_acl_equiv_mode(acl, &inode->i_mode); | |
132 | - if (error < 0) | |
133 | + error = posix_acl_update_mode(inode, &inode->i_mode, &acl); | |
134 | + if (error) | |
135 | return error; | |
136 | - else { | |
137 | - inode->i_ctime = CURRENT_TIME_SEC; | |
138 | - mark_inode_dirty(inode); | |
139 | - if (error == 0) | |
140 | - acl = NULL; | |
141 | - } | |
142 | + inode->i_ctime = CURRENT_TIME_SEC; | |
143 | + mark_inode_dirty(inode); | |
144 | } | |
145 | break; | |
146 | ||
147 | --- a/fs/ext4/acl.c | |
148 | +++ b/fs/ext4/acl.c | |
149 | @@ -196,15 +196,11 @@ __ext4_set_acl(handle_t *handle, struct | |
150 | case ACL_TYPE_ACCESS: | |
151 | name_index = EXT4_XATTR_INDEX_POSIX_ACL_ACCESS; | |
152 | if (acl) { | |
153 | - error = posix_acl_equiv_mode(acl, &inode->i_mode); | |
154 | - if (error < 0) | |
155 | + error = posix_acl_update_mode(inode, &inode->i_mode, &acl); | |
156 | + if (error) | |
157 | return error; | |
158 | - else { | |
159 | - inode->i_ctime = ext4_current_time(inode); | |
160 | - ext4_mark_inode_dirty(handle, inode); | |
161 | - if (error == 0) | |
162 | - acl = NULL; | |
163 | - } | |
164 | + inode->i_ctime = ext4_current_time(inode); | |
165 | + ext4_mark_inode_dirty(handle, inode); | |
166 | } | |
167 | break; | |
168 | ||
169 | --- a/fs/f2fs/acl.c | |
170 | +++ b/fs/f2fs/acl.c | |
171 | @@ -214,12 +214,10 @@ static int __f2fs_set_acl(struct inode * | |
172 | case ACL_TYPE_ACCESS: | |
173 | name_index = F2FS_XATTR_INDEX_POSIX_ACL_ACCESS; | |
174 | if (acl) { | |
175 | - error = posix_acl_equiv_mode(acl, &inode->i_mode); | |
176 | - if (error < 0) | |
177 | + error = posix_acl_update_mode(inode, &inode->i_mode, &acl); | |
178 | + if (error) | |
179 | return error; | |
180 | set_acl_inode(fi, inode->i_mode); | |
181 | - if (error == 0) | |
182 | - acl = NULL; | |
183 | } | |
184 | break; | |
185 | ||
186 | --- a/fs/gfs2/acl.c | |
187 | +++ b/fs/gfs2/acl.c | |
188 | @@ -79,17 +79,11 @@ int gfs2_set_acl(struct inode *inode, st | |
189 | if (type == ACL_TYPE_ACCESS) { | |
190 | umode_t mode = inode->i_mode; | |
191 | ||
192 | - error = posix_acl_equiv_mode(acl, &mode); | |
193 | - if (error < 0) | |
194 | + error = posix_acl_update_mode(inode, &inode->i_mode, &acl); | |
195 | + if (error) | |
196 | return error; | |
197 | - | |
198 | - if (error == 0) | |
199 | - acl = NULL; | |
200 | - | |
201 | - if (mode != inode->i_mode) { | |
202 | - inode->i_mode = mode; | |
203 | + if (mode != inode->i_mode) | |
204 | mark_inode_dirty(inode); | |
205 | - } | |
206 | } | |
207 | ||
208 | if (acl) { | |
209 | --- a/fs/hfsplus/posix_acl.c | |
210 | +++ b/fs/hfsplus/posix_acl.c | |
211 | @@ -68,8 +68,8 @@ int hfsplus_set_posix_acl(struct inode * | |
212 | case ACL_TYPE_ACCESS: | |
213 | xattr_name = POSIX_ACL_XATTR_ACCESS; | |
214 | if (acl) { | |
215 | - err = posix_acl_equiv_mode(acl, &inode->i_mode); | |
216 | - if (err < 0) | |
217 | + err = posix_acl_update_mode(inode, &inode->i_mode, &acl); | |
218 | + if (err) | |
219 | return err; | |
220 | } | |
221 | err = 0; | |
222 | --- a/fs/jffs2/acl.c | |
223 | +++ b/fs/jffs2/acl.c | |
224 | @@ -235,9 +235,10 @@ int jffs2_set_acl(struct inode *inode, s | |
225 | case ACL_TYPE_ACCESS: | |
226 | xprefix = JFFS2_XPREFIX_ACL_ACCESS; | |
227 | if (acl) { | |
228 | - umode_t mode = inode->i_mode; | |
229 | - rc = posix_acl_equiv_mode(acl, &mode); | |
230 | - if (rc < 0) | |
231 | + umode_t mode; | |
232 | + | |
233 | + rc = posix_acl_update_mode(inode, &mode, &acl); | |
234 | + if (rc) | |
235 | return rc; | |
236 | if (inode->i_mode != mode) { | |
237 | struct iattr attr; | |
238 | @@ -249,8 +250,6 @@ int jffs2_set_acl(struct inode *inode, s | |
239 | if (rc < 0) | |
240 | return rc; | |
241 | } | |
242 | - if (rc == 0) | |
243 | - acl = NULL; | |
244 | } | |
245 | break; | |
246 | case ACL_TYPE_DEFAULT: | |
247 | --- a/fs/jfs/acl.c | |
248 | +++ b/fs/jfs/acl.c | |
249 | @@ -84,13 +84,11 @@ static int __jfs_set_acl(tid_t tid, stru | |
250 | case ACL_TYPE_ACCESS: | |
251 | ea_name = POSIX_ACL_XATTR_ACCESS; | |
252 | if (acl) { | |
253 | - rc = posix_acl_equiv_mode(acl, &inode->i_mode); | |
254 | - if (rc < 0) | |
255 | + rc = posix_acl_update_mode(inode, &inode->i_mode, &acl); | |
256 | + if (rc) | |
257 | return rc; | |
258 | inode->i_ctime = CURRENT_TIME; | |
259 | mark_inode_dirty(inode); | |
260 | - if (rc == 0) | |
261 | - acl = NULL; | |
262 | } | |
263 | break; | |
264 | case ACL_TYPE_DEFAULT: | |
265 | --- a/fs/ocfs2/acl.c | |
266 | +++ b/fs/ocfs2/acl.c | |
267 | @@ -241,13 +241,11 @@ int ocfs2_set_acl(handle_t *handle, | |
268 | case ACL_TYPE_ACCESS: | |
269 | name_index = OCFS2_XATTR_INDEX_POSIX_ACL_ACCESS; | |
270 | if (acl) { | |
271 | - umode_t mode = inode->i_mode; | |
272 | - ret = posix_acl_equiv_mode(acl, &mode); | |
273 | - if (ret < 0) | |
274 | - return ret; | |
275 | + umode_t mode; | |
276 | ||
277 | - if (ret == 0) | |
278 | - acl = NULL; | |
279 | + ret = posix_acl_update_mode(inode, &mode, &acl); | |
280 | + if (ret) | |
281 | + return ret; | |
282 | ||
283 | ret = ocfs2_acl_set_mode(inode, di_bh, | |
284 | handle, mode); | |
285 | --- a/fs/posix_acl.c | |
286 | +++ b/fs/posix_acl.c | |
287 | @@ -592,6 +592,37 @@ no_mem: | |
288 | } | |
289 | EXPORT_SYMBOL_GPL(posix_acl_create); | |
290 | ||
291 | +/** | |
292 | + * posix_acl_update_mode - update mode in set_acl | |
293 | + * | |
294 | + * Update the file mode when setting an ACL: compute the new file permission | |
295 | + * bits based on the ACL. In addition, if the ACL is equivalent to the new | |
296 | + * file mode, set *acl to NULL to indicate that no ACL should be set. | |
297 | + * | |
298 | + * As with chmod, clear the setgit bit if the caller is not in the owning group | |
299 | + * or capable of CAP_FSETID (see inode_change_ok). | |
300 | + * | |
301 | + * Called from set_acl inode operations. | |
302 | + */ | |
303 | +int posix_acl_update_mode(struct inode *inode, umode_t *mode_p, | |
304 | + struct posix_acl **acl) | |
305 | +{ | |
306 | + umode_t mode = inode->i_mode; | |
307 | + int error; | |
308 | + | |
309 | + error = posix_acl_equiv_mode(*acl, &mode); | |
310 | + if (error < 0) | |
311 | + return error; | |
312 | + if (error == 0) | |
313 | + *acl = NULL; | |
314 | + if (!in_group_p(inode->i_gid) && | |
315 | + !capable_wrt_inode_uidgid(inode, CAP_FSETID)) | |
316 | + mode &= ~S_ISGID; | |
317 | + *mode_p = mode; | |
318 | + return 0; | |
319 | +} | |
320 | +EXPORT_SYMBOL(posix_acl_update_mode); | |
321 | + | |
322 | /* | |
323 | * Fix up the uids and gids in posix acl extended attributes in place. | |
324 | */ | |
325 | --- a/fs/reiserfs/xattr_acl.c | |
326 | +++ b/fs/reiserfs/xattr_acl.c | |
327 | @@ -246,13 +246,9 @@ __reiserfs_set_acl(struct reiserfs_trans | |
328 | case ACL_TYPE_ACCESS: | |
329 | name = POSIX_ACL_XATTR_ACCESS; | |
330 | if (acl) { | |
331 | - error = posix_acl_equiv_mode(acl, &inode->i_mode); | |
332 | - if (error < 0) | |
333 | + error = posix_acl_update_mode(inode, &inode->i_mode, &acl); | |
334 | + if (error) | |
335 | return error; | |
336 | - else { | |
337 | - if (error == 0) | |
338 | - acl = NULL; | |
339 | - } | |
340 | } | |
341 | break; | |
342 | case ACL_TYPE_DEFAULT: | |
343 | --- a/fs/xfs/xfs_acl.c | |
344 | +++ b/fs/xfs/xfs_acl.c | |
345 | @@ -288,16 +288,11 @@ xfs_set_acl(struct inode *inode, struct | |
346 | return error; | |
347 | ||
348 | if (type == ACL_TYPE_ACCESS) { | |
349 | - umode_t mode = inode->i_mode; | |
350 | - error = posix_acl_equiv_mode(acl, &mode); | |
351 | - | |
352 | - if (error <= 0) { | |
353 | - acl = NULL; | |
354 | - | |
355 | - if (error < 0) | |
356 | - return error; | |
357 | - } | |
358 | + umode_t mode; | |
359 | ||
360 | + error = posix_acl_update_mode(inode, &mode, &acl); | |
361 | + if (error) | |
362 | + return error; | |
363 | error = xfs_set_mode(inode, mode); | |
364 | if (error) | |
365 | return error; | |
366 | --- a/include/linux/posix_acl.h | |
367 | +++ b/include/linux/posix_acl.h | |
368 | @@ -95,6 +95,7 @@ extern int set_posix_acl(struct inode *, | |
369 | extern int posix_acl_chmod(struct inode *, umode_t); | |
370 | extern int posix_acl_create(struct inode *, umode_t *, struct posix_acl **, | |
371 | struct posix_acl **); | |
372 | +extern int posix_acl_update_mode(struct inode *, umode_t *, struct posix_acl **); | |
373 | ||
374 | extern int simple_set_acl(struct inode *, struct posix_acl *, int); | |
375 | extern int simple_acl_create(struct inode *, struct inode *); |