xref: /openbmc/linux/fs/mnt_idmapping.c (revision 3707d84c)
1*3707d84cSChristian Brauner // SPDX-License-Identifier: GPL-2.0
2*3707d84cSChristian Brauner /* Copyright (c) 2022 Christian Brauner <brauner@kernel.org> */
3*3707d84cSChristian Brauner 
4*3707d84cSChristian Brauner #include <linux/cred.h>
5*3707d84cSChristian Brauner #include <linux/fs.h>
6*3707d84cSChristian Brauner #include <linux/mnt_idmapping.h>
7*3707d84cSChristian Brauner #include <linux/slab.h>
8*3707d84cSChristian Brauner #include <linux/user_namespace.h>
9*3707d84cSChristian Brauner 
10*3707d84cSChristian Brauner #include "internal.h"
11*3707d84cSChristian Brauner 
12*3707d84cSChristian Brauner struct mnt_idmap {
13*3707d84cSChristian Brauner 	struct user_namespace *owner;
14*3707d84cSChristian Brauner 	refcount_t count;
15*3707d84cSChristian Brauner };
16*3707d84cSChristian Brauner 
17*3707d84cSChristian Brauner /*
18*3707d84cSChristian Brauner  * Carries the initial idmapping of 0:0:4294967295 which is an identity
19*3707d84cSChristian Brauner  * mapping. This means that {g,u}id 0 is mapped to {g,u}id 0, {g,u}id 1 is
20*3707d84cSChristian Brauner  * mapped to {g,u}id 1, [...], {g,u}id 1000 to {g,u}id 1000, [...].
21*3707d84cSChristian Brauner  */
22*3707d84cSChristian Brauner struct mnt_idmap nop_mnt_idmap = {
23*3707d84cSChristian Brauner 	.owner	= &init_user_ns,
24*3707d84cSChristian Brauner 	.count	= REFCOUNT_INIT(1),
25*3707d84cSChristian Brauner };
26*3707d84cSChristian Brauner EXPORT_SYMBOL_GPL(nop_mnt_idmap);
27*3707d84cSChristian Brauner 
28*3707d84cSChristian Brauner /**
29*3707d84cSChristian Brauner  * check_fsmapping - check whether an mount idmapping is allowed
30*3707d84cSChristian Brauner  * @idmap: idmap of the relevent mount
31*3707d84cSChristian Brauner  * @sb:    super block of the filesystem
32*3707d84cSChristian Brauner  *
33*3707d84cSChristian Brauner  * Return: true if @idmap is allowed, false if not.
34*3707d84cSChristian Brauner  */
check_fsmapping(const struct mnt_idmap * idmap,const struct super_block * sb)35*3707d84cSChristian Brauner bool check_fsmapping(const struct mnt_idmap *idmap,
36*3707d84cSChristian Brauner 		     const struct super_block *sb)
37*3707d84cSChristian Brauner {
38*3707d84cSChristian Brauner 	return idmap->owner != sb->s_user_ns;
39*3707d84cSChristian Brauner }
40*3707d84cSChristian Brauner 
41*3707d84cSChristian Brauner /**
42*3707d84cSChristian Brauner  * initial_idmapping - check whether this is the initial mapping
43*3707d84cSChristian Brauner  * @ns: idmapping to check
44*3707d84cSChristian Brauner  *
45*3707d84cSChristian Brauner  * Check whether this is the initial mapping, mapping 0 to 0, 1 to 1,
46*3707d84cSChristian Brauner  * [...], 1000 to 1000 [...].
47*3707d84cSChristian Brauner  *
48*3707d84cSChristian Brauner  * Return: true if this is the initial mapping, false if not.
49*3707d84cSChristian Brauner  */
initial_idmapping(const struct user_namespace * ns)50*3707d84cSChristian Brauner static inline bool initial_idmapping(const struct user_namespace *ns)
51*3707d84cSChristian Brauner {
52*3707d84cSChristian Brauner 	return ns == &init_user_ns;
53*3707d84cSChristian Brauner }
54*3707d84cSChristian Brauner 
55*3707d84cSChristian Brauner /**
56*3707d84cSChristian Brauner  * no_idmapping - check whether we can skip remapping a kuid/gid
57*3707d84cSChristian Brauner  * @mnt_userns: the mount's idmapping
58*3707d84cSChristian Brauner  * @fs_userns: the filesystem's idmapping
59*3707d84cSChristian Brauner  *
60*3707d84cSChristian Brauner  * This function can be used to check whether a remapping between two
61*3707d84cSChristian Brauner  * idmappings is required.
62*3707d84cSChristian Brauner  * An idmapped mount is a mount that has an idmapping attached to it that
63*3707d84cSChristian Brauner  * is different from the filsystem's idmapping and the initial idmapping.
64*3707d84cSChristian Brauner  * If the initial mapping is used or the idmapping of the mount and the
65*3707d84cSChristian Brauner  * filesystem are identical no remapping is required.
66*3707d84cSChristian Brauner  *
67*3707d84cSChristian Brauner  * Return: true if remapping can be skipped, false if not.
68*3707d84cSChristian Brauner  */
no_idmapping(const struct user_namespace * mnt_userns,const struct user_namespace * fs_userns)69*3707d84cSChristian Brauner static inline bool no_idmapping(const struct user_namespace *mnt_userns,
70*3707d84cSChristian Brauner 				const struct user_namespace *fs_userns)
71*3707d84cSChristian Brauner {
72*3707d84cSChristian Brauner 	return initial_idmapping(mnt_userns) || mnt_userns == fs_userns;
73*3707d84cSChristian Brauner }
74*3707d84cSChristian Brauner 
75*3707d84cSChristian Brauner /**
76*3707d84cSChristian Brauner  * make_vfsuid - map a filesystem kuid according to an idmapping
77*3707d84cSChristian Brauner  * @idmap: the mount's idmapping
78*3707d84cSChristian Brauner  * @fs_userns: the filesystem's idmapping
79*3707d84cSChristian Brauner  * @kuid : kuid to be mapped
80*3707d84cSChristian Brauner  *
81*3707d84cSChristian Brauner  * Take a @kuid and remap it from @fs_userns into @idmap. Use this
82*3707d84cSChristian Brauner  * function when preparing a @kuid to be reported to userspace.
83*3707d84cSChristian Brauner  *
84*3707d84cSChristian Brauner  * If no_idmapping() determines that this is not an idmapped mount we can
85*3707d84cSChristian Brauner  * simply return @kuid unchanged.
86*3707d84cSChristian Brauner  * If initial_idmapping() tells us that the filesystem is not mounted with an
87*3707d84cSChristian Brauner  * idmapping we know the value of @kuid won't change when calling
88*3707d84cSChristian Brauner  * from_kuid() so we can simply retrieve the value via __kuid_val()
89*3707d84cSChristian Brauner  * directly.
90*3707d84cSChristian Brauner  *
91*3707d84cSChristian Brauner  * Return: @kuid mapped according to @idmap.
92*3707d84cSChristian Brauner  * If @kuid has no mapping in either @idmap or @fs_userns INVALID_UID is
93*3707d84cSChristian Brauner  * returned.
94*3707d84cSChristian Brauner  */
95*3707d84cSChristian Brauner 
make_vfsuid(struct mnt_idmap * idmap,struct user_namespace * fs_userns,kuid_t kuid)96*3707d84cSChristian Brauner vfsuid_t make_vfsuid(struct mnt_idmap *idmap,
97*3707d84cSChristian Brauner 				   struct user_namespace *fs_userns,
98*3707d84cSChristian Brauner 				   kuid_t kuid)
99*3707d84cSChristian Brauner {
100*3707d84cSChristian Brauner 	uid_t uid;
101*3707d84cSChristian Brauner 	struct user_namespace *mnt_userns = idmap->owner;
102*3707d84cSChristian Brauner 
103*3707d84cSChristian Brauner 	if (no_idmapping(mnt_userns, fs_userns))
104*3707d84cSChristian Brauner 		return VFSUIDT_INIT(kuid);
105*3707d84cSChristian Brauner 	if (initial_idmapping(fs_userns))
106*3707d84cSChristian Brauner 		uid = __kuid_val(kuid);
107*3707d84cSChristian Brauner 	else
108*3707d84cSChristian Brauner 		uid = from_kuid(fs_userns, kuid);
109*3707d84cSChristian Brauner 	if (uid == (uid_t)-1)
110*3707d84cSChristian Brauner 		return INVALID_VFSUID;
111*3707d84cSChristian Brauner 	return VFSUIDT_INIT(make_kuid(mnt_userns, uid));
112*3707d84cSChristian Brauner }
113*3707d84cSChristian Brauner EXPORT_SYMBOL_GPL(make_vfsuid);
114*3707d84cSChristian Brauner 
115*3707d84cSChristian Brauner /**
116*3707d84cSChristian Brauner  * make_vfsgid - map a filesystem kgid according to an idmapping
117*3707d84cSChristian Brauner  * @idmap: the mount's idmapping
118*3707d84cSChristian Brauner  * @fs_userns: the filesystem's idmapping
119*3707d84cSChristian Brauner  * @kgid : kgid to be mapped
120*3707d84cSChristian Brauner  *
121*3707d84cSChristian Brauner  * Take a @kgid and remap it from @fs_userns into @idmap. Use this
122*3707d84cSChristian Brauner  * function when preparing a @kgid to be reported to userspace.
123*3707d84cSChristian Brauner  *
124*3707d84cSChristian Brauner  * If no_idmapping() determines that this is not an idmapped mount we can
125*3707d84cSChristian Brauner  * simply return @kgid unchanged.
126*3707d84cSChristian Brauner  * If initial_idmapping() tells us that the filesystem is not mounted with an
127*3707d84cSChristian Brauner  * idmapping we know the value of @kgid won't change when calling
128*3707d84cSChristian Brauner  * from_kgid() so we can simply retrieve the value via __kgid_val()
129*3707d84cSChristian Brauner  * directly.
130*3707d84cSChristian Brauner  *
131*3707d84cSChristian Brauner  * Return: @kgid mapped according to @idmap.
132*3707d84cSChristian Brauner  * If @kgid has no mapping in either @idmap or @fs_userns INVALID_GID is
133*3707d84cSChristian Brauner  * returned.
134*3707d84cSChristian Brauner  */
make_vfsgid(struct mnt_idmap * idmap,struct user_namespace * fs_userns,kgid_t kgid)135*3707d84cSChristian Brauner vfsgid_t make_vfsgid(struct mnt_idmap *idmap,
136*3707d84cSChristian Brauner 		     struct user_namespace *fs_userns, kgid_t kgid)
137*3707d84cSChristian Brauner {
138*3707d84cSChristian Brauner 	gid_t gid;
139*3707d84cSChristian Brauner 	struct user_namespace *mnt_userns = idmap->owner;
140*3707d84cSChristian Brauner 
141*3707d84cSChristian Brauner 	if (no_idmapping(mnt_userns, fs_userns))
142*3707d84cSChristian Brauner 		return VFSGIDT_INIT(kgid);
143*3707d84cSChristian Brauner 	if (initial_idmapping(fs_userns))
144*3707d84cSChristian Brauner 		gid = __kgid_val(kgid);
145*3707d84cSChristian Brauner 	else
146*3707d84cSChristian Brauner 		gid = from_kgid(fs_userns, kgid);
147*3707d84cSChristian Brauner 	if (gid == (gid_t)-1)
148*3707d84cSChristian Brauner 		return INVALID_VFSGID;
149*3707d84cSChristian Brauner 	return VFSGIDT_INIT(make_kgid(mnt_userns, gid));
150*3707d84cSChristian Brauner }
151*3707d84cSChristian Brauner EXPORT_SYMBOL_GPL(make_vfsgid);
152*3707d84cSChristian Brauner 
153*3707d84cSChristian Brauner /**
154*3707d84cSChristian Brauner  * from_vfsuid - map a vfsuid into the filesystem idmapping
155*3707d84cSChristian Brauner  * @idmap: the mount's idmapping
156*3707d84cSChristian Brauner  * @fs_userns: the filesystem's idmapping
157*3707d84cSChristian Brauner  * @vfsuid : vfsuid to be mapped
158*3707d84cSChristian Brauner  *
159*3707d84cSChristian Brauner  * Map @vfsuid into the filesystem idmapping. This function has to be used in
160*3707d84cSChristian Brauner  * order to e.g. write @vfsuid to inode->i_uid.
161*3707d84cSChristian Brauner  *
162*3707d84cSChristian Brauner  * Return: @vfsuid mapped into the filesystem idmapping
163*3707d84cSChristian Brauner  */
from_vfsuid(struct mnt_idmap * idmap,struct user_namespace * fs_userns,vfsuid_t vfsuid)164*3707d84cSChristian Brauner kuid_t from_vfsuid(struct mnt_idmap *idmap,
165*3707d84cSChristian Brauner 		   struct user_namespace *fs_userns, vfsuid_t vfsuid)
166*3707d84cSChristian Brauner {
167*3707d84cSChristian Brauner 	uid_t uid;
168*3707d84cSChristian Brauner 	struct user_namespace *mnt_userns = idmap->owner;
169*3707d84cSChristian Brauner 
170*3707d84cSChristian Brauner 	if (no_idmapping(mnt_userns, fs_userns))
171*3707d84cSChristian Brauner 		return AS_KUIDT(vfsuid);
172*3707d84cSChristian Brauner 	uid = from_kuid(mnt_userns, AS_KUIDT(vfsuid));
173*3707d84cSChristian Brauner 	if (uid == (uid_t)-1)
174*3707d84cSChristian Brauner 		return INVALID_UID;
175*3707d84cSChristian Brauner 	if (initial_idmapping(fs_userns))
176*3707d84cSChristian Brauner 		return KUIDT_INIT(uid);
177*3707d84cSChristian Brauner 	return make_kuid(fs_userns, uid);
178*3707d84cSChristian Brauner }
179*3707d84cSChristian Brauner EXPORT_SYMBOL_GPL(from_vfsuid);
180*3707d84cSChristian Brauner 
181*3707d84cSChristian Brauner /**
182*3707d84cSChristian Brauner  * from_vfsgid - map a vfsgid into the filesystem idmapping
183*3707d84cSChristian Brauner  * @idmap: the mount's idmapping
184*3707d84cSChristian Brauner  * @fs_userns: the filesystem's idmapping
185*3707d84cSChristian Brauner  * @vfsgid : vfsgid to be mapped
186*3707d84cSChristian Brauner  *
187*3707d84cSChristian Brauner  * Map @vfsgid into the filesystem idmapping. This function has to be used in
188*3707d84cSChristian Brauner  * order to e.g. write @vfsgid to inode->i_gid.
189*3707d84cSChristian Brauner  *
190*3707d84cSChristian Brauner  * Return: @vfsgid mapped into the filesystem idmapping
191*3707d84cSChristian Brauner  */
from_vfsgid(struct mnt_idmap * idmap,struct user_namespace * fs_userns,vfsgid_t vfsgid)192*3707d84cSChristian Brauner kgid_t from_vfsgid(struct mnt_idmap *idmap,
193*3707d84cSChristian Brauner 		   struct user_namespace *fs_userns, vfsgid_t vfsgid)
194*3707d84cSChristian Brauner {
195*3707d84cSChristian Brauner 	gid_t gid;
196*3707d84cSChristian Brauner 	struct user_namespace *mnt_userns = idmap->owner;
197*3707d84cSChristian Brauner 
198*3707d84cSChristian Brauner 	if (no_idmapping(mnt_userns, fs_userns))
199*3707d84cSChristian Brauner 		return AS_KGIDT(vfsgid);
200*3707d84cSChristian Brauner 	gid = from_kgid(mnt_userns, AS_KGIDT(vfsgid));
201*3707d84cSChristian Brauner 	if (gid == (gid_t)-1)
202*3707d84cSChristian Brauner 		return INVALID_GID;
203*3707d84cSChristian Brauner 	if (initial_idmapping(fs_userns))
204*3707d84cSChristian Brauner 		return KGIDT_INIT(gid);
205*3707d84cSChristian Brauner 	return make_kgid(fs_userns, gid);
206*3707d84cSChristian Brauner }
207*3707d84cSChristian Brauner EXPORT_SYMBOL_GPL(from_vfsgid);
208*3707d84cSChristian Brauner 
209*3707d84cSChristian Brauner #ifdef CONFIG_MULTIUSER
210*3707d84cSChristian Brauner /**
211*3707d84cSChristian Brauner  * vfsgid_in_group_p() - check whether a vfsuid matches the caller's groups
212*3707d84cSChristian Brauner  * @vfsgid: the mnt gid to match
213*3707d84cSChristian Brauner  *
214*3707d84cSChristian Brauner  * This function can be used to determine whether @vfsuid matches any of the
215*3707d84cSChristian Brauner  * caller's groups.
216*3707d84cSChristian Brauner  *
217*3707d84cSChristian Brauner  * Return: 1 if vfsuid matches caller's groups, 0 if not.
218*3707d84cSChristian Brauner  */
vfsgid_in_group_p(vfsgid_t vfsgid)219*3707d84cSChristian Brauner int vfsgid_in_group_p(vfsgid_t vfsgid)
220*3707d84cSChristian Brauner {
221*3707d84cSChristian Brauner 	return in_group_p(AS_KGIDT(vfsgid));
222*3707d84cSChristian Brauner }
223*3707d84cSChristian Brauner #else
vfsgid_in_group_p(vfsgid_t vfsgid)224*3707d84cSChristian Brauner int vfsgid_in_group_p(vfsgid_t vfsgid)
225*3707d84cSChristian Brauner {
226*3707d84cSChristian Brauner 	return 1;
227*3707d84cSChristian Brauner }
228*3707d84cSChristian Brauner #endif
229*3707d84cSChristian Brauner EXPORT_SYMBOL_GPL(vfsgid_in_group_p);
230*3707d84cSChristian Brauner 
alloc_mnt_idmap(struct user_namespace * mnt_userns)231*3707d84cSChristian Brauner struct mnt_idmap *alloc_mnt_idmap(struct user_namespace *mnt_userns)
232*3707d84cSChristian Brauner {
233*3707d84cSChristian Brauner 	struct mnt_idmap *idmap;
234*3707d84cSChristian Brauner 
235*3707d84cSChristian Brauner 	idmap = kzalloc(sizeof(struct mnt_idmap), GFP_KERNEL_ACCOUNT);
236*3707d84cSChristian Brauner 	if (!idmap)
237*3707d84cSChristian Brauner 		return ERR_PTR(-ENOMEM);
238*3707d84cSChristian Brauner 
239*3707d84cSChristian Brauner 	idmap->owner = get_user_ns(mnt_userns);
240*3707d84cSChristian Brauner 	refcount_set(&idmap->count, 1);
241*3707d84cSChristian Brauner 	return idmap;
242*3707d84cSChristian Brauner }
243*3707d84cSChristian Brauner 
244*3707d84cSChristian Brauner /**
245*3707d84cSChristian Brauner  * mnt_idmap_get - get a reference to an idmapping
246*3707d84cSChristian Brauner  * @idmap: the idmap to bump the reference on
247*3707d84cSChristian Brauner  *
248*3707d84cSChristian Brauner  * If @idmap is not the @nop_mnt_idmap bump the reference count.
249*3707d84cSChristian Brauner  *
250*3707d84cSChristian Brauner  * Return: @idmap with reference count bumped if @not_mnt_idmap isn't passed.
251*3707d84cSChristian Brauner  */
mnt_idmap_get(struct mnt_idmap * idmap)252*3707d84cSChristian Brauner struct mnt_idmap *mnt_idmap_get(struct mnt_idmap *idmap)
253*3707d84cSChristian Brauner {
254*3707d84cSChristian Brauner 	if (idmap != &nop_mnt_idmap)
255*3707d84cSChristian Brauner 		refcount_inc(&idmap->count);
256*3707d84cSChristian Brauner 
257*3707d84cSChristian Brauner 	return idmap;
258*3707d84cSChristian Brauner }
259*3707d84cSChristian Brauner 
260*3707d84cSChristian Brauner /**
261*3707d84cSChristian Brauner  * mnt_idmap_put - put a reference to an idmapping
262*3707d84cSChristian Brauner  * @idmap: the idmap to put the reference on
263*3707d84cSChristian Brauner  *
264*3707d84cSChristian Brauner  * If this is a non-initial idmapping, put the reference count when a mount is
265*3707d84cSChristian Brauner  * released and free it if we're the last user.
266*3707d84cSChristian Brauner  */
mnt_idmap_put(struct mnt_idmap * idmap)267*3707d84cSChristian Brauner void mnt_idmap_put(struct mnt_idmap *idmap)
268*3707d84cSChristian Brauner {
269*3707d84cSChristian Brauner 	if (idmap != &nop_mnt_idmap && refcount_dec_and_test(&idmap->count)) {
270*3707d84cSChristian Brauner 		put_user_ns(idmap->owner);
271*3707d84cSChristian Brauner 		kfree(idmap);
272*3707d84cSChristian Brauner 	}
273*3707d84cSChristian Brauner }
274