Skip to content

Commit 2f221d6

Browse files
author
Christian Brauner
committed
attr: handle idmapped mounts
When file attributes are changed most filesystems rely on the setattr_prepare(), setattr_copy(), and notify_change() helpers for initialization and permission checking. Let them handle idmapped mounts. If the inode is accessed through an idmapped mount map it into the mount's user namespace. Afterwards the checks are identical to non-idmapped mounts. If the initial user namespace is passed nothing changes so non-idmapped mounts will see identical behavior as before. Helpers that perform checks on the ia_uid and ia_gid fields in struct iattr assume that ia_uid and ia_gid are intended values and have already been mapped correctly at the userspace-kernelspace boundary as we already do today. If the initial user namespace is passed nothing changes so non-idmapped mounts will see identical behavior as before. Link: https://lore.kernel.org/r/20210121131959.646623-8-christian.brauner@ubuntu.com Cc: Christoph Hellwig <hch@lst.de> Cc: David Howells <dhowells@redhat.com> Cc: Al Viro <viro@zeniv.linux.org.uk> Cc: linux-fsdevel@vger.kernel.org Reviewed-by: Christoph Hellwig <hch@lst.de> Signed-off-by: Christian Brauner <christian.brauner@ubuntu.com>
1 parent 21cb47b commit 2f221d6

File tree

57 files changed

+206
-137
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

57 files changed

+206
-137
lines changed

arch/powerpc/platforms/cell/spufs/inode.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ spufs_setattr(struct dentry *dentry, struct iattr *attr)
9898
if ((attr->ia_valid & ATTR_SIZE) &&
9999
(attr->ia_size != inode->i_size))
100100
return -EINVAL;
101-
setattr_copy(inode, attr);
101+
setattr_copy(&init_user_ns, inode, attr);
102102
mark_inode_dirty(inode);
103103
return 0;
104104
}

drivers/base/devtmpfs.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,7 @@ static int handle_create(const char *nodename, umode_t mode, kuid_t uid,
221221
newattrs.ia_gid = gid;
222222
newattrs.ia_valid = ATTR_MODE|ATTR_UID|ATTR_GID;
223223
inode_lock(d_inode(dentry));
224-
notify_change(dentry, &newattrs, NULL);
224+
notify_change(&init_user_ns, dentry, &newattrs, NULL);
225225
inode_unlock(d_inode(dentry));
226226

227227
/* mark as kernel-created inode */
@@ -328,7 +328,7 @@ static int handle_remove(const char *nodename, struct device *dev)
328328
newattrs.ia_valid =
329329
ATTR_UID|ATTR_GID|ATTR_MODE;
330330
inode_lock(d_inode(dentry));
331-
notify_change(dentry, &newattrs, NULL);
331+
notify_change(&init_user_ns, dentry, &newattrs, NULL);
332332
inode_unlock(d_inode(dentry));
333333
err = vfs_unlink(d_inode(parent.dentry), dentry, NULL);
334334
if (!err || err == -ENOENT)

fs/9p/vfs_inode.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1062,7 +1062,7 @@ static int v9fs_vfs_setattr(struct dentry *dentry, struct iattr *iattr)
10621062
struct p9_wstat wstat;
10631063

10641064
p9_debug(P9_DEBUG_VFS, "\n");
1065-
retval = setattr_prepare(dentry, iattr);
1065+
retval = setattr_prepare(&init_user_ns, dentry, iattr);
10661066
if (retval)
10671067
return retval;
10681068

@@ -1118,7 +1118,7 @@ static int v9fs_vfs_setattr(struct dentry *dentry, struct iattr *iattr)
11181118

11191119
v9fs_invalidate_inode_attr(d_inode(dentry));
11201120

1121-
setattr_copy(d_inode(dentry), iattr);
1121+
setattr_copy(&init_user_ns, d_inode(dentry), iattr);
11221122
mark_inode_dirty(d_inode(dentry));
11231123
return 0;
11241124
}

fs/9p/vfs_inode_dotl.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -549,7 +549,7 @@ int v9fs_vfs_setattr_dotl(struct dentry *dentry, struct iattr *iattr)
549549

550550
p9_debug(P9_DEBUG_VFS, "\n");
551551

552-
retval = setattr_prepare(dentry, iattr);
552+
retval = setattr_prepare(&init_user_ns, dentry, iattr);
553553
if (retval)
554554
return retval;
555555

@@ -590,7 +590,7 @@ int v9fs_vfs_setattr_dotl(struct dentry *dentry, struct iattr *iattr)
590590
truncate_setsize(inode, iattr->ia_size);
591591

592592
v9fs_invalidate_inode_attr(inode);
593-
setattr_copy(inode, iattr);
593+
setattr_copy(&init_user_ns, inode, iattr);
594594
mark_inode_dirty(inode);
595595
if (iattr->ia_valid & ATTR_MODE) {
596596
/* We also want to update ACL when we update mode bits */

fs/adfs/inode.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -299,7 +299,7 @@ adfs_notify_change(struct dentry *dentry, struct iattr *attr)
299299
unsigned int ia_valid = attr->ia_valid;
300300
int error;
301301

302-
error = setattr_prepare(dentry, attr);
302+
error = setattr_prepare(&init_user_ns, dentry, attr);
303303

304304
/*
305305
* we can't change the UID or GID of any file -

fs/affs/inode.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,7 @@ affs_notify_change(struct dentry *dentry, struct iattr *attr)
223223

224224
pr_debug("notify_change(%lu,0x%x)\n", inode->i_ino, attr->ia_valid);
225225

226-
error = setattr_prepare(dentry, attr);
226+
error = setattr_prepare(&init_user_ns, dentry, attr);
227227
if (error)
228228
goto out;
229229

@@ -249,7 +249,7 @@ affs_notify_change(struct dentry *dentry, struct iattr *attr)
249249
affs_truncate(inode);
250250
}
251251

252-
setattr_copy(inode, attr);
252+
setattr_copy(&init_user_ns, inode, attr);
253253
mark_inode_dirty(inode);
254254

255255
if (attr->ia_valid & ATTR_MODE)

fs/attr.c

Lines changed: 90 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -18,34 +18,63 @@
1818
#include <linux/evm.h>
1919
#include <linux/ima.h>
2020

21-
static bool chown_ok(const struct inode *inode, kuid_t uid)
21+
/**
22+
* chown_ok - verify permissions to chown inode
23+
* @mnt_userns: user namespace of the mount @inode was found from
24+
* @inode: inode to check permissions on
25+
* @uid: uid to chown @inode to
26+
*
27+
* If the inode has been found through an idmapped mount the user namespace of
28+
* the vfsmount must be passed through @mnt_userns. This function will then
29+
* take care to map the inode according to @mnt_userns before checking
30+
* permissions. On non-idmapped mounts or if permission checking is to be
31+
* performed on the raw inode simply passs init_user_ns.
32+
*/
33+
static bool chown_ok(struct user_namespace *mnt_userns,
34+
const struct inode *inode,
35+
kuid_t uid)
2236
{
23-
if (uid_eq(current_fsuid(), inode->i_uid) &&
24-
uid_eq(uid, inode->i_uid))
37+
kuid_t kuid = i_uid_into_mnt(mnt_userns, inode);
38+
if (uid_eq(current_fsuid(), kuid) && uid_eq(uid, kuid))
2539
return true;
26-
if (capable_wrt_inode_uidgid(&init_user_ns, inode, CAP_CHOWN))
40+
if (capable_wrt_inode_uidgid(mnt_userns, inode, CAP_CHOWN))
2741
return true;
28-
if (uid_eq(inode->i_uid, INVALID_UID) &&
42+
if (uid_eq(kuid, INVALID_UID) &&
2943
ns_capable(inode->i_sb->s_user_ns, CAP_CHOWN))
3044
return true;
3145
return false;
3246
}
3347

34-
static bool chgrp_ok(const struct inode *inode, kgid_t gid)
48+
/**
49+
* chgrp_ok - verify permissions to chgrp inode
50+
* @mnt_userns: user namespace of the mount @inode was found from
51+
* @inode: inode to check permissions on
52+
* @gid: gid to chown @inode to
53+
*
54+
* If the inode has been found through an idmapped mount the user namespace of
55+
* the vfsmount must be passed through @mnt_userns. This function will then
56+
* take care to map the inode according to @mnt_userns before checking
57+
* permissions. On non-idmapped mounts or if permission checking is to be
58+
* performed on the raw inode simply passs init_user_ns.
59+
*/
60+
static bool chgrp_ok(struct user_namespace *mnt_userns,
61+
const struct inode *inode, kgid_t gid)
3562
{
36-
if (uid_eq(current_fsuid(), inode->i_uid) &&
37-
(in_group_p(gid) || gid_eq(gid, inode->i_gid)))
63+
kgid_t kgid = i_gid_into_mnt(mnt_userns, inode);
64+
if (uid_eq(current_fsuid(), i_uid_into_mnt(mnt_userns, inode)) &&
65+
(in_group_p(gid) || gid_eq(gid, kgid)))
3866
return true;
39-
if (capable_wrt_inode_uidgid(&init_user_ns, inode, CAP_CHOWN))
67+
if (capable_wrt_inode_uidgid(mnt_userns, inode, CAP_CHOWN))
4068
return true;
41-
if (gid_eq(inode->i_gid, INVALID_GID) &&
69+
if (gid_eq(kgid, INVALID_GID) &&
4270
ns_capable(inode->i_sb->s_user_ns, CAP_CHOWN))
4371
return true;
4472
return false;
4573
}
4674

4775
/**
4876
* setattr_prepare - check if attribute changes to a dentry are allowed
77+
* @mnt_userns: user namespace of the mount the inode was found from
4978
* @dentry: dentry to check
5079
* @attr: attributes to change
5180
*
@@ -55,10 +84,17 @@ static bool chgrp_ok(const struct inode *inode, kgid_t gid)
5584
* SGID bit from mode if user is not allowed to set it. Also file capabilities
5685
* and IMA extended attributes are cleared if ATTR_KILL_PRIV is set.
5786
*
87+
* If the inode has been found through an idmapped mount the user namespace of
88+
* the vfsmount must be passed through @mnt_userns. This function will then
89+
* take care to map the inode according to @mnt_userns before checking
90+
* permissions. On non-idmapped mounts or if permission checking is to be
91+
* performed on the raw inode simply passs init_user_ns.
92+
*
5893
* Should be called as the first thing in ->setattr implementations,
5994
* possibly after taking additional locks.
6095
*/
61-
int setattr_prepare(struct dentry *dentry, struct iattr *attr)
96+
int setattr_prepare(struct user_namespace *mnt_userns, struct dentry *dentry,
97+
struct iattr *attr)
6298
{
6399
struct inode *inode = d_inode(dentry);
64100
unsigned int ia_valid = attr->ia_valid;
@@ -78,27 +114,27 @@ int setattr_prepare(struct dentry *dentry, struct iattr *attr)
78114
goto kill_priv;
79115

80116
/* Make sure a caller can chown. */
81-
if ((ia_valid & ATTR_UID) && !chown_ok(inode, attr->ia_uid))
117+
if ((ia_valid & ATTR_UID) && !chown_ok(mnt_userns, inode, attr->ia_uid))
82118
return -EPERM;
83119

84120
/* Make sure caller can chgrp. */
85-
if ((ia_valid & ATTR_GID) && !chgrp_ok(inode, attr->ia_gid))
121+
if ((ia_valid & ATTR_GID) && !chgrp_ok(mnt_userns, inode, attr->ia_gid))
86122
return -EPERM;
87123

88124
/* Make sure a caller can chmod. */
89125
if (ia_valid & ATTR_MODE) {
90-
if (!inode_owner_or_capable(&init_user_ns, inode))
126+
if (!inode_owner_or_capable(mnt_userns, inode))
91127
return -EPERM;
92128
/* Also check the setgid bit! */
93-
if (!in_group_p((ia_valid & ATTR_GID) ? attr->ia_gid :
94-
inode->i_gid) &&
95-
!capable_wrt_inode_uidgid(&init_user_ns, inode, CAP_FSETID))
129+
if (!in_group_p((ia_valid & ATTR_GID) ? attr->ia_gid :
130+
i_gid_into_mnt(mnt_userns, inode)) &&
131+
!capable_wrt_inode_uidgid(mnt_userns, inode, CAP_FSETID))
96132
attr->ia_mode &= ~S_ISGID;
97133
}
98134

99135
/* Check for setting the inode time. */
100136
if (ia_valid & (ATTR_MTIME_SET | ATTR_ATIME_SET | ATTR_TIMES_SET)) {
101-
if (!inode_owner_or_capable(&init_user_ns, inode))
137+
if (!inode_owner_or_capable(mnt_userns, inode))
102138
return -EPERM;
103139
}
104140

@@ -162,20 +198,33 @@ EXPORT_SYMBOL(inode_newsize_ok);
162198

163199
/**
164200
* setattr_copy - copy simple metadata updates into the generic inode
201+
* @mnt_userns: user namespace of the mount the inode was found from
165202
* @inode: the inode to be updated
166203
* @attr: the new attributes
167204
*
168205
* setattr_copy must be called with i_mutex held.
169206
*
170207
* setattr_copy updates the inode's metadata with that specified
171-
* in attr. Noticeably missing is inode size update, which is more complex
208+
* in attr on idmapped mounts. If file ownership is changed setattr_copy
209+
* doesn't map ia_uid and ia_gid. It will asssume the caller has already
210+
* provided the intended values. Necessary permission checks to determine
211+
* whether or not the S_ISGID property needs to be removed are performed with
212+
* the correct idmapped mount permission helpers.
213+
* Noticeably missing is inode size update, which is more complex
172214
* as it requires pagecache updates.
173215
*
216+
* If the inode has been found through an idmapped mount the user namespace of
217+
* the vfsmount must be passed through @mnt_userns. This function will then
218+
* take care to map the inode according to @mnt_userns before checking
219+
* permissions. On non-idmapped mounts or if permission checking is to be
220+
* performed on the raw inode simply passs init_user_ns.
221+
*
174222
* The inode is not marked as dirty after this operation. The rationale is
175223
* that for "simple" filesystems, the struct inode is the inode storage.
176224
* The caller is free to mark the inode dirty afterwards if needed.
177225
*/
178-
void setattr_copy(struct inode *inode, const struct iattr *attr)
226+
void setattr_copy(struct user_namespace *mnt_userns, struct inode *inode,
227+
const struct iattr *attr)
179228
{
180229
unsigned int ia_valid = attr->ia_valid;
181230

@@ -191,9 +240,9 @@ void setattr_copy(struct inode *inode, const struct iattr *attr)
191240
inode->i_ctime = attr->ia_ctime;
192241
if (ia_valid & ATTR_MODE) {
193242
umode_t mode = attr->ia_mode;
194-
195-
if (!in_group_p(inode->i_gid) &&
196-
!capable_wrt_inode_uidgid(&init_user_ns, inode, CAP_FSETID))
243+
kgid_t kgid = i_gid_into_mnt(mnt_userns, inode);
244+
if (!in_group_p(kgid) &&
245+
!capable_wrt_inode_uidgid(mnt_userns, inode, CAP_FSETID))
197246
mode &= ~S_ISGID;
198247
inode->i_mode = mode;
199248
}
@@ -202,6 +251,7 @@ EXPORT_SYMBOL(setattr_copy);
202251

203252
/**
204253
* notify_change - modify attributes of a filesytem object
254+
* @mnt_userns: user namespace of the mount the inode was found from
205255
* @dentry: object affected
206256
* @attr: new attributes
207257
* @delegated_inode: returns inode, if the inode is delegated
@@ -214,13 +264,23 @@ EXPORT_SYMBOL(setattr_copy);
214264
* retry. Because breaking a delegation may take a long time, the
215265
* caller should drop the i_mutex before doing so.
216266
*
267+
* If file ownership is changed notify_change() doesn't map ia_uid and
268+
* ia_gid. It will asssume the caller has already provided the intended values.
269+
*
217270
* Alternatively, a caller may pass NULL for delegated_inode. This may
218271
* be appropriate for callers that expect the underlying filesystem not
219272
* to be NFS exported. Also, passing NULL is fine for callers holding
220273
* the file open for write, as there can be no conflicting delegation in
221274
* that case.
275+
*
276+
* If the inode has been found through an idmapped mount the user namespace of
277+
* the vfsmount must be passed through @mnt_userns. This function will then
278+
* take care to map the inode according to @mnt_userns before checking
279+
* permissions. On non-idmapped mounts or if permission checking is to be
280+
* performed on the raw inode simply passs init_user_ns.
222281
*/
223-
int notify_change(struct dentry * dentry, struct iattr * attr, struct inode **delegated_inode)
282+
int notify_change(struct user_namespace *mnt_userns, struct dentry *dentry,
283+
struct iattr *attr, struct inode **delegated_inode)
224284
{
225285
struct inode *inode = dentry->d_inode;
226286
umode_t mode = inode->i_mode;
@@ -243,9 +303,8 @@ int notify_change(struct dentry * dentry, struct iattr * attr, struct inode **de
243303
if (IS_IMMUTABLE(inode))
244304
return -EPERM;
245305

246-
if (!inode_owner_or_capable(&init_user_ns, inode)) {
247-
error = inode_permission(&init_user_ns, inode,
248-
MAY_WRITE);
306+
if (!inode_owner_or_capable(mnt_userns, inode)) {
307+
error = inode_permission(mnt_userns, inode, MAY_WRITE);
249308
if (error)
250309
return error;
251310
}
@@ -321,9 +380,11 @@ int notify_change(struct dentry * dentry, struct iattr * attr, struct inode **de
321380
/* Don't allow modifications of files with invalid uids or
322381
* gids unless those uids & gids are being made valid.
323382
*/
324-
if (!(ia_valid & ATTR_UID) && !uid_valid(inode->i_uid))
383+
if (!(ia_valid & ATTR_UID) &&
384+
!uid_valid(i_uid_into_mnt(mnt_userns, inode)))
325385
return -EOVERFLOW;
326-
if (!(ia_valid & ATTR_GID) && !gid_valid(inode->i_gid))
386+
if (!(ia_valid & ATTR_GID) &&
387+
!gid_valid(i_gid_into_mnt(mnt_userns, inode)))
327388
return -EOVERFLOW;
328389

329390
error = security_inode_setattr(dentry, attr);

fs/btrfs/inode.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5054,7 +5054,7 @@ static int btrfs_setattr(struct dentry *dentry, struct iattr *attr)
50545054
if (btrfs_root_readonly(root))
50555055
return -EROFS;
50565056

5057-
err = setattr_prepare(dentry, attr);
5057+
err = setattr_prepare(&init_user_ns, dentry, attr);
50585058
if (err)
50595059
return err;
50605060

@@ -5065,7 +5065,7 @@ static int btrfs_setattr(struct dentry *dentry, struct iattr *attr)
50655065
}
50665066

50675067
if (attr->ia_valid) {
5068-
setattr_copy(inode, attr);
5068+
setattr_copy(&init_user_ns, inode, attr);
50695069
inode_inc_iversion(inode);
50705070
err = btrfs_dirty_inode(inode);
50715071

fs/cachefiles/interface.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -470,14 +470,14 @@ static int cachefiles_attr_changed(struct fscache_object *_object)
470470
_debug("discard tail %llx", oi_size);
471471
newattrs.ia_valid = ATTR_SIZE;
472472
newattrs.ia_size = oi_size & PAGE_MASK;
473-
ret = notify_change(object->backer, &newattrs, NULL);
473+
ret = notify_change(&init_user_ns, object->backer, &newattrs, NULL);
474474
if (ret < 0)
475475
goto truncate_failed;
476476
}
477477

478478
newattrs.ia_valid = ATTR_SIZE;
479479
newattrs.ia_size = ni_size;
480-
ret = notify_change(object->backer, &newattrs, NULL);
480+
ret = notify_change(&init_user_ns, object->backer, &newattrs, NULL);
481481

482482
truncate_failed:
483483
inode_unlock(d_inode(object->backer));

fs/ceph/inode.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2247,7 +2247,7 @@ int ceph_setattr(struct dentry *dentry, struct iattr *attr)
22472247
if (ceph_snap(inode) != CEPH_NOSNAP)
22482248
return -EROFS;
22492249

2250-
err = setattr_prepare(dentry, attr);
2250+
err = setattr_prepare(&init_user_ns, dentry, attr);
22512251
if (err != 0)
22522252
return err;
22532253

0 commit comments

Comments
 (0)