diff options
Diffstat (limited to 'net/sunrpc/svcauth_unix.c')
-rw-r--r-- | net/sunrpc/svcauth_unix.c | 225 |
1 files changed, 220 insertions, 5 deletions
diff --git a/net/sunrpc/svcauth_unix.c b/net/sunrpc/svcauth_unix.c index 4b775dbf580d..9bae4090254c 100644 --- a/net/sunrpc/svcauth_unix.c +++ b/net/sunrpc/svcauth_unix.c | |||
@@ -418,6 +418,214 @@ svcauth_unix_info_release(void *info) | |||
418 | cache_put(&ipm->h, &ip_map_cache); | 418 | cache_put(&ipm->h, &ip_map_cache); |
419 | } | 419 | } |
420 | 420 | ||
421 | /**************************************************************************** | ||
422 | * auth.unix.gid cache | ||
423 | * simple cache to map a UID to a list of GIDs | ||
424 | * because AUTH_UNIX aka AUTH_SYS has a max of 16 | ||
425 | */ | ||
426 | #define GID_HASHBITS 8 | ||
427 | #define GID_HASHMAX (1<<GID_HASHBITS) | ||
428 | #define GID_HASHMASK (GID_HASHMAX - 1) | ||
429 | |||
430 | struct unix_gid { | ||
431 | struct cache_head h; | ||
432 | uid_t uid; | ||
433 | struct group_info *gi; | ||
434 | }; | ||
435 | static struct cache_head *gid_table[GID_HASHMAX]; | ||
436 | |||
437 | static void unix_gid_put(struct kref *kref) | ||
438 | { | ||
439 | struct cache_head *item = container_of(kref, struct cache_head, ref); | ||
440 | struct unix_gid *ug = container_of(item, struct unix_gid, h); | ||
441 | if (test_bit(CACHE_VALID, &item->flags) && | ||
442 | !test_bit(CACHE_NEGATIVE, &item->flags)) | ||
443 | put_group_info(ug->gi); | ||
444 | kfree(ug); | ||
445 | } | ||
446 | |||
447 | static int unix_gid_match(struct cache_head *corig, struct cache_head *cnew) | ||
448 | { | ||
449 | struct unix_gid *orig = container_of(corig, struct unix_gid, h); | ||
450 | struct unix_gid *new = container_of(cnew, struct unix_gid, h); | ||
451 | return orig->uid == new->uid; | ||
452 | } | ||
453 | static void unix_gid_init(struct cache_head *cnew, struct cache_head *citem) | ||
454 | { | ||
455 | struct unix_gid *new = container_of(cnew, struct unix_gid, h); | ||
456 | struct unix_gid *item = container_of(citem, struct unix_gid, h); | ||
457 | new->uid = item->uid; | ||
458 | } | ||
459 | static void unix_gid_update(struct cache_head *cnew, struct cache_head *citem) | ||
460 | { | ||
461 | struct unix_gid *new = container_of(cnew, struct unix_gid, h); | ||
462 | struct unix_gid *item = container_of(citem, struct unix_gid, h); | ||
463 | |||
464 | get_group_info(item->gi); | ||
465 | new->gi = item->gi; | ||
466 | } | ||
467 | static struct cache_head *unix_gid_alloc(void) | ||
468 | { | ||
469 | struct unix_gid *g = kmalloc(sizeof(*g), GFP_KERNEL); | ||
470 | if (g) | ||
471 | return &g->h; | ||
472 | else | ||
473 | return NULL; | ||
474 | } | ||
475 | |||
476 | static void unix_gid_request(struct cache_detail *cd, | ||
477 | struct cache_head *h, | ||
478 | char **bpp, int *blen) | ||
479 | { | ||
480 | char tuid[20]; | ||
481 | struct unix_gid *ug = container_of(h, struct unix_gid, h); | ||
482 | |||
483 | snprintf(tuid, 20, "%u", ug->uid); | ||
484 | qword_add(bpp, blen, tuid); | ||
485 | (*bpp)[-1] = '\n'; | ||
486 | } | ||
487 | |||
488 | static struct unix_gid *unix_gid_lookup(uid_t uid); | ||
489 | extern struct cache_detail unix_gid_cache; | ||
490 | |||
491 | static int unix_gid_parse(struct cache_detail *cd, | ||
492 | char *mesg, int mlen) | ||
493 | { | ||
494 | /* uid expiry Ngid gid0 gid1 ... gidN-1 */ | ||
495 | int uid; | ||
496 | int gids; | ||
497 | int rv; | ||
498 | int i; | ||
499 | int err; | ||
500 | time_t expiry; | ||
501 | struct unix_gid ug, *ugp; | ||
502 | |||
503 | if (mlen <= 0 || mesg[mlen-1] != '\n') | ||
504 | return -EINVAL; | ||
505 | mesg[mlen-1] = 0; | ||
506 | |||
507 | rv = get_int(&mesg, &uid); | ||
508 | if (rv) | ||
509 | return -EINVAL; | ||
510 | ug.uid = uid; | ||
511 | |||
512 | expiry = get_expiry(&mesg); | ||
513 | if (expiry == 0) | ||
514 | return -EINVAL; | ||
515 | |||
516 | rv = get_int(&mesg, &gids); | ||
517 | if (rv || gids < 0 || gids > 8192) | ||
518 | return -EINVAL; | ||
519 | |||
520 | ug.gi = groups_alloc(gids); | ||
521 | if (!ug.gi) | ||
522 | return -ENOMEM; | ||
523 | |||
524 | for (i = 0 ; i < gids ; i++) { | ||
525 | int gid; | ||
526 | rv = get_int(&mesg, &gid); | ||
527 | err = -EINVAL; | ||
528 | if (rv) | ||
529 | goto out; | ||
530 | GROUP_AT(ug.gi, i) = gid; | ||
531 | } | ||
532 | |||
533 | ugp = unix_gid_lookup(uid); | ||
534 | if (ugp) { | ||
535 | struct cache_head *ch; | ||
536 | ug.h.flags = 0; | ||
537 | ug.h.expiry_time = expiry; | ||
538 | ch = sunrpc_cache_update(&unix_gid_cache, | ||
539 | &ug.h, &ugp->h, | ||
540 | hash_long(uid, GID_HASHBITS)); | ||
541 | if (!ch) | ||
542 | err = -ENOMEM; | ||
543 | else { | ||
544 | err = 0; | ||
545 | cache_put(ch, &unix_gid_cache); | ||
546 | } | ||
547 | } else | ||
548 | err = -ENOMEM; | ||
549 | out: | ||
550 | if (ug.gi) | ||
551 | put_group_info(ug.gi); | ||
552 | return err; | ||
553 | } | ||
554 | |||
555 | static int unix_gid_show(struct seq_file *m, | ||
556 | struct cache_detail *cd, | ||
557 | struct cache_head *h) | ||
558 | { | ||
559 | struct unix_gid *ug; | ||
560 | int i; | ||
561 | int glen; | ||
562 | |||
563 | if (h == NULL) { | ||
564 | seq_puts(m, "#uid cnt: gids...\n"); | ||
565 | return 0; | ||
566 | } | ||
567 | ug = container_of(h, struct unix_gid, h); | ||
568 | if (test_bit(CACHE_VALID, &h->flags) && | ||
569 | !test_bit(CACHE_NEGATIVE, &h->flags)) | ||
570 | glen = ug->gi->ngroups; | ||
571 | else | ||
572 | glen = 0; | ||
573 | |||
574 | seq_printf(m, "%d %d:", ug->uid, glen); | ||
575 | for (i = 0; i < glen; i++) | ||
576 | seq_printf(m, " %d", GROUP_AT(ug->gi, i)); | ||
577 | seq_printf(m, "\n"); | ||
578 | return 0; | ||
579 | } | ||
580 | |||
581 | struct cache_detail unix_gid_cache = { | ||
582 | .owner = THIS_MODULE, | ||
583 | .hash_size = GID_HASHMAX, | ||
584 | .hash_table = gid_table, | ||
585 | .name = "auth.unix.gid", | ||
586 | .cache_put = unix_gid_put, | ||
587 | .cache_request = unix_gid_request, | ||
588 | .cache_parse = unix_gid_parse, | ||
589 | .cache_show = unix_gid_show, | ||
590 | .match = unix_gid_match, | ||
591 | .init = unix_gid_init, | ||
592 | .update = unix_gid_update, | ||
593 | .alloc = unix_gid_alloc, | ||
594 | }; | ||
595 | |||
596 | static struct unix_gid *unix_gid_lookup(uid_t uid) | ||
597 | { | ||
598 | struct unix_gid ug; | ||
599 | struct cache_head *ch; | ||
600 | |||
601 | ug.uid = uid; | ||
602 | ch = sunrpc_cache_lookup(&unix_gid_cache, &ug.h, | ||
603 | hash_long(uid, GID_HASHBITS)); | ||
604 | if (ch) | ||
605 | return container_of(ch, struct unix_gid, h); | ||
606 | else | ||
607 | return NULL; | ||
608 | } | ||
609 | |||
610 | static int unix_gid_find(uid_t uid, struct group_info **gip, | ||
611 | struct svc_rqst *rqstp) | ||
612 | { | ||
613 | struct unix_gid *ug = unix_gid_lookup(uid); | ||
614 | if (!ug) | ||
615 | return -EAGAIN; | ||
616 | switch (cache_check(&unix_gid_cache, &ug->h, &rqstp->rq_chandle)) { | ||
617 | case -ENOENT: | ||
618 | *gip = NULL; | ||
619 | return 0; | ||
620 | case 0: | ||
621 | *gip = ug->gi; | ||
622 | get_group_info(*gip); | ||
623 | return 0; | ||
624 | default: | ||
625 | return -EAGAIN; | ||
626 | } | ||
627 | } | ||
628 | |||
421 | static int | 629 | static int |
422 | svcauth_unix_set_client(struct svc_rqst *rqstp) | 630 | svcauth_unix_set_client(struct svc_rqst *rqstp) |
423 | { | 631 | { |
@@ -543,12 +751,19 @@ svcauth_unix_accept(struct svc_rqst *rqstp, __be32 *authp) | |||
543 | slen = svc_getnl(argv); /* gids length */ | 751 | slen = svc_getnl(argv); /* gids length */ |
544 | if (slen > 16 || (len -= (slen + 2)*4) < 0) | 752 | if (slen > 16 || (len -= (slen + 2)*4) < 0) |
545 | goto badcred; | 753 | goto badcred; |
546 | cred->cr_group_info = groups_alloc(slen); | 754 | if (unix_gid_find(cred->cr_uid, &cred->cr_group_info, rqstp) |
547 | if (cred->cr_group_info == NULL) | 755 | == -EAGAIN) |
548 | return SVC_DROP; | 756 | return SVC_DROP; |
549 | for (i = 0; i < slen; i++) | 757 | if (cred->cr_group_info == NULL) { |
550 | GROUP_AT(cred->cr_group_info, i) = svc_getnl(argv); | 758 | cred->cr_group_info = groups_alloc(slen); |
551 | 759 | if (cred->cr_group_info == NULL) | |
760 | return SVC_DROP; | ||
761 | for (i = 0; i < slen; i++) | ||
762 | GROUP_AT(cred->cr_group_info, i) = svc_getnl(argv); | ||
763 | } else { | ||
764 | for (i = 0; i < slen ; i++) | ||
765 | svc_getnl(argv); | ||
766 | } | ||
552 | if (svc_getu32(argv) != htonl(RPC_AUTH_NULL) || svc_getu32(argv) != 0) { | 767 | if (svc_getu32(argv) != htonl(RPC_AUTH_NULL) || svc_getu32(argv) != 0) { |
553 | *authp = rpc_autherr_badverf; | 768 | *authp = rpc_autherr_badverf; |
554 | return SVC_DENIED; | 769 | return SVC_DENIED; |