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