nfsd: fix v4.0 GSSAPI callback channel auth failures
Subsystems (3)
v2 cover nfsd: fix v4.0 GSSAPI callback channel auth failures
This is a respin of the kernel patchset that I sent back in early April to fix the client's GSSAPI authentication in the v4.0 callback channel. The userland piece of this has already been merged into nfs-utils.
The only real change here is to move the "stringification" of the acceptor name out of the rpc_done callback and into the synchronous caller. As Trond pointed out, we can't do that in rpciod callbacks as it could recurse into writeback.
The callback now just takes a reference to the rpccred and stores a pointer to it in the nfs4_setclientid struct. If that's non-NULL after the call returns, we do the stringification there and then put the cred.
Jeff Layton (3):
auth_gss: fetch the acceptor name out of the downcall
sunrpc: add a new "stringify_acceptor" rpc_credop
nfs4: copy acceptor name from context to nfs_client
fs/nfs/callback.c | 12 ++++++ fs/nfs/client.c | 1 + fs/nfs/nfs4proc.c | 33 ++++++++++++++++- include/linux/nfs_fs_sb.h | 1 + include/linux/nfs_xdr.h | 1 + include/linux/sunrpc/auth.h | 2 + include/linux/sunrpc/auth_gss.h | 1 + net/sunrpc/auth.c | 9 +++++ net/sunrpc/auth_gss/auth_gss.c | 82 +++++++++++++++++++++++++++++------------ 9 files changed, 118 insertions(+), 24 deletions(-)
Signature · 1 line
1.9.3
PATCH v2 1/3 auth_gss: fetch the acceptor name out of the downcall
From: Jeff Layton <[email protected]>
If rpc.gssd sends us an acceptor name string trailing the context token, stash it as part of the context.
--- include/linux/sunrpc/auth_gss.h | 1 + net/sunrpc/auth_gss/auth_gss.c | 20 +++++++++++++++++--- 2 files changed, 18 insertions(+), 3 deletions(-)
include/linux/sunrpc/auth_gss.h +1 -0
@@ -71,6 +71,7 @@ struct gss_cl_ctx {spinlock_t gc_seq_lock;struct gss_ctx __rcu *gc_gss_ctx;struct xdr_netobj gc_wire_ctx; +struct xdr_netobj gc_acceptor;u32 gc_win;unsigned long gc_expiry;struct rcu_head gc_rcu;
net/sunrpc/auth_gss/auth_gss.c +17 -3
@@ -262,9 +262,22 @@ gss_fill_context(const void *p, const void *end, struct gss_cl_ctx *ctx, struct p =ERR_PTR (ret); goto err; } -dprintk ("RPC: %s Success. gc_expiry %lu now %lu timeout %u\n" , - __func__, ctx->gc_expiry, now, timeout); -return q; + +/* is there any trailing data? */ +if (q == end) { + p = q; + goto done; + } + +/* pull in acceptor name (if there is one) */ + p =simple_get_netobj (q, end, &ctx->gc_acceptor); +if (IS_ERR (p)) + goto err; +done: +dprintk ("RPC: %s Success. gc_expiry %lu now %lu timeout %u acceptor %.*s\n" , + __func__, ctx->gc_expiry, now, timeout, ctx->gc_acceptor.len, + ctx->gc_acceptor.data); +return p; err:dprintk ("RPC: %s returns error %ld\n" , __func__, -PTR_ERR (p));return p;
@@ -1225,6 +1238,7 @@ gss_do_free_ctx(struct gss_cl_ctx *ctx)gss_delete_sec_context (&ctx->gc_gss_ctx);kfree (ctx->gc_wire_ctx.data); +kfree (ctx->gc_acceptor.data);kfree (ctx); }
Signature · 1 line
1.9.3
PATCH v2 2/3 sunrpc: add a new "stringify_acceptor" rpc_credop
In reply to Jeff LaytonFrom: Jeff Layton <[email protected]>
...and add an new rpc_auth function to call it when it exists. This is only applicable for AUTH_GSS mechanisms, so we only specify this for those sorts of credentials.
--- include/linux/sunrpc/auth.h | 2 ++ net/sunrpc/auth.c | 9 ++++++ net/sunrpc/auth_gss/auth_gss.c | 62 ++++++++++++++++++++++++++++-------------- 3 files changed, 53 insertions(+), 20 deletions(-)
include/linux/sunrpc/auth.h +2 -0
@@ -140,6 +140,7 @@ struct rpc_credops { void *, __be32 *, void *);int (*crkey_timeout)(struct rpc_cred *);bool (*crkey_to_expire)(struct rpc_cred *); +char * (*crstringify_acceptor)(struct rpc_cred *); };extern const struct rpc_authops authunix_ops;
@@ -182,6 +183,7 @@ void rpcauth_clear_credcache(struct rpc_cred_cache *);int rpcauth_key_timeout_notify (struct rpc_auth *,struct rpc_cred *);bool rpcauth_cred_key_to_expire (struct rpc_cred *); +char *rpcauth_stringify_acceptor (struct rpc_cred *);static inline struct rpc_cred *get_rpccred (struct rpc_cred *cred)
net/sunrpc/auth.c +9 -0
@@ -363,6 +363,15 @@ rpcauth_cred_key_to_expire(struct rpc_cred *cred) }EXPORT_SYMBOL_GPL (rpcauth_cred_key_to_expire); +char * +rpcauth_stringify_acceptor (struct rpc_cred *cred) +{ +if (!cred->cr_ops->crstringify_acceptor) +return NULL ; +return cred->cr_ops->crstringify_acceptor (cred); +} +EXPORT_SYMBOL_GPL (rpcauth_stringify_acceptor); +/* * Destroy a list of credentials */
net/sunrpc/auth_gss/auth_gss.c +42 -20
77 lines not on this page · raw message
Signature · 1 line
1.9.3
PATCH v2 3/3 nfs4: copy acceptor name from context to nfs_client
In reply to Jeff LaytonFrom: Jeff Layton <[email protected]>
The current CB_COMPOUND handling code tries to compare the principal name of the request with the cl_hostname in the client. This is not guaranteed to ever work, particularly if the client happened to mount a CNAME of the server or a non-fqdn.
Fix this by instead comparing the cr_principal string with the acceptor name that we get from gssd. In the event that gssd didn't send one down (i.e. it was too old), then we fall back to trying to use the cl_hostname as we do today.
--- fs/nfs/callback.c | 12 ++++++++++++ fs/nfs/client.c | 1 + fs/nfs/nfs4proc.c | 33 ++++++++++++++++++++++++++++++++- include/linux/nfs_fs_sb.h | 1 + include/linux/nfs_xdr.h | 1 + 5 files changed, 47 insertions(+), 1 deletion(-)
fs/nfs/callback.c +12 -0
@@ -428,6 +428,18 @@ check_gss_callback_principal(struct nfs_client *clp, struct svc_rqst *rqstp)if (p ==NULL )return 0 ; +/* +* Did we get the acceptor from userland during the SETCLIENID +* negotiation? +*/ +if (clp->cl_acceptor) +return !strcmp (p, clp->cl_acceptor); + +/* +* Otherwise try to verify it using the cl_hostname. Note that this +* doesn't work if a non-canonical hostname was used in the devname. +*/ +/* Expect a GSS_C_NT_HOSTBASED_NAME like "nfs@serverhostname" */ if (memcmp (p,"nfs@" ,4 ) !=0 )
fs/nfs/client.c +1 -0
@@ -252,6 +252,7 @@ void nfs_free_client(struct nfs_client *clp)put_net (clp->cl_net);put_nfs_version (clp->cl_nfs_mod);kfree (clp->cl_hostname); +kfree (clp->cl_acceptor);kfree (clp);dprintk ("<-- nfs_free_client()\n" );
fs/nfs/nfs4proc.c +32 -1
@@ -4886,6 +4886,18 @@ nfs4_init_callback_netid(const struct nfs_client *clp, char *buf, size_t len)return scnprintf (buf, len,"tcp" ); } +static void nfs4_setclientid_done (struct rpc_task *task,void *calldata) +{ +struct nfs4_setclientid *sc = calldata; + +if (task->tk_status ==0 ) + sc->sc_cred =get_rpccred (task->tk_rqstp->rq_cred); +} + +static const struct rpc_call_ops nfs4_setclientid_ops = { + .rpc_call_done = nfs4_setclientid_done, +}; +/** * nfs4_proc_setclientid - Negotiate client ID * @clp: state data structure
@@ -4912,6 +4924,14 @@ int nfs4_proc_setclientid(struct nfs_client *clp, u32 program, .rpc_resp = res, .rpc_cred = cred, }; +struct rpc_task *task; +struct rpc_task_setup task_setup_data = { + .rpc_client = clp->cl_rpcclient, + .rpc_message = &msg, + .callback_ops = &nfs4_setclientid_ops, + .callback_data = &setclientid, + .flags =RPC_TASK_TIMEOUT , + };int status;/* nfs_client_id4 */
@@ -4938,7 +4958,18 @@ int nfs4_proc_setclientid(struct nfs_client *clp, u32 program,dprintk ("NFS call setclientid auth=%s, '%.*s'\n" , clp->cl_rpcclient->cl_auth->au_ops->au_name, setclientid.sc_name_len, setclientid.sc_name); - status =rpc_call_sync (clp->cl_rpcclient, &msg,RPC_TASK_TIMEOUT ); + task =rpc_run_task (&task_setup_data); +if (IS_ERR (task)) { + status =PTR_ERR (task); + goto out; + } + status = task->tk_status; +if (setclientid.sc_cred) { + clp->cl_acceptor =rpcauth_stringify_acceptor (setclientid.sc_cred); +put_rpccred (setclientid.sc_cred); + } +rpc_put_task (task); +out:trace_nfs4_setclientid (clp, status);dprintk ("NFS reply setclientid: %d\n" , status);return status;
include/linux/nfs_fs_sb.h +1 -0
@@ -45,6 +45,7 @@ struct nfs_client {struct sockaddr_storage cl_addr;/* server identifier */ size_t cl_addrlen;char * cl_hostname;/* hostname of server */ +char * cl_acceptor;/* GSSAPI acceptor name */ struct list_head cl_share_link;/* link in global client list */ struct list_head cl_superblocks;/* List of nfs_server structs */
include/linux/nfs_xdr.h +1 -0
@@ -993,6 +993,7 @@ struct nfs4_setclientid {unsigned int sc_uaddr_len;char sc_uaddr[RPCBIND_MAXUADDRLEN +1 ];u32 sc_cb_ident; +struct rpc_cred *sc_cred; };struct nfs4_setclientid_res {
Signature · 1 line
1.9.3