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
 
 
--- /dev/null
+/*
+ *  linux/net/sunrpc/gss_rpc_upcall.c
+ *
+ *  Copyright (C) 2012 Simo Sorce <simo@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,
+};
 
--- /dev/null
+/*
+ *  linux/net/sunrpc/gss_rpc_upcall.h
+ *
+ *  Copyright (C) 2012 Simo Sorce <simo@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 */
 
--- /dev/null
+/*
+ * GSS Proxy upcall module
+ *
+ *  Copyright (C) 2012 Simo Sorce <simo@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;
+}
 
--- /dev/null
+/*
+ * GSS Proxy upcall module
+ *
+ *  Copyright (C) 2012 Simo Sorce <simo@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 */
 
        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
 
        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;