2897 lines
78 KiB
Diff
2897 lines
78 KiB
Diff
From 7e5eee0a24ea886a0b68a8521117c5ef97668443 Mon Sep 17 00:00:00 2001
|
||
From: Trond Myklebust <Trond.Myklebust@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@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@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@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@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@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@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@redhat.com>
|
||
Signed-off-by: J. Bruce Fields <bfields@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@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@redhat.com>
|
||
[bfields: containerization, concurrent upcalls, misc. fixes and cleanup]
|
||
Signed-off-by: J. Bruce Fields <bfields@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@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@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@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@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@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@redhat.com>
|
||
[bfields: containerization, negotiation api]
|
||
Signed-off-by: J. Bruce Fields <bfields@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@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@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@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@redhat.com>
|
||
Signed-off-by: Fengguang Wu <fengguang.wu@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@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@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@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@linux-m68k.org>
|
||
Signed-off-by: J. Bruce Fields <bfields@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@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@oracle.com>
|
||
Signed-off-by: J. Bruce Fields <bfields@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@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@oracle.com>
|
||
Signed-off-by: J. Bruce Fields <bfields@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@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@redhat.com>
|
||
Signed-off-by: J. Bruce Fields <bfields@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
|
||
|