Commit | Line | Data |
---|---|---|
a793d79e CB |
1 | /* SPDX-License-Identifier: GPL-2.0 */ |
2 | #ifndef _LINUX_MNT_IDMAPPING_H | |
3 | #define _LINUX_MNT_IDMAPPING_H | |
4 | ||
5 | #include <linux/types.h> | |
6 | #include <linux/uidgid.h> | |
7 | ||
256c8aed | 8 | struct mnt_idmap; |
a793d79e | 9 | struct user_namespace; |
256c8aed CB |
10 | |
11 | extern struct mnt_idmap nop_mnt_idmap; | |
a793d79e CB |
12 | extern struct user_namespace init_user_ns; |
13 | ||
1e5267cd CB |
14 | typedef struct { |
15 | uid_t val; | |
16 | } vfsuid_t; | |
17 | ||
18 | typedef struct { | |
19 | gid_t val; | |
20 | } vfsgid_t; | |
21 | ||
45c31150 CB |
22 | static_assert(sizeof(vfsuid_t) == sizeof(kuid_t)); |
23 | static_assert(sizeof(vfsgid_t) == sizeof(kgid_t)); | |
24 | static_assert(offsetof(vfsuid_t, val) == offsetof(kuid_t, val)); | |
25 | static_assert(offsetof(vfsgid_t, val) == offsetof(kgid_t, val)); | |
26 | ||
1e5267cd CB |
27 | #ifdef CONFIG_MULTIUSER |
28 | static inline uid_t __vfsuid_val(vfsuid_t uid) | |
29 | { | |
30 | return uid.val; | |
31 | } | |
32 | ||
33 | static inline gid_t __vfsgid_val(vfsgid_t gid) | |
34 | { | |
35 | return gid.val; | |
36 | } | |
37 | #else | |
38 | static inline uid_t __vfsuid_val(vfsuid_t uid) | |
39 | { | |
40 | return 0; | |
41 | } | |
42 | ||
43 | static inline gid_t __vfsgid_val(vfsgid_t gid) | |
44 | { | |
45 | return 0; | |
46 | } | |
47 | #endif | |
48 | ||
49 | static inline bool vfsuid_valid(vfsuid_t uid) | |
50 | { | |
51 | return __vfsuid_val(uid) != (uid_t)-1; | |
52 | } | |
53 | ||
54 | static inline bool vfsgid_valid(vfsgid_t gid) | |
55 | { | |
56 | return __vfsgid_val(gid) != (gid_t)-1; | |
57 | } | |
58 | ||
59 | static inline bool vfsuid_eq(vfsuid_t left, vfsuid_t right) | |
60 | { | |
4d0548a7 | 61 | return vfsuid_valid(left) && __vfsuid_val(left) == __vfsuid_val(right); |
1e5267cd CB |
62 | } |
63 | ||
64 | static inline bool vfsgid_eq(vfsgid_t left, vfsgid_t right) | |
65 | { | |
4d0548a7 | 66 | return vfsgid_valid(left) && __vfsgid_val(left) == __vfsgid_val(right); |
1e5267cd CB |
67 | } |
68 | ||
69 | /** | |
70 | * vfsuid_eq_kuid - check whether kuid and vfsuid have the same value | |
1e5267cd | 71 | * @vfsuid: the vfsuid to compare |
77940f0d | 72 | * @kuid: the kuid to compare |
1e5267cd | 73 | * |
77940f0d | 74 | * Check whether @vfsuid and @kuid have the same values. |
1e5267cd | 75 | * |
77940f0d | 76 | * Return: true if @vfsuid and @kuid have the same value, false if not. |
4d0548a7 | 77 | * Comparison between two invalid uids returns false. |
1e5267cd CB |
78 | */ |
79 | static inline bool vfsuid_eq_kuid(vfsuid_t vfsuid, kuid_t kuid) | |
80 | { | |
4d0548a7 | 81 | return vfsuid_valid(vfsuid) && __vfsuid_val(vfsuid) == __kuid_val(kuid); |
1e5267cd CB |
82 | } |
83 | ||
84 | /** | |
85 | * vfsgid_eq_kgid - check whether kgid and vfsgid have the same value | |
1e5267cd | 86 | * @vfsgid: the vfsgid to compare |
77940f0d | 87 | * @kgid: the kgid to compare |
1e5267cd | 88 | * |
77940f0d | 89 | * Check whether @vfsgid and @kgid have the same values. |
1e5267cd | 90 | * |
77940f0d | 91 | * Return: true if @vfsgid and @kgid have the same value, false if not. |
4d0548a7 | 92 | * Comparison between two invalid gids returns false. |
1e5267cd | 93 | */ |
77940f0d | 94 | static inline bool vfsgid_eq_kgid(vfsgid_t vfsgid, kgid_t kgid) |
1e5267cd | 95 | { |
4d0548a7 | 96 | return vfsgid_valid(vfsgid) && __vfsgid_val(vfsgid) == __kgid_val(kgid); |
1e5267cd CB |
97 | } |
98 | ||
99 | /* | |
100 | * vfs{g,u}ids are created from k{g,u}ids. | |
101 | * We don't allow them to be created from regular {u,g}id. | |
102 | */ | |
103 | #define VFSUIDT_INIT(val) (vfsuid_t){ __kuid_val(val) } | |
104 | #define VFSGIDT_INIT(val) (vfsgid_t){ __kgid_val(val) } | |
105 | ||
106 | #define INVALID_VFSUID VFSUIDT_INIT(INVALID_UID) | |
107 | #define INVALID_VFSGID VFSGIDT_INIT(INVALID_GID) | |
108 | ||
109 | /* | |
110 | * Allow a vfs{g,u}id to be used as a k{g,u}id where we want to compare | |
111 | * whether the mapped value is identical to value of a k{g,u}id. | |
112 | */ | |
113 | #define AS_KUIDT(val) (kuid_t){ __vfsuid_val(val) } | |
114 | #define AS_KGIDT(val) (kgid_t){ __vfsgid_val(val) } | |
115 | ||
116 | #ifdef CONFIG_MULTIUSER | |
117 | /** | |
118 | * vfsgid_in_group_p() - check whether a vfsuid matches the caller's groups | |
119 | * @vfsgid: the mnt gid to match | |
120 | * | |
121 | * This function can be used to determine whether @vfsuid matches any of the | |
122 | * caller's groups. | |
123 | * | |
124 | * Return: 1 if vfsuid matches caller's groups, 0 if not. | |
125 | */ | |
126 | static inline int vfsgid_in_group_p(vfsgid_t vfsgid) | |
127 | { | |
128 | return in_group_p(AS_KGIDT(vfsgid)); | |
129 | } | |
130 | #else | |
131 | static inline int vfsgid_in_group_p(vfsgid_t vfsgid) | |
132 | { | |
133 | return 1; | |
134 | } | |
135 | #endif | |
136 | ||
1ac2a410 CB |
137 | /** |
138 | * initial_idmapping - check whether this is the initial mapping | |
139 | * @ns: idmapping to check | |
140 | * | |
141 | * Check whether this is the initial mapping, mapping 0 to 0, 1 to 1, | |
142 | * [...], 1000 to 1000 [...]. | |
143 | * | |
144 | * Return: true if this is the initial mapping, false if not. | |
145 | */ | |
146 | static inline bool initial_idmapping(const struct user_namespace *ns) | |
147 | { | |
148 | return ns == &init_user_ns; | |
149 | } | |
150 | ||
151 | /** | |
152 | * no_idmapping - check whether we can skip remapping a kuid/gid | |
153 | * @mnt_userns: the mount's idmapping | |
154 | * @fs_userns: the filesystem's idmapping | |
155 | * | |
156 | * This function can be used to check whether a remapping between two | |
157 | * idmappings is required. | |
158 | * An idmapped mount is a mount that has an idmapping attached to it that | |
159 | * is different from the filsystem's idmapping and the initial idmapping. | |
160 | * If the initial mapping is used or the idmapping of the mount and the | |
161 | * filesystem are identical no remapping is required. | |
162 | * | |
163 | * Return: true if remapping can be skipped, false if not. | |
164 | */ | |
165 | static inline bool no_idmapping(const struct user_namespace *mnt_userns, | |
166 | const struct user_namespace *fs_userns) | |
167 | { | |
168 | return initial_idmapping(mnt_userns) || mnt_userns == fs_userns; | |
169 | } | |
170 | ||
171 | /** | |
77940f0d | 172 | * make_vfsuid - map a filesystem kuid into a mnt_userns |
4d7ca409 | 173 | * @idmap: the mount's idmapping |
1ac2a410 CB |
174 | * @fs_userns: the filesystem's idmapping |
175 | * @kuid : kuid to be mapped | |
176 | * | |
177 | * Take a @kuid and remap it from @fs_userns into @mnt_userns. Use this | |
178 | * function when preparing a @kuid to be reported to userspace. | |
179 | * | |
180 | * If no_idmapping() determines that this is not an idmapped mount we can | |
181 | * simply return @kuid unchanged. | |
182 | * If initial_idmapping() tells us that the filesystem is not mounted with an | |
183 | * idmapping we know the value of @kuid won't change when calling | |
184 | * from_kuid() so we can simply retrieve the value via __kuid_val() | |
185 | * directly. | |
186 | * | |
187 | * Return: @kuid mapped according to @mnt_userns. | |
188 | * If @kuid has no mapping in either @mnt_userns or @fs_userns INVALID_UID is | |
189 | * returned. | |
190 | */ | |
1e5267cd | 191 | |
4d7ca409 | 192 | static inline vfsuid_t make_vfsuid(struct mnt_idmap *idmap, |
1e5267cd CB |
193 | struct user_namespace *fs_userns, |
194 | kuid_t kuid) | |
1ac2a410 CB |
195 | { |
196 | uid_t uid; | |
4d7ca409 | 197 | struct user_namespace *mnt_userns = mnt_idmap_owner(idmap); |
1ac2a410 CB |
198 | |
199 | if (no_idmapping(mnt_userns, fs_userns)) | |
1e5267cd | 200 | return VFSUIDT_INIT(kuid); |
1ac2a410 CB |
201 | if (initial_idmapping(fs_userns)) |
202 | uid = __kuid_val(kuid); | |
203 | else | |
204 | uid = from_kuid(fs_userns, kuid); | |
205 | if (uid == (uid_t)-1) | |
1e5267cd CB |
206 | return INVALID_VFSUID; |
207 | return VFSUIDT_INIT(make_kuid(mnt_userns, uid)); | |
208 | } | |
209 | ||
1ac2a410 | 210 | /** |
77940f0d | 211 | * make_vfsgid - map a filesystem kgid into a mnt_userns |
4d7ca409 | 212 | * @idmap: the mount's idmapping |
1ac2a410 CB |
213 | * @fs_userns: the filesystem's idmapping |
214 | * @kgid : kgid to be mapped | |
215 | * | |
216 | * Take a @kgid and remap it from @fs_userns into @mnt_userns. Use this | |
217 | * function when preparing a @kgid to be reported to userspace. | |
218 | * | |
219 | * If no_idmapping() determines that this is not an idmapped mount we can | |
220 | * simply return @kgid unchanged. | |
221 | * If initial_idmapping() tells us that the filesystem is not mounted with an | |
222 | * idmapping we know the value of @kgid won't change when calling | |
223 | * from_kgid() so we can simply retrieve the value via __kgid_val() | |
224 | * directly. | |
225 | * | |
226 | * Return: @kgid mapped according to @mnt_userns. | |
227 | * If @kgid has no mapping in either @mnt_userns or @fs_userns INVALID_GID is | |
228 | * returned. | |
229 | */ | |
1e5267cd | 230 | |
4d7ca409 | 231 | static inline vfsgid_t make_vfsgid(struct mnt_idmap *idmap, |
1e5267cd CB |
232 | struct user_namespace *fs_userns, |
233 | kgid_t kgid) | |
1ac2a410 CB |
234 | { |
235 | gid_t gid; | |
4d7ca409 | 236 | struct user_namespace *mnt_userns = mnt_idmap_owner(idmap); |
1ac2a410 CB |
237 | |
238 | if (no_idmapping(mnt_userns, fs_userns)) | |
1e5267cd | 239 | return VFSGIDT_INIT(kgid); |
1ac2a410 CB |
240 | if (initial_idmapping(fs_userns)) |
241 | gid = __kgid_val(kgid); | |
242 | else | |
243 | gid = from_kgid(fs_userns, kgid); | |
244 | if (gid == (gid_t)-1) | |
1e5267cd CB |
245 | return INVALID_VFSGID; |
246 | return VFSGIDT_INIT(make_kgid(mnt_userns, gid)); | |
247 | } | |
248 | ||
1e5267cd CB |
249 | /** |
250 | * from_vfsuid - map a vfsuid into the filesystem idmapping | |
4d7ca409 | 251 | * @idmap: the mount's idmapping |
1e5267cd CB |
252 | * @fs_userns: the filesystem's idmapping |
253 | * @vfsuid : vfsuid to be mapped | |
254 | * | |
255 | * Map @vfsuid into the filesystem idmapping. This function has to be used in | |
256 | * order to e.g. write @vfsuid to inode->i_uid. | |
257 | * | |
258 | * Return: @vfsuid mapped into the filesystem idmapping | |
259 | */ | |
4d7ca409 | 260 | static inline kuid_t from_vfsuid(struct mnt_idmap *idmap, |
1e5267cd CB |
261 | struct user_namespace *fs_userns, |
262 | vfsuid_t vfsuid) | |
263 | { | |
264 | uid_t uid; | |
4d7ca409 | 265 | struct user_namespace *mnt_userns = mnt_idmap_owner(idmap); |
1e5267cd CB |
266 | |
267 | if (no_idmapping(mnt_userns, fs_userns)) | |
268 | return AS_KUIDT(vfsuid); | |
269 | uid = from_kuid(mnt_userns, AS_KUIDT(vfsuid)); | |
270 | if (uid == (uid_t)-1) | |
271 | return INVALID_UID; | |
272 | if (initial_idmapping(fs_userns)) | |
273 | return KUIDT_INIT(uid); | |
274 | return make_kuid(fs_userns, uid); | |
1ac2a410 CB |
275 | } |
276 | ||
1e5267cd CB |
277 | /** |
278 | * vfsuid_has_fsmapping - check whether a vfsuid maps into the filesystem | |
4d7ca409 | 279 | * @idmap: the mount's idmapping |
1e5267cd CB |
280 | * @fs_userns: the filesystem's idmapping |
281 | * @vfsuid: vfsuid to be mapped | |
282 | * | |
283 | * Check whether @vfsuid has a mapping in the filesystem idmapping. Use this | |
284 | * function to check whether the filesystem idmapping has a mapping for | |
285 | * @vfsuid. | |
286 | * | |
287 | * Return: true if @vfsuid has a mapping in the filesystem, false if not. | |
288 | */ | |
4d7ca409 | 289 | static inline bool vfsuid_has_fsmapping(struct mnt_idmap *idmap, |
1e5267cd CB |
290 | struct user_namespace *fs_userns, |
291 | vfsuid_t vfsuid) | |
292 | { | |
4d7ca409 | 293 | return uid_valid(from_vfsuid(idmap, fs_userns, vfsuid)); |
1e5267cd CB |
294 | } |
295 | ||
9c4f28dd CB |
296 | static inline bool vfsuid_has_mapping(struct user_namespace *userns, |
297 | vfsuid_t vfsuid) | |
298 | { | |
299 | return from_kuid(userns, AS_KUIDT(vfsuid)) != (uid_t)-1; | |
300 | } | |
301 | ||
c9fa2b07 CB |
302 | /** |
303 | * vfsuid_into_kuid - convert vfsuid into kuid | |
304 | * @vfsuid: the vfsuid to convert | |
305 | * | |
306 | * This can be used when a vfsuid is committed as a kuid. | |
307 | * | |
308 | * Return: a kuid with the value of @vfsuid | |
309 | */ | |
310 | static inline kuid_t vfsuid_into_kuid(vfsuid_t vfsuid) | |
311 | { | |
312 | return AS_KUIDT(vfsuid); | |
313 | } | |
314 | ||
1e5267cd CB |
315 | /** |
316 | * from_vfsgid - map a vfsgid into the filesystem idmapping | |
4d7ca409 | 317 | * @idmap: the mount's idmapping |
1e5267cd CB |
318 | * @fs_userns: the filesystem's idmapping |
319 | * @vfsgid : vfsgid to be mapped | |
320 | * | |
321 | * Map @vfsgid into the filesystem idmapping. This function has to be used in | |
322 | * order to e.g. write @vfsgid to inode->i_gid. | |
323 | * | |
324 | * Return: @vfsgid mapped into the filesystem idmapping | |
325 | */ | |
4d7ca409 | 326 | static inline kgid_t from_vfsgid(struct mnt_idmap *idmap, |
1e5267cd CB |
327 | struct user_namespace *fs_userns, |
328 | vfsgid_t vfsgid) | |
329 | { | |
330 | gid_t gid; | |
4d7ca409 | 331 | struct user_namespace *mnt_userns = mnt_idmap_owner(idmap); |
1ac2a410 CB |
332 | |
333 | if (no_idmapping(mnt_userns, fs_userns)) | |
1e5267cd CB |
334 | return AS_KGIDT(vfsgid); |
335 | gid = from_kgid(mnt_userns, AS_KGIDT(vfsgid)); | |
336 | if (gid == (gid_t)-1) | |
337 | return INVALID_GID; | |
1ac2a410 | 338 | if (initial_idmapping(fs_userns)) |
1e5267cd CB |
339 | return KGIDT_INIT(gid); |
340 | return make_kgid(fs_userns, gid); | |
1ac2a410 CB |
341 | } |
342 | ||
1e5267cd CB |
343 | /** |
344 | * vfsgid_has_fsmapping - check whether a vfsgid maps into the filesystem | |
4d7ca409 | 345 | * @idmap: the mount's idmapping |
1e5267cd CB |
346 | * @fs_userns: the filesystem's idmapping |
347 | * @vfsgid: vfsgid to be mapped | |
348 | * | |
349 | * Check whether @vfsgid has a mapping in the filesystem idmapping. Use this | |
350 | * function to check whether the filesystem idmapping has a mapping for | |
351 | * @vfsgid. | |
352 | * | |
353 | * Return: true if @vfsgid has a mapping in the filesystem, false if not. | |
354 | */ | |
4d7ca409 | 355 | static inline bool vfsgid_has_fsmapping(struct mnt_idmap *idmap, |
1e5267cd CB |
356 | struct user_namespace *fs_userns, |
357 | vfsgid_t vfsgid) | |
358 | { | |
4d7ca409 | 359 | return gid_valid(from_vfsgid(idmap, fs_userns, vfsgid)); |
1ac2a410 CB |
360 | } |
361 | ||
9c4f28dd CB |
362 | static inline bool vfsgid_has_mapping(struct user_namespace *userns, |
363 | vfsgid_t vfsgid) | |
364 | { | |
365 | return from_kgid(userns, AS_KGIDT(vfsgid)) != (gid_t)-1; | |
366 | } | |
367 | ||
c9fa2b07 CB |
368 | /** |
369 | * vfsgid_into_kgid - convert vfsgid into kgid | |
370 | * @vfsgid: the vfsgid to convert | |
371 | * | |
372 | * This can be used when a vfsgid is committed as a kgid. | |
373 | * | |
374 | * Return: a kgid with the value of @vfsgid | |
375 | */ | |
376 | static inline kgid_t vfsgid_into_kgid(vfsgid_t vfsgid) | |
377 | { | |
378 | return AS_KGIDT(vfsgid); | |
379 | } | |
380 | ||
a793d79e | 381 | /** |
c14329d3 CB |
382 | * mapped_fsuid - return caller's fsuid mapped according to an idmapping |
383 | * @idmap: the mount's idmapping | |
209188ce | 384 | * @fs_userns: the filesystem's idmapping |
a793d79e CB |
385 | * |
386 | * Use this helper to initialize a new vfs or filesystem object based on | |
387 | * the caller's fsuid. A common example is initializing the i_uid field of | |
388 | * a newly allocated inode triggered by a creation event such as mkdir or | |
389 | * O_CREAT. Other examples include the allocation of quotas for a specific | |
390 | * user. | |
391 | * | |
c14329d3 | 392 | * Return: the caller's current fsuid mapped up according to @idmap. |
a793d79e | 393 | */ |
c14329d3 | 394 | static inline kuid_t mapped_fsuid(struct mnt_idmap *idmap, |
209188ce | 395 | struct user_namespace *fs_userns) |
a793d79e | 396 | { |
4d7ca409 | 397 | return from_vfsuid(idmap, fs_userns, VFSUIDT_INIT(current_fsuid())); |
a793d79e CB |
398 | } |
399 | ||
400 | /** | |
c14329d3 CB |
401 | * mapped_fsgid - return caller's fsgid mapped according to an idmapping |
402 | * @idmap: the mount's idmapping | |
209188ce | 403 | * @fs_userns: the filesystem's idmapping |
a793d79e CB |
404 | * |
405 | * Use this helper to initialize a new vfs or filesystem object based on | |
406 | * the caller's fsgid. A common example is initializing the i_gid field of | |
407 | * a newly allocated inode triggered by a creation event such as mkdir or | |
408 | * O_CREAT. Other examples include the allocation of quotas for a specific | |
409 | * user. | |
410 | * | |
c14329d3 | 411 | * Return: the caller's current fsgid mapped up according to @idmap. |
a793d79e | 412 | */ |
c14329d3 | 413 | static inline kgid_t mapped_fsgid(struct mnt_idmap *idmap, |
209188ce | 414 | struct user_namespace *fs_userns) |
a793d79e | 415 | { |
4d7ca409 | 416 | return from_vfsgid(idmap, fs_userns, VFSGIDT_INIT(current_fsgid())); |
a793d79e CB |
417 | } |
418 | ||
419 | #endif /* _LINUX_MNT_IDMAPPING_H */ |