Commit 3fc605a2aa38899c12180ca311f1eeb61a6d867e

Authored by NeilBrown
Committed by Linus Torvalds
1 parent af6a4e280e

[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>

Showing 2 changed files with 224 additions and 6 deletions Side-by-side Diff

net/sunrpc/sunrpc_syms.c
... ... @@ -137,7 +137,7 @@
137 137  
138 138 extern int register_rpc_pipefs(void);
139 139 extern void unregister_rpc_pipefs(void);
140   -extern struct cache_detail ip_map_cache;
  140 +extern struct cache_detail ip_map_cache, unix_gid_cache;
141 141 extern int init_socket_xprt(void);
142 142 extern void cleanup_socket_xprt(void);
143 143  
... ... @@ -157,6 +157,7 @@
157 157 rpc_proc_init();
158 158 #endif
159 159 cache_register(&ip_map_cache);
  160 + cache_register(&unix_gid_cache);
160 161 init_socket_xprt();
161 162 out:
162 163 return err;
... ... @@ -170,6 +171,8 @@
170 171 rpc_destroy_mempool();
171 172 if (cache_unregister(&ip_map_cache))
172 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 176 #ifdef RPC_DEBUG
174 177 rpc_unregister_sysctl();
175 178 #endif
net/sunrpc/svcauth_unix.c
... ... @@ -418,6 +418,214 @@
418 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 629 static int
422 630 svcauth_unix_set_client(struct svc_rqst *rqstp)
423 631 {
424 632  
... ... @@ -543,12 +751,19 @@
543 751 slen = svc_getnl(argv); /* gids length */
544 752 if (slen > 16 || (len -= (slen + 2)*4) < 0)
545 753 goto badcred;
546   - cred->cr_group_info = groups_alloc(slen);
547   - if (cred->cr_group_info == NULL)
  754 + if (unix_gid_find(cred->cr_uid, &cred->cr_group_info, rqstp)
  755 + == -EAGAIN)
548 756 return SVC_DROP;
549   - for (i = 0; i < slen; i++)
550   - GROUP_AT(cred->cr_group_info, i) = svc_getnl(argv);
551   -
  757 + if (cred->cr_group_info == NULL) {
  758 + cred->cr_group_info = groups_alloc(slen);
  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 767 if (svc_getu32(argv) != htonl(RPC_AUTH_NULL) || svc_getu32(argv) != 0) {
553 768 *authp = rpc_autherr_badverf;
554 769 return SVC_DENIED;