From ca4d8d15577f63012f0d561e886cf7ddec760319 Mon Sep 17 00:00:00 2001 From: Josh Boyer Date: Tue, 4 Jun 2013 13:39:30 -0400 Subject: [PATCH] Add gssproxy backport from J. Bruce Fields --- gssproxy-backport.patch | 2896 +++++++++++++++++++++++++++++++++++++++ kernel.spec | 11 +- 2 files changed, 2905 insertions(+), 2 deletions(-) create mode 100644 gssproxy-backport.patch diff --git a/gssproxy-backport.patch b/gssproxy-backport.patch new file mode 100644 index 000000000..05efe2794 --- /dev/null +++ b/gssproxy-backport.patch @@ -0,0 +1,2896 @@ +From 7e5eee0a24ea886a0b68a8521117c5ef97668443 Mon Sep 17 00:00:00 2001 +From: Trond Myklebust +Date: Sun, 14 Apr 2013 11:42:00 -0400 +Subject: [PATCH 01/13] SUNRPC: Allow rpc_create() to request that TCP slots be + unlimited + +This is mainly for use by NFSv4.1, where the session negotiation +ultimately wants to decide how many RPC slots we can fill. + +Signed-off-by: Trond Myklebust +--- + include/linux/sunrpc/clnt.h | 1 + + include/linux/sunrpc/xprt.h | 3 +++ + net/sunrpc/clnt.c | 2 ++ + net/sunrpc/xprtsock.c | 6 +++++- + 4 files changed, 11 insertions(+), 1 deletion(-) + +diff --git a/include/linux/sunrpc/clnt.h b/include/linux/sunrpc/clnt.h +index 2cf4ffa..e7d492c 100644 +--- a/include/linux/sunrpc/clnt.h ++++ b/include/linux/sunrpc/clnt.h +@@ -124,6 +124,7 @@ struct rpc_create_args { + #define RPC_CLNT_CREATE_NOPING (1UL << 4) + #define RPC_CLNT_CREATE_DISCRTRY (1UL << 5) + #define RPC_CLNT_CREATE_QUIET (1UL << 6) ++#define RPC_CLNT_CREATE_INFINITE_SLOTS (1UL << 7) + + struct rpc_clnt *rpc_create(struct rpc_create_args *args); + struct rpc_clnt *rpc_bind_new_program(struct rpc_clnt *, +diff --git a/include/linux/sunrpc/xprt.h b/include/linux/sunrpc/xprt.h +index 30834be..12afb29 100644 +--- a/include/linux/sunrpc/xprt.h ++++ b/include/linux/sunrpc/xprt.h +@@ -255,6 +255,8 @@ static inline int bc_prealloc(struct rpc_rqst *req) + } + #endif /* CONFIG_SUNRPC_BACKCHANNEL */ + ++#define XPRT_CREATE_INFINITE_SLOTS (1U) ++ + struct xprt_create { + int ident; /* XPRT_TRANSPORT identifier */ + struct net * net; +@@ -263,6 +265,7 @@ struct xprt_create { + size_t addrlen; + const char *servername; + struct svc_xprt *bc_xprt; /* NFSv4.1 backchannel */ ++ unsigned int flags; + }; + + struct xprt_class { +diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c +index d5f35f1..54b69fc 100644 +--- a/net/sunrpc/clnt.c ++++ b/net/sunrpc/clnt.c +@@ -411,6 +411,8 @@ struct rpc_clnt *rpc_create(struct rpc_create_args *args) + }; + char servername[48]; + ++ if (args->flags & RPC_CLNT_CREATE_INFINITE_SLOTS) ++ xprtargs.flags |= XPRT_CREATE_INFINITE_SLOTS; + /* + * If the caller chooses not to specify a hostname, whip + * up a string representation of the passed-in address. +diff --git a/net/sunrpc/xprtsock.c b/net/sunrpc/xprtsock.c +index 3d02130..b08d314 100644 +--- a/net/sunrpc/xprtsock.c ++++ b/net/sunrpc/xprtsock.c +@@ -2767,9 +2767,13 @@ static struct rpc_xprt *xs_setup_tcp(struct xprt_create *args) + struct rpc_xprt *xprt; + struct sock_xprt *transport; + struct rpc_xprt *ret; ++ unsigned int max_slot_table_size = xprt_max_tcp_slot_table_entries; ++ ++ if (args->flags & XPRT_CREATE_INFINITE_SLOTS) ++ max_slot_table_size = RPC_MAX_SLOT_TABLE_LIMIT; + + xprt = xs_setup_xprt(args, xprt_tcp_slot_table_entries, +- xprt_max_tcp_slot_table_entries); ++ max_slot_table_size); + if (IS_ERR(xprt)) + return xprt; + transport = container_of(xprt, struct sock_xprt, xprt); +-- +1.8.1.4 + + +From 932c7301413eb94f7b60efaa1a80cb8cf0264459 Mon Sep 17 00:00:00 2001 +From: "J. Bruce Fields" +Date: Thu, 21 Feb 2013 10:14:22 -0500 +Subject: [PATCH 02/13] SUNRPC: attempt AF_LOCAL connect on setup + +In the gss-proxy case, setup time is when I know I'll have the right +namespace for the connect. + +In other cases, it might be useful to get any connection errors +earlier--though actually in practice it doesn't make any difference for +rpcbind. + +Signed-off-by: J. Bruce Fields +--- + net/sunrpc/xprtsock.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/net/sunrpc/xprtsock.c b/net/sunrpc/xprtsock.c +index b08d314..867ce36 100644 +--- a/net/sunrpc/xprtsock.c ++++ b/net/sunrpc/xprtsock.c +@@ -2655,6 +2655,9 @@ static struct rpc_xprt *xs_setup_local(struct xprt_create *args) + } + xprt_set_bound(xprt); + xs_format_peer_addresses(xprt, "local", RPCBIND_NETID_LOCAL); ++ ret = ERR_PTR(xs_local_setup_socket(transport)); ++ if (ret) ++ goto out_err; + break; + default: + ret = ERR_PTR(-EAFNOSUPPORT); +-- +1.8.1.4 + + +From 915d3592cc8718cc3e83164bb78c532d3a7d1f00 Mon Sep 17 00:00:00 2001 +From: "J. Bruce Fields" +Date: Thu, 11 Apr 2013 15:06:36 -0400 +Subject: [PATCH 03/13] SUNRPC: allow disabling idle timeout + +In the gss-proxy case we don't want to have to reconnect at random--we +want to connect only on gss-proxy startup when we can steal gss-proxy's +context to do the connect in the right namespace. + +So, provide a flag that allows the rpc_create caller to turn off the +idle timeout. + +Signed-off-by: J. Bruce Fields +--- + include/linux/sunrpc/clnt.h | 1 + + include/linux/sunrpc/xprt.h | 1 + + net/sunrpc/clnt.c | 2 ++ + net/sunrpc/xprt.c | 2 ++ + 4 files changed, 6 insertions(+) + +diff --git a/include/linux/sunrpc/clnt.h b/include/linux/sunrpc/clnt.h +index e7d492c..bfe11be 100644 +--- a/include/linux/sunrpc/clnt.h ++++ b/include/linux/sunrpc/clnt.h +@@ -125,6 +125,7 @@ struct rpc_create_args { + #define RPC_CLNT_CREATE_DISCRTRY (1UL << 5) + #define RPC_CLNT_CREATE_QUIET (1UL << 6) + #define RPC_CLNT_CREATE_INFINITE_SLOTS (1UL << 7) ++#define RPC_CLNT_CREATE_NO_IDLE_TIMEOUT (1UL << 8) + + struct rpc_clnt *rpc_create(struct rpc_create_args *args); + struct rpc_clnt *rpc_bind_new_program(struct rpc_clnt *, +diff --git a/include/linux/sunrpc/xprt.h b/include/linux/sunrpc/xprt.h +index 12afb29..c8708d8 100644 +--- a/include/linux/sunrpc/xprt.h ++++ b/include/linux/sunrpc/xprt.h +@@ -256,6 +256,7 @@ static inline int bc_prealloc(struct rpc_rqst *req) + #endif /* CONFIG_SUNRPC_BACKCHANNEL */ + + #define XPRT_CREATE_INFINITE_SLOTS (1U) ++#define XPRT_CREATE_NO_IDLE_TIMEOUT (1U << 1) + + struct xprt_create { + int ident; /* XPRT_TRANSPORT identifier */ +diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c +index 54b69fc..6d29b53 100644 +--- a/net/sunrpc/clnt.c ++++ b/net/sunrpc/clnt.c +@@ -413,6 +413,8 @@ struct rpc_clnt *rpc_create(struct rpc_create_args *args) + + if (args->flags & RPC_CLNT_CREATE_INFINITE_SLOTS) + xprtargs.flags |= XPRT_CREATE_INFINITE_SLOTS; ++ if (args->flags & RPC_CLNT_CREATE_NO_IDLE_TIMEOUT) ++ xprtargs.flags |= XPRT_CREATE_NO_IDLE_TIMEOUT; + /* + * If the caller chooses not to specify a hostname, whip + * up a string representation of the passed-in address. +diff --git a/net/sunrpc/xprt.c b/net/sunrpc/xprt.c +index b7478d5..33fac38 100644 +--- a/net/sunrpc/xprt.c ++++ b/net/sunrpc/xprt.c +@@ -1245,6 +1245,8 @@ found: + -PTR_ERR(xprt)); + goto out; + } ++ if (args->flags & XPRT_CREATE_NO_IDLE_TIMEOUT) ++ xprt->idle_timeout = 0; + INIT_WORK(&xprt->task_cleanup, xprt_autoclose); + if (xprt_has_timer(xprt)) + setup_timer(&xprt->timer, xprt_init_autodisconnect, +-- +1.8.1.4 + + +From faa25a9e80ab40a0e923011771aca6a1ddeea30d Mon Sep 17 00:00:00 2001 +From: Simo Sorce +Date: Fri, 25 May 2012 18:09:53 -0400 +Subject: [PATCH 04/13] SUNRPC: conditionally return endtime from + import_sec_context + +We expose this parameter for a future caller. +It will be used to extract the endtime from the gss-proxy upcall mechanism, +in order to set the rsc cache expiration time. + +Signed-off-by: Simo Sorce +Signed-off-by: J. Bruce Fields +--- + include/linux/sunrpc/gss_api.h | 2 ++ + net/sunrpc/auth_gss/auth_gss.c | 2 +- + net/sunrpc/auth_gss/gss_krb5_mech.c | 7 +++++-- + net/sunrpc/auth_gss/gss_mech_switch.c | 5 +++-- + net/sunrpc/auth_gss/svcauth_gss.c | 3 ++- + 5 files changed, 13 insertions(+), 6 deletions(-) + +diff --git a/include/linux/sunrpc/gss_api.h b/include/linux/sunrpc/gss_api.h +index a19e254..04d03bb 100644 +--- a/include/linux/sunrpc/gss_api.h ++++ b/include/linux/sunrpc/gss_api.h +@@ -37,6 +37,7 @@ int gss_import_sec_context( + size_t bufsize, + struct gss_api_mech *mech, + struct gss_ctx **ctx_id, ++ time_t *endtime, + gfp_t gfp_mask); + u32 gss_get_mic( + struct gss_ctx *ctx_id, +@@ -92,6 +93,7 @@ struct gss_api_ops { + const void *input_token, + size_t bufsize, + struct gss_ctx *ctx_id, ++ time_t *endtime, + gfp_t gfp_mask); + u32 (*gss_get_mic)( + struct gss_ctx *ctx_id, +diff --git a/net/sunrpc/auth_gss/auth_gss.c b/net/sunrpc/auth_gss/auth_gss.c +index 5257d29..23563e7 100644 +--- a/net/sunrpc/auth_gss/auth_gss.c ++++ b/net/sunrpc/auth_gss/auth_gss.c +@@ -238,7 +238,7 @@ gss_fill_context(const void *p, const void *end, struct gss_cl_ctx *ctx, struct + p = ERR_PTR(-EFAULT); + goto err; + } +- ret = gss_import_sec_context(p, seclen, gm, &ctx->gc_gss_ctx, GFP_NOFS); ++ ret = gss_import_sec_context(p, seclen, gm, &ctx->gc_gss_ctx, NULL, GFP_NOFS); + if (ret < 0) { + p = ERR_PTR(ret); + goto err; +diff --git a/net/sunrpc/auth_gss/gss_krb5_mech.c b/net/sunrpc/auth_gss/gss_krb5_mech.c +index d3611f1..3bc4a23 100644 +--- a/net/sunrpc/auth_gss/gss_krb5_mech.c ++++ b/net/sunrpc/auth_gss/gss_krb5_mech.c +@@ -679,6 +679,7 @@ out_err: + static int + gss_import_sec_context_kerberos(const void *p, size_t len, + struct gss_ctx *ctx_id, ++ time_t *endtime, + gfp_t gfp_mask) + { + const void *end = (const void *)((const char *)p + len); +@@ -694,9 +695,11 @@ gss_import_sec_context_kerberos(const void *p, size_t len, + else + ret = gss_import_v2_context(p, end, ctx, gfp_mask); + +- if (ret == 0) ++ if (ret == 0) { + ctx_id->internal_ctx_id = ctx; +- else ++ if (endtime) ++ *endtime = ctx->endtime; ++ } else + kfree(ctx); + + dprintk("RPC: %s: returning %d\n", __func__, ret); +diff --git a/net/sunrpc/auth_gss/gss_mech_switch.c b/net/sunrpc/auth_gss/gss_mech_switch.c +index f0f4eee..43fd5bb 100644 +--- a/net/sunrpc/auth_gss/gss_mech_switch.c ++++ b/net/sunrpc/auth_gss/gss_mech_switch.c +@@ -325,14 +325,15 @@ int + gss_import_sec_context(const void *input_token, size_t bufsize, + struct gss_api_mech *mech, + struct gss_ctx **ctx_id, ++ time_t *endtime, + gfp_t gfp_mask) + { + if (!(*ctx_id = kzalloc(sizeof(**ctx_id), gfp_mask))) + return -ENOMEM; + (*ctx_id)->mech_type = gss_mech_get(mech); + +- return mech->gm_ops +- ->gss_import_sec_context(input_token, bufsize, *ctx_id, gfp_mask); ++ return mech->gm_ops->gss_import_sec_context(input_token, bufsize, ++ *ctx_id, endtime, gfp_mask); + } + + /* gss_get_mic: compute a mic over message and return mic_token. */ +diff --git a/net/sunrpc/auth_gss/svcauth_gss.c b/net/sunrpc/auth_gss/svcauth_gss.c +index 5ead605..20eedec 100644 +--- a/net/sunrpc/auth_gss/svcauth_gss.c ++++ b/net/sunrpc/auth_gss/svcauth_gss.c +@@ -497,7 +497,8 @@ static int rsc_parse(struct cache_detail *cd, + len = qword_get(&mesg, buf, mlen); + if (len < 0) + goto out; +- status = gss_import_sec_context(buf, len, gm, &rsci.mechctx, GFP_KERNEL); ++ status = gss_import_sec_context(buf, len, gm, &rsci.mechctx, ++ NULL, GFP_KERNEL); + if (status) + goto out; + +-- +1.8.1.4 + + +From ffc614331a36038700b7bc13bc2da6b8f120b9d6 Mon Sep 17 00:00:00 2001 +From: Simo Sorce +Date: Fri, 25 May 2012 18:09:55 -0400 +Subject: [PATCH 05/13] SUNRPC: Add RPC based upcall mechanism for RPCGSS auth + +This patch implements a sunrpc client to use the services of the gssproxy +userspace daemon. + +In particular it allows to perform calls in user space using an RPC +call instead of custom hand-coded upcall/downcall messages. + +Currently only accept_sec_context is implemented as that is all is needed for +the server case. + +File server modules like NFS and CIFS can use full gssapi services this way, +once init_sec_context is also implemented. + +For the NFS server case this code allow to lift the limit of max 2k krb5 +tickets. This limit is prevents legitimate kerberos deployments from using krb5 +authentication with the Linux NFS server as they have normally ticket that are +many kilobytes large. + +It will also allow to lift the limitation on the size of the credential set +(uid,gid,gids) passed down from user space for users that have very many groups +associated. Currently the downcall mechanism used by rpc.svcgssd is limited +to around 2k secondary groups of the 65k allowed by kernel structures. + +Signed-off-by: Simo Sorce +[bfields: containerization, concurrent upcalls, misc. fixes and cleanup] +Signed-off-by: J. Bruce Fields +--- + net/sunrpc/auth_gss/Makefile | 3 +- + net/sunrpc/auth_gss/gss_rpc_upcall.c | 355 +++++++++++++++ + net/sunrpc/auth_gss/gss_rpc_upcall.h | 47 ++ + net/sunrpc/auth_gss/gss_rpc_xdr.c | 832 +++++++++++++++++++++++++++++++++++ + net/sunrpc/auth_gss/gss_rpc_xdr.h | 264 +++++++++++ + net/sunrpc/clnt.c | 1 + + net/sunrpc/netns.h | 3 + + 7 files changed, 1504 insertions(+), 1 deletion(-) + create mode 100644 net/sunrpc/auth_gss/gss_rpc_upcall.c + create mode 100644 net/sunrpc/auth_gss/gss_rpc_upcall.h + create mode 100644 net/sunrpc/auth_gss/gss_rpc_xdr.c + create mode 100644 net/sunrpc/auth_gss/gss_rpc_xdr.h + +diff --git a/net/sunrpc/auth_gss/Makefile b/net/sunrpc/auth_gss/Makefile +index 9e4cb59..14e9e53 100644 +--- a/net/sunrpc/auth_gss/Makefile ++++ b/net/sunrpc/auth_gss/Makefile +@@ -5,7 +5,8 @@ + obj-$(CONFIG_SUNRPC_GSS) += auth_rpcgss.o + + auth_rpcgss-y := auth_gss.o gss_generic_token.o \ +- gss_mech_switch.o svcauth_gss.o ++ gss_mech_switch.o svcauth_gss.o \ ++ gss_rpc_upcall.o gss_rpc_xdr.o + + obj-$(CONFIG_RPCSEC_GSS_KRB5) += rpcsec_gss_krb5.o + +diff --git a/net/sunrpc/auth_gss/gss_rpc_upcall.c b/net/sunrpc/auth_gss/gss_rpc_upcall.c +new file mode 100644 +index 0000000..2d33ddf +--- /dev/null ++++ b/net/sunrpc/auth_gss/gss_rpc_upcall.c +@@ -0,0 +1,355 @@ ++/* ++ * linux/net/sunrpc/gss_rpc_upcall.c ++ * ++ * Copyright (C) 2012 Simo Sorce ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#include ++ ++#include ++#include "gss_rpc_upcall.h" ++ ++#define GSSPROXY_SOCK_PATHNAME "/var/run/gssproxy.sock" ++ ++#define GSSPROXY_PROGRAM (400112u) ++#define GSSPROXY_VERS_1 (1u) ++ ++/* ++ * Encoding/Decoding functions ++ */ ++ ++enum { ++ GSSX_NULL = 0, /* Unused */ ++ GSSX_INDICATE_MECHS = 1, ++ GSSX_GET_CALL_CONTEXT = 2, ++ GSSX_IMPORT_AND_CANON_NAME = 3, ++ GSSX_EXPORT_CRED = 4, ++ GSSX_IMPORT_CRED = 5, ++ GSSX_ACQUIRE_CRED = 6, ++ GSSX_STORE_CRED = 7, ++ GSSX_INIT_SEC_CONTEXT = 8, ++ GSSX_ACCEPT_SEC_CONTEXT = 9, ++ GSSX_RELEASE_HANDLE = 10, ++ GSSX_GET_MIC = 11, ++ GSSX_VERIFY = 12, ++ GSSX_WRAP = 13, ++ GSSX_UNWRAP = 14, ++ GSSX_WRAP_SIZE_LIMIT = 15, ++}; ++ ++#define PROC(proc, name) \ ++[GSSX_##proc] = { \ ++ .p_proc = GSSX_##proc, \ ++ .p_encode = (kxdreproc_t)gssx_enc_##name, \ ++ .p_decode = (kxdrdproc_t)gssx_dec_##name, \ ++ .p_arglen = GSSX_ARG_##name##_sz, \ ++ .p_replen = GSSX_RES_##name##_sz, \ ++ .p_statidx = GSSX_##proc, \ ++ .p_name = #proc, \ ++} ++ ++struct rpc_procinfo gssp_procedures[] = { ++ PROC(INDICATE_MECHS, indicate_mechs), ++ PROC(GET_CALL_CONTEXT, get_call_context), ++ PROC(IMPORT_AND_CANON_NAME, import_and_canon_name), ++ PROC(EXPORT_CRED, export_cred), ++ PROC(IMPORT_CRED, import_cred), ++ PROC(ACQUIRE_CRED, acquire_cred), ++ PROC(STORE_CRED, store_cred), ++ PROC(INIT_SEC_CONTEXT, init_sec_context), ++ PROC(ACCEPT_SEC_CONTEXT, accept_sec_context), ++ PROC(RELEASE_HANDLE, release_handle), ++ PROC(GET_MIC, get_mic), ++ PROC(VERIFY, verify), ++ PROC(WRAP, wrap), ++ PROC(UNWRAP, unwrap), ++ PROC(WRAP_SIZE_LIMIT, wrap_size_limit), ++}; ++ ++ ++ ++/* ++ * Common transport functions ++ */ ++ ++static const struct rpc_program gssp_program; ++ ++static int gssp_rpc_create(struct net *net, struct rpc_clnt **_clnt) ++{ ++ static const struct sockaddr_un gssp_localaddr = { ++ .sun_family = AF_LOCAL, ++ .sun_path = GSSPROXY_SOCK_PATHNAME, ++ }; ++ struct rpc_create_args args = { ++ .net = net, ++ .protocol = XPRT_TRANSPORT_LOCAL, ++ .address = (struct sockaddr *)&gssp_localaddr, ++ .addrsize = sizeof(gssp_localaddr), ++ .servername = "localhost", ++ .program = &gssp_program, ++ .version = GSSPROXY_VERS_1, ++ .authflavor = RPC_AUTH_NULL, ++ /* ++ * Note we want connection to be done in the caller's ++ * filesystem namespace. We therefore turn off the idle ++ * timeout, which would result in reconnections being ++ * done without the correct namespace: ++ */ ++ .flags = RPC_CLNT_CREATE_NOPING | ++ RPC_CLNT_CREATE_NO_IDLE_TIMEOUT ++ }; ++ struct rpc_clnt *clnt; ++ int result = 0; ++ ++ clnt = rpc_create(&args); ++ if (IS_ERR(clnt)) { ++ dprintk("RPC: failed to create AF_LOCAL gssproxy " ++ "client (errno %ld).\n", PTR_ERR(clnt)); ++ result = -PTR_ERR(clnt); ++ *_clnt = NULL; ++ goto out; ++ } ++ ++ dprintk("RPC: created new gssp local client (gssp_local_clnt: " ++ "%p)\n", clnt); ++ *_clnt = clnt; ++ ++out: ++ return result; ++} ++ ++void init_gssp_clnt(struct sunrpc_net *sn) ++{ ++ mutex_init(&sn->gssp_lock); ++ sn->gssp_clnt = NULL; ++} ++ ++int set_gssp_clnt(struct net *net) ++{ ++ struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); ++ struct rpc_clnt *clnt; ++ int ret; ++ ++ mutex_lock(&sn->gssp_lock); ++ ret = gssp_rpc_create(net, &clnt); ++ if (!ret) { ++ if (sn->gssp_clnt) ++ rpc_shutdown_client(sn->gssp_clnt); ++ sn->gssp_clnt = clnt; ++ } ++ mutex_unlock(&sn->gssp_lock); ++ return ret; ++} ++ ++void clear_gssp_clnt(struct sunrpc_net *sn) ++{ ++ mutex_lock(&sn->gssp_lock); ++ if (sn->gssp_clnt) { ++ rpc_shutdown_client(sn->gssp_clnt); ++ sn->gssp_clnt = NULL; ++ } ++ mutex_unlock(&sn->gssp_lock); ++} ++ ++static struct rpc_clnt *get_gssp_clnt(struct sunrpc_net *sn) ++{ ++ struct rpc_clnt *clnt; ++ ++ mutex_lock(&sn->gssp_lock); ++ clnt = sn->gssp_clnt; ++ if (clnt) ++ atomic_inc(&clnt->cl_count); ++ mutex_unlock(&sn->gssp_lock); ++ return clnt; ++} ++ ++static int gssp_call(struct net *net, struct rpc_message *msg) ++{ ++ struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); ++ struct rpc_clnt *clnt; ++ int status; ++ ++ clnt = get_gssp_clnt(sn); ++ if (!clnt) ++ return -EIO; ++ status = rpc_call_sync(clnt, msg, 0); ++ if (status < 0) { ++ dprintk("gssp: rpc_call returned error %d\n", -status); ++ switch (status) { ++ case -EPROTONOSUPPORT: ++ status = -EINVAL; ++ break; ++ case -ECONNREFUSED: ++ case -ETIMEDOUT: ++ case -ENOTCONN: ++ status = -EAGAIN; ++ break; ++ case -ERESTARTSYS: ++ if (signalled ()) ++ status = -EINTR; ++ break; ++ default: ++ break; ++ } ++ } ++ rpc_release_client(clnt); ++ return status; ++} ++ ++ ++/* ++ * Public functions ++ */ ++ ++/* numbers somewhat arbitrary but large enough for current needs */ ++#define GSSX_MAX_OUT_HANDLE 128 ++#define GSSX_MAX_MECH_OID 16 ++#define GSSX_MAX_SRC_PRINC 256 ++#define GSSX_KMEMBUF (GSSX_max_output_handle_sz + \ ++ GSSX_max_oid_sz + \ ++ GSSX_max_princ_sz + \ ++ sizeof(struct svc_cred)) ++ ++int gssp_accept_sec_context_upcall(struct net *net, ++ struct gssp_upcall_data *data) ++{ ++ struct gssx_ctx ctxh = { ++ .state = data->in_handle ++ }; ++ struct gssx_arg_accept_sec_context arg = { ++ .input_token = data->in_token, ++ }; ++ struct gssx_ctx rctxh = { ++ /* ++ * pass in the max length we expect for each of these ++ * buffers but let the xdr code kmalloc them: ++ */ ++ .exported_context_token.len = GSSX_max_output_handle_sz, ++ .mech.len = GSSX_max_oid_sz, ++ .src_name.display_name.len = GSSX_max_princ_sz ++ }; ++ struct gssx_res_accept_sec_context res = { ++ .context_handle = &rctxh, ++ .output_token = &data->out_token ++ }; ++ struct rpc_message msg = { ++ .rpc_proc = &gssp_procedures[GSSX_ACCEPT_SEC_CONTEXT], ++ .rpc_argp = &arg, ++ .rpc_resp = &res, ++ .rpc_cred = NULL, /* FIXME ? */ ++ }; ++ struct xdr_netobj client_name = { 0 , NULL }; ++ int ret; ++ ++ if (data->in_handle.len != 0) ++ arg.context_handle = &ctxh; ++ res.output_token->len = GSSX_max_output_token_sz; ++ ++ /* use nfs/ for targ_name ? */ ++ ++ ret = gssp_call(net, &msg); ++ ++ /* we need to fetch all data even in case of error so ++ * that we can free special strctures is they have been allocated */ ++ data->major_status = res.status.major_status; ++ data->minor_status = res.status.minor_status; ++ if (res.context_handle) { ++ data->out_handle = rctxh.exported_context_token; ++ data->mech_oid = rctxh.mech; ++ client_name = rctxh.src_name.display_name; ++ } ++ ++ if (res.options.count == 1) { ++ gssx_buffer *value = &res.options.data[0].value; ++ /* Currently we only decode CREDS_VALUE, if we add ++ * anything else we'll have to loop and match on the ++ * option name */ ++ if (value->len == 1) { ++ /* steal group info from struct svc_cred */ ++ data->creds = *(struct svc_cred *)value->data; ++ data->found_creds = 1; ++ } ++ /* whether we use it or not, free data */ ++ kfree(value->data); ++ } ++ ++ if (res.options.count != 0) { ++ kfree(res.options.data); ++ } ++ ++ /* convert to GSS_NT_HOSTBASED_SERVICE form and set into creds */ ++ if (data->found_creds && client_name.data != NULL) { ++ char *c; ++ ++ data->creds.cr_principal = kstrndup(client_name.data, ++ client_name.len, GFP_KERNEL); ++ if (data->creds.cr_principal) { ++ /* terminate and remove realm part */ ++ c = strchr(data->creds.cr_principal, '@'); ++ if (c) { ++ *c = '\0'; ++ ++ /* change service-hostname delimiter */ ++ c = strchr(data->creds.cr_principal, '/'); ++ if (c) *c = '@'; ++ } ++ if (!c) { ++ /* not a service principal */ ++ kfree(data->creds.cr_principal); ++ data->creds.cr_principal = NULL; ++ } ++ } ++ } ++ kfree(client_name.data); ++ ++ return ret; ++} ++ ++void gssp_free_upcall_data(struct gssp_upcall_data *data) ++{ ++ kfree(data->in_handle.data); ++ kfree(data->out_handle.data); ++ kfree(data->out_token.data); ++ kfree(data->mech_oid.data); ++ free_svc_cred(&data->creds); ++} ++ ++/* ++ * Initialization stuff ++ */ ++ ++static const struct rpc_version gssp_version1 = { ++ .number = GSSPROXY_VERS_1, ++ .nrprocs = ARRAY_SIZE(gssp_procedures), ++ .procs = gssp_procedures, ++}; ++ ++static const struct rpc_version *gssp_version[] = { ++ NULL, ++ &gssp_version1, ++}; ++ ++static struct rpc_stat gssp_stats; ++ ++static const struct rpc_program gssp_program = { ++ .name = "gssproxy", ++ .number = GSSPROXY_PROGRAM, ++ .nrvers = ARRAY_SIZE(gssp_version), ++ .version = gssp_version, ++ .stats = &gssp_stats, ++}; +diff --git a/net/sunrpc/auth_gss/gss_rpc_upcall.h b/net/sunrpc/auth_gss/gss_rpc_upcall.h +new file mode 100644 +index 0000000..4c2caaa +--- /dev/null ++++ b/net/sunrpc/auth_gss/gss_rpc_upcall.h +@@ -0,0 +1,47 @@ ++/* ++ * linux/net/sunrpc/gss_rpc_upcall.h ++ * ++ * Copyright (C) 2012 Simo Sorce ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#ifndef _GSS_RPC_UPCALL_H ++#define _GSS_RPC_UPCALL_H ++ ++#include ++#include "gss_rpc_xdr.h" ++#include "../netns.h" ++ ++struct gssp_upcall_data { ++ struct xdr_netobj in_handle; ++ struct gssp_in_token in_token; ++ struct xdr_netobj out_handle; ++ struct xdr_netobj out_token; ++ struct xdr_netobj mech_oid; ++ struct svc_cred creds; ++ int found_creds; ++ int major_status; ++ int minor_status; ++}; ++ ++int gssp_accept_sec_context_upcall(struct net *net, ++ struct gssp_upcall_data *data); ++void gssp_free_upcall_data(struct gssp_upcall_data *data); ++ ++void init_gssp_clnt(struct sunrpc_net *); ++int set_gssp_clnt(struct net *); ++void clear_gssp_clnt(struct sunrpc_net *); ++#endif /* _GSS_RPC_UPCALL_H */ +diff --git a/net/sunrpc/auth_gss/gss_rpc_xdr.c b/net/sunrpc/auth_gss/gss_rpc_xdr.c +new file mode 100644 +index 0000000..d0ccdff +--- /dev/null ++++ b/net/sunrpc/auth_gss/gss_rpc_xdr.c +@@ -0,0 +1,832 @@ ++/* ++ * GSS Proxy upcall module ++ * ++ * Copyright (C) 2012 Simo Sorce ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#include "gss_rpc_xdr.h" ++ ++static bool gssx_check_pointer(struct xdr_stream *xdr) ++{ ++ __be32 *p; ++ ++ p = xdr_reserve_space(xdr, 4); ++ if (unlikely(p == NULL)) ++ return -ENOSPC; ++ return *p?true:false; ++} ++ ++static int gssx_enc_bool(struct xdr_stream *xdr, int v) ++{ ++ __be32 *p; ++ ++ p = xdr_reserve_space(xdr, 4); ++ if (unlikely(p == NULL)) ++ return -ENOSPC; ++ *p = v ? xdr_one : xdr_zero; ++ return 0; ++} ++ ++static int gssx_dec_bool(struct xdr_stream *xdr, u32 *v) ++{ ++ __be32 *p; ++ ++ p = xdr_inline_decode(xdr, 4); ++ if (unlikely(p == NULL)) ++ return -ENOSPC; ++ *v = be32_to_cpu(*p); ++ return 0; ++} ++ ++static int gssx_enc_buffer(struct xdr_stream *xdr, ++ gssx_buffer *buf) ++{ ++ __be32 *p; ++ ++ p = xdr_reserve_space(xdr, sizeof(u32) + buf->len); ++ if (!p) ++ return -ENOSPC; ++ xdr_encode_opaque(p, buf->data, buf->len); ++ return 0; ++} ++ ++static int gssx_enc_in_token(struct xdr_stream *xdr, ++ struct gssp_in_token *in) ++{ ++ __be32 *p; ++ ++ p = xdr_reserve_space(xdr, 4); ++ if (!p) ++ return -ENOSPC; ++ *p = cpu_to_be32(in->page_len); ++ ++ /* all we need to do is to write pages */ ++ xdr_write_pages(xdr, in->pages, in->page_base, in->page_len); ++ ++ return 0; ++} ++ ++ ++static int gssx_dec_buffer(struct xdr_stream *xdr, ++ gssx_buffer *buf) ++{ ++ u32 length; ++ __be32 *p; ++ ++ p = xdr_inline_decode(xdr, 4); ++ if (unlikely(p == NULL)) ++ return -ENOSPC; ++ ++ length = be32_to_cpup(p); ++ p = xdr_inline_decode(xdr, length); ++ if (unlikely(p == NULL)) ++ return -ENOSPC; ++ ++ if (buf->len == 0) { ++ /* we intentionally are not interested in this buffer */ ++ return 0; ++ } ++ if (length > buf->len) ++ return -ENOSPC; ++ ++ if (!buf->data) { ++ buf->data = kmemdup(p, length, GFP_KERNEL); ++ if (!buf->data) ++ return -ENOMEM; ++ } else { ++ memcpy(buf->data, p, length); ++ } ++ buf->len = length; ++ return 0; ++} ++ ++static int gssx_enc_option(struct xdr_stream *xdr, ++ struct gssx_option *opt) ++{ ++ int err; ++ ++ err = gssx_enc_buffer(xdr, &opt->option); ++ if (err) ++ return err; ++ err = gssx_enc_buffer(xdr, &opt->value); ++ return err; ++} ++ ++static int gssx_dec_option(struct xdr_stream *xdr, ++ struct gssx_option *opt) ++{ ++ int err; ++ ++ err = gssx_dec_buffer(xdr, &opt->option); ++ if (err) ++ return err; ++ err = gssx_dec_buffer(xdr, &opt->value); ++ return err; ++} ++ ++static int dummy_enc_opt_array(struct xdr_stream *xdr, ++ struct gssx_option_array *oa) ++{ ++ __be32 *p; ++ ++ if (oa->count != 0) ++ return -EINVAL; ++ ++ p = xdr_reserve_space(xdr, 4); ++ if (!p) ++ return -ENOSPC; ++ *p = 0; ++ ++ return 0; ++} ++ ++static int dummy_dec_opt_array(struct xdr_stream *xdr, ++ struct gssx_option_array *oa) ++{ ++ struct gssx_option dummy; ++ u32 count, i; ++ __be32 *p; ++ ++ p = xdr_inline_decode(xdr, 4); ++ if (unlikely(p == NULL)) ++ return -ENOSPC; ++ count = be32_to_cpup(p++); ++ memset(&dummy, 0, sizeof(dummy)); ++ for (i = 0; i < count; i++) { ++ gssx_dec_option(xdr, &dummy); ++ } ++ ++ oa->count = 0; ++ oa->data = NULL; ++ return 0; ++} ++ ++static int get_s32(void **p, void *max, s32 *res) ++{ ++ void *base = *p; ++ void *next = (void *)((char *)base + sizeof(s32)); ++ if (unlikely(next > max || next < base)) ++ return -EINVAL; ++ memcpy(res, base, sizeof(s32)); ++ *p = next; ++ return 0; ++} ++ ++static int gssx_dec_linux_creds(struct xdr_stream *xdr, ++ struct svc_cred *creds) ++{ ++ u32 length; ++ __be32 *p; ++ void *q, *end; ++ s32 tmp; ++ int N, i, err; ++ ++ p = xdr_inline_decode(xdr, 4); ++ if (unlikely(p == NULL)) ++ return -ENOSPC; ++ ++ length = be32_to_cpup(p); ++ ++ /* FIXME: we do not want to use the scratch buffer for this one ++ * may need to use functions that allows us to access an io vector ++ * directly */ ++ p = xdr_inline_decode(xdr, length); ++ if (unlikely(p == NULL)) ++ return -ENOSPC; ++ ++ q = p; ++ end = q + length; ++ ++ /* uid */ ++ err = get_s32(&q, end, &tmp); ++ if (err) ++ return err; ++ creds->cr_uid = tmp; ++ ++ /* gid */ ++ err = get_s32(&q, end, &tmp); ++ if (err) ++ return err; ++ creds->cr_gid = tmp; ++ ++ /* number of additional gid's */ ++ err = get_s32(&q, end, &tmp); ++ if (err) ++ return err; ++ N = tmp; ++ creds->cr_group_info = groups_alloc(N); ++ if (creds->cr_group_info == NULL) ++ return -ENOMEM; ++ ++ /* gid's */ ++ for (i = 0; i < N; i++) { ++ err = get_s32(&q, end, &tmp); ++ if (err) { ++ groups_free(creds->cr_group_info); ++ return err; ++ } ++ GROUP_AT(creds->cr_group_info, i) = tmp; ++ } ++ ++ return 0; ++} ++ ++static int gssx_dec_option_array(struct xdr_stream *xdr, ++ struct gssx_option_array *oa) ++{ ++ struct svc_cred *creds; ++ u32 count, i; ++ __be32 *p; ++ int err; ++ ++ p = xdr_inline_decode(xdr, 4); ++ if (unlikely(p == NULL)) ++ return -ENOSPC; ++ count = be32_to_cpup(p++); ++ if (count != 0) { ++ /* we recognize only 1 currently: CREDS_VALUE */ ++ oa->count = 1; ++ ++ oa->data = kmalloc(sizeof(struct gssx_option), GFP_KERNEL); ++ if (!oa->data) ++ return -ENOMEM; ++ ++ creds = kmalloc(sizeof(struct svc_cred), GFP_KERNEL); ++ if (!creds) { ++ kfree(oa->data); ++ return -ENOMEM; ++ } ++ ++ oa->data[0].option.data = CREDS_VALUE; ++ oa->data[0].option.len = sizeof(CREDS_VALUE); ++ oa->data[0].value.data = (void *)creds; ++ oa->data[0].value.len = 0; ++ } ++ for (i = 0; i < count; i++) { ++ gssx_buffer dummy = { 0, NULL }; ++ u32 length; ++ ++ /* option buffer */ ++ p = xdr_inline_decode(xdr, 4); ++ if (unlikely(p == NULL)) ++ return -ENOSPC; ++ ++ length = be32_to_cpup(p); ++ p = xdr_inline_decode(xdr, length); ++ if (unlikely(p == NULL)) ++ return -ENOSPC; ++ ++ if (length == sizeof(CREDS_VALUE) && ++ memcmp(p, CREDS_VALUE, sizeof(CREDS_VALUE)) == 0) { ++ /* We have creds here. parse them */ ++ err = gssx_dec_linux_creds(xdr, creds); ++ if (err) ++ return err; ++ oa->data[0].value.len = 1; /* presence */ ++ } else { ++ /* consume uninteresting buffer */ ++ err = gssx_dec_buffer(xdr, &dummy); ++ if (err) ++ return err; ++ } ++ } ++ return 0; ++} ++ ++static int gssx_dec_status(struct xdr_stream *xdr, ++ struct gssx_status *status) ++{ ++ __be32 *p; ++ int err; ++ ++ /* status->major_status */ ++ p = xdr_inline_decode(xdr, 8); ++ if (unlikely(p == NULL)) ++ return -ENOSPC; ++ p = xdr_decode_hyper(p, &status->major_status); ++ ++ /* status->mech */ ++ err = gssx_dec_buffer(xdr, &status->mech); ++ if (err) ++ return err; ++ ++ /* status->minor_status */ ++ p = xdr_inline_decode(xdr, 8); ++ if (unlikely(p == NULL)) ++ return -ENOSPC; ++ p = xdr_decode_hyper(p, &status->minor_status); ++ ++ /* status->major_status_string */ ++ err = gssx_dec_buffer(xdr, &status->major_status_string); ++ if (err) ++ return err; ++ ++ /* status->minor_status_string */ ++ err = gssx_dec_buffer(xdr, &status->minor_status_string); ++ if (err) ++ return err; ++ ++ /* status->server_ctx */ ++ err = gssx_dec_buffer(xdr, &status->server_ctx); ++ if (err) ++ return err; ++ ++ /* we assume we have no options for now, so simply consume them */ ++ /* status->options */ ++ err = dummy_dec_opt_array(xdr, &status->options); ++ ++ return err; ++} ++ ++static int gssx_enc_call_ctx(struct xdr_stream *xdr, ++ struct gssx_call_ctx *ctx) ++{ ++ struct gssx_option opt; ++ __be32 *p; ++ int err; ++ ++ /* ctx->locale */ ++ err = gssx_enc_buffer(xdr, &ctx->locale); ++ if (err) ++ return err; ++ ++ /* ctx->server_ctx */ ++ err = gssx_enc_buffer(xdr, &ctx->server_ctx); ++ if (err) ++ return err; ++ ++ /* we always want to ask for lucid contexts */ ++ /* ctx->options */ ++ p = xdr_reserve_space(xdr, 4); ++ *p = cpu_to_be32(2); ++ ++ /* we want a lucid_v1 context */ ++ opt.option.data = LUCID_OPTION; ++ opt.option.len = sizeof(LUCID_OPTION); ++ opt.value.data = LUCID_VALUE; ++ opt.value.len = sizeof(LUCID_VALUE); ++ err = gssx_enc_option(xdr, &opt); ++ ++ /* ..and user creds */ ++ opt.option.data = CREDS_OPTION; ++ opt.option.len = sizeof(CREDS_OPTION); ++ opt.value.data = CREDS_VALUE; ++ opt.value.len = sizeof(CREDS_VALUE); ++ err = gssx_enc_option(xdr, &opt); ++ ++ return err; ++} ++ ++static int gssx_dec_name_attr(struct xdr_stream *xdr, ++ struct gssx_name_attr *attr) ++{ ++ int err; ++ ++ /* attr->attr */ ++ err = gssx_dec_buffer(xdr, &attr->attr); ++ if (err) ++ return err; ++ ++ /* attr->value */ ++ err = gssx_dec_buffer(xdr, &attr->value); ++ if (err) ++ return err; ++ ++ /* attr->extensions */ ++ err = dummy_dec_opt_array(xdr, &attr->extensions); ++ ++ return err; ++} ++ ++static int dummy_enc_nameattr_array(struct xdr_stream *xdr, ++ struct gssx_name_attr_array *naa) ++{ ++ __be32 *p; ++ ++ if (naa->count != 0) ++ return -EINVAL; ++ ++ p = xdr_reserve_space(xdr, 4); ++ if (!p) ++ return -ENOSPC; ++ *p = 0; ++ ++ return 0; ++} ++ ++static int dummy_dec_nameattr_array(struct xdr_stream *xdr, ++ struct gssx_name_attr_array *naa) ++{ ++ struct gssx_name_attr dummy; ++ u32 count, i; ++ __be32 *p; ++ ++ p = xdr_inline_decode(xdr, 4); ++ if (unlikely(p == NULL)) ++ return -ENOSPC; ++ count = be32_to_cpup(p++); ++ for (i = 0; i < count; i++) { ++ gssx_dec_name_attr(xdr, &dummy); ++ } ++ ++ naa->count = 0; ++ naa->data = NULL; ++ return 0; ++} ++ ++static struct xdr_netobj zero_netobj = {}; ++ ++static struct gssx_name_attr_array zero_name_attr_array = {}; ++ ++static struct gssx_option_array zero_option_array = {}; ++ ++static int gssx_enc_name(struct xdr_stream *xdr, ++ struct gssx_name *name) ++{ ++ int err; ++ ++ /* name->display_name */ ++ err = gssx_enc_buffer(xdr, &name->display_name); ++ if (err) ++ return err; ++ ++ /* name->name_type */ ++ err = gssx_enc_buffer(xdr, &zero_netobj); ++ if (err) ++ return err; ++ ++ /* name->exported_name */ ++ err = gssx_enc_buffer(xdr, &zero_netobj); ++ if (err) ++ return err; ++ ++ /* name->exported_composite_name */ ++ err = gssx_enc_buffer(xdr, &zero_netobj); ++ if (err) ++ return err; ++ ++ /* leave name_attributes empty for now, will add once we have any ++ * to pass up at all */ ++ /* name->name_attributes */ ++ err = dummy_enc_nameattr_array(xdr, &zero_name_attr_array); ++ if (err) ++ return err; ++ ++ /* leave options empty for now, will add once we have any options ++ * to pass up at all */ ++ /* name->extensions */ ++ err = dummy_enc_opt_array(xdr, &zero_option_array); ++ ++ return err; ++} ++ ++static int gssx_dec_name(struct xdr_stream *xdr, ++ struct gssx_name *name) ++{ ++ struct xdr_netobj dummy_netobj; ++ struct gssx_name_attr_array dummy_name_attr_array; ++ struct gssx_option_array dummy_option_array; ++ int err; ++ ++ /* name->display_name */ ++ err = gssx_dec_buffer(xdr, &name->display_name); ++ if (err) ++ return err; ++ ++ /* name->name_type */ ++ err = gssx_dec_buffer(xdr, &dummy_netobj); ++ if (err) ++ return err; ++ ++ /* name->exported_name */ ++ err = gssx_dec_buffer(xdr, &dummy_netobj); ++ if (err) ++ return err; ++ ++ /* name->exported_composite_name */ ++ err = gssx_dec_buffer(xdr, &dummy_netobj); ++ if (err) ++ return err; ++ ++ /* we assume we have no attributes for now, so simply consume them */ ++ /* name->name_attributes */ ++ err = dummy_dec_nameattr_array(xdr, &dummy_name_attr_array); ++ if (err) ++ return err; ++ ++ /* we assume we have no options for now, so simply consume them */ ++ /* name->extensions */ ++ err = dummy_dec_opt_array(xdr, &dummy_option_array); ++ ++ return err; ++} ++ ++static int dummy_enc_credel_array(struct xdr_stream *xdr, ++ struct gssx_cred_element_array *cea) ++{ ++ __be32 *p; ++ ++ if (cea->count != 0) ++ return -EINVAL; ++ ++ p = xdr_reserve_space(xdr, 4); ++ if (!p) ++ return -ENOSPC; ++ *p = 0; ++ ++ return 0; ++} ++ ++static int gssx_enc_cred(struct xdr_stream *xdr, ++ struct gssx_cred *cred) ++{ ++ int err; ++ ++ /* cred->desired_name */ ++ err = gssx_enc_name(xdr, &cred->desired_name); ++ if (err) ++ return err; ++ ++ /* cred->elements */ ++ err = dummy_enc_credel_array(xdr, &cred->elements); ++ ++ /* cred->cred_handle_reference */ ++ err = gssx_enc_buffer(xdr, &cred->cred_handle_reference); ++ if (err) ++ return err; ++ ++ /* cred->needs_release */ ++ err = gssx_enc_bool(xdr, cred->needs_release); ++ ++ return err; ++} ++ ++static int gssx_enc_ctx(struct xdr_stream *xdr, ++ struct gssx_ctx *ctx) ++{ ++ __be32 *p; ++ int err; ++ ++ /* ctx->exported_context_token */ ++ err = gssx_enc_buffer(xdr, &ctx->exported_context_token); ++ if (err) ++ return err; ++ ++ /* ctx->state */ ++ err = gssx_enc_buffer(xdr, &ctx->state); ++ if (err) ++ return err; ++ ++ /* ctx->need_release */ ++ err = gssx_enc_bool(xdr, ctx->need_release); ++ if (err) ++ return err; ++ ++ /* ctx->mech */ ++ err = gssx_enc_buffer(xdr, &ctx->mech); ++ if (err) ++ return err; ++ ++ /* ctx->src_name */ ++ err = gssx_enc_name(xdr, &ctx->src_name); ++ if (err) ++ return err; ++ ++ /* ctx->targ_name */ ++ err = gssx_enc_name(xdr, &ctx->targ_name); ++ if (err) ++ return err; ++ ++ /* ctx->lifetime */ ++ p = xdr_reserve_space(xdr, 8+8); ++ if (!p) ++ return -ENOSPC; ++ p = xdr_encode_hyper(p, ctx->lifetime); ++ ++ /* ctx->ctx_flags */ ++ p = xdr_encode_hyper(p, ctx->ctx_flags); ++ ++ /* ctx->locally_initiated */ ++ err = gssx_enc_bool(xdr, ctx->locally_initiated); ++ if (err) ++ return err; ++ ++ /* ctx->open */ ++ err = gssx_enc_bool(xdr, ctx->open); ++ if (err) ++ return err; ++ ++ /* leave options empty for now, will add once we have any options ++ * to pass up at all */ ++ /* ctx->options */ ++ err = dummy_enc_opt_array(xdr, &ctx->options); ++ ++ return err; ++} ++ ++static int gssx_dec_ctx(struct xdr_stream *xdr, ++ struct gssx_ctx *ctx) ++{ ++ __be32 *p; ++ int err; ++ ++ /* ctx->exported_context_token */ ++ err = gssx_dec_buffer(xdr, &ctx->exported_context_token); ++ if (err) ++ return err; ++ ++ /* ctx->state */ ++ err = gssx_dec_buffer(xdr, &ctx->state); ++ if (err) ++ return err; ++ ++ /* ctx->need_release */ ++ err = gssx_dec_bool(xdr, &ctx->need_release); ++ if (err) ++ return err; ++ ++ /* ctx->mech */ ++ err = gssx_dec_buffer(xdr, &ctx->mech); ++ if (err) ++ return err; ++ ++ /* ctx->src_name */ ++ err = gssx_dec_name(xdr, &ctx->src_name); ++ if (err) ++ return err; ++ ++ /* ctx->targ_name */ ++ err = gssx_dec_name(xdr, &ctx->targ_name); ++ if (err) ++ return err; ++ ++ /* ctx->lifetime */ ++ p = xdr_inline_decode(xdr, 8+8); ++ if (unlikely(p == NULL)) ++ return -ENOSPC; ++ p = xdr_decode_hyper(p, &ctx->lifetime); ++ ++ /* ctx->ctx_flags */ ++ p = xdr_decode_hyper(p, &ctx->ctx_flags); ++ ++ /* ctx->locally_initiated */ ++ err = gssx_dec_bool(xdr, &ctx->locally_initiated); ++ if (err) ++ return err; ++ ++ /* ctx->open */ ++ err = gssx_dec_bool(xdr, &ctx->open); ++ if (err) ++ return err; ++ ++ /* we assume we have no options for now, so simply consume them */ ++ /* ctx->options */ ++ err = dummy_dec_opt_array(xdr, &ctx->options); ++ ++ return err; ++} ++ ++static int gssx_enc_cb(struct xdr_stream *xdr, struct gssx_cb *cb) ++{ ++ __be32 *p; ++ int err; ++ ++ /* cb->initiator_addrtype */ ++ p = xdr_reserve_space(xdr, 8); ++ if (!p) ++ return -ENOSPC; ++ p = xdr_encode_hyper(p, cb->initiator_addrtype); ++ ++ /* cb->initiator_address */ ++ err = gssx_enc_buffer(xdr, &cb->initiator_address); ++ if (err) ++ return err; ++ ++ /* cb->acceptor_addrtype */ ++ p = xdr_reserve_space(xdr, 8); ++ if (!p) ++ return -ENOSPC; ++ p = xdr_encode_hyper(p, cb->acceptor_addrtype); ++ ++ /* cb->acceptor_address */ ++ err = gssx_enc_buffer(xdr, &cb->acceptor_address); ++ if (err) ++ return err; ++ ++ /* cb->application_data */ ++ err = gssx_enc_buffer(xdr, &cb->application_data); ++ ++ return err; ++} ++ ++void gssx_enc_accept_sec_context(struct rpc_rqst *req, ++ struct xdr_stream *xdr, ++ struct gssx_arg_accept_sec_context *arg) ++{ ++ int err; ++ ++ err = gssx_enc_call_ctx(xdr, &arg->call_ctx); ++ if (err) ++ goto done; ++ ++ /* arg->context_handle */ ++ if (arg->context_handle) { ++ err = gssx_enc_ctx(xdr, arg->context_handle); ++ if (err) ++ goto done; ++ } else { ++ err = gssx_enc_bool(xdr, 0); ++ } ++ ++ /* arg->cred_handle */ ++ if (arg->cred_handle) { ++ err = gssx_enc_cred(xdr, arg->cred_handle); ++ if (err) ++ goto done; ++ } else { ++ err = gssx_enc_bool(xdr, 0); ++ } ++ ++ /* arg->input_token */ ++ err = gssx_enc_in_token(xdr, &arg->input_token); ++ if (err) ++ goto done; ++ ++ /* arg->input_cb */ ++ if (arg->input_cb) { ++ err = gssx_enc_cb(xdr, arg->input_cb); ++ if (err) ++ goto done; ++ } else { ++ err = gssx_enc_bool(xdr, 0); ++ } ++ ++ err = gssx_enc_bool(xdr, arg->ret_deleg_cred); ++ if (err) ++ goto done; ++ ++ /* leave options empty for now, will add once we have any options ++ * to pass up at all */ ++ /* arg->options */ ++ err = dummy_enc_opt_array(xdr, &arg->options); ++ ++done: ++ if (err) ++ dprintk("RPC: gssx_enc_accept_sec_context: %d\n", err); ++} ++ ++int gssx_dec_accept_sec_context(struct rpc_rqst *rqstp, ++ struct xdr_stream *xdr, ++ struct gssx_res_accept_sec_context *res) ++{ ++ int err; ++ ++ /* res->status */ ++ err = gssx_dec_status(xdr, &res->status); ++ if (err) ++ return err; ++ ++ /* res->context_handle */ ++ if (gssx_check_pointer(xdr)) { ++ err = gssx_dec_ctx(xdr, res->context_handle); ++ if (err) ++ return err; ++ } else { ++ res->context_handle = NULL; ++ } ++ ++ /* res->output_token */ ++ if (gssx_check_pointer(xdr)) { ++ err = gssx_dec_buffer(xdr, res->output_token); ++ if (err) ++ return err; ++ } else { ++ res->output_token = NULL; ++ } ++ ++ /* res->delegated_cred_handle */ ++ if (gssx_check_pointer(xdr)) { ++ /* we do not support upcall servers sending this data. */ ++ return -EINVAL; ++ } ++ ++ /* res->options */ ++ err = gssx_dec_option_array(xdr, &res->options); ++ ++ return err; ++} +diff --git a/net/sunrpc/auth_gss/gss_rpc_xdr.h b/net/sunrpc/auth_gss/gss_rpc_xdr.h +new file mode 100644 +index 0000000..1c98b27 +--- /dev/null ++++ b/net/sunrpc/auth_gss/gss_rpc_xdr.h +@@ -0,0 +1,264 @@ ++/* ++ * GSS Proxy upcall module ++ * ++ * Copyright (C) 2012 Simo Sorce ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#ifndef _LINUX_GSS_RPC_XDR_H ++#define _LINUX_GSS_RPC_XDR_H ++ ++#include ++#include ++#include ++ ++#ifdef RPC_DEBUG ++# define RPCDBG_FACILITY RPCDBG_AUTH ++#endif ++ ++#define LUCID_OPTION "exported_context_type" ++#define LUCID_VALUE "linux_lucid_v1" ++#define CREDS_OPTION "exported_creds_type" ++#define CREDS_VALUE "linux_creds_v1" ++ ++typedef struct xdr_netobj gssx_buffer; ++typedef struct xdr_netobj utf8string; ++typedef struct xdr_netobj gssx_OID; ++ ++enum gssx_cred_usage { ++ GSSX_C_INITIATE = 1, ++ GSSX_C_ACCEPT = 2, ++ GSSX_C_BOTH = 3, ++}; ++ ++struct gssx_option { ++ gssx_buffer option; ++ gssx_buffer value; ++}; ++ ++struct gssx_option_array { ++ u32 count; ++ struct gssx_option *data; ++}; ++ ++struct gssx_status { ++ u64 major_status; ++ gssx_OID mech; ++ u64 minor_status; ++ utf8string major_status_string; ++ utf8string minor_status_string; ++ gssx_buffer server_ctx; ++ struct gssx_option_array options; ++}; ++ ++struct gssx_call_ctx { ++ utf8string locale; ++ gssx_buffer server_ctx; ++ struct gssx_option_array options; ++}; ++ ++struct gssx_name_attr { ++ gssx_buffer attr; ++ gssx_buffer value; ++ struct gssx_option_array extensions; ++}; ++ ++struct gssx_name_attr_array { ++ u32 count; ++ struct gssx_name_attr *data; ++}; ++ ++struct gssx_name { ++ gssx_buffer display_name; ++}; ++typedef struct gssx_name gssx_name; ++ ++struct gssx_cred_element { ++ gssx_name MN; ++ gssx_OID mech; ++ u32 cred_usage; ++ u64 initiator_time_rec; ++ u64 acceptor_time_rec; ++ struct gssx_option_array options; ++}; ++ ++struct gssx_cred_element_array { ++ u32 count; ++ struct gssx_cred_element *data; ++}; ++ ++struct gssx_cred { ++ gssx_name desired_name; ++ struct gssx_cred_element_array elements; ++ gssx_buffer cred_handle_reference; ++ u32 needs_release; ++}; ++ ++struct gssx_ctx { ++ gssx_buffer exported_context_token; ++ gssx_buffer state; ++ u32 need_release; ++ gssx_OID mech; ++ gssx_name src_name; ++ gssx_name targ_name; ++ u64 lifetime; ++ u64 ctx_flags; ++ u32 locally_initiated; ++ u32 open; ++ struct gssx_option_array options; ++}; ++ ++struct gssx_cb { ++ u64 initiator_addrtype; ++ gssx_buffer initiator_address; ++ u64 acceptor_addrtype; ++ gssx_buffer acceptor_address; ++ gssx_buffer application_data; ++}; ++ ++ ++/* This structure is not defined in the protocol. ++ * It is used in the kernel to carry around a big buffer ++ * as a set of pages */ ++struct gssp_in_token { ++ struct page **pages; /* Array of contiguous pages */ ++ unsigned int page_base; /* Start of page data */ ++ unsigned int page_len; /* Length of page data */ ++}; ++ ++struct gssx_arg_accept_sec_context { ++ struct gssx_call_ctx call_ctx; ++ struct gssx_ctx *context_handle; ++ struct gssx_cred *cred_handle; ++ struct gssp_in_token input_token; ++ struct gssx_cb *input_cb; ++ u32 ret_deleg_cred; ++ struct gssx_option_array options; ++}; ++ ++struct gssx_res_accept_sec_context { ++ struct gssx_status status; ++ struct gssx_ctx *context_handle; ++ gssx_buffer *output_token; ++ /* struct gssx_cred *delegated_cred_handle; not used in kernel */ ++ struct gssx_option_array options; ++}; ++ ++ ++ ++#define gssx_enc_indicate_mechs NULL ++#define gssx_dec_indicate_mechs NULL ++#define gssx_enc_get_call_context NULL ++#define gssx_dec_get_call_context NULL ++#define gssx_enc_import_and_canon_name NULL ++#define gssx_dec_import_and_canon_name NULL ++#define gssx_enc_export_cred NULL ++#define gssx_dec_export_cred NULL ++#define gssx_enc_import_cred NULL ++#define gssx_dec_import_cred NULL ++#define gssx_enc_acquire_cred NULL ++#define gssx_dec_acquire_cred NULL ++#define gssx_enc_store_cred NULL ++#define gssx_dec_store_cred NULL ++#define gssx_enc_init_sec_context NULL ++#define gssx_dec_init_sec_context NULL ++void gssx_enc_accept_sec_context(struct rpc_rqst *req, ++ struct xdr_stream *xdr, ++ struct gssx_arg_accept_sec_context *args); ++int gssx_dec_accept_sec_context(struct rpc_rqst *rqstp, ++ struct xdr_stream *xdr, ++ struct gssx_res_accept_sec_context *res); ++#define gssx_enc_release_handle NULL ++#define gssx_dec_release_handle NULL ++#define gssx_enc_get_mic NULL ++#define gssx_dec_get_mic NULL ++#define gssx_enc_verify NULL ++#define gssx_dec_verify NULL ++#define gssx_enc_wrap NULL ++#define gssx_dec_wrap NULL ++#define gssx_enc_unwrap NULL ++#define gssx_dec_unwrap NULL ++#define gssx_enc_wrap_size_limit NULL ++#define gssx_dec_wrap_size_limit NULL ++ ++/* non implemented calls are set to 0 size */ ++#define GSSX_ARG_indicate_mechs_sz 0 ++#define GSSX_RES_indicate_mechs_sz 0 ++#define GSSX_ARG_get_call_context_sz 0 ++#define GSSX_RES_get_call_context_sz 0 ++#define GSSX_ARG_import_and_canon_name_sz 0 ++#define GSSX_RES_import_and_canon_name_sz 0 ++#define GSSX_ARG_export_cred_sz 0 ++#define GSSX_RES_export_cred_sz 0 ++#define GSSX_ARG_import_cred_sz 0 ++#define GSSX_RES_import_cred_sz 0 ++#define GSSX_ARG_acquire_cred_sz 0 ++#define GSSX_RES_acquire_cred_sz 0 ++#define GSSX_ARG_store_cred_sz 0 ++#define GSSX_RES_store_cred_sz 0 ++#define GSSX_ARG_init_sec_context_sz 0 ++#define GSSX_RES_init_sec_context_sz 0 ++ ++#define GSSX_default_in_call_ctx_sz (4 + 4 + 4 + \ ++ 8 + sizeof(LUCID_OPTION) + sizeof(LUCID_VALUE) + \ ++ 8 + sizeof(CREDS_OPTION) + sizeof(CREDS_VALUE)) ++#define GSSX_default_in_ctx_hndl_sz (4 + 4+8 + 4 + 4 + 6*4 + 6*4 + 8 + 8 + \ ++ 4 + 4 + 4) ++#define GSSX_default_in_cred_sz 4 /* we send in no cred_handle */ ++#define GSSX_default_in_token_sz 4 /* does *not* include token data */ ++#define GSSX_default_in_cb_sz 4 /* we do not use channel bindings */ ++#define GSSX_ARG_accept_sec_context_sz (GSSX_default_in_call_ctx_sz + \ ++ GSSX_default_in_ctx_hndl_sz + \ ++ GSSX_default_in_cred_sz + \ ++ GSSX_default_in_token_sz + \ ++ GSSX_default_in_cb_sz + \ ++ 4 /* no deleg creds boolean */ + \ ++ 4) /* empty options */ ++ ++/* somewhat arbitrary numbers but large enough (we ignore some of the data ++ * sent down, but it is part of the protocol so we need enough space to take ++ * it in) */ ++#define GSSX_default_status_sz 8 + 24 + 8 + 256 + 256 + 16 + 4 ++#define GSSX_max_output_handle_sz 128 ++#define GSSX_max_oid_sz 16 ++#define GSSX_max_princ_sz 256 ++#define GSSX_default_ctx_sz (GSSX_max_output_handle_sz + \ ++ 16 + 4 + GSSX_max_oid_sz + \ ++ 2 * GSSX_max_princ_sz + \ ++ 8 + 8 + 4 + 4 + 4) ++#define GSSX_max_output_token_sz 1024 ++#define GSSX_max_creds_sz (4 + 4 + 4 + NGROUPS_MAX * 4) ++#define GSSX_RES_accept_sec_context_sz (GSSX_default_status_sz + \ ++ GSSX_default_ctx_sz + \ ++ GSSX_max_output_token_sz + \ ++ 4 + GSSX_max_creds_sz) ++ ++#define GSSX_ARG_release_handle_sz 0 ++#define GSSX_RES_release_handle_sz 0 ++#define GSSX_ARG_get_mic_sz 0 ++#define GSSX_RES_get_mic_sz 0 ++#define GSSX_ARG_verify_sz 0 ++#define GSSX_RES_verify_sz 0 ++#define GSSX_ARG_wrap_sz 0 ++#define GSSX_RES_wrap_sz 0 ++#define GSSX_ARG_unwrap_sz 0 ++#define GSSX_RES_unwrap_sz 0 ++#define GSSX_ARG_wrap_size_limit_sz 0 ++#define GSSX_RES_wrap_size_limit_sz 0 ++ ++ ++ ++#endif /* _LINUX_GSS_RPC_XDR_H */ +diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c +index 6d29b53..eb4cb72 100644 +--- a/net/sunrpc/clnt.c ++++ b/net/sunrpc/clnt.c +@@ -683,6 +683,7 @@ rpc_release_client(struct rpc_clnt *clnt) + if (atomic_dec_and_test(&clnt->cl_count)) + rpc_free_auth(clnt); + } ++EXPORT_SYMBOL_GPL(rpc_release_client); + + /** + * rpc_bind_new_program - bind a new RPC program to an existing client +diff --git a/net/sunrpc/netns.h b/net/sunrpc/netns.h +index ce7bd44..e9f8895 100644 +--- a/net/sunrpc/netns.h ++++ b/net/sunrpc/netns.h +@@ -23,6 +23,9 @@ struct sunrpc_net { + struct rpc_clnt *rpcb_local_clnt4; + spinlock_t rpcb_clnt_lock; + unsigned int rpcb_users; ++ ++ struct mutex gssp_lock; ++ struct rpc_clnt *gssp_clnt; + }; + + extern int sunrpc_net_id; +-- +1.8.1.4 + + +From f682043df7bb81715124c82e9cea8bc68ded9667 Mon Sep 17 00:00:00 2001 +From: Simo Sorce +Date: Fri, 25 May 2012 18:09:56 -0400 +Subject: [PATCH 06/13] SUNRPC: Use gssproxy upcall for server RPCGSS + authentication. + +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 +[bfields: containerization, negotiation api] +Signed-off-by: J. Bruce Fields +--- + Documentation/filesystems/nfs/00-INDEX | 2 + + Documentation/filesystems/nfs/rpc-server-gss.txt | 91 ++++++ + net/sunrpc/auth_gss/gss_rpc_upcall.c | 2 + + net/sunrpc/auth_gss/svcauth_gss.c | 347 ++++++++++++++++++++++- + net/sunrpc/netns.h | 3 + + 5 files changed, 436 insertions(+), 9 deletions(-) + create mode 100644 Documentation/filesystems/nfs/rpc-server-gss.txt + +diff --git a/Documentation/filesystems/nfs/00-INDEX b/Documentation/filesystems/nfs/00-INDEX +index 1716874..66eb6c8 100644 +--- a/Documentation/filesystems/nfs/00-INDEX ++++ b/Documentation/filesystems/nfs/00-INDEX +@@ -20,3 +20,5 @@ rpc-cache.txt + - introduction to the caching mechanisms in the sunrpc layer. + idmapper.txt + - information for configuring request-keys to be used by idmapper ++knfsd-rpcgss.txt ++ - Information on GSS authentication support in the NFS Server +diff --git a/Documentation/filesystems/nfs/rpc-server-gss.txt b/Documentation/filesystems/nfs/rpc-server-gss.txt +new file mode 100644 +index 0000000..716f4be +--- /dev/null ++++ b/Documentation/filesystems/nfs/rpc-server-gss.txt +@@ -0,0 +1,91 @@ ++ ++rpcsec_gss support for kernel RPC servers ++========================================= ++ ++This document gives references to the standards and protocols used to ++implement RPCGSS authentication in kernel RPC servers such as the NFS ++server and the NFS client's NFSv4.0 callback server. (But note that ++NFSv4.1 and higher don't require the client to act as a server for the ++purposes of authentication.) ++ ++RPCGSS is specified in a few IETF documents: ++ - RFC2203 v1: http://tools.ietf.org/rfc/rfc2203.txt ++ - RFC5403 v2: http://tools.ietf.org/rfc/rfc5403.txt ++and there is a 3rd version being proposed: ++ - http://tools.ietf.org/id/draft-williams-rpcsecgssv3.txt ++ (At draft n. 02 at the time of writing) ++ ++Background ++---------- ++ ++The RPCGSS Authentication method describes a way to perform GSSAPI ++Authentication for NFS. Although GSSAPI is itself completely mechanism ++agnostic, in many cases only the KRB5 mechanism is supported by NFS ++implementations. ++ ++The Linux kernel, at the moment, supports only the KRB5 mechanism, and ++depends on GSSAPI extensions that are KRB5 specific. ++ ++GSSAPI is a complex library, and implementing it completely in kernel is ++unwarranted. However GSSAPI operations are fundementally separable in 2 ++parts: ++- initial context establishment ++- integrity/privacy protection (signing and encrypting of individual ++ packets) ++ ++The former is more complex and policy-independent, but less ++performance-sensitive. The latter is simpler and needs to be very fast. ++ ++Therefore, we perform per-packet integrity and privacy protection in the ++kernel, but leave the initial context establishment to userspace. We ++need upcalls to request userspace to perform context establishment. ++ ++NFS Server Legacy Upcall Mechanism ++---------------------------------- ++ ++The classic upcall mechanism uses a custom text based upcall mechanism ++to talk to a custom daemon called rpc.svcgssd that is provide by the ++nfs-utils package. ++ ++This upcall mechanism has 2 limitations: ++ ++A) It can handle tokens that are no bigger than 2KiB ++ ++In some Kerberos deployment GSSAPI tokens can be quite big, up and ++beyond 64KiB in size due to various authorization extensions attacked to ++the Kerberos tickets, that needs to be sent through the GSS layer in ++order to perform context establishment. ++ ++B) It does not properly handle creds where the user is member of more ++than a few housand groups (the current hard limit in the kernel is 65K ++groups) due to limitation on the size of the buffer that can be send ++back to the kernel (4KiB). ++ ++NFS Server New RPC Upcall Mechanism ++----------------------------------- ++ ++The newer upcall mechanism uses RPC over a unix socket to a daemon ++called gss-proxy, implemented by a userspace program called Gssproxy. ++ ++The gss_proxy RPC protocol is currently documented here: ++ ++ https://fedorahosted.org/gss-proxy/wiki/ProtocolDocumentation ++ ++This upcall mechanism uses the kernel rpc client and connects to the gssproxy ++userspace program over a regular unix socket. The gssproxy protocol does not ++suffer from the size limitations of the legacy protocol. ++ ++Negotiating Upcall Mechanisms ++----------------------------- ++ ++To provide backward compatibility, the kernel defaults to using the ++legacy mechanism. To switch to the new mechanism, gss-proxy must bind ++to /var/run/gssproxy.sock and then write "1" to ++/proc/net/rpc/use-gss-proxy. If gss-proxy dies, it must repeat both ++steps. ++ ++Once the upcall mechanism is chosen, it cannot be changed. To prevent ++locking into the legacy mechanisms, the above steps must be performed ++before starting nfsd. Whoever starts nfsd can guarantee this by reading ++from /proc/net/rpc/use-gss-proxy and checking that it contains a ++"1"--the read will block until gss-proxy has done its write to the file. +diff --git a/net/sunrpc/auth_gss/gss_rpc_upcall.c b/net/sunrpc/auth_gss/gss_rpc_upcall.c +index 2d33ddf..3f874d7 100644 +--- a/net/sunrpc/auth_gss/gss_rpc_upcall.c ++++ b/net/sunrpc/auth_gss/gss_rpc_upcall.c +@@ -137,6 +137,7 @@ void init_gssp_clnt(struct sunrpc_net *sn) + { + mutex_init(&sn->gssp_lock); + sn->gssp_clnt = NULL; ++ init_waitqueue_head(&sn->gssp_wq); + } + + int set_gssp_clnt(struct net *net) +@@ -153,6 +154,7 @@ int set_gssp_clnt(struct net *net) + sn->gssp_clnt = clnt; + } + mutex_unlock(&sn->gssp_lock); ++ wake_up(&sn->gssp_wq); + return ret; + } + +diff --git a/net/sunrpc/auth_gss/svcauth_gss.c b/net/sunrpc/auth_gss/svcauth_gss.c +index 20eedec..58f5bc3 100644 +--- a/net/sunrpc/auth_gss/svcauth_gss.c ++++ b/net/sunrpc/auth_gss/svcauth_gss.c +@@ -48,8 +48,8 @@ + #include + #include + #include ++#include "gss_rpc_upcall.h" + +-#include "../netns.h" + + #ifdef RPC_DEBUG + # define RPCDBG_FACILITY RPCDBG_AUTH +@@ -988,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) +@@ -1010,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; +@@ -1022,6 +1036,40 @@ 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; ++ ++ 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, +@@ -1049,7 +1097,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]; +@@ -1089,6 +1137,278 @@ out: + return ret; + } + ++static int gss_proxy_save_rsc(struct cache_detail *cd, ++ struct gssp_upcall_data *ud, ++ uint64_t *handle) ++{ ++ struct rsc rsci, *rscp = NULL; ++ static atomic64_t ctxhctr; ++ long long ctxh; ++ struct gss_api_mech *gm = NULL; ++ time_t expiry; ++ 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); ++ ++ /* make a copy for the caller */ ++ *handle = ctxh; ++ ++ /* make a copy for the rsc cache */ ++ if (dup_to_netobj(&rsci.handle, (char *)handle, sizeof(uint64_t))) ++ goto out; ++ rscp = rsc_lookup(cd, &rsci); ++ if (!rscp) ++ goto out; ++ ++ /* creds */ ++ if (!ud->found_creds) { ++ /* userspace seem buggy, we should always get at least a ++ * mapping to nobody */ ++ dprintk("RPC: No creds found, marking Negative!\n"); ++ set_bit(CACHE_NEGATIVE, &rsci.h.flags); ++ } else { ++ ++ /* steal creds */ ++ rsci.cred = ud->creds; ++ memset(&ud->creds, 0, sizeof(struct svc_cred)); ++ ++ 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; ++ } ++ ++ 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; ++ uint64_t handle; ++ int status; ++ int ret; ++ struct net *net = rqstp->rq_xprt->xpt_net; ++ struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); ++ ++ 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(net, &ud); ++ if (status) ++ goto out; ++ ++ 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; ++ break; ++ case GSS_S_COMPLETE: ++ status = gss_proxy_save_rsc(sn->rsc_cache, &ud, &handle); ++ if (status) ++ goto out; ++ cli_handle.data = (u8 *)&handle; ++ cli_handle.len = sizeof(handle); ++ 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); ++ return ret; ++} ++ ++DEFINE_SPINLOCK(use_gssp_lock); ++ ++static bool use_gss_proxy(struct net *net) ++{ ++ struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); ++ ++ if (sn->use_gss_proxy != -1) ++ return sn->use_gss_proxy; ++ spin_lock(&use_gssp_lock); ++ /* ++ * If you wanted gss-proxy, you should have said so before ++ * starting to accept requests: ++ */ ++ sn->use_gss_proxy = 0; ++ spin_unlock(&use_gssp_lock); ++ return 0; ++} ++ ++static bool set_gss_proxy(struct net *net, int type) ++{ ++ struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); ++ int ret = 0; ++ ++ WARN_ON_ONCE(type != 0 && type != 1); ++ spin_lock(&use_gssp_lock); ++ if (sn->use_gss_proxy == -1 || sn->use_gss_proxy == type) ++ sn->use_gss_proxy = type; ++ else ++ ret = -EBUSY; ++ spin_unlock(&use_gssp_lock); ++ wake_up(&sn->gssp_wq); ++ return ret; ++} ++ ++static inline bool gssp_ready(struct sunrpc_net *sn) ++{ ++ switch (sn->use_gss_proxy) { ++ case -1: ++ return false; ++ case 0: ++ return true; ++ case 1: ++ return sn->gssp_clnt; ++ } ++ WARN_ON_ONCE(1); ++ return false; ++} ++ ++static int wait_for_gss_proxy(struct net *net) ++{ ++ struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); ++ ++ return wait_event_interruptible(sn->gssp_wq, gssp_ready(sn)); ++} ++ ++#ifdef CONFIG_PROC_FS ++ ++static ssize_t write_gssp(struct file *file, const char __user *buf, ++ size_t count, loff_t *ppos) ++{ ++ struct net *net = PDE(file->f_path.dentry->d_inode)->data; ++ char tbuf[20]; ++ unsigned long i; ++ int res; ++ ++ if (*ppos || count > sizeof(tbuf)-1) ++ return -EINVAL; ++ if (copy_from_user(tbuf, buf, count)) ++ return -EFAULT; ++ ++ tbuf[count] = 0; ++ res = kstrtoul(tbuf, 0, &i); ++ if (res) ++ return res; ++ if (i != 1) ++ return -EINVAL; ++ res = set_gss_proxy(net, 1); ++ if (res) ++ return res; ++ res = set_gssp_clnt(net); ++ if (res) ++ return res; ++ return count; ++} ++ ++static ssize_t read_gssp(struct file *file, char __user *buf, ++ size_t count, loff_t *ppos) ++{ ++ struct net *net = PDE(file->f_path.dentry->d_inode)->data; ++ unsigned long p = *ppos; ++ char tbuf[10]; ++ size_t len; ++ int ret; ++ ++ ret = wait_for_gss_proxy(net); ++ if (ret) ++ return ret; ++ ++ snprintf(tbuf, sizeof(tbuf), "%d\n", use_gss_proxy(net)); ++ len = strlen(tbuf); ++ if (p >= len) ++ return 0; ++ len -= p; ++ if (len > count) ++ len = count; ++ if (copy_to_user(buf, (void *)(tbuf+p), len)) ++ return -EFAULT; ++ *ppos += len; ++ return len; ++} ++ ++static const struct file_operations use_gss_proxy_ops = { ++ .open = nonseekable_open, ++ .write = write_gssp, ++ .read = read_gssp, ++}; ++ ++static int create_use_gss_proxy_proc_entry(struct net *net) ++{ ++ struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); ++ struct proc_dir_entry **p = &sn->use_gssp_proc; ++ ++ sn->use_gss_proxy = -1; ++ *p = proc_create_data("use-gss-proxy", S_IFREG|S_IRUSR|S_IWUSR, ++ sn->proc_net_rpc, ++ &use_gss_proxy_ops, net); ++ if (!*p) ++ return -ENOMEM; ++ init_gssp_clnt(sn); ++ return 0; ++} ++ ++static void destroy_use_gss_proxy_proc_entry(struct net *net) ++{ ++ struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); ++ ++ if (sn->use_gssp_proc) { ++ remove_proc_entry("use-gss-proxy", sn->proc_net_rpc); ++ clear_gssp_clnt(sn); ++ } ++} ++ ++#endif /* CONFIG_PROC_FS */ ++ + /* + * Accept an rpcsec packet. + * If context establishment, punt to user space +@@ -1155,7 +1475,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_gss_proxy(SVC_NET(rqstp))) ++ 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: */ +@@ -1530,7 +1853,12 @@ gss_svc_init_net(struct net *net) + rv = rsi_cache_create_net(net); + if (rv) + goto out1; ++ rv = create_use_gss_proxy_proc_entry(net); ++ if (rv) ++ goto out2; + return 0; ++out2: ++ destroy_use_gss_proxy_proc_entry(net); + out1: + rsc_cache_destroy_net(net); + return rv; +@@ -1539,6 +1867,7 @@ out1: + void + gss_svc_shutdown_net(struct net *net) + { ++ destroy_use_gss_proxy_proc_entry(net); + rsi_cache_destroy_net(net); + rsc_cache_destroy_net(net); + } +diff --git a/net/sunrpc/netns.h b/net/sunrpc/netns.h +index e9f8895..7111a4c 100644 +--- a/net/sunrpc/netns.h ++++ b/net/sunrpc/netns.h +@@ -25,7 +25,10 @@ struct sunrpc_net { + unsigned int rpcb_users; + + struct mutex gssp_lock; ++ wait_queue_head_t gssp_wq; + struct rpc_clnt *gssp_clnt; ++ int use_gss_proxy; ++ struct proc_dir_entry *use_gssp_proc; + }; + + extern int sunrpc_net_id; +-- +1.8.1.4 + + +From 06404241b88b51c50427b833268d7cad7dec30f5 Mon Sep 17 00:00:00 2001 +From: "J. Bruce Fields" +Date: Mon, 29 Apr 2013 17:03:31 -0400 +Subject: [PATCH 07/13] SUNRPC: define + {create,destroy}_use_gss_proxy_proc_entry in !PROC case + +Though I wonder whether we should really just depend on CONFIG_PROC_FS +at some point. + +Reported-by: kbuild test robot +--- + net/sunrpc/auth_gss/svcauth_gss.c | 11 ++++++++++- + 1 file changed, 10 insertions(+), 1 deletion(-) + +diff --git a/net/sunrpc/auth_gss/svcauth_gss.c b/net/sunrpc/auth_gss/svcauth_gss.c +index 58f5bc3..71446b6 100644 +--- a/net/sunrpc/auth_gss/svcauth_gss.c ++++ b/net/sunrpc/auth_gss/svcauth_gss.c +@@ -1283,6 +1283,8 @@ static bool use_gss_proxy(struct net *net) + return 0; + } + ++#ifdef CONFIG_PROC_FS ++ + static bool set_gss_proxy(struct net *net, int type) + { + struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); +@@ -1320,7 +1322,6 @@ static int wait_for_gss_proxy(struct net *net) + return wait_event_interruptible(sn->gssp_wq, gssp_ready(sn)); + } + +-#ifdef CONFIG_PROC_FS + + static ssize_t write_gssp(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +@@ -1406,6 +1407,14 @@ static void destroy_use_gss_proxy_proc_entry(struct net *net) + clear_gssp_clnt(sn); + } + } ++#else /* CONFIG_PROC_FS */ ++ ++static int create_use_gss_proxy_proc_entry(struct net *net) ++{ ++ return 0; ++} ++ ++static void destroy_use_gss_proxy_proc_entry(struct net *net) {} + + #endif /* CONFIG_PROC_FS */ + +-- +1.8.1.4 + + +From 3cc961ce9784f0b4a9cb21217dd4a8403efc220d Mon Sep 17 00:00:00 2001 +From: Fengguang Wu +Date: Mon, 29 Apr 2013 17:19:48 -0400 +Subject: [PATCH 08/13] SUNRPC: gssp_procedures[] can be static + +Cc: Simo Sorce +Signed-off-by: Fengguang Wu +--- + net/sunrpc/auth_gss/gss_rpc_upcall.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/net/sunrpc/auth_gss/gss_rpc_upcall.c b/net/sunrpc/auth_gss/gss_rpc_upcall.c +index 3f874d7..98818d6 100644 +--- a/net/sunrpc/auth_gss/gss_rpc_upcall.c ++++ b/net/sunrpc/auth_gss/gss_rpc_upcall.c +@@ -63,7 +63,7 @@ enum { + .p_name = #proc, \ + } + +-struct rpc_procinfo gssp_procedures[] = { ++static struct rpc_procinfo gssp_procedures[] = { + PROC(INDICATE_MECHS, indicate_mechs), + PROC(GET_CALL_CONTEXT, get_call_context), + PROC(IMPORT_AND_CANON_NAME, import_and_canon_name), +-- +1.8.1.4 + + +From d6afcafd515bbf16e39817170cd212a7debd8959 Mon Sep 17 00:00:00 2001 +From: "J. Bruce Fields" +Date: Mon, 29 Apr 2013 18:21:29 -0400 +Subject: [PATCH 09/13] svcrpc: fix gss-proxy to respect user namespaces + +Signed-off-by: J. Bruce Fields +--- + net/sunrpc/auth_gss/gss_rpc_xdr.c | 20 +++++++++++++------- + 1 file changed, 13 insertions(+), 7 deletions(-) + +diff --git a/net/sunrpc/auth_gss/gss_rpc_xdr.c b/net/sunrpc/auth_gss/gss_rpc_xdr.c +index d0ccdff..5c4c61d 100644 +--- a/net/sunrpc/auth_gss/gss_rpc_xdr.c ++++ b/net/sunrpc/auth_gss/gss_rpc_xdr.c +@@ -216,13 +216,13 @@ static int gssx_dec_linux_creds(struct xdr_stream *xdr, + err = get_s32(&q, end, &tmp); + if (err) + return err; +- creds->cr_uid = tmp; ++ creds->cr_uid = make_kuid(&init_user_ns, tmp); + + /* gid */ + err = get_s32(&q, end, &tmp); + if (err) + return err; +- creds->cr_gid = tmp; ++ creds->cr_gid = make_kgid(&init_user_ns, tmp); + + /* number of additional gid's */ + err = get_s32(&q, end, &tmp); +@@ -235,15 +235,21 @@ static int gssx_dec_linux_creds(struct xdr_stream *xdr, + + /* gid's */ + for (i = 0; i < N; i++) { ++ kgid_t kgid; + err = get_s32(&q, end, &tmp); +- if (err) { +- groups_free(creds->cr_group_info); +- return err; +- } +- GROUP_AT(creds->cr_group_info, i) = tmp; ++ if (err) ++ goto out_free_groups; ++ err = -EINVAL; ++ kgid = make_kgid(&init_user_ns, tmp); ++ if (!gid_valid(kgid)) ++ goto out_free_groups; ++ GROUP_AT(creds->cr_group_info, i) = kgid; + } + + return 0; ++out_free_groups: ++ groups_free(creds->cr_group_info); ++ return err; + } + + static int gssx_dec_option_array(struct xdr_stream *xdr, +-- +1.8.1.4 + + +From e4f7a037aa575bdbe6d1e42e58283cb4d663b000 Mon Sep 17 00:00:00 2001 +From: Geert Uytterhoeven +Date: Mon, 6 May 2013 09:21:03 +0200 +Subject: [PATCH 10/13] SUNRPC: Refactor gssx_dec_option_array() to kill + uninitialized warning +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +net/sunrpc/auth_gss/gss_rpc_xdr.c: In function ‘gssx_dec_option_array’: +net/sunrpc/auth_gss/gss_rpc_xdr.c:258: warning: ‘creds’ may be used uninitialized in this function + +Return early if count is zero, to make it clearer to the compiler (and the +casual reviewer) that no more processing is done. + +Signed-off-by: Geert Uytterhoeven +Signed-off-by: J. Bruce Fields +--- + net/sunrpc/auth_gss/gss_rpc_xdr.c | 32 +++++++++++++++++--------------- + 1 file changed, 17 insertions(+), 15 deletions(-) + +diff --git a/net/sunrpc/auth_gss/gss_rpc_xdr.c b/net/sunrpc/auth_gss/gss_rpc_xdr.c +index 5c4c61d..a1e1b1a 100644 +--- a/net/sunrpc/auth_gss/gss_rpc_xdr.c ++++ b/net/sunrpc/auth_gss/gss_rpc_xdr.c +@@ -264,25 +264,27 @@ static int gssx_dec_option_array(struct xdr_stream *xdr, + if (unlikely(p == NULL)) + return -ENOSPC; + count = be32_to_cpup(p++); +- if (count != 0) { +- /* we recognize only 1 currently: CREDS_VALUE */ +- oa->count = 1; ++ if (!count) ++ return 0; + +- oa->data = kmalloc(sizeof(struct gssx_option), GFP_KERNEL); +- if (!oa->data) +- return -ENOMEM; ++ /* we recognize only 1 currently: CREDS_VALUE */ ++ oa->count = 1; + +- creds = kmalloc(sizeof(struct svc_cred), GFP_KERNEL); +- if (!creds) { +- kfree(oa->data); +- return -ENOMEM; +- } ++ oa->data = kmalloc(sizeof(struct gssx_option), GFP_KERNEL); ++ if (!oa->data) ++ return -ENOMEM; + +- oa->data[0].option.data = CREDS_VALUE; +- oa->data[0].option.len = sizeof(CREDS_VALUE); +- oa->data[0].value.data = (void *)creds; +- oa->data[0].value.len = 0; ++ creds = kmalloc(sizeof(struct svc_cred), GFP_KERNEL); ++ if (!creds) { ++ kfree(oa->data); ++ return -ENOMEM; + } ++ ++ oa->data[0].option.data = CREDS_VALUE; ++ oa->data[0].option.len = sizeof(CREDS_VALUE); ++ oa->data[0].value.data = (void *)creds; ++ oa->data[0].value.len = 0; ++ + for (i = 0; i < count; i++) { + gssx_buffer dummy = { 0, NULL }; + u32 length; +-- +1.8.1.4 + + +From f174f54afb853cd818e2121a0b5dc66012a4a3eb Mon Sep 17 00:00:00 2001 +From: "J. Bruce Fields" +Date: Tue, 7 May 2013 17:45:20 -0400 +Subject: [PATCH 11/13] SUNRPC: fix decoding of optional gss-proxy xdr fields + +The current code works, but sort of by accident: it obviously didn't +intend the error return to be interpreted as "true". + +Reported-by: Dan Carpenter +Signed-off-by: J. Bruce Fields +--- + net/sunrpc/auth_gss/gss_rpc_xdr.c | 26 +++++++++++++------------- + 1 file changed, 13 insertions(+), 13 deletions(-) + +diff --git a/net/sunrpc/auth_gss/gss_rpc_xdr.c b/net/sunrpc/auth_gss/gss_rpc_xdr.c +index a1e1b1a..357f613 100644 +--- a/net/sunrpc/auth_gss/gss_rpc_xdr.c ++++ b/net/sunrpc/auth_gss/gss_rpc_xdr.c +@@ -21,16 +21,6 @@ + #include + #include "gss_rpc_xdr.h" + +-static bool gssx_check_pointer(struct xdr_stream *xdr) +-{ +- __be32 *p; +- +- p = xdr_reserve_space(xdr, 4); +- if (unlikely(p == NULL)) +- return -ENOSPC; +- return *p?true:false; +-} +- + static int gssx_enc_bool(struct xdr_stream *xdr, int v) + { + __be32 *p; +@@ -802,6 +792,7 @@ int gssx_dec_accept_sec_context(struct rpc_rqst *rqstp, + struct xdr_stream *xdr, + struct gssx_res_accept_sec_context *res) + { ++ u32 value_follows; + int err; + + /* res->status */ +@@ -810,7 +801,10 @@ int gssx_dec_accept_sec_context(struct rpc_rqst *rqstp, + return err; + + /* res->context_handle */ +- if (gssx_check_pointer(xdr)) { ++ err = gssx_dec_bool(xdr, &value_follows); ++ if (err) ++ return err; ++ if (value_follows) { + err = gssx_dec_ctx(xdr, res->context_handle); + if (err) + return err; +@@ -819,7 +813,10 @@ int gssx_dec_accept_sec_context(struct rpc_rqst *rqstp, + } + + /* res->output_token */ +- if (gssx_check_pointer(xdr)) { ++ err = gssx_dec_bool(xdr, &value_follows); ++ if (err) ++ return err; ++ if (value_follows) { + err = gssx_dec_buffer(xdr, res->output_token); + if (err) + return err; +@@ -828,7 +825,10 @@ int gssx_dec_accept_sec_context(struct rpc_rqst *rqstp, + } + + /* res->delegated_cred_handle */ +- if (gssx_check_pointer(xdr)) { ++ err = gssx_dec_bool(xdr, &value_follows); ++ if (err) ++ return err; ++ if (value_follows) { + /* we do not support upcall servers sending this data. */ + return -EINVAL; + } +-- +1.8.1.4 + + +From 653ba539c4d845b004c7d29416c9083cb74f8270 Mon Sep 17 00:00:00 2001 +From: Dan Carpenter +Date: Sat, 11 May 2013 19:13:49 +0300 +Subject: [PATCH 12/13] svcauth_gss: fix error code in use_gss_proxy() + +This should return zero on success and -EBUSY on error so the type +needs to be int instead of bool. + +Signed-off-by: Dan Carpenter +Signed-off-by: J. Bruce Fields +--- + net/sunrpc/auth_gss/svcauth_gss.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/net/sunrpc/auth_gss/svcauth_gss.c b/net/sunrpc/auth_gss/svcauth_gss.c +index 71446b6..141902e 100644 +--- a/net/sunrpc/auth_gss/svcauth_gss.c ++++ b/net/sunrpc/auth_gss/svcauth_gss.c +@@ -1285,7 +1285,7 @@ static bool use_gss_proxy(struct net *net) + + #ifdef CONFIG_PROC_FS + +-static bool set_gss_proxy(struct net *net, int type) ++static int set_gss_proxy(struct net *net, int type) + { + struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); + int ret = 0; +-- +1.8.1.4 + + +From 66a26c8c42ebbdfb7dbf297b3d7f404f0dc86ed8 Mon Sep 17 00:00:00 2001 +From: "J. Bruce Fields" +Date: Fri, 24 May 2013 09:47:49 -0400 +Subject: [PATCH 13/13] svcrpc: implement O_NONBLOCK behavior for use-gss-proxy + +Somebody noticed LTP was complaining about O_NONBLOCK opens of +/proc/net/rpc/use-gss-proxy succeeding and then a following read +hanging. + +I'm not convinced LTP really has any business opening random proc files +and expecting them to behave a certain way. Maybe this isn't really a +bug. + +But in any case the O_NONBLOCK behavior could be useful for someone that +wants to test whether gss-proxy is up without waiting. + +Reported-by: Jan Stancek +Signed-off-by: J. Bruce Fields +--- + net/sunrpc/auth_gss/svcauth_gss.c | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +diff --git a/net/sunrpc/auth_gss/svcauth_gss.c b/net/sunrpc/auth_gss/svcauth_gss.c +index 141902e..a7d4dfa 100644 +--- a/net/sunrpc/auth_gss/svcauth_gss.c ++++ b/net/sunrpc/auth_gss/svcauth_gss.c +@@ -1315,10 +1315,12 @@ static inline bool gssp_ready(struct sunrpc_net *sn) + return false; + } + +-static int wait_for_gss_proxy(struct net *net) ++static int wait_for_gss_proxy(struct net *net, struct file *file) + { + struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); + ++ if (file->f_flags & O_NONBLOCK && !gssp_ready(sn)) ++ return -EAGAIN; + return wait_event_interruptible(sn->gssp_wq, gssp_ready(sn)); + } + +@@ -1360,7 +1362,7 @@ static ssize_t read_gssp(struct file *file, char __user *buf, + size_t len; + int ret; + +- ret = wait_for_gss_proxy(net); ++ ret = wait_for_gss_proxy(net, file); + if (ret) + return ret; + +-- +1.8.1.4 + diff --git a/kernel.spec b/kernel.spec index aaceefd1a..5bf9baf19 100644 --- a/kernel.spec +++ b/kernel.spec @@ -62,7 +62,7 @@ Summary: The Linux kernel # For non-released -rc kernels, this will be appended after the rcX and # gitX tags, so a 3 here would become part of release "0.rcX.gitX.3" # -%global baserelease 301 +%global baserelease 302 %global fedora_build %{baserelease} # base_sublevel is the kernel version we're starting with and patching @@ -774,6 +774,9 @@ Patch25025: iscsi-target-fix-heap-buffer-overflow-on-error.patch #rhbz 964335 Patch25026: Modify-UEFI-anti-bricking-code.patch +# Needed for F19 gssproxy feature +Patch25030: gssproxy-backport.patch + # END OF PATCH DEFINITIONS %endif @@ -1494,6 +1497,9 @@ ApplyPatch iscsi-target-fix-heap-buffer-overflow-on-error.patch #rhbz 964335 ApplyPatch Modify-UEFI-anti-bricking-code.patch +# Needed for F19 gssproxy feature +ApplyPatch gssproxy-backport.patch + # END OF PATCH APPLICATIONS %endif @@ -2321,7 +2327,8 @@ fi # and build. %changelog -* Tue Jun 04 2013 Josh Boyer +* Tue Jun 04 2013 Josh Boyer - 3.9.4-302 +- Add gssproxy backport from J. Bruce Fields - Fix build issue with PowerPC MSI patches (rhbz 962496) * Mon Jun 03 2013 Josh Boyer - 3.9.4-301