[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