aboutsummaryrefslogtreecommitdiffstats
path: root/net/sunrpc
diff options
context:
space:
mode:
authorNeilBrown <neilb@suse.de>2007-02-14 03:33:13 -0500
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2007-02-14 11:09:53 -0500
commit3fc605a2aa38899c12180ca311f1eeb61a6d867e (patch)
tree4a75b90cc8cffca4661642360cbec42a424bf9cf /net/sunrpc
parentaf6a4e280e3ff453653f39190b57b345ff0bec16 (diff)
[PATCH] knfsd: allow the server to provide a gid list when using AUTH_UNIX authentication
AUTH_UNIX authentication (the standard with NFS) has a limit of 16 groups ids. This causes problems for people in more than 16 groups. So allow the server to map a uid into a list of group ids based on local knowledge rather depending on the (possibly truncated) list from the client. If there is no process on the server responding to upcalls, the gidlist in the request will still be used. Signed-off-by: Neil Brown <neilb@suse.de> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'net/sunrpc')
-rw-r--r--net/sunrpc/sunrpc_syms.c5
-rw-r--r--net/sunrpc/svcauth_unix.c225
2 files changed, 224 insertions, 6 deletions
diff --git a/net/sunrpc/sunrpc_syms.c b/net/sunrpc/sunrpc_syms.c
index d85fddeb6388..d3865265fc18 100644
--- a/net/sunrpc/sunrpc_syms.c
+++ b/net/sunrpc/sunrpc_syms.c
@@ -137,7 +137,7 @@ EXPORT_SYMBOL(nlm_debug);
137 137
138extern int register_rpc_pipefs(void); 138extern int register_rpc_pipefs(void);
139extern void unregister_rpc_pipefs(void); 139extern void unregister_rpc_pipefs(void);
140extern struct cache_detail ip_map_cache; 140extern struct cache_detail ip_map_cache, unix_gid_cache;
141extern int init_socket_xprt(void); 141extern int init_socket_xprt(void);
142extern void cleanup_socket_xprt(void); 142extern void cleanup_socket_xprt(void);
143 143
@@ -157,6 +157,7 @@ init_sunrpc(void)
157 rpc_proc_init(); 157 rpc_proc_init();
158#endif 158#endif
159 cache_register(&ip_map_cache); 159 cache_register(&ip_map_cache);
160 cache_register(&unix_gid_cache);
160 init_socket_xprt(); 161 init_socket_xprt();
161out: 162out:
162 return err; 163 return err;
@@ -170,6 +171,8 @@ cleanup_sunrpc(void)
170 rpc_destroy_mempool(); 171 rpc_destroy_mempool();
171 if (cache_unregister(&ip_map_cache)) 172 if (cache_unregister(&ip_map_cache))
172 printk(KERN_ERR "sunrpc: failed to unregister ip_map cache\n"); 173 printk(KERN_ERR "sunrpc: failed to unregister ip_map cache\n");
174 if (cache_unregister(&unix_gid_cache))
175 printk(KERN_ERR "sunrpc: failed to unregister unix_gid cache\n");
173#ifdef RPC_DEBUG 176#ifdef RPC_DEBUG
174 rpc_unregister_sysctl(); 177 rpc_unregister_sysctl();
175#endif 178#endif
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;