xref: /linux/fs/mnt_idmapping.c (revision 55d0969c451159cff86949b38c39171cab962069)
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 /*
13  * Outside of this file vfs{g,u}id_t are always created from k{g,u}id_t,
14  * never from raw values. These are just internal helpers.
15  */
16 #define VFSUIDT_INIT_RAW(val) (vfsuid_t){ val }
17 #define VFSGIDT_INIT_RAW(val) (vfsgid_t){ val }
18 
19 struct mnt_idmap {
20 	struct uid_gid_map uid_map;
21 	struct uid_gid_map gid_map;
22 	refcount_t count;
23 };
24 
25 /*
26  * Carries the initial idmapping of 0:0:4294967295 which is an identity
27  * mapping. This means that {g,u}id 0 is mapped to {g,u}id 0, {g,u}id 1 is
28  * mapped to {g,u}id 1, [...], {g,u}id 1000 to {g,u}id 1000, [...].
29  */
30 struct mnt_idmap nop_mnt_idmap = {
31 	.count	= REFCOUNT_INIT(1),
32 };
33 EXPORT_SYMBOL_GPL(nop_mnt_idmap);
34 
35 /*
36  * Carries the invalid idmapping of a full 0-4294967295 {g,u}id range.
37  * This means that all {g,u}ids are mapped to INVALID_VFS{G,U}ID.
38  */
39 struct mnt_idmap invalid_mnt_idmap = {
40 	.count	= REFCOUNT_INIT(1),
41 };
42 EXPORT_SYMBOL_GPL(invalid_mnt_idmap);
43 
44 /**
45  * initial_idmapping - check whether this is the initial mapping
46  * @ns: idmapping to check
47  *
48  * Check whether this is the initial mapping, mapping 0 to 0, 1 to 1,
49  * [...], 1000 to 1000 [...].
50  *
51  * Return: true if this is the initial mapping, false if not.
52  */
53 static inline bool initial_idmapping(const struct user_namespace *ns)
54 {
55 	return ns == &init_user_ns;
56 }
57 
58 /**
59  * make_vfsuid - map a filesystem kuid according to an idmapping
60  * @idmap: the mount's idmapping
61  * @fs_userns: the filesystem's idmapping
62  * @kuid : kuid to be mapped
63  *
64  * Take a @kuid and remap it from @fs_userns into @idmap. Use this
65  * function when preparing a @kuid to be reported to userspace.
66  *
67  * If initial_idmapping() determines that this is not an idmapped mount
68  * we can simply return @kuid unchanged.
69  * If initial_idmapping() tells us that the filesystem is not mounted with an
70  * idmapping we know the value of @kuid won't change when calling
71  * from_kuid() so we can simply retrieve the value via __kuid_val()
72  * directly.
73  *
74  * Return: @kuid mapped according to @idmap.
75  * If @kuid has no mapping in either @idmap or @fs_userns INVALID_UID is
76  * returned.
77  */
78 
79 vfsuid_t make_vfsuid(struct mnt_idmap *idmap,
80 		     struct user_namespace *fs_userns,
81 		     kuid_t kuid)
82 {
83 	uid_t uid;
84 
85 	if (idmap == &nop_mnt_idmap)
86 		return VFSUIDT_INIT(kuid);
87 	if (idmap == &invalid_mnt_idmap)
88 		return INVALID_VFSUID;
89 	if (initial_idmapping(fs_userns))
90 		uid = __kuid_val(kuid);
91 	else
92 		uid = from_kuid(fs_userns, kuid);
93 	if (uid == (uid_t)-1)
94 		return INVALID_VFSUID;
95 	return VFSUIDT_INIT_RAW(map_id_down(&idmap->uid_map, uid));
96 }
97 EXPORT_SYMBOL_GPL(make_vfsuid);
98 
99 /**
100  * make_vfsgid - map a filesystem kgid according to an idmapping
101  * @idmap: the mount's idmapping
102  * @fs_userns: the filesystem's idmapping
103  * @kgid : kgid to be mapped
104  *
105  * Take a @kgid and remap it from @fs_userns into @idmap. Use this
106  * function when preparing a @kgid to be reported to userspace.
107  *
108  * If initial_idmapping() determines that this is not an idmapped mount
109  * we can simply return @kgid unchanged.
110  * If initial_idmapping() tells us that the filesystem is not mounted with an
111  * idmapping we know the value of @kgid won't change when calling
112  * from_kgid() so we can simply retrieve the value via __kgid_val()
113  * directly.
114  *
115  * Return: @kgid mapped according to @idmap.
116  * If @kgid has no mapping in either @idmap or @fs_userns INVALID_GID is
117  * returned.
118  */
119 vfsgid_t make_vfsgid(struct mnt_idmap *idmap,
120 		     struct user_namespace *fs_userns, kgid_t kgid)
121 {
122 	gid_t gid;
123 
124 	if (idmap == &nop_mnt_idmap)
125 		return VFSGIDT_INIT(kgid);
126 	if (idmap == &invalid_mnt_idmap)
127 		return INVALID_VFSGID;
128 	if (initial_idmapping(fs_userns))
129 		gid = __kgid_val(kgid);
130 	else
131 		gid = from_kgid(fs_userns, kgid);
132 	if (gid == (gid_t)-1)
133 		return INVALID_VFSGID;
134 	return VFSGIDT_INIT_RAW(map_id_down(&idmap->gid_map, gid));
135 }
136 EXPORT_SYMBOL_GPL(make_vfsgid);
137 
138 /**
139  * from_vfsuid - map a vfsuid into the filesystem idmapping
140  * @idmap: the mount's idmapping
141  * @fs_userns: the filesystem's idmapping
142  * @vfsuid : vfsuid to be mapped
143  *
144  * Map @vfsuid into the filesystem idmapping. This function has to be used in
145  * order to e.g. write @vfsuid to inode->i_uid.
146  *
147  * Return: @vfsuid mapped into the filesystem idmapping
148  */
149 kuid_t from_vfsuid(struct mnt_idmap *idmap,
150 		   struct user_namespace *fs_userns, vfsuid_t vfsuid)
151 {
152 	uid_t uid;
153 
154 	if (idmap == &nop_mnt_idmap)
155 		return AS_KUIDT(vfsuid);
156 	if (idmap == &invalid_mnt_idmap)
157 		return INVALID_UID;
158 	uid = map_id_up(&idmap->uid_map, __vfsuid_val(vfsuid));
159 	if (uid == (uid_t)-1)
160 		return INVALID_UID;
161 	if (initial_idmapping(fs_userns))
162 		return KUIDT_INIT(uid);
163 	return make_kuid(fs_userns, uid);
164 }
165 EXPORT_SYMBOL_GPL(from_vfsuid);
166 
167 /**
168  * from_vfsgid - map a vfsgid into the filesystem idmapping
169  * @idmap: the mount's idmapping
170  * @fs_userns: the filesystem's idmapping
171  * @vfsgid : vfsgid to be mapped
172  *
173  * Map @vfsgid into the filesystem idmapping. This function has to be used in
174  * order to e.g. write @vfsgid to inode->i_gid.
175  *
176  * Return: @vfsgid mapped into the filesystem idmapping
177  */
178 kgid_t from_vfsgid(struct mnt_idmap *idmap,
179 		   struct user_namespace *fs_userns, vfsgid_t vfsgid)
180 {
181 	gid_t gid;
182 
183 	if (idmap == &nop_mnt_idmap)
184 		return AS_KGIDT(vfsgid);
185 	if (idmap == &invalid_mnt_idmap)
186 		return INVALID_GID;
187 	gid = map_id_up(&idmap->gid_map, __vfsgid_val(vfsgid));
188 	if (gid == (gid_t)-1)
189 		return INVALID_GID;
190 	if (initial_idmapping(fs_userns))
191 		return KGIDT_INIT(gid);
192 	return make_kgid(fs_userns, gid);
193 }
194 EXPORT_SYMBOL_GPL(from_vfsgid);
195 
196 #ifdef CONFIG_MULTIUSER
197 /**
198  * vfsgid_in_group_p() - check whether a vfsuid matches the caller's groups
199  * @vfsgid: the mnt gid to match
200  *
201  * This function can be used to determine whether @vfsuid matches any of the
202  * caller's groups.
203  *
204  * Return: 1 if vfsuid matches caller's groups, 0 if not.
205  */
206 int vfsgid_in_group_p(vfsgid_t vfsgid)
207 {
208 	return in_group_p(AS_KGIDT(vfsgid));
209 }
210 #else
211 int vfsgid_in_group_p(vfsgid_t vfsgid)
212 {
213 	return 1;
214 }
215 #endif
216 EXPORT_SYMBOL_GPL(vfsgid_in_group_p);
217 
218 static int copy_mnt_idmap(struct uid_gid_map *map_from,
219 			  struct uid_gid_map *map_to)
220 {
221 	struct uid_gid_extent *forward, *reverse;
222 	u32 nr_extents = READ_ONCE(map_from->nr_extents);
223 	/* Pairs with smp_wmb() when writing the idmapping. */
224 	smp_rmb();
225 
226 	/*
227 	 * Don't blindly copy @map_to into @map_from if nr_extents is
228 	 * smaller or equal to UID_GID_MAP_MAX_BASE_EXTENTS. Since we
229 	 * read @nr_extents someone could have written an idmapping and
230 	 * then we might end up with inconsistent data. So just don't do
231 	 * anything at all.
232 	 */
233 	if (nr_extents == 0)
234 		return -EINVAL;
235 
236 	/*
237 	 * Here we know that nr_extents is greater than zero which means
238 	 * a map has been written. Since idmappings can't be changed
239 	 * once they have been written we know that we can safely copy
240 	 * from @map_to into @map_from.
241 	 */
242 
243 	if (nr_extents <= UID_GID_MAP_MAX_BASE_EXTENTS) {
244 		*map_to = *map_from;
245 		return 0;
246 	}
247 
248 	forward = kmemdup_array(map_from->forward, nr_extents,
249 				sizeof(struct uid_gid_extent),
250 				GFP_KERNEL_ACCOUNT);
251 	if (!forward)
252 		return -ENOMEM;
253 
254 	reverse = kmemdup_array(map_from->reverse, nr_extents,
255 				sizeof(struct uid_gid_extent),
256 				GFP_KERNEL_ACCOUNT);
257 	if (!reverse) {
258 		kfree(forward);
259 		return -ENOMEM;
260 	}
261 
262 	/*
263 	 * The idmapping isn't exposed anywhere so we don't need to care
264 	 * about ordering between extent pointers and @nr_extents
265 	 * initialization.
266 	 */
267 	map_to->forward = forward;
268 	map_to->reverse = reverse;
269 	map_to->nr_extents = nr_extents;
270 	return 0;
271 }
272 
273 static void free_mnt_idmap(struct mnt_idmap *idmap)
274 {
275 	if (idmap->uid_map.nr_extents > UID_GID_MAP_MAX_BASE_EXTENTS) {
276 		kfree(idmap->uid_map.forward);
277 		kfree(idmap->uid_map.reverse);
278 	}
279 	if (idmap->gid_map.nr_extents > UID_GID_MAP_MAX_BASE_EXTENTS) {
280 		kfree(idmap->gid_map.forward);
281 		kfree(idmap->gid_map.reverse);
282 	}
283 	kfree(idmap);
284 }
285 
286 struct mnt_idmap *alloc_mnt_idmap(struct user_namespace *mnt_userns)
287 {
288 	struct mnt_idmap *idmap;
289 	int ret;
290 
291 	idmap = kzalloc(sizeof(struct mnt_idmap), GFP_KERNEL_ACCOUNT);
292 	if (!idmap)
293 		return ERR_PTR(-ENOMEM);
294 
295 	refcount_set(&idmap->count, 1);
296 	ret = copy_mnt_idmap(&mnt_userns->uid_map, &idmap->uid_map);
297 	if (!ret)
298 		ret = copy_mnt_idmap(&mnt_userns->gid_map, &idmap->gid_map);
299 	if (ret) {
300 		free_mnt_idmap(idmap);
301 		idmap = ERR_PTR(ret);
302 	}
303 	return idmap;
304 }
305 
306 /**
307  * mnt_idmap_get - get a reference to an idmapping
308  * @idmap: the idmap to bump the reference on
309  *
310  * If @idmap is not the @nop_mnt_idmap bump the reference count.
311  *
312  * Return: @idmap with reference count bumped if @not_mnt_idmap isn't passed.
313  */
314 struct mnt_idmap *mnt_idmap_get(struct mnt_idmap *idmap)
315 {
316 	if (idmap != &nop_mnt_idmap && idmap != &invalid_mnt_idmap)
317 		refcount_inc(&idmap->count);
318 
319 	return idmap;
320 }
321 EXPORT_SYMBOL_GPL(mnt_idmap_get);
322 
323 /**
324  * mnt_idmap_put - put a reference to an idmapping
325  * @idmap: the idmap to put the reference on
326  *
327  * If this is a non-initial idmapping, put the reference count when a mount is
328  * released and free it if we're the last user.
329  */
330 void mnt_idmap_put(struct mnt_idmap *idmap)
331 {
332 	if (idmap != &nop_mnt_idmap && idmap != &invalid_mnt_idmap &&
333 	    refcount_dec_and_test(&idmap->count))
334 		free_mnt_idmap(idmap);
335 }
336 EXPORT_SYMBOL_GPL(mnt_idmap_put);
337