1 | /* |
2 | * Mapping of UID/GIDs to name and vice versa. |
3 | * |
4 | * Copyright (c) 2002, 2003 The Regents of the University of |
5 | * Michigan. All rights reserved. |
6 | * |
7 | * Marius Aamodt Eriksen <marius@umich.edu> |
8 | * |
9 | * Redistribution and use in source and binary forms, with or without |
10 | * modification, are permitted provided that the following conditions |
11 | * are met: |
12 | * |
13 | * 1. Redistributions of source code must retain the above copyright |
14 | * notice, this list of conditions and the following disclaimer. |
15 | * 2. Redistributions in binary form must reproduce the above copyright |
16 | * notice, this list of conditions and the following disclaimer in the |
17 | * documentation and/or other materials provided with the distribution. |
18 | * 3. Neither the name of the University nor the names of its |
19 | * contributors may be used to endorse or promote products derived |
20 | * from this software without specific prior written permission. |
21 | * |
22 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED |
23 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF |
24 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
25 | * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
26 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
27 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
28 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR |
29 | * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
30 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
31 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
32 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
33 | */ |
34 | |
35 | #include <linux/module.h> |
36 | #include <linux/seq_file.h> |
37 | #include <linux/sched.h> |
38 | #include <linux/slab.h> |
39 | #include <linux/sunrpc/svc_xprt.h> |
40 | #include <net/net_namespace.h> |
41 | #include "idmap.h" |
42 | #include "nfsd.h" |
43 | #include "netns.h" |
44 | #include "vfs.h" |
45 | |
46 | /* |
47 | * Turn off idmapping when using AUTH_SYS. |
48 | */ |
49 | static bool nfs4_disable_idmapping = true; |
50 | module_param(nfs4_disable_idmapping, bool, 0644); |
51 | MODULE_PARM_DESC(nfs4_disable_idmapping, |
52 | "Turn off server's NFSv4 idmapping when using 'sec=sys'" ); |
53 | |
54 | /* |
55 | * Cache entry |
56 | */ |
57 | |
58 | /* |
59 | * XXX we know that IDMAP_NAMESZ < PAGE_SIZE, but it's ugly to rely on |
60 | * that. |
61 | */ |
62 | |
63 | struct ent { |
64 | struct cache_head h; |
65 | int type; /* User / Group */ |
66 | u32 id; |
67 | char name[IDMAP_NAMESZ]; |
68 | char authname[IDMAP_NAMESZ]; |
69 | struct rcu_head rcu_head; |
70 | }; |
71 | |
72 | /* Common entry handling */ |
73 | |
74 | #define ENT_HASHBITS 8 |
75 | #define ENT_HASHMAX (1 << ENT_HASHBITS) |
76 | |
77 | static void |
78 | ent_init(struct cache_head *cnew, struct cache_head *citm) |
79 | { |
80 | struct ent *new = container_of(cnew, struct ent, h); |
81 | struct ent *itm = container_of(citm, struct ent, h); |
82 | |
83 | new->id = itm->id; |
84 | new->type = itm->type; |
85 | |
86 | strscpy(p: new->name, q: itm->name, size: sizeof(new->name)); |
87 | strscpy(p: new->authname, q: itm->authname, size: sizeof(new->authname)); |
88 | } |
89 | |
90 | static void |
91 | ent_put(struct kref *ref) |
92 | { |
93 | struct ent *map = container_of(ref, struct ent, h.ref); |
94 | kfree_rcu(map, rcu_head); |
95 | } |
96 | |
97 | static struct cache_head * |
98 | ent_alloc(void) |
99 | { |
100 | struct ent *e = kmalloc(size: sizeof(*e), GFP_KERNEL); |
101 | if (e) |
102 | return &e->h; |
103 | else |
104 | return NULL; |
105 | } |
106 | |
107 | /* |
108 | * ID -> Name cache |
109 | */ |
110 | |
111 | static uint32_t |
112 | idtoname_hash(struct ent *ent) |
113 | { |
114 | uint32_t hash; |
115 | |
116 | hash = hash_str(name: ent->authname, ENT_HASHBITS); |
117 | hash = hash_long(hash ^ ent->id, ENT_HASHBITS); |
118 | |
119 | /* Flip LSB for user/group */ |
120 | if (ent->type == IDMAP_TYPE_GROUP) |
121 | hash ^= 1; |
122 | |
123 | return hash; |
124 | } |
125 | |
126 | static int |
127 | idtoname_upcall(struct cache_detail *cd, struct cache_head *h) |
128 | { |
129 | return sunrpc_cache_pipe_upcall_timeout(detail: cd, h); |
130 | } |
131 | |
132 | static void |
133 | idtoname_request(struct cache_detail *cd, struct cache_head *ch, char **bpp, |
134 | int *blen) |
135 | { |
136 | struct ent *ent = container_of(ch, struct ent, h); |
137 | char idstr[11]; |
138 | |
139 | qword_add(bpp, lp: blen, str: ent->authname); |
140 | snprintf(buf: idstr, size: sizeof(idstr), fmt: "%u" , ent->id); |
141 | qword_add(bpp, lp: blen, str: ent->type == IDMAP_TYPE_GROUP ? "group" : "user" ); |
142 | qword_add(bpp, lp: blen, str: idstr); |
143 | |
144 | (*bpp)[-1] = '\n'; |
145 | } |
146 | |
147 | static int |
148 | idtoname_match(struct cache_head *ca, struct cache_head *cb) |
149 | { |
150 | struct ent *a = container_of(ca, struct ent, h); |
151 | struct ent *b = container_of(cb, struct ent, h); |
152 | |
153 | return (a->id == b->id && a->type == b->type && |
154 | strcmp(a->authname, b->authname) == 0); |
155 | } |
156 | |
157 | static int |
158 | idtoname_show(struct seq_file *m, struct cache_detail *cd, struct cache_head *h) |
159 | { |
160 | struct ent *ent; |
161 | |
162 | if (h == NULL) { |
163 | seq_puts(m, s: "#domain type id [name]\n" ); |
164 | return 0; |
165 | } |
166 | ent = container_of(h, struct ent, h); |
167 | seq_printf(m, fmt: "%s %s %u" , ent->authname, |
168 | ent->type == IDMAP_TYPE_GROUP ? "group" : "user" , |
169 | ent->id); |
170 | if (test_bit(CACHE_VALID, &h->flags)) |
171 | seq_printf(m, fmt: " %s" , ent->name); |
172 | seq_putc(m, c: '\n'); |
173 | return 0; |
174 | } |
175 | |
176 | static void |
177 | warn_no_idmapd(struct cache_detail *detail, int has_died) |
178 | { |
179 | printk("nfsd: nfsv4 idmapping failing: has idmapd %s?\n" , |
180 | has_died ? "died" : "not been started" ); |
181 | } |
182 | |
183 | |
184 | static int idtoname_parse(struct cache_detail *, char *, int); |
185 | static struct ent *idtoname_lookup(struct cache_detail *, struct ent *); |
186 | static struct ent *idtoname_update(struct cache_detail *, struct ent *, |
187 | struct ent *); |
188 | |
189 | static const struct cache_detail idtoname_cache_template = { |
190 | .owner = THIS_MODULE, |
191 | .hash_size = ENT_HASHMAX, |
192 | .name = "nfs4.idtoname" , |
193 | .cache_put = ent_put, |
194 | .cache_upcall = idtoname_upcall, |
195 | .cache_request = idtoname_request, |
196 | .cache_parse = idtoname_parse, |
197 | .cache_show = idtoname_show, |
198 | .warn_no_listener = warn_no_idmapd, |
199 | .match = idtoname_match, |
200 | .init = ent_init, |
201 | .update = ent_init, |
202 | .alloc = ent_alloc, |
203 | }; |
204 | |
205 | static int |
206 | idtoname_parse(struct cache_detail *cd, char *buf, int buflen) |
207 | { |
208 | struct ent ent, *res; |
209 | char *buf1, *bp; |
210 | int len; |
211 | int error = -EINVAL; |
212 | |
213 | if (buf[buflen - 1] != '\n') |
214 | return (-EINVAL); |
215 | buf[buflen - 1]= '\0'; |
216 | |
217 | buf1 = kmalloc(PAGE_SIZE, GFP_KERNEL); |
218 | if (buf1 == NULL) |
219 | return (-ENOMEM); |
220 | |
221 | memset(&ent, 0, sizeof(ent)); |
222 | |
223 | /* Authentication name */ |
224 | len = qword_get(bpp: &buf, dest: buf1, PAGE_SIZE); |
225 | if (len <= 0 || len >= IDMAP_NAMESZ) |
226 | goto out; |
227 | memcpy(ent.authname, buf1, sizeof(ent.authname)); |
228 | |
229 | /* Type */ |
230 | if (qword_get(bpp: &buf, dest: buf1, PAGE_SIZE) <= 0) |
231 | goto out; |
232 | ent.type = strcmp(buf1, "user" ) == 0 ? |
233 | IDMAP_TYPE_USER : IDMAP_TYPE_GROUP; |
234 | |
235 | /* ID */ |
236 | if (qword_get(bpp: &buf, dest: buf1, PAGE_SIZE) <= 0) |
237 | goto out; |
238 | ent.id = simple_strtoul(buf1, &bp, 10); |
239 | if (bp == buf1) |
240 | goto out; |
241 | |
242 | /* expiry */ |
243 | error = get_expiry(bpp: &buf, rvp: &ent.h.expiry_time); |
244 | if (error) |
245 | goto out; |
246 | |
247 | error = -ENOMEM; |
248 | res = idtoname_lookup(cd, &ent); |
249 | if (!res) |
250 | goto out; |
251 | |
252 | /* Name */ |
253 | error = -EINVAL; |
254 | len = qword_get(bpp: &buf, dest: buf1, PAGE_SIZE); |
255 | if (len < 0 || len >= IDMAP_NAMESZ) |
256 | goto out; |
257 | if (len == 0) |
258 | set_bit(nr: CACHE_NEGATIVE, addr: &ent.h.flags); |
259 | else |
260 | memcpy(ent.name, buf1, sizeof(ent.name)); |
261 | error = -ENOMEM; |
262 | res = idtoname_update(cd, &ent, res); |
263 | if (res == NULL) |
264 | goto out; |
265 | |
266 | cache_put(h: &res->h, cd); |
267 | error = 0; |
268 | out: |
269 | kfree(objp: buf1); |
270 | return error; |
271 | } |
272 | |
273 | static struct ent * |
274 | idtoname_lookup(struct cache_detail *cd, struct ent *item) |
275 | { |
276 | struct cache_head *ch = sunrpc_cache_lookup_rcu(detail: cd, key: &item->h, |
277 | hash: idtoname_hash(ent: item)); |
278 | if (ch) |
279 | return container_of(ch, struct ent, h); |
280 | else |
281 | return NULL; |
282 | } |
283 | |
284 | static struct ent * |
285 | idtoname_update(struct cache_detail *cd, struct ent *new, struct ent *old) |
286 | { |
287 | struct cache_head *ch = sunrpc_cache_update(detail: cd, new: &new->h, old: &old->h, |
288 | hash: idtoname_hash(ent: new)); |
289 | if (ch) |
290 | return container_of(ch, struct ent, h); |
291 | else |
292 | return NULL; |
293 | } |
294 | |
295 | |
296 | /* |
297 | * Name -> ID cache |
298 | */ |
299 | |
300 | static inline int |
301 | nametoid_hash(struct ent *ent) |
302 | { |
303 | return hash_str(name: ent->name, ENT_HASHBITS); |
304 | } |
305 | |
306 | static int |
307 | nametoid_upcall(struct cache_detail *cd, struct cache_head *h) |
308 | { |
309 | return sunrpc_cache_pipe_upcall_timeout(detail: cd, h); |
310 | } |
311 | |
312 | static void |
313 | nametoid_request(struct cache_detail *cd, struct cache_head *ch, char **bpp, |
314 | int *blen) |
315 | { |
316 | struct ent *ent = container_of(ch, struct ent, h); |
317 | |
318 | qword_add(bpp, lp: blen, str: ent->authname); |
319 | qword_add(bpp, lp: blen, str: ent->type == IDMAP_TYPE_GROUP ? "group" : "user" ); |
320 | qword_add(bpp, lp: blen, str: ent->name); |
321 | |
322 | (*bpp)[-1] = '\n'; |
323 | } |
324 | |
325 | static int |
326 | nametoid_match(struct cache_head *ca, struct cache_head *cb) |
327 | { |
328 | struct ent *a = container_of(ca, struct ent, h); |
329 | struct ent *b = container_of(cb, struct ent, h); |
330 | |
331 | return (a->type == b->type && strcmp(a->name, b->name) == 0 && |
332 | strcmp(a->authname, b->authname) == 0); |
333 | } |
334 | |
335 | static int |
336 | nametoid_show(struct seq_file *m, struct cache_detail *cd, struct cache_head *h) |
337 | { |
338 | struct ent *ent; |
339 | |
340 | if (h == NULL) { |
341 | seq_puts(m, s: "#domain type name [id]\n" ); |
342 | return 0; |
343 | } |
344 | ent = container_of(h, struct ent, h); |
345 | seq_printf(m, fmt: "%s %s %s" , ent->authname, |
346 | ent->type == IDMAP_TYPE_GROUP ? "group" : "user" , |
347 | ent->name); |
348 | if (test_bit(CACHE_VALID, &h->flags)) |
349 | seq_printf(m, fmt: " %u" , ent->id); |
350 | seq_putc(m, c: '\n'); |
351 | return 0; |
352 | } |
353 | |
354 | static struct ent *nametoid_lookup(struct cache_detail *, struct ent *); |
355 | static struct ent *nametoid_update(struct cache_detail *, struct ent *, |
356 | struct ent *); |
357 | static int nametoid_parse(struct cache_detail *, char *, int); |
358 | |
359 | static const struct cache_detail nametoid_cache_template = { |
360 | .owner = THIS_MODULE, |
361 | .hash_size = ENT_HASHMAX, |
362 | .name = "nfs4.nametoid" , |
363 | .cache_put = ent_put, |
364 | .cache_upcall = nametoid_upcall, |
365 | .cache_request = nametoid_request, |
366 | .cache_parse = nametoid_parse, |
367 | .cache_show = nametoid_show, |
368 | .warn_no_listener = warn_no_idmapd, |
369 | .match = nametoid_match, |
370 | .init = ent_init, |
371 | .update = ent_init, |
372 | .alloc = ent_alloc, |
373 | }; |
374 | |
375 | static int |
376 | nametoid_parse(struct cache_detail *cd, char *buf, int buflen) |
377 | { |
378 | struct ent ent, *res; |
379 | char *buf1; |
380 | int len, error = -EINVAL; |
381 | |
382 | if (buf[buflen - 1] != '\n') |
383 | return (-EINVAL); |
384 | buf[buflen - 1]= '\0'; |
385 | |
386 | buf1 = kmalloc(PAGE_SIZE, GFP_KERNEL); |
387 | if (buf1 == NULL) |
388 | return (-ENOMEM); |
389 | |
390 | memset(&ent, 0, sizeof(ent)); |
391 | |
392 | /* Authentication name */ |
393 | len = qword_get(bpp: &buf, dest: buf1, PAGE_SIZE); |
394 | if (len <= 0 || len >= IDMAP_NAMESZ) |
395 | goto out; |
396 | memcpy(ent.authname, buf1, sizeof(ent.authname)); |
397 | |
398 | /* Type */ |
399 | if (qword_get(bpp: &buf, dest: buf1, PAGE_SIZE) <= 0) |
400 | goto out; |
401 | ent.type = strcmp(buf1, "user" ) == 0 ? |
402 | IDMAP_TYPE_USER : IDMAP_TYPE_GROUP; |
403 | |
404 | /* Name */ |
405 | len = qword_get(bpp: &buf, dest: buf1, PAGE_SIZE); |
406 | if (len <= 0 || len >= IDMAP_NAMESZ) |
407 | goto out; |
408 | memcpy(ent.name, buf1, sizeof(ent.name)); |
409 | |
410 | /* expiry */ |
411 | error = get_expiry(bpp: &buf, rvp: &ent.h.expiry_time); |
412 | if (error) |
413 | goto out; |
414 | |
415 | /* ID */ |
416 | error = get_int(bpp: &buf, anint: &ent.id); |
417 | if (error == -EINVAL) |
418 | goto out; |
419 | if (error == -ENOENT) |
420 | set_bit(nr: CACHE_NEGATIVE, addr: &ent.h.flags); |
421 | |
422 | error = -ENOMEM; |
423 | res = nametoid_lookup(cd, &ent); |
424 | if (res == NULL) |
425 | goto out; |
426 | res = nametoid_update(cd, &ent, res); |
427 | if (res == NULL) |
428 | goto out; |
429 | |
430 | cache_put(h: &res->h, cd); |
431 | error = 0; |
432 | out: |
433 | kfree(objp: buf1); |
434 | return (error); |
435 | } |
436 | |
437 | |
438 | static struct ent * |
439 | nametoid_lookup(struct cache_detail *cd, struct ent *item) |
440 | { |
441 | struct cache_head *ch = sunrpc_cache_lookup_rcu(detail: cd, key: &item->h, |
442 | hash: nametoid_hash(ent: item)); |
443 | if (ch) |
444 | return container_of(ch, struct ent, h); |
445 | else |
446 | return NULL; |
447 | } |
448 | |
449 | static struct ent * |
450 | nametoid_update(struct cache_detail *cd, struct ent *new, struct ent *old) |
451 | { |
452 | struct cache_head *ch = sunrpc_cache_update(detail: cd, new: &new->h, old: &old->h, |
453 | hash: nametoid_hash(ent: new)); |
454 | if (ch) |
455 | return container_of(ch, struct ent, h); |
456 | else |
457 | return NULL; |
458 | } |
459 | |
460 | /* |
461 | * Exported API |
462 | */ |
463 | |
464 | int |
465 | nfsd_idmap_init(struct net *net) |
466 | { |
467 | int rv; |
468 | struct nfsd_net *nn = net_generic(net, id: nfsd_net_id); |
469 | |
470 | nn->idtoname_cache = cache_create_net(tmpl: &idtoname_cache_template, net); |
471 | if (IS_ERR(ptr: nn->idtoname_cache)) |
472 | return PTR_ERR(ptr: nn->idtoname_cache); |
473 | rv = cache_register_net(cd: nn->idtoname_cache, net); |
474 | if (rv) |
475 | goto destroy_idtoname_cache; |
476 | nn->nametoid_cache = cache_create_net(tmpl: &nametoid_cache_template, net); |
477 | if (IS_ERR(ptr: nn->nametoid_cache)) { |
478 | rv = PTR_ERR(ptr: nn->nametoid_cache); |
479 | goto unregister_idtoname_cache; |
480 | } |
481 | rv = cache_register_net(cd: nn->nametoid_cache, net); |
482 | if (rv) |
483 | goto destroy_nametoid_cache; |
484 | return 0; |
485 | |
486 | destroy_nametoid_cache: |
487 | cache_destroy_net(cd: nn->nametoid_cache, net); |
488 | unregister_idtoname_cache: |
489 | cache_unregister_net(cd: nn->idtoname_cache, net); |
490 | destroy_idtoname_cache: |
491 | cache_destroy_net(cd: nn->idtoname_cache, net); |
492 | return rv; |
493 | } |
494 | |
495 | void |
496 | nfsd_idmap_shutdown(struct net *net) |
497 | { |
498 | struct nfsd_net *nn = net_generic(net, id: nfsd_net_id); |
499 | |
500 | cache_unregister_net(cd: nn->idtoname_cache, net); |
501 | cache_unregister_net(cd: nn->nametoid_cache, net); |
502 | cache_destroy_net(cd: nn->idtoname_cache, net); |
503 | cache_destroy_net(cd: nn->nametoid_cache, net); |
504 | } |
505 | |
506 | static int |
507 | idmap_lookup(struct svc_rqst *rqstp, |
508 | struct ent *(*lookup_fn)(struct cache_detail *, struct ent *), |
509 | struct ent *key, struct cache_detail *detail, struct ent **item) |
510 | { |
511 | int ret; |
512 | |
513 | *item = lookup_fn(detail, key); |
514 | if (!*item) |
515 | return -ENOMEM; |
516 | retry: |
517 | ret = cache_check(detail, h: &(*item)->h, rqstp: &rqstp->rq_chandle); |
518 | |
519 | if (ret == -ETIMEDOUT) { |
520 | struct ent *prev_item = *item; |
521 | *item = lookup_fn(detail, key); |
522 | if (*item != prev_item) |
523 | goto retry; |
524 | cache_put(h: &(*item)->h, cd: detail); |
525 | } |
526 | return ret; |
527 | } |
528 | |
529 | static char * |
530 | rqst_authname(struct svc_rqst *rqstp) |
531 | { |
532 | struct auth_domain *clp; |
533 | |
534 | clp = rqstp->rq_gssclient ? rqstp->rq_gssclient : rqstp->rq_client; |
535 | return clp->name; |
536 | } |
537 | |
538 | static __be32 |
539 | idmap_name_to_id(struct svc_rqst *rqstp, int type, const char *name, u32 namelen, |
540 | u32 *id) |
541 | { |
542 | struct ent *item, key = { |
543 | .type = type, |
544 | }; |
545 | int ret; |
546 | struct nfsd_net *nn = net_generic(SVC_NET(rqstp), id: nfsd_net_id); |
547 | |
548 | if (namelen + 1 > sizeof(key.name)) |
549 | return nfserr_badowner; |
550 | memcpy(key.name, name, namelen); |
551 | key.name[namelen] = '\0'; |
552 | strscpy(p: key.authname, q: rqst_authname(rqstp), size: sizeof(key.authname)); |
553 | ret = idmap_lookup(rqstp, lookup_fn: nametoid_lookup, key: &key, detail: nn->nametoid_cache, item: &item); |
554 | if (ret == -ENOENT) |
555 | return nfserr_badowner; |
556 | if (ret) |
557 | return nfserrno(errno: ret); |
558 | *id = item->id; |
559 | cache_put(h: &item->h, cd: nn->nametoid_cache); |
560 | return 0; |
561 | } |
562 | |
563 | static __be32 encode_ascii_id(struct xdr_stream *xdr, u32 id) |
564 | { |
565 | char buf[11]; |
566 | int len; |
567 | __be32 *p; |
568 | |
569 | len = sprintf(buf, fmt: "%u" , id); |
570 | p = xdr_reserve_space(xdr, nbytes: len + 4); |
571 | if (!p) |
572 | return nfserr_resource; |
573 | p = xdr_encode_opaque(p, ptr: buf, len); |
574 | return 0; |
575 | } |
576 | |
577 | static __be32 idmap_id_to_name(struct xdr_stream *xdr, |
578 | struct svc_rqst *rqstp, int type, u32 id) |
579 | { |
580 | struct ent *item, key = { |
581 | .id = id, |
582 | .type = type, |
583 | }; |
584 | __be32 *p; |
585 | int ret; |
586 | struct nfsd_net *nn = net_generic(SVC_NET(rqstp), id: nfsd_net_id); |
587 | |
588 | strscpy(p: key.authname, q: rqst_authname(rqstp), size: sizeof(key.authname)); |
589 | ret = idmap_lookup(rqstp, lookup_fn: idtoname_lookup, key: &key, detail: nn->idtoname_cache, item: &item); |
590 | if (ret == -ENOENT) |
591 | return encode_ascii_id(xdr, id); |
592 | if (ret) |
593 | return nfserrno(errno: ret); |
594 | ret = strlen(item->name); |
595 | WARN_ON_ONCE(ret > IDMAP_NAMESZ); |
596 | p = xdr_reserve_space(xdr, nbytes: ret + 4); |
597 | if (!p) |
598 | return nfserr_resource; |
599 | p = xdr_encode_opaque(p, ptr: item->name, len: ret); |
600 | cache_put(h: &item->h, cd: nn->idtoname_cache); |
601 | return 0; |
602 | } |
603 | |
604 | static bool |
605 | numeric_name_to_id(struct svc_rqst *rqstp, int type, const char *name, u32 namelen, u32 *id) |
606 | { |
607 | int ret; |
608 | char buf[11]; |
609 | |
610 | if (namelen + 1 > sizeof(buf)) |
611 | /* too long to represent a 32-bit id: */ |
612 | return false; |
613 | /* Just to make sure it's null-terminated: */ |
614 | memcpy(buf, name, namelen); |
615 | buf[namelen] = '\0'; |
616 | ret = kstrtouint(s: buf, base: 10, res: id); |
617 | return ret == 0; |
618 | } |
619 | |
620 | static __be32 |
621 | do_name_to_id(struct svc_rqst *rqstp, int type, const char *name, u32 namelen, u32 *id) |
622 | { |
623 | if (nfs4_disable_idmapping && rqstp->rq_cred.cr_flavor < RPC_AUTH_GSS) |
624 | if (numeric_name_to_id(rqstp, type, name, namelen, id)) |
625 | return 0; |
626 | /* |
627 | * otherwise, fall through and try idmapping, for |
628 | * backwards compatibility with clients sending names: |
629 | */ |
630 | return idmap_name_to_id(rqstp, type, name, namelen, id); |
631 | } |
632 | |
633 | static __be32 encode_name_from_id(struct xdr_stream *xdr, |
634 | struct svc_rqst *rqstp, int type, u32 id) |
635 | { |
636 | if (nfs4_disable_idmapping && rqstp->rq_cred.cr_flavor < RPC_AUTH_GSS) |
637 | return encode_ascii_id(xdr, id); |
638 | return idmap_id_to_name(xdr, rqstp, type, id); |
639 | } |
640 | |
641 | __be32 |
642 | nfsd_map_name_to_uid(struct svc_rqst *rqstp, const char *name, size_t namelen, |
643 | kuid_t *uid) |
644 | { |
645 | __be32 status; |
646 | u32 id = -1; |
647 | |
648 | if (name == NULL || namelen == 0) |
649 | return nfserr_inval; |
650 | |
651 | status = do_name_to_id(rqstp, IDMAP_TYPE_USER, name, namelen, id: &id); |
652 | *uid = make_kuid(from: nfsd_user_namespace(rqstp), uid: id); |
653 | if (!uid_valid(uid: *uid)) |
654 | status = nfserr_badowner; |
655 | return status; |
656 | } |
657 | |
658 | __be32 |
659 | nfsd_map_name_to_gid(struct svc_rqst *rqstp, const char *name, size_t namelen, |
660 | kgid_t *gid) |
661 | { |
662 | __be32 status; |
663 | u32 id = -1; |
664 | |
665 | if (name == NULL || namelen == 0) |
666 | return nfserr_inval; |
667 | |
668 | status = do_name_to_id(rqstp, IDMAP_TYPE_GROUP, name, namelen, id: &id); |
669 | *gid = make_kgid(from: nfsd_user_namespace(rqstp), gid: id); |
670 | if (!gid_valid(gid: *gid)) |
671 | status = nfserr_badowner; |
672 | return status; |
673 | } |
674 | |
675 | __be32 nfsd4_encode_user(struct xdr_stream *xdr, struct svc_rqst *rqstp, |
676 | kuid_t uid) |
677 | { |
678 | u32 id = from_kuid_munged(to: nfsd_user_namespace(rqstp), uid); |
679 | return encode_name_from_id(xdr, rqstp, IDMAP_TYPE_USER, id); |
680 | } |
681 | |
682 | __be32 nfsd4_encode_group(struct xdr_stream *xdr, struct svc_rqst *rqstp, |
683 | kgid_t gid) |
684 | { |
685 | u32 id = from_kgid_munged(to: nfsd_user_namespace(rqstp), gid); |
686 | return encode_name_from_id(xdr, rqstp, IDMAP_TYPE_GROUP, id); |
687 | } |
688 | |