[kernel/f19] Add gssproxy backport from J. Bruce Fields
Josh Boyer
jwboyer at fedoraproject.org
Tue Jun 4 17:39:47 UTC 2013
commit ca4d8d15577f63012f0d561e886cf7ddec760319
Author: Josh Boyer <jwboyer at redhat.com>
Date: Tue Jun 4 13:39:30 2013 -0400
Add gssproxy backport from J. Bruce Fields
gssproxy-backport.patch | 2896 +++++++++++++++++++++++++++++++++++++++++++++++
kernel.spec | 11 +-
2 files changed, 2905 insertions(+), 2 deletions(-)
---
diff --git a/gssproxy-backport.patch b/gssproxy-backport.patch
new file mode 100644
index 0000000..05efe27
--- /dev/null
+++ b/gssproxy-backport.patch
@@ -0,0 +1,2896 @@
+From 7e5eee0a24ea886a0b68a8521117c5ef97668443 Mon Sep 17 00:00:00 2001
+From: Trond Myklebust <Trond.Myklebust at netapp.com>
+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 <Trond.Myklebust at netapp.com>
+---
+ 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" <bfields at redhat.com>
+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 <bfields at redhat.com>
+---
+ 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" <bfields at redhat.com>
+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 <bfields at redhat.com>
+---
+ 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 <simo at redhat.com>
+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 <simo at redhat.com>
+Signed-off-by: J. Bruce Fields <bfields at redhat.com>
+---
+ 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 <simo at redhat.com>
+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 <simo at redhat.com>
+[bfields: containerization, concurrent upcalls, misc. fixes and cleanup]
+Signed-off-by: J. Bruce Fields <bfields at redhat.com>
+---
+ 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 <simo at redhat.com>
++ *
++ * 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 <linux/types.h>
++#include <linux/un.h>
++
++#include <linux/sunrpc/svcauth.h>
++#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 <simo at redhat.com>
++ *
++ * 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 <linux/sunrpc/auth_gss.h>
++#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 <simo at redhat.com>
++ *
++ * 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 <linux/sunrpc/svcauth.h>
++#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 <simo at redhat.com>
++ *
++ * 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 <linux/sunrpc/xdr.h>
++#include <linux/sunrpc/clnt.h>
++#include <linux/sunrpc/xprtsock.h>
++
++#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 <simo at redhat.com>
+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 <simo at redhat.com>
+[bfields: containerization, negotiation api]
+Signed-off-by: J. Bruce Fields <bfields at redhat.com>
+---
+ 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 <linux/sunrpc/svcauth.h>
+ #include <linux/sunrpc/svcauth_gss.h>
+ #include <linux/sunrpc/cache.h>
++#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" <bfields at redhat.com>
+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 <fengguang.wu at intel.com>
+---
+ 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 <fengguang.wu at intel.com>
+Date: Mon, 29 Apr 2013 17:19:48 -0400
+Subject: [PATCH 08/13] SUNRPC: gssp_procedures[] can be static
+
+Cc: Simo Sorce <simo at redhat.com>
+Signed-off-by: Fengguang Wu <fengguang.wu at intel.com>
+---
+ 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" <bfields at redhat.com>
+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 <bfields at redhat.com>
+---
+ 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 <geert at linux-m68k.org>
+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 <geert at linux-m68k.org>
+Signed-off-by: J. Bruce Fields <bfields at redhat.com>
+---
+ 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" <bfields at redhat.com>
+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 <dan.carpenter at oracle.com>
+Signed-off-by: J. Bruce Fields <bfields at redhat.com>
+---
+ 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 <linux/sunrpc/svcauth.h>
+ #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 <dan.carpenter at oracle.com>
+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 <dan.carpenter at oracle.com>
+Signed-off-by: J. Bruce Fields <bfields at redhat.com>
+---
+ 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" <bfields at redhat.com>
+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 <jstancek at redhat.com>
+Signed-off-by: J. Bruce Fields <bfields at redhat.com>
+---
+ 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 aaceefd..5bf9baf 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 <jwboyer at redhat.com>
+* Tue Jun 04 2013 Josh Boyer <jwboyer at redhat.com> - 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 <jwboyer at redhat.com> - 3.9.4-301
More information about the scm-commits
mailing list