aboutsummaryrefslogtreecommitdiffstats
path: root/net/sunrpc/svcauth_unix.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/sunrpc/svcauth_unix.c')
-rw-r--r--net/sunrpc/svcauth_unix.c225
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
430struct unix_gid {
431 struct cache_head h;
432 uid_t uid;
433 struct group_info *gi;
434};
435static struct cache_head *gid_table[GID_HASHMAX];
436
437static 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
447static 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}
453static 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}
459static 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}
467static 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
476static 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
488static struct unix_gid *unix_gid_lookup(uid_t uid);
489extern struct cache_detail unix_gid_cache;
490
491static 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
555static 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
581struct 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
596static 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
610static 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
421static int 629static int
422svcauth_unix_set_client(struct svc_rqst *rqstp) 630svcauth_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;