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   */
initial_idmapping(const struct user_namespace * ns)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  
make_vfsuid(struct mnt_idmap * idmap,struct user_namespace * fs_userns,kuid_t kuid)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   */
make_vfsgid(struct mnt_idmap * idmap,struct user_namespace * fs_userns,kgid_t kgid)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   */
from_vfsuid(struct mnt_idmap * idmap,struct user_namespace * fs_userns,vfsuid_t vfsuid)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   */
from_vfsgid(struct mnt_idmap * idmap,struct user_namespace * fs_userns,vfsgid_t vfsgid)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   */
vfsgid_in_group_p(vfsgid_t vfsgid)206  int vfsgid_in_group_p(vfsgid_t vfsgid)
207  {
208  	return in_group_p(AS_KGIDT(vfsgid));
209  }
210  #else
vfsgid_in_group_p(vfsgid_t vfsgid)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  
copy_mnt_idmap(struct uid_gid_map * map_from,struct uid_gid_map * map_to)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  
free_mnt_idmap(struct mnt_idmap * idmap)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  
alloc_mnt_idmap(struct user_namespace * mnt_userns)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   */
mnt_idmap_get(struct mnt_idmap * idmap)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   */
mnt_idmap_put(struct mnt_idmap * idmap)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