From 6bf03de1d2686d5aafbbf40b2c328b5735ce672a Mon Sep 17 00:00:00 2001 From: Neil Brown Date: Fri, 4 Mar 2005 17:13:22 -0800 Subject: [PATCH] nfsd: svcrpc: add a per-flavor set_client method Add a set_client method to the server rpc auth_ops struct, used to set the client (for the purposes of nfsd export authorization) using flavor-specific information. Signed-off-by: J. Bruce Fields Signed-off-by: Neil Brown Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/sunrpc/svcauth.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include/linux/sunrpc') diff --git a/include/linux/sunrpc/svcauth.h b/include/linux/sunrpc/svcauth.h index 5c16a7519c6c..29a1b14bb218 100644 --- a/include/linux/sunrpc/svcauth.h +++ b/include/linux/sunrpc/svcauth.h @@ -92,6 +92,7 @@ struct auth_ops { int (*accept)(struct svc_rqst *rq, u32 *authp); int (*release)(struct svc_rqst *rq); void (*domain_release)(struct auth_domain *); + int (*set_client)(struct svc_rqst *rq); }; #define SVC_GARBAGE 1 @@ -107,6 +108,7 @@ struct auth_ops { extern int svc_authenticate(struct svc_rqst *rqstp, u32 *authp); extern int svc_authorise(struct svc_rqst *rqstp); +extern int svc_set_client(struct svc_rqst *rqstp); extern int svc_auth_register(rpc_authflavor_t flavor, struct auth_ops *aops); extern void svc_auth_unregister(rpc_authflavor_t flavor); -- cgit v1.2.3 From 6147285bf4c2328556f98a501fc97973f6a3a23f Mon Sep 17 00:00:00 2001 From: Neil Brown Date: Fri, 4 Mar 2005 17:13:35 -0800 Subject: [PATCH] nfsd: svcrpc: rename pg_authenticate Later patches remove pg_authenticate and use the name for a different purpose; so rename it to pg_authenticate_obsolete for now. Signed-off-by: J. Bruce Fields Signed-off-by: Neil Brown Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/nfs/callback.c | 2 +- include/linux/sunrpc/svc.h | 2 +- net/sunrpc/svc.c | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) (limited to 'include/linux/sunrpc') diff --git a/fs/nfs/callback.c b/fs/nfs/callback.c index ad7677b4f21d..d4dc04fe01c6 100644 --- a/fs/nfs/callback.c +++ b/fs/nfs/callback.c @@ -321,5 +321,5 @@ static struct svc_program nfs4_callback_program = { .pg_name = "NFSv4 callback", /* service name */ .pg_class = "nfs", /* authentication class */ .pg_stats = &nfs4_callback_stats, - .pg_authenticate = nfs_callback_auth, + .pg_authenticate_obsolete = nfs_callback_auth, }; diff --git a/include/linux/sunrpc/svc.h b/include/linux/sunrpc/svc.h index f464260d6fdb..f2a9cb676329 100644 --- a/include/linux/sunrpc/svc.h +++ b/include/linux/sunrpc/svc.h @@ -252,7 +252,7 @@ struct svc_program { char * pg_class; /* class name: services sharing authentication */ struct svc_stat * pg_stats; /* rpc statistics */ /* Override authentication. NULL means use default */ - int (*pg_authenticate)(struct svc_rqst *, u32 *); + int (*pg_authenticate_obsolete)(struct svc_rqst *, u32 *); }; /* diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c index 8c5b0517db7f..3c89c744b795 100644 --- a/net/sunrpc/svc.c +++ b/net/sunrpc/svc.c @@ -311,8 +311,8 @@ svc_process(struct svc_serv *serv, struct svc_rqst *rqstp) * We do this before anything else in order to get a decent * auth verifier. */ - if (progp->pg_authenticate != NULL) - auth_res = progp->pg_authenticate(rqstp, &auth_stat); + if (progp->pg_authenticate_obsolete != NULL) + auth_res = progp->pg_authenticate_obsolete(rqstp, &auth_stat); else auth_res = svc_authenticate(rqstp, &auth_stat); switch (auth_res) { -- cgit v1.2.3 From 556d600cd16d20949f6c9bbfe07c7ca2e8f85789 Mon Sep 17 00:00:00 2001 From: Neil Brown Date: Fri, 4 Mar 2005 17:13:48 -0800 Subject: [PATCH] nfsd: svcrpc: move export table checks to a per-program pg_add_client method svcauth_null_accept() and svcauth_unix_accept() are currently hard-wired to check the source ip address on an incoming request against the export table, which make sense for nfsd but not necessarily for other rpc-based services. So instead we have the accept() method call a program-specific pg_authenticate() method. We also move the call to this method into svc_process instead of calling it from the flavor-specific accept() routines. Signed-off-by: J. Bruce Fields Signed-off-by: Neil Brown Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/lockd/svc.c | 15 +++++++++++++++ fs/nfsd/nfssvc.c | 2 ++ include/linux/sunrpc/svc.h | 1 + net/sunrpc/auth_gss/svcauth_gss.c | 9 ++------- net/sunrpc/svc.c | 12 +++++++++++- net/sunrpc/svcauth_unix.c | 18 ++---------------- 6 files changed, 33 insertions(+), 24 deletions(-) (limited to 'include/linux/sunrpc') diff --git a/fs/lockd/svc.c b/fs/lockd/svc.c index d3ee09c5196e..6cba9b0ee7ed 100644 --- a/fs/lockd/svc.c +++ b/fs/lockd/svc.c @@ -403,6 +403,20 @@ static int param_set_##name(const char *val, struct kernel_param *kp) \ return 0; \ } +static int lockd_authenticate(struct svc_rqst *rqstp) +{ + rqstp->rq_client = NULL; + switch (rqstp->rq_authop->flavour) { + case RPC_AUTH_NULL: + case RPC_AUTH_UNIX: + if (rqstp->rq_proc == 0) + return SVC_OK; + return svc_set_client(rqstp); + } + return SVC_DENIED; +} + + param_set_min_max(port, int, simple_strtol, 0, 65535) param_set_min_max(grace_period, unsigned long, simple_strtoul, nlm_grace_period_min, nlm_grace_period_max) @@ -483,4 +497,5 @@ static struct svc_program nlmsvc_program = { .pg_name = "lockd", /* service name */ .pg_class = "nfsd", /* share authentication with nfsd */ .pg_stats = &nlmsvc_stats, /* stats table */ + .pg_authenticate = &lockd_authenticate /* export authentication */ }; diff --git a/fs/nfsd/nfssvc.c b/fs/nfsd/nfssvc.c index 291dd8cd3214..2b360eb284a1 100644 --- a/fs/nfsd/nfssvc.c +++ b/fs/nfsd/nfssvc.c @@ -378,4 +378,6 @@ struct svc_program nfsd_program = { .pg_name = "nfsd", /* program name */ .pg_class = "nfsd", /* authentication class */ .pg_stats = &nfsd_svcstats, /* version table */ + .pg_authenticate = &svc_set_client, /* export authentication */ + }; diff --git a/include/linux/sunrpc/svc.h b/include/linux/sunrpc/svc.h index f2a9cb676329..08944c9f1754 100644 --- a/include/linux/sunrpc/svc.h +++ b/include/linux/sunrpc/svc.h @@ -253,6 +253,7 @@ struct svc_program { struct svc_stat * pg_stats; /* rpc statistics */ /* Override authentication. NULL means use default */ int (*pg_authenticate_obsolete)(struct svc_rqst *, u32 *); + int (*pg_authenticate)(struct svc_rqst *); }; /* diff --git a/net/sunrpc/auth_gss/svcauth_gss.c b/net/sunrpc/auth_gss/svcauth_gss.c index c74fa221a303..d806bafe106c 100644 --- a/net/sunrpc/auth_gss/svcauth_gss.c +++ b/net/sunrpc/auth_gss/svcauth_gss.c @@ -906,11 +906,6 @@ svcauth_gss_accept(struct svc_rqst *rqstp, u32 *authp) svc_putu32(resv, rpc_success); goto complete; case RPC_GSS_PROC_DATA: - *authp = rpc_autherr_badcred; - rqstp->rq_client = - find_gss_auth_domain(rsci->mechctx, gc->gc_svc); - if (rqstp->rq_client == NULL) - goto auth_err; *authp = rpcsec_gsserr_ctxproblem; if (gss_write_verf(rqstp, rsci->mechctx, gc->gc_seq)) goto auth_err; @@ -924,8 +919,6 @@ svcauth_gss_accept(struct svc_rqst *rqstp, u32 *authp) if (unwrap_integ_data(&rqstp->rq_arg, gc->gc_seq, rsci->mechctx)) goto auth_err; - svcdata->rsci = rsci; - cache_get(&rsci->h); /* placeholders for length and seq. number: */ svcdata->body_start = resv->iov_base + resv->iov_len; svc_putu32(resv, 0); @@ -936,6 +929,8 @@ svcauth_gss_accept(struct svc_rqst *rqstp, u32 *authp) default: goto auth_err; } + svcdata->rsci = rsci; + cache_get(&rsci->h); ret = SVC_OK; goto out; } diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c index 3c89c744b795..42344b166b5c 100644 --- a/net/sunrpc/svc.c +++ b/net/sunrpc/svc.c @@ -264,6 +264,7 @@ svc_process(struct svc_serv *serv, struct svc_rqst *rqstp) u32 dir, prog, vers, proc, auth_stat, rpc_stat; int auth_res; + u32 *accept_statp; rpc_stat = rpc_success; @@ -299,6 +300,9 @@ svc_process(struct svc_serv *serv, struct svc_rqst *rqstp) if (vers != 2) /* RPC version number */ goto err_bad_rpc; + /* Save position in case we later decide to reject: */ + accept_statp = resv->iov_base + resv->iov_len; + svc_putu32(resv, xdr_zero); /* ACCEPT */ rqstp->rq_prog = prog = ntohl(svc_getu32(argv)); /* program number */ @@ -315,6 +319,11 @@ svc_process(struct svc_serv *serv, struct svc_rqst *rqstp) auth_res = progp->pg_authenticate_obsolete(rqstp, &auth_stat); else auth_res = svc_authenticate(rqstp, &auth_stat); + /* Also give the program a chance to reject this call: */ + if (auth_res == SVC_OK) { + auth_stat = rpc_autherr_badcred; + auth_res = progp->pg_authenticate(rqstp); + } switch (auth_res) { case SVC_OK: break; @@ -437,7 +446,8 @@ err_bad_rpc: err_bad_auth: dprintk("svc: authentication failed (%d)\n", ntohl(auth_stat)); serv->sv_stats->rpcbadauth++; - resv->iov_len -= 4; + /* Restore write pointer to location of accept status: */ + xdr_ressize_check(rqstp, accept_statp); svc_putu32(resv, xdr_one); /* REJECT */ svc_putu32(resv, xdr_one); /* AUTH_ERROR */ svc_putu32(resv, auth_stat); /* status */ diff --git a/net/sunrpc/svcauth_unix.c b/net/sunrpc/svcauth_unix.c index 01f26cf87586..2b99b4028d31 100644 --- a/net/sunrpc/svcauth_unix.c +++ b/net/sunrpc/svcauth_unix.c @@ -368,7 +368,6 @@ svcauth_null_accept(struct svc_rqst *rqstp, u32 *authp) struct kvec *argv = &rqstp->rq_arg.head[0]; struct kvec *resv = &rqstp->rq_res.head[0]; struct svc_cred *cred = &rqstp->rq_cred; - int rv=0; cred->cr_group_info = NULL; rqstp->rq_client = NULL; @@ -394,19 +393,11 @@ svcauth_null_accept(struct svc_rqst *rqstp, u32 *authp) if (cred->cr_group_info == NULL) return SVC_DROP; /* kmalloc failure - client must retry */ - rv = svcauth_unix_set_client(rqstp); - if (rv == SVC_DENIED) - goto badcred; - /* Put NULL verifier */ svc_putu32(resv, RPC_AUTH_NULL); svc_putu32(resv, 0); - return rv; - -badcred: - *authp = rpc_autherr_badcred; - return SVC_DENIED; + return SVC_OK; } static int @@ -441,7 +432,6 @@ svcauth_unix_accept(struct svc_rqst *rqstp, u32 *authp) struct svc_cred *cred = &rqstp->rq_cred; u32 slen, i; int len = argv->iov_len; - int rv=0; cred->cr_group_info = NULL; rqstp->rq_client = NULL; @@ -473,15 +463,11 @@ svcauth_unix_accept(struct svc_rqst *rqstp, u32 *authp) return SVC_DENIED; } - rv = svcauth_unix_set_client(rqstp); - if (rv == SVC_DENIED) - goto badcred; - /* Put NULL verifier */ svc_putu32(resv, RPC_AUTH_NULL); svc_putu32(resv, 0); - return rv; + return SVC_OK; badcred: *authp = rpc_autherr_badcred; -- cgit v1.2.3 From c59e02e193f1206b57718d9c50160b489f20c258 Mon Sep 17 00:00:00 2001 From: Neil Brown Date: Fri, 4 Mar 2005 17:14:29 -0800 Subject: [PATCH] nfsd: nfsd: remove pg_authenticate field The pg_authenticate (now pg_authenticate_obsolete) callback was only being used by the nfs4 client callback code to circumvent the svcauth_unix code's insistence on checking all requests against the export table. With that problem solved, we no longer need it. Signed-off-by: J. Bruce Fields Signed-off-by: Neil Brown Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/sunrpc/svc.h | 2 -- net/sunrpc/svc.c | 5 +---- 2 files changed, 1 insertion(+), 6 deletions(-) (limited to 'include/linux/sunrpc') diff --git a/include/linux/sunrpc/svc.h b/include/linux/sunrpc/svc.h index 08944c9f1754..37003970cf2e 100644 --- a/include/linux/sunrpc/svc.h +++ b/include/linux/sunrpc/svc.h @@ -251,8 +251,6 @@ struct svc_program { char * pg_name; /* service name */ char * pg_class; /* class name: services sharing authentication */ struct svc_stat * pg_stats; /* rpc statistics */ - /* Override authentication. NULL means use default */ - int (*pg_authenticate_obsolete)(struct svc_rqst *, u32 *); int (*pg_authenticate)(struct svc_rqst *); }; diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c index 42344b166b5c..bb2d99f33315 100644 --- a/net/sunrpc/svc.c +++ b/net/sunrpc/svc.c @@ -315,10 +315,7 @@ svc_process(struct svc_serv *serv, struct svc_rqst *rqstp) * We do this before anything else in order to get a decent * auth verifier. */ - if (progp->pg_authenticate_obsolete != NULL) - auth_res = progp->pg_authenticate_obsolete(rqstp, &auth_stat); - else - auth_res = svc_authenticate(rqstp, &auth_stat); + auth_res = svc_authenticate(rqstp, &auth_stat); /* Also give the program a chance to reject this call: */ if (auth_res == SVC_OK) { auth_stat = rpc_autherr_badcred; -- cgit v1.2.3 From ba15d6aa95ae689b083a133a3689ff95f4c47321 Mon Sep 17 00:00:00 2001 From: Neil Brown Date: Fri, 4 Mar 2005 17:15:09 -0800 Subject: [PATCH] nfsd: discard CACHE_HASHED flag, keeping information in refcount instead. This patch should fix a problem that has been experienced on at-least one busy NFS server, but it has not had lots of testing yet. If -mm could provide that ..... The rpc auth cache currently differentiates between a reference due to being in a hash chain (signalled by CACHE_HASHED flag) and any other reference (counted in refcnt). This is an artificial difference due to an historical accident, and it makes cache_put unsafe. This patch removes the distinction so now existance in a hash chain is counted just like any other reference. Thus a race window in cache_put is closed. Signed-off-by: Neil Brown Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/sunrpc/cache.h | 19 +++++-------------- net/sunrpc/cache.c | 4 +--- net/sunrpc/svcauth.c | 8 ++++---- 3 files changed, 10 insertions(+), 21 deletions(-) (limited to 'include/linux/sunrpc') diff --git a/include/linux/sunrpc/cache.h b/include/linux/sunrpc/cache.h index b902425d2be5..6864063d1b9f 100644 --- a/include/linux/sunrpc/cache.h +++ b/include/linux/sunrpc/cache.h @@ -37,8 +37,7 @@ * Entries have a ref count and a 'hashed' flag which counts the existance * in the hash table. * We only expire entries when refcount is zero. - * Existance in the cache is not measured in refcount but rather in - * CACHE_HASHED flag. + * Existance in the cache is counted the refcount. */ /* Every cache item has a common header that is used @@ -57,7 +56,6 @@ struct cache_head { #define CACHE_VALID 0 /* Entry contains valid data */ #define CACHE_NEGATIVE 1 /* Negative entry - there is no match for the key */ #define CACHE_PENDING 2 /* An upcall has been sent but no reply received yet*/ -#define CACHE_HASHED 3 /* Entry is in a hash table */ #define CACHE_NEW_EXPIRY 120 /* keep new things pending confirmation for 120 seconds */ @@ -185,7 +183,6 @@ RTN *FNAME ARGS \ \ if (new) \ {INIT;} \ - cache_get(&tmp->MEMBER); \ if (set) { \ if (!INPLACE && test_bit(CACHE_VALID, &tmp->MEMBER.flags))\ { /* need to swap in new */ \ @@ -194,8 +191,6 @@ RTN *FNAME ARGS \ new->MEMBER.next = tmp->MEMBER.next; \ *hp = &new->MEMBER; \ tmp->MEMBER.next = NULL; \ - set_bit(CACHE_HASHED, &new->MEMBER.flags); \ - clear_bit(CACHE_HASHED, &tmp->MEMBER.flags); \ t2 = tmp; tmp = new; new = t2; \ } \ if (test_bit(CACHE_NEGATIVE, &item->MEMBER.flags)) \ @@ -205,6 +200,7 @@ RTN *FNAME ARGS \ clear_bit(CACHE_NEGATIVE, &tmp->MEMBER.flags); \ } \ } \ + cache_get(&tmp->MEMBER); \ if (set||new) write_unlock(&(DETAIL)->hash_lock); \ else read_unlock(&(DETAIL)->hash_lock); \ if (set) \ @@ -220,7 +216,7 @@ RTN *FNAME ARGS \ new->MEMBER.next = *head; \ *head = &new->MEMBER; \ (DETAIL)->entries ++; \ - set_bit(CACHE_HASHED, &new->MEMBER.flags); \ + cache_get(&new->MEMBER); \ if (set) { \ tmp = new; \ if (test_bit(CACHE_NEGATIVE, &item->MEMBER.flags)) \ @@ -268,15 +264,10 @@ static inline struct cache_head *cache_get(struct cache_head *h) static inline int cache_put(struct cache_head *h, struct cache_detail *cd) { - atomic_dec(&h->refcnt); - if (!atomic_read(&h->refcnt) && + if (atomic_read(&h->refcnt) <= 2 && h->expiry_time < cd->nextcheck) cd->nextcheck = h->expiry_time; - if (!test_bit(CACHE_HASHED, &h->flags) && - !atomic_read(&h->refcnt)) - return 1; - - return 0; + return atomic_dec_and_test(&h->refcnt); } extern void cache_init(struct cache_head *h); diff --git a/net/sunrpc/cache.c b/net/sunrpc/cache.c index bf5a2dee8800..900f5bc7e336 100644 --- a/net/sunrpc/cache.c +++ b/net/sunrpc/cache.c @@ -321,12 +321,10 @@ static int cache_clean(void) if (test_and_clear_bit(CACHE_PENDING, &ch->flags)) queue_loose(current_detail, ch); - if (!atomic_read(&ch->refcnt)) + if (atomic_read(&ch->refcnt) == 1) break; } if (ch) { - cache_get(ch); - clear_bit(CACHE_HASHED, &ch->flags); *cp = ch->next; ch->next = NULL; current_detail->entries--; diff --git a/net/sunrpc/svcauth.c b/net/sunrpc/svcauth.c index 7fefed46d591..bde8147ef2db 100644 --- a/net/sunrpc/svcauth.c +++ b/net/sunrpc/svcauth.c @@ -178,12 +178,12 @@ auth_domain_lookup(struct auth_domain *item, int set) tmp = container_of(*hp, struct auth_domain, h); if (!auth_domain_match(tmp, item)) continue; - cache_get(&tmp->h); - if (!set) + if (!set) { + cache_get(&tmp->h); goto out_noset; + } *hp = tmp->h.next; tmp->h.next = NULL; - clear_bit(CACHE_HASHED, &tmp->h.flags); auth_domain_drop(&tmp->h, &auth_domain_cache); goto out_set; } @@ -192,9 +192,9 @@ auth_domain_lookup(struct auth_domain *item, int set) goto out_nada; auth_domain_cache.entries++; out_set: - set_bit(CACHE_HASHED, &item->h.flags); item->h.next = *head; *head = &item->h; + cache_get(&item->h); write_unlock(&auth_domain_cache.hash_lock); cache_fresh(&auth_domain_cache, &item->h, item->h.expiry_time); cache_get(&item->h); -- cgit v1.2.3