From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: linux-nfs-owner@vger.kernel.org Received: from fieldses.org ([174.143.236.118]:47155 "EHLO fieldses.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753198Ab2EVWsT (ORCPT ); Tue, 22 May 2012 18:48:19 -0400 Date: Tue, 22 May 2012 18:48:18 -0400 To: Simo Sorce Cc: bfields@redhat.com, linux-nfs@vger.kernel.org Subject: Re: [PATCH 4/4] SUNRPC: Use gssproxy upcall for nfsd's RPCGSS authentication. Message-ID: <20120522224818.GA6435@fieldses.org> References: <1337087550-9821-1-git-send-email-simo@redhat.com> <1337087550-9821-5-git-send-email-simo@redhat.com> MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii In-Reply-To: <1337087550-9821-5-git-send-email-simo@redhat.com> From: "J. Bruce Fields" Sender: linux-nfs-owner@vger.kernel.org List-ID: On Tue, May 15, 2012 at 09:12:30AM -0400, Simo Sorce wrote: > The main advantge of this new upcall mechanism is that it can handle > big tickets as seen in Kerberos implementations where tickets carry > authorization data like the MS-PAC buffer with AD or the Posix Authorization > Data being discussed in IETF on the krbwg working group. > > The Gssproxy program is used to perform the accept_sec_context call on the > kernel's behalf. The code is changed to also pass the input buffer straight > to upcall mechanism to avoid allocating and copying many pages as tokens can > be as big (potentially more in future) as 64KiB. > > Signed-off-by: Simo Sorce > --- > include/linux/sunrpc/auth_gss.h | 3 + > include/linux/sunrpc/svcauth_gss.h | 2 +- > net/sunrpc/auth_gss/auth_gss.c | 9 +- > net/sunrpc/auth_gss/svcauth_gss.c | 249 ++++++++++++++++++++++++++++++++++-- > 4 files changed, 248 insertions(+), 15 deletions(-) > > diff --git a/include/linux/sunrpc/auth_gss.h b/include/linux/sunrpc/auth_gss.h > index f1cfd4c85cd047c4b2fadd367eeb819aabc57d29..eb2670f6cf9113f1b4c161b9deda05ee4757fa85 100644 > --- a/include/linux/sunrpc/auth_gss.h > +++ b/include/linux/sunrpc/auth_gss.h > @@ -19,6 +19,9 @@ > > #define RPC_GSS_VERSION 1 > > +#define GSS_UPCALL_LEGACY 0 > +#define GSS_UPCALL_GSSPROXY 1 > + > #define MAXSEQ 0x80000000 /* maximum legal sequence number, from rfc 2203 */ > > enum rpc_gss_proc { > diff --git a/include/linux/sunrpc/svcauth_gss.h b/include/linux/sunrpc/svcauth_gss.h > index 7c32daa025eb07b644d8185a27c8ea10d8b7c55f..678c6fc8f1593bc53bc3d875175ed7098cd4db40 100644 > --- a/include/linux/sunrpc/svcauth_gss.h > +++ b/include/linux/sunrpc/svcauth_gss.h > @@ -16,7 +16,7 @@ > #include > #include > > -int gss_svc_init(void); > +int gss_svc_init(unsigned int upcall_type); > void gss_svc_shutdown(void); > int gss_svc_init_net(struct net *net); > void gss_svc_shutdown_net(struct net *net); > diff --git a/net/sunrpc/auth_gss/auth_gss.c b/net/sunrpc/auth_gss/auth_gss.c > index 836cbecb1947235d38c62eadf79ae96ad73906e6..97fe72609387cb8b948bc3aa4d14db4956138d3c 100644 > --- a/net/sunrpc/auth_gss/auth_gss.c > +++ b/net/sunrpc/auth_gss/auth_gss.c > @@ -60,6 +60,8 @@ static const struct rpc_credops gss_nullops; > #define GSS_RETRY_EXPIRED 5 > static unsigned int gss_expired_cred_retry_delay = GSS_RETRY_EXPIRED; > > +static unsigned int gss_upcall_daemon_type = GSS_UPCALL_LEGACY; > + > #ifdef RPC_DEBUG > # define RPCDBG_FACILITY RPCDBG_AUTH > #endif > @@ -1687,7 +1689,7 @@ static int __init init_rpcsec_gss(void) > err = rpcauth_register(&authgss_ops); > if (err) > goto out; > - err = gss_svc_init(); > + err = gss_svc_init(gss_upcall_daemon_type); > if (err) > goto out_unregister; > err = register_pernet_subsys(&rpcsec_gss_net_ops); > @@ -1717,6 +1719,11 @@ module_param_named(expired_cred_retry_delay, > uint, 0644); > MODULE_PARM_DESC(expired_cred_retry_delay, "Timeout (in seconds) until " > "the RPC engine retries an expired credential"); > +module_param_named(upcall_daemon_type, > + gss_upcall_daemon_type, > + uint, 0644); > +MODULE_PARM_DESC(upcall_daemon_type, "Type of svcgss upcall daemon used " > + "(legacy=0 or gssproxy=1)"); > > module_init(init_rpcsec_gss) > module_exit(exit_rpcsec_gss) > diff --git a/net/sunrpc/auth_gss/svcauth_gss.c b/net/sunrpc/auth_gss/svcauth_gss.c > index aa1b649749741c82e60f0f528ac645197fd7ab35..87dcc837fa10e8ee7176379b1cc27235800bd612 100644 > --- a/net/sunrpc/auth_gss/svcauth_gss.c > +++ b/net/sunrpc/auth_gss/svcauth_gss.c > @@ -47,6 +47,7 @@ > #include > #include > #include > +#include "gss_rpc_upcall.h" > > #include "../netns.h" > > @@ -54,6 +55,8 @@ > # define RPCDBG_FACILITY RPCDBG_AUTH > #endif > > +static bool use_gssp = false; > + > /* The rpcsec_init cache is used for mapping RPCSEC_GSS_{,CONT_}INIT requests > * into replies. > * > @@ -554,6 +557,7 @@ static struct rsc *rsc_update(struct cache_detail *cd, struct rsc *new, struct r > } > > > + > static struct rsc * > gss_svc_searchbyctx(struct cache_detail *cd, struct xdr_netobj *handle) > { > @@ -984,13 +988,10 @@ gss_write_init_verf(struct cache_detail *cd, struct svc_rqst *rqstp, > } > > static inline int > -gss_read_verf(struct rpc_gss_wire_cred *gc, > - struct kvec *argv, __be32 *authp, > - struct xdr_netobj *in_handle, > - struct xdr_netobj *in_token) > +gss_read_common_verf(struct rpc_gss_wire_cred *gc, > + struct kvec *argv, __be32 *authp, > + struct xdr_netobj *in_handle) > { > - struct xdr_netobj tmpobj; > - > /* Read the verifier; should be NULL: */ > *authp = rpc_autherr_badverf; > if (argv->iov_len < 2 * 4) > @@ -1006,6 +1007,23 @@ gss_read_verf(struct rpc_gss_wire_cred *gc, > if (dup_netobj(in_handle, &gc->gc_ctx)) > return SVC_CLOSE; > *authp = rpc_autherr_badverf; > + > + return 0; > +} > + > +static inline int > +gss_read_verf(struct rpc_gss_wire_cred *gc, > + struct kvec *argv, __be32 *authp, > + struct xdr_netobj *in_handle, > + struct xdr_netobj *in_token) > +{ > + struct xdr_netobj tmpobj; > + int res; > + > + res = gss_read_common_verf(gc, argv, authp, in_handle); > + if (res) > + return res; > + > if (svc_safe_getnetobj(argv, &tmpobj)) { > kfree(in_handle->data); > return SVC_DENIED; > @@ -1018,6 +1036,42 @@ gss_read_verf(struct rpc_gss_wire_cred *gc, > return 0; > } > > +/* Ok this is really heavily depending on a set of semantics in > + * how rqstp is set up by svc_recv and pages laid down by the > + * server when reading a request. We are basically guaranteed that > + * the token lays all down linearly across a set of pages, starting > + * at iov_base in rq_arg.head[0] which happens to be the first of a > + * set of pages stored in rq_pages[]. > + * rq_arg.head[0].iov_base will provide us the page_base to pass > + * to the upcall. > + */ > +static inline int > +gss_read_proxy_verf(struct svc_rqst *rqstp, > + struct rpc_gss_wire_cred *gc, __be32 *authp, > + struct xdr_netobj *in_handle, > + struct gssp_in_token *in_token) > +{ > + struct kvec *argv = &rqstp->rq_arg.head[0]; > + u32 inlen; > + int res; > + > + res = gss_read_common_verf(gc, argv, authp, in_handle); > + if (res) > + return res; > + > + inlen = svc_getnl(argv); > + if (inlen > (argv->iov_len + rqstp->rq_arg.page_len)) > + return SVC_DENIED; > + > + in_token->pages = rqstp->rq_pages; > + in_token->page_base = (ulong)argv->iov_base & ~PAGE_MASK; > + in_token->page_len = inlen; > + > + /* FIXME: change argv to point to the end of in_token ? */ There's nothing left to read, so it looks like there's no problem here; drop the comment? > + > + return 0; > +} > + > static inline int > gss_write_resv(struct kvec *resv, size_t size_limit, > struct xdr_netobj *out_handle, struct xdr_netobj *out_token, > @@ -1045,7 +1099,7 @@ gss_write_resv(struct kvec *resv, size_t size_limit, > * the upcall results are available, write the verifier and result. > * Otherwise, drop the request pending an answer to the upcall. > */ > -static int svcauth_gss_handle_init(struct svc_rqst *rqstp, > +static int svcauth_gss_legacy_init(struct svc_rqst *rqstp, > struct rpc_gss_wire_cred *gc, __be32 *authp) > { > struct kvec *argv = &rqstp->rq_arg.head[0]; > @@ -1085,6 +1139,158 @@ out: > return ret; > } > > +static int gss_proxy_save_rsc(struct cache_detail *cd, > + struct gssp_upcall_data *ud, > + struct xdr_netobj *handle) > +{ > + struct rsc rsci, *rscp = NULL; > + static atomic64_t ctxhctr; > + long long ctxh; > + struct gss_api_mech *gm = NULL; > + time_t expiry; > + char *c; > + int status = -EINVAL; > + > + memset(&rsci, 0, sizeof(rsci)); > + /* context handle */ > + status = -ENOMEM; > + /* the handle needs to be just a unique id, > + * use a static counter */ > + ctxh = atomic64_inc_return(&ctxhctr); > + handle->data = kmemdup(&ctxh, sizeof(ctxh), GFP_KERNEL); Looks like you use this only to dup again immediately below in dup_to_netobj, so you should just be able to make that: handle->data = &ctxh; That's simpler and looks like it avoids a leak on exit. > + if (handle->data == NULL) > + goto out; > + handle->len = sizeof(ctxh); > + if (dup_to_netobj(&rsci.handle, handle->data, handle->len)) > + goto out; > + > + rscp = rsc_lookup(cd, &rsci); > + if (!rscp) > + goto out; > + > + /* creds */ > + if (!ud->creds) { > + dprintk("RPC: No creds found, marking Negative!\n"); > + set_bit(CACHE_NEGATIVE, &rsci.h.flags); When does this happen, out of curiosity? > + } else { > + > + /* steal creds */ > + rsci.cred = *ud->creds; > + ud->creds->cr_group_info = NULL; > + > + status = -EOPNOTSUPP; > + /* get mech handle from OID */ > + gm = gss_mech_get_by_OID(&ud->mech_oid); > + if (!gm) > + goto out; > + > + status = -EINVAL; > + /* mech-specific data: */ > + status = gss_import_sec_context(ud->out_handle.data, > + ud->out_handle.len, > + gm, &rsci.mechctx, > + &expiry, GFP_KERNEL); > + if (status) > + goto out; > + > + /* get client name */ > + if (ud->client_name.len != 0) { > + status = -ENOMEM; > + /* convert to GSS_NT_HOSTBASED_SERVICE form */ > + rsci.client_name = kstrndup(ud->client_name.data, > + ud->client_name.len, > + GFP_KERNEL); > + if (!rsci.client_name) > + goto out; > + /* terminate and remove realm part */ > + c = strchr(rsci.client_name, '@'); > + if (c) { > + *c = '\0'; > + > + /* change service-hostname delimiter */ > + c = strchr(rsci.client_name, '/'); > + if (c) *c = '@'; > + } > + if (!c) { > + /* not a service principal */ > + kfree(rsci.client_name); > + rsci.client_name = NULL; > + } > + } > + } > + > + rsci.h.expiry_time = expiry; > + rscp = rsc_update(cd, &rsci, rscp); > + status = 0; > +out: > + gss_mech_put(gm); > + rsc_free(&rsci); > + if (rscp) > + cache_put(&rscp->h, cd); > + else > + status = -ENOMEM; > + return status; > +} > + > +static int svcauth_gss_proxy_init(struct svc_rqst *rqstp, > + struct rpc_gss_wire_cred *gc, __be32 *authp) > +{ > + struct kvec *resv = &rqstp->rq_res.head[0]; > + struct xdr_netobj cli_handle; > + struct gssp_upcall_data ud; > + int status; > + int ret; > + struct sunrpc_net *sn = net_generic(rqstp->rq_xprt->xpt_net, sunrpc_net_id); > + > + memset(&cli_handle, 0, sizeof(cli_handle)); > + memset(&ud, 0, sizeof(ud)); > + ret = gss_read_proxy_verf(rqstp, gc, authp, > + &ud.in_handle, &ud.in_token); > + if (ret) > + return ret; > + > + ret = SVC_CLOSE; > + > + /* Perform synchronous upcall to gss-proxy */ > + status = gssp_accept_sec_context_upcall(&ud); > + if (status) { > + goto out; > + } Ditch the {}'s. > + > + dprintk("RPC: svcauth_gss: gss major status = %d\n", > + ud.major_status); > + > + switch (ud.major_status) { > + case GSS_S_CONTINUE_NEEDED: > + cli_handle = ud.out_handle; > + ud.out_handle.data = NULL; > + break; > + case GSS_S_COMPLETE: > + status = gss_proxy_save_rsc(sn->rsc_cache, &ud, &cli_handle); > + if (status) > + goto out; > + break; > + default: > + ret = SVC_CLOSE; > + goto out; > + } > + > + /* Got an answer to the upcall; use it: */ > + if (gss_write_init_verf(sn->rsc_cache, rqstp, > + &cli_handle, &ud.major_status)) > + goto out; > + if (gss_write_resv(resv, PAGE_SIZE, > + &cli_handle, &ud.out_token, > + ud.major_status, ud.minor_status)) > + goto out; > + > + ret = SVC_COMPLETE; > +out: > + gssp_free_upcall_data(&ud); > + kfree(cli_handle.data); > + return ret; > +} > + > /* > * Accept an rpcsec packet. > * If context establishment, punt to user space > @@ -1151,7 +1357,10 @@ svcauth_gss_accept(struct svc_rqst *rqstp, __be32 *authp) > switch (gc->gc_proc) { > case RPC_GSS_PROC_INIT: > case RPC_GSS_PROC_CONTINUE_INIT: > - return svcauth_gss_handle_init(rqstp, gc, authp); > + if (use_gssp) > + return svcauth_gss_proxy_init(rqstp, gc, authp); > + else > + return svcauth_gss_legacy_init(rqstp, gc, authp); > case RPC_GSS_PROC_DATA: > case RPC_GSS_PROC_DESTROY: > /* Look up the context, and check the verifier: */ > @@ -1523,9 +1732,12 @@ gss_svc_init_net(struct net *net) > rv = rsc_cache_create_net(net); > if (rv) > return rv; > - rv = rsi_cache_create_net(net); > - if (rv) > - goto out1; > + if (!use_gssp) { > + rv = rsi_cache_create_net(net); > + if (rv) > + goto out1; > + } > + > return 0; > out1: > rsc_cache_destroy_net(net); > @@ -1535,13 +1747,24 @@ out1: > void > gss_svc_shutdown_net(struct net *net) > { > - rsi_cache_destroy_net(net); > + if (!use_gssp) > + rsi_cache_destroy_net(net); > rsc_cache_destroy_net(net); > } > > int > -gss_svc_init(void) > +gss_svc_init(unsigned int upcall_type) > { > + switch (upcall_type) { > + case GSS_UPCALL_LEGACY: > + break; > + case GSS_UPCALL_GSSPROXY: > + dprintk("RPC: svcauth_gss: Initializing for use with gss-proxy\n"); > + use_gssp = true; > + break; > + default: > + return -EINVAL; > + } > return svc_auth_register(RPC_AUTH_GSS, &svcauthops_gss); > } > > -- > 1.7.7.6 > > -- > To unsubscribe from this list: send the line "unsubscribe linux-nfs" in > the body of a message to majordomo@vger.kernel.org > More majordomo info at http://vger.kernel.org/majordomo-info.html