Line data Source code
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 : */
35 0 : bool check_fsmapping(const struct mnt_idmap *idmap,
36 : const struct super_block *sb)
37 : {
38 0 : 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 : */
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 : */
69 : static inline bool no_idmapping(const struct user_namespace *mnt_userns,
70 : const struct user_namespace *fs_userns)
71 : {
72 32 : 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 :
96 13 : vfsuid_t make_vfsuid(struct mnt_idmap *idmap,
97 : struct user_namespace *fs_userns,
98 : kuid_t kuid)
99 : {
100 : uid_t uid;
101 13 : struct user_namespace *mnt_userns = idmap->owner;
102 :
103 13 : if (no_idmapping(mnt_userns, fs_userns))
104 13 : return VFSUIDT_INIT(kuid);
105 0 : if (initial_idmapping(fs_userns))
106 0 : uid = __kuid_val(kuid);
107 : else
108 0 : uid = from_kuid(fs_userns, kuid);
109 0 : if (uid == (uid_t)-1)
110 0 : return INVALID_VFSUID;
111 0 : 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 : */
135 3 : vfsgid_t make_vfsgid(struct mnt_idmap *idmap,
136 : struct user_namespace *fs_userns, kgid_t kgid)
137 : {
138 : gid_t gid;
139 3 : struct user_namespace *mnt_userns = idmap->owner;
140 :
141 3 : if (no_idmapping(mnt_userns, fs_userns))
142 3 : return VFSGIDT_INIT(kgid);
143 0 : if (initial_idmapping(fs_userns))
144 0 : gid = __kgid_val(kgid);
145 : else
146 0 : gid = from_kgid(fs_userns, kgid);
147 0 : if (gid == (gid_t)-1)
148 0 : return INVALID_VFSGID;
149 0 : 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 : */
164 8 : kuid_t from_vfsuid(struct mnt_idmap *idmap,
165 : struct user_namespace *fs_userns, vfsuid_t vfsuid)
166 : {
167 : uid_t uid;
168 8 : struct user_namespace *mnt_userns = idmap->owner;
169 :
170 8 : if (no_idmapping(mnt_userns, fs_userns))
171 8 : return AS_KUIDT(vfsuid);
172 0 : uid = from_kuid(mnt_userns, AS_KUIDT(vfsuid));
173 0 : if (uid == (uid_t)-1)
174 0 : return INVALID_UID;
175 0 : if (initial_idmapping(fs_userns))
176 0 : return KUIDT_INIT(uid);
177 0 : 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 : */
192 8 : kgid_t from_vfsgid(struct mnt_idmap *idmap,
193 : struct user_namespace *fs_userns, vfsgid_t vfsgid)
194 : {
195 : gid_t gid;
196 8 : struct user_namespace *mnt_userns = idmap->owner;
197 :
198 8 : if (no_idmapping(mnt_userns, fs_userns))
199 8 : return AS_KGIDT(vfsgid);
200 0 : gid = from_kgid(mnt_userns, AS_KGIDT(vfsgid));
201 0 : if (gid == (gid_t)-1)
202 0 : return INVALID_GID;
203 0 : if (initial_idmapping(fs_userns))
204 0 : return KGIDT_INIT(gid);
205 0 : 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 : */
219 0 : int vfsgid_in_group_p(vfsgid_t vfsgid)
220 : {
221 0 : return in_group_p(AS_KGIDT(vfsgid));
222 : }
223 : #else
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 :
231 0 : struct mnt_idmap *alloc_mnt_idmap(struct user_namespace *mnt_userns)
232 : {
233 : struct mnt_idmap *idmap;
234 :
235 0 : idmap = kzalloc(sizeof(struct mnt_idmap), GFP_KERNEL_ACCOUNT);
236 0 : if (!idmap)
237 : return ERR_PTR(-ENOMEM);
238 :
239 0 : idmap->owner = get_user_ns(mnt_userns);
240 0 : refcount_set(&idmap->count, 1);
241 0 : 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 : */
252 0 : struct mnt_idmap *mnt_idmap_get(struct mnt_idmap *idmap)
253 : {
254 0 : if (idmap != &nop_mnt_idmap)
255 0 : refcount_inc(&idmap->count);
256 :
257 0 : 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 : */
267 5 : void mnt_idmap_put(struct mnt_idmap *idmap)
268 : {
269 5 : if (idmap != &nop_mnt_idmap && refcount_dec_and_test(&idmap->count)) {
270 0 : put_user_ns(idmap->owner);
271 0 : kfree(idmap);
272 : }
273 5 : }
|