2016-12-23 16:06:31

by Andy Adamson

[permalink] [raw]
Subject: [PATCH Version 3 00/16] RFC: RPCSEC_GSS Version 3 prototype: Full Mode MAC

From: Andy Adamson <[email protected]>

This is a Request for Comment on this GSSv3 prototype that implements full
mode Mandatory Access Control (MAC) when run with NFSv4.2 Labeled NFS using
SeLinux MAC.

Note: I have 3 debug patches to verify functionality with _way_ to many printk
calls that display verifiers and context handle refcounts and labels
available upon request...

Requires:
--------
gssd patches: "RFC: GSSD changes for RPCSEC_GSS version 3"
libtirpc patches "RFC: Libtirpc changes for RPCSEC_GSS version 3"

Prototye Features:
-----------------

GSSv3
- RPC_GSS_SVC_CHANNEL_PROT marked as unsupported
- Negotiate GSS version - start with GSSv3, fall back to GSSv1
- New GSSv3 reply verifier
- RPCSEC_GSS_CREATE call
- label assertion payload carries each client SeLinux thread label
- Supports one label assertion payload per RPCSEC_GSS_CREATE
- Kerberos pseudoflavor support (krb5, krb5i, krb5p)

TODO:
----
- Ensure SeLinux function exported in patch
SELINUX export security_current_sid_to_context" is OK.
- Add user configuration to indicate Full Mode MAC is desired and that
NFSv4.2 Labeled NFS (LNFS) is used.

Prototype description:
---------------------
Parent GSS context: normal GSSv3 context
- Same as a GSSv1 context except new GSS Version and new reply verifier
- Called the parent contex
If SeLinux is enabled and GSSv3 is in use, assume LNFS and GSSv3 full mode MAC.

Each new GSS context (parent) kicks off an RPCSEC_GSS_CREATE with the
client thread's SeLinux label as a payload.
- Upon success, creates a GSSv3 child context handle that asserts the
thread label, and uses the parent context for encrytion services.
- CLIENT: Child context and assertion is stored in an assertion list
off the struct gss_cl_ctx.
- SERVER: Child context has it's own rsc cache entry, and the child
handle is stored in a list of children handles off the parent rsc entry.
- CLIENT and SERVER: child contexts are destroyed when parent context is
destroyed.
- CLIENT: child context associated with the client NFS request thread
is used for the NFS request.
- SERVER: Using the child context handle looks up the child rsc entry.
Using the parent context handle stored in the child rsc entry looks up
the parent rsc entry to use for MIC creation/verification, integrity
(krb5i) and/or privacy (krb5p).
- SERVER: the label asserted by the NFS request child handle is imposed
upon the NFSD thread servicing the request just like the UID/GIDs in
the rpc credential.

Each time a call is made, the clinet makes a check in gss_match to see if
the curren thread's SeLinux label has an associated GSS3 child context handle
to use. If not, an RPCSEC_GSS_CREATE call is kicked off to establish the
child context prior to the NFS request being sent. The NFS request then
uses the child context that asserts the client NFS request thread label
when sending the NFS request to the server.

Smoke Test;
----------
Setup:
- Ensure SeLinux is enabled on both client and server
- Turn on NFSv4.2 in client and server
- SERVER: in /etc/sysconfig/nfs: RPCNFSDARGS="-V 4.2"
- CLIENT: mount -o v4.2 <server>:<export> <mntpoint>
- Useful SeLinux commands
- ls -Z (shows label and fetches it via GETATTR usin LNFS)
- ls -scontext (shows just the label)

Test: (run with wireshark capture)
Note: labels and conext values are from my setup. I restart gssd each test run.
Note: my server /etc/export is "/export *(sec=krb5:sys,rw,no_root_squash)"

- # mount -o v4.2,sec=krb5 <server>:<export> <mntpoint>
# ls <mntpoint>
- This will create a parent GSS context for kb5i (010000000000000 say 01),
and a GSS3 child context for krb5i parent with the client thread
label "system_u:system_r:kernel_t:s0" with handle 02.
- The child handle 02 is used for the EXCHANGE_ID, CREATE_SESSION, and
RECLAIM_COMPLETE calls.
- Then a parent GSS handle is created for krb5 (03) and a GSS3 child
context for the krb5 parent with hanel (04) for the client thread label
"unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023"
- The child handle 04 is used for the PUTROOT_FH, all the mount GETATTRs
and the LOOKUP of "/export"
- A new GSS3 child context (05) is created for krb5 parent with label
"system_u:system_r:kernel_t:s0"
The child handle 05 is used for LOOKUP, ACCESS, READDIR, etc.

- # umount <mntpoint>
- This will create a new GSS3 child context (06) for krb5i parent with the
client thread label
"unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023"
- The child handle 06 is used for DESTROY_SESSION and DESTROY_CLIENTID.
- RPCSEC_GSS_DESTROY messages are sent for the two parent contexts 01 and 03.
- CHILD: the parent contexts and associated child contexts are destroyed.
- SERVER: the parent context and associated child contexts are destroyed.

-->Andy


Andy Adamson (15):
SUNRPC handle unsupported RPC_GSS_SVC_CHANNEL_PROT
SUNRPC: add a null call with payload GSSv3
SELINUX export security_current_sid_to_context
SUNRPC GSSv3: base definitions
SUNRPC AUTH_GSS get RPCSEC_GSS version from gssd downcall
SUNRPC AUTH_GSS gss3 reply verifier
SUNRPC AUTH_GSS RPCSEC_GSS_CREATE with label payload
SUNRPC AUTH_GSS store and use gss3 label assertion
SUNRPC AUTH_GSS free assertions
SUNRPC: AUTH_GSS add RPC_GSS_PROC_CREATE case for wrap and unwrap
SUNRPC SVCAUTH_GSS allow RPCSEC_GSS version 1 or 3
SUNRPC SVCAUTH_GSS gss3 reply verifier
SUNRPC SVCAUTH_GSS gss3 create label
SUNRPC SVCAUTH_GSS set gss3 label on nfsd thread
SUNRPC SVCAUTH_gss store gss3 child handles in parent rsc

Neil Brown (1):
SUNRPC SVCAUTH_GSS reap the rsc cache entry on RPC_GSS_PROC_DESTROY

fs/nfsd/auth.c | 11 +-
include/linux/selinux.h | 1 +
include/linux/sunrpc/auth_gss.h | 76 +++++-
include/linux/sunrpc/cache.h | 1 +
include/linux/sunrpc/clnt.h | 3 +
include/linux/sunrpc/gss_api.h | 11 +
include/linux/sunrpc/svcauth.h | 1 +
include/linux/sunrpc/svcauth_gss.h | 1 +
net/sunrpc/auth_gss/auth_gss.c | 509 +++++++++++++++++++++++++++++++++++-
net/sunrpc/auth_gss/svcauth_gss.c | 522 +++++++++++++++++++++++++++++++++++--
net/sunrpc/cache.c | 13 +
net/sunrpc/clnt.c | 20 ++
security/selinux/hooks.c | 12 +
13 files changed, 1141 insertions(+), 40 deletions(-)

--
1.8.3.1



2016-12-23 16:06:31

by Andy Adamson

[permalink] [raw]
Subject: [PATCH Version 3 01/16] SUNRPC handle unsupported RPC_GSS_SVC_CHANNEL_PROT

From: Andy Adamson <[email protected]>

Signed-off-by: Andy Adamson <[email protected]>
---
net/sunrpc/auth_gss/auth_gss.c | 10 ++++++++++
1 file changed, 10 insertions(+)

diff --git a/net/sunrpc/auth_gss/auth_gss.c b/net/sunrpc/auth_gss/auth_gss.c
index 16cea00..96135da 100644
--- a/net/sunrpc/auth_gss/auth_gss.c
+++ b/net/sunrpc/auth_gss/auth_gss.c
@@ -1869,6 +1869,11 @@ static void gss_wrap_req_encode(kxdreproc_t encode, struct rpc_rqst *rqstp,
case RPC_GSS_SVC_PRIVACY:
status = gss_wrap_req_priv(cred, ctx, encode, rqstp, p, obj);
break;
+ case RPC_GSS_SVC_CHANNEL_PROT:
+ status = -EIO;
+ pr_warn("RPC Unsupported service level %d\n",
+ gss_cred->gc_service);
+ break;
}
out:
gss_put_ctx(ctx);
@@ -1979,6 +1984,11 @@ static void gss_wrap_req_encode(kxdreproc_t encode, struct rpc_rqst *rqstp,
if (status)
goto out;
break;
+ case RPC_GSS_SVC_CHANNEL_PROT:
+ status = -EIO;
+ pr_warn("RPC Unsupported service level %d\n",
+ gss_cred->gc_service);
+ goto out;
}
/* take into account extra slack for integrity and privacy cases: */
cred->cr_auth->au_rslack = cred->cr_auth->au_verfsize + (p - savedp)
--
1.8.3.1


2016-12-23 16:06:31

by Andy Adamson

[permalink] [raw]
Subject: [PATCH Version 3 02/16] SUNRPC: add a null call with payload GSSv3

From: Andy Adamson <[email protected]>

RPCSEC_GSS_CREATE and RPCSEC_GSS_LIST are NULL calls with a payload

Signed-off-by: Andy Adamson <[email protected]>
---
include/linux/sunrpc/clnt.h | 3 +++
net/sunrpc/clnt.c | 20 ++++++++++++++++++++
2 files changed, 23 insertions(+)

diff --git a/include/linux/sunrpc/clnt.h b/include/linux/sunrpc/clnt.h
index 85cc819..880537d 100644
--- a/include/linux/sunrpc/clnt.h
+++ b/include/linux/sunrpc/clnt.h
@@ -175,6 +175,9 @@ int rpc_call_sync(struct rpc_clnt *clnt,
const struct rpc_message *msg, int flags);
struct rpc_task *rpc_call_null(struct rpc_clnt *clnt, struct rpc_cred *cred,
int flags);
+struct rpc_task *rpc_call_null_payload(struct rpc_clnt *clnt,
+ struct rpc_cred *cred, int flags, void *argp,
+ void *resp, struct rpc_procinfo *pinfo);
int rpc_restart_call_prepare(struct rpc_task *);
int rpc_restart_call(struct rpc_task *);
void rpc_setbufsize(struct rpc_clnt *, unsigned int, unsigned int);
diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c
index 1efbe48..9156c62 100644
--- a/net/sunrpc/clnt.c
+++ b/net/sunrpc/clnt.c
@@ -2503,6 +2503,26 @@ static int rpcproc_decode_null(void *rqstp, struct xdr_stream *xdr, void *obj)
return 0;
}

+struct rpc_task *
+rpc_call_null_payload(struct rpc_clnt *clnt, struct rpc_cred *cred, int flags,
+ void *argp, void *resp, struct rpc_procinfo *pinfo)
+{
+ struct rpc_message msg = {
+ .rpc_proc = pinfo,
+ .rpc_argp = argp,
+ .rpc_resp = resp,
+ .rpc_cred = cred,
+ };
+ struct rpc_task_setup task_setup_data = {
+ .rpc_client = clnt,
+ .rpc_message = &msg,
+ .callback_ops = &rpc_default_ops,
+ .flags = flags,
+ };
+ return rpc_run_task(&task_setup_data);
+}
+EXPORT_SYMBOL_GPL(rpc_call_null_payload);
+
static struct rpc_procinfo rpcproc_null = {
.p_encode = rpcproc_encode_null,
.p_decode = rpcproc_decode_null,
--
1.8.3.1


2016-12-23 16:06:31

by Andy Adamson

[permalink] [raw]
Subject: [PATCH Version 3 05/16] SUNRPC AUTH_GSS get RPCSEC_GSS version from gssd downcall

From: Andy Adamson <[email protected]>

Signed-off-by: Andy Adamson <[email protected]>
---
net/sunrpc/auth_gss/auth_gss.c | 16 ++++++++++++----
1 file changed, 12 insertions(+), 4 deletions(-)

diff --git a/net/sunrpc/auth_gss/auth_gss.c b/net/sunrpc/auth_gss/auth_gss.c
index 96135da..9288cc2 100644
--- a/net/sunrpc/auth_gss/auth_gss.c
+++ b/net/sunrpc/auth_gss/auth_gss.c
@@ -213,6 +213,7 @@ struct gss_auth {
unsigned int seclen;
unsigned int timeout;
unsigned long now = jiffies;
+ unsigned int gss_v;
u32 window_size;
int ret;

@@ -226,6 +227,13 @@ struct gss_auth {
if (timeout == 0)
timeout = GSSD_MIN_TIMEOUT;
ctx->gc_expiry = now + ((unsigned long)timeout * HZ);
+
+ /* RPCSEC_GSS version used to obtain context */
+ p = simple_get_bytes(p, end, &gss_v, sizeof(gss_v));
+ if (IS_ERR(p))
+ goto err;
+ ctx->gc_v = gss_v;
+
/* Sequence number window. Determines the maximum number of
* simultaneous requests
*/
@@ -1511,10 +1519,10 @@ static void gss_pipe_free(struct gss_pipe *p)
req->rq_seqno = ctx->gc_seq++;
spin_unlock(&ctx->gc_seq_lock);

- *p++ = htonl((u32) RPC_GSS_VERSION);
- *p++ = htonl((u32) ctx->gc_proc);
- *p++ = htonl((u32) req->rq_seqno);
- *p++ = htonl((u32) gss_cred->gc_service);
+ *p++ = htonl((u32)ctx->gc_v);
+ *p++ = htonl((u32)ctx->gc_proc);
+ *p++ = htonl((u32)req->rq_seqno);
+ *p++ = htonl((u32)gss_cred->gc_service);
p = xdr_encode_netobj(p, &ctx->gc_wire_ctx);
*cred_len = htonl((p - (cred_len + 1)) << 2);

--
1.8.3.1


2016-12-23 16:06:31

by Andy Adamson

[permalink] [raw]
Subject: [PATCH Version 3 03/16] SELINUX export security_current_sid_to_context

From: Andy Adamson <[email protected]>

RPCSEC_GSS Version 3 label assertions require the client thread
sid in string context form

Signed-off-by: Andy Adamson <[email protected]>
---
include/linux/selinux.h | 1 +
security/selinux/hooks.c | 12 ++++++++++++
2 files changed, 13 insertions(+)

diff --git a/include/linux/selinux.h b/include/linux/selinux.h
index 44f4596..3dfaf41 100644
--- a/include/linux/selinux.h
+++ b/include/linux/selinux.h
@@ -24,6 +24,7 @@
* selinux_is_enabled - is SELinux enabled?
*/
bool selinux_is_enabled(void);
+int security_current_sid_to_context(char **scontext, u32 *scontext_len);
#else

static inline bool selinux_is_enabled(void)
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 09fd610..f355c49 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -6463,3 +6463,15 @@ int selinux_disable(void)
return 0;
}
#endif
+
+/**
+ * RPCSEC_GSS Version 3 Full Mode labeling needs this interface
+ * or one like it.
+ */
+int security_current_sid_to_context(char **scontext, u32 *scontext_len)
+{
+ const struct task_security_struct *ts = current_security();
+
+ return security_sid_to_context(ts->sid, scontext, scontext_len);
+}
+EXPORT_SYMBOL_GPL(security_current_sid_to_context);
--
1.8.3.1


2016-12-23 16:06:31

by Andy Adamson

[permalink] [raw]
Subject: [PATCH Version 3 06/16] SUNRPC AUTH_GSS gss3 reply verifier

From: Andy Adamson <[email protected]>

The new GSS Version 3 reply verifier is taken over the same data as
the call verifier, caveat REPLY direction

Verifier Data

xid tk_rqstp->rq_xid
direction REPLY (always a 1) RPC_REPLY
rpcvers RPC_VERSION
prog clnt->cl_prog
vers clnt->cl_vers
proc tk_msg.rpc_proc->p_proc
credential
flavor RPC_AUTH_GSS
length cred_len is in gss_marshal (new gv_crlen)
gss version ctx->gc_v
gss proc ctx->gv_proc
gss seq tk_rqstp->rq_seqno
gss svc gss_cred->gc_service
gss ctx len ctx->gc_wire_ctx
gss ctx data ctx->gc_wire_ctx

Signed-off-by: Andy Adamson <[email protected]>
---
net/sunrpc/auth_gss/auth_gss.c | 60 ++++++++++++++++++++++++++++++++++++++++--
1 file changed, 58 insertions(+), 2 deletions(-)

diff --git a/net/sunrpc/auth_gss/auth_gss.c b/net/sunrpc/auth_gss/auth_gss.c
index 9288cc2..d11f421 100644
--- a/net/sunrpc/auth_gss/auth_gss.c
+++ b/net/sunrpc/auth_gss/auth_gss.c
@@ -1624,6 +1624,53 @@ static int gss_cred_is_negative_entry(struct rpc_cred *cred)
return 0;
}

+/**
+ * gss3_reply_verifier: The new gssv3 verifier uses same data as call
+ * caveat REPLY direction - see rpc_encode_header
+ */
+static void *
+gss3_reply_verifier(struct rpc_cred *cred, struct gss_cl_ctx *ctx,
+ struct rpc_task *task, __be32 *seq, struct kvec *iov)
+{
+ struct gss_cred *g_cred = container_of(cred, struct gss_cred, gc_base);
+ void *gss3_buf = NULL;
+ __be32 *crlen, *ptr = NULL;
+ int len;
+
+ /* freed in gss_validate */
+ len = (13 * 4) + ctx->gc_wire_ctx.len;
+ gss3_buf = kmalloc(len, GFP_NOFS);
+ if (!gss3_buf) {
+ gss3_buf = ERR_PTR(-EIO);
+ goto out;
+ }
+ ptr = (__be32 *)gss3_buf;
+
+ *ptr++ = htonl(task->tk_rqstp->rq_xid);
+ *ptr++ = htonl(RPC_REPLY);
+ *ptr++ = htonl(RPC_VERSION);
+ *ptr++ = htonl(task->tk_client->cl_prog);
+ *ptr++ = htonl(task->tk_client->cl_vers);
+ *ptr++ = htonl(task->tk_msg.rpc_proc->p_proc);
+ *ptr++ = htonl(RPC_AUTH_GSS);
+
+ /* credential */
+ crlen = ptr++;
+ *ptr++ = htonl(ctx->gc_v);
+ *ptr++ = htonl(ctx->gc_proc);
+ *ptr++ = *seq;
+ *ptr++ = htonl(g_cred->gc_service);
+ ptr = xdr_encode_netobj(ptr, &ctx->gc_wire_ctx);
+
+ /* backfill cred length */
+ *crlen = htonl((ptr - (crlen + 1)) << 2);
+
+ iov->iov_base = gss3_buf;
+ iov->iov_len = (ptr - (__be32 *)gss3_buf) << 2;
+out:
+ return gss3_buf;
+}
+
static __be32 *
gss_validate(struct rpc_task *task, __be32 *p)
{
@@ -1633,6 +1680,7 @@ static int gss_cred_is_negative_entry(struct rpc_cred *cred)
struct kvec iov;
struct xdr_buf verf_buf;
struct xdr_netobj mic;
+ void *g3_buf = NULL;
u32 flav,len;
u32 maj_stat;
__be32 *ret = ERR_PTR(-EIO);
@@ -1648,14 +1696,22 @@ static int gss_cred_is_negative_entry(struct rpc_cred *cred)
if (!seq)
goto out_bad;
*seq = htonl(task->tk_rqstp->rq_seqno);
- iov.iov_base = seq;
- iov.iov_len = 4;
+ if (ctx->gc_v == RPC_GSS_VERSION) {
+ iov.iov_base = seq;
+ iov.iov_len = 4;
+ }
+ if (ctx->gc_v == RPC_GSS3_VERSION) {
+ g3_buf = gss3_reply_verifier(cred, ctx, task, seq, &iov);
+ if (IS_ERR(g3_buf))
+ goto out_bad;
+ }
xdr_buf_from_iov(&iov, &verf_buf);
mic.data = (u8 *)p;
mic.len = len;

ret = ERR_PTR(-EACCES);
maj_stat = gss_verify_mic(ctx->gc_gss_ctx, &verf_buf, &mic);
+ kfree(g3_buf);
if (maj_stat == GSS_S_CONTEXT_EXPIRED)
clear_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags);
if (maj_stat) {
--
1.8.3.1


2016-12-23 16:06:34

by Andy Adamson

[permalink] [raw]
Subject: [PATCH Version 3 07/16] SUNRPC AUTH_GSS RPCSEC_GSS_CREATE with label payload

From: Andy Adamson <[email protected]>

Signed-off-by: Andy Adamson <[email protected]>
---
include/linux/sunrpc/auth_gss.h | 54 ++++++++
net/sunrpc/auth_gss/auth_gss.c | 282 ++++++++++++++++++++++++++++++++++++++++
2 files changed, 336 insertions(+)

diff --git a/include/linux/sunrpc/auth_gss.h b/include/linux/sunrpc/auth_gss.h
index 4ab63a9..b2a5a61 100644
--- a/include/linux/sunrpc/auth_gss.h
+++ b/include/linux/sunrpc/auth_gss.h
@@ -93,6 +93,60 @@ struct gss_cred {
unsigned long gc_upcall_timestamp;
};

+/** GSS3 */
+enum gss3_type {
+ GSS3_LABEL = 0,
+ GSS3_PRIVS = 1,
+};
+
+struct gss3_chan_binding {
+ u32 cb_len;
+ void *cb_binding;
+};
+
+struct gss3_mp_auth {
+ u32 mp_handle_len;
+ void *mp_handle;
+ u32 *mp_mic_len;
+ void *mp_mic; /* header mic */
+};
+
+struct gss3_label {
+ u32 la_lfs;
+ u32 la_pi;
+ struct xdr_netobj la_label;
+};
+
+struct gss3_privs {
+ char *pr_name;
+ u32 pr_num;
+ void *pr_data;
+};
+
+struct gss3_assertion_u {
+ u32 au_type;
+ union {
+ struct gss3_label au_label;
+ struct gss3_privs au_privs;
+ } u;
+};
+
+struct gss3_create_args {
+ struct gss3_mp_auth *ca_mp_auth;
+ struct gss3_chan_binding *ca_chan_bind;
+ u32 ca_num;
+ struct gss3_assertion_u *ca_assertions;
+};
+
+struct gss3_create_res {
+ u32 cr_hlen;
+ void *cr_handle;
+ struct gss3_mp_auth *cr_mp_auth;
+ struct gss3_chan_binding *cr_chan_bind;
+ u32 cr_num;
+ struct gss3_assertion_u *cr_assertions;
+};
+
#endif /* __KERNEL__ */
#endif /* _LINUX_SUNRPC_AUTH_GSS_H */

diff --git a/net/sunrpc/auth_gss/auth_gss.c b/net/sunrpc/auth_gss/auth_gss.c
index d11f421..e39c1af 100644
--- a/net/sunrpc/auth_gss/auth_gss.c
+++ b/net/sunrpc/auth_gss/auth_gss.c
@@ -52,9 +52,12 @@
#include <linux/sunrpc/gss_api.h>
#include <asm/uaccess.h>
#include <linux/hashtable.h>
+#include <linux/security.h>

#include "../netns.h"

+static int gss3_create_label(struct rpc_cred *cred);
+
static const struct rpc_authops authgss_ops;

static const struct rpc_credops gss_credops;
@@ -145,6 +148,8 @@ struct gss_auth {
set_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags);
smp_mb__before_atomic();
clear_bit(RPCAUTH_CRED_NEW, &cred->cr_flags);
+ /* XXXX make void? check retunr ?? */
+ gss3_create_label(cred);
}

static const void *
@@ -1591,6 +1596,283 @@ static int gss_cred_is_negative_entry(struct rpc_cred *cred)
return 0;
}

+/**
+ * GSS3_createargs_maxsz and GSS3_createres_maxsz
+ * include no rgss3_assertion_u payload.
+ *
+ * GSS3_createargs_maxsz is currently for one label.
+ */
+#define GSS3_createargs_maxsz (1 /* empty ca_mp_auth */ + \
+ 1 /* empty ca_chan_bind */ + \
+ 1 /* ca_num */ + \
+ 1 /* au_type */)
+#define GSS3_labelargs_maxsz (1 /* la_lfs */ + \
+ 1 /* la_pi */ + \
+ 1 /* la_label.len */ + \
+ XDR_QUADLEN(1024) /* la_label.data */)
+#define GSS3_createres_maxsz (1 /* cr_hlen */ + \
+ XDR_QUADLEN(1024) /* cr_handle*/ + \
+ GSS3_createargs_maxsz)
+#define GSS3_labelres_maxsz GSS3_labelargs_maxsz
+
+#define GSS3_listargs_maxsz (1) /* dummy */
+#define GSS3_listres_maxsz (1) /* dummy */
+
+static void
+gss3_enc_label(struct rpc_rqst *req, struct xdr_stream *xdr,
+ const struct gss3_create_args *g3ca)
+{
+ struct gss3_label *gl;
+ __be32 *p;
+
+ gl = &g3ca->ca_assertions[0].u.au_label;
+
+ dprintk("RPC: %5u encoding GSSv3 label %s:%d\n", req->rq_task->tk_pid,
+ (char *)gl->la_label.data, gl->la_label.len);
+
+ p = xdr_reserve_space(xdr, GSS3_labelargs_maxsz << 2);
+ *p++ = cpu_to_be32(0); /* la_lfs */
+ *p++ = cpu_to_be32(0); /* la_pi */
+ p = xdr_encode_netobj(p, &gl->la_label);
+}
+
+static void
+gss3_enc_create(struct rpc_rqst *req, struct xdr_stream *xdr,
+ const struct gss3_create_args *g3ca)
+{
+ u32 type;
+ __be32 *p;
+
+ p = xdr_reserve_space(xdr, GSS3_createargs_maxsz << 2);
+ *p++ = cpu_to_be32(0); /* NULL ca_mp_auth */
+ *p++ = cpu_to_be32(0); /* NULL ca_chan_bind */
+ *p++ = cpu_to_be32(g3ca->ca_num);
+ /* the payload is a gss3_label */
+ type = cpu_to_be32(g3ca->ca_assertions->au_type);
+ *p++ = type;
+ switch (type) {
+ case GSS3_LABEL:
+ gss3_enc_label(req, xdr, g3ca);
+ break;
+ case GSS3_PRIVS:
+ default:
+ /* drop through to return */
+ pr_warn("RPC Unsupported gss3 create assertion %d\n", type);
+ }
+}
+
+static int
+gss3_dec_label(struct rpc_rqst *req, struct xdr_stream *xdr,
+ struct gss3_create_res *g3cr)
+{
+ struct gss3_label *gl;
+ struct gss3_assertion_u *g3a;
+ __be32 *p;
+
+ g3a = kzalloc(sizeof(*g3a), GFP_KERNEL);
+ if (!g3a)
+ goto out_err;
+
+ g3a->au_type = GSS3_LABEL;
+ gl = &g3a->u.au_label;
+
+ p = xdr_inline_decode(xdr, 12);
+ if (unlikely(!p))
+ goto out_overflow;
+
+ gl->la_lfs = be32_to_cpup(p++);
+ gl->la_pi = be32_to_cpup(p++);
+ gl->la_label.len = be32_to_cpup(p++);
+
+ p = xdr_inline_decode(xdr, gl->la_label.len);
+ if (unlikely(!p))
+ goto out_overflow;
+
+ gl->la_label.data = kmemdup(p, gl->la_label.len, GFP_KERNEL);
+ if (!gl->la_label.data)
+ goto out_free_assert;
+ /**
+ * Note: need to store the assertion with the child handle
+ * in the gss context cache.
+ */
+
+ g3cr->cr_assertions = g3a;
+
+ return 0;
+
+out_free_assert:
+ kfree(g3a);
+out_err:
+ return -EIO;
+out_overflow:
+ pr_warn("RPC %s End of receive buffer. Remaining len: %tu words.\n",
+ __func__, xdr->end - xdr->p);
+ goto out_free_assert;
+}
+
+static int
+gss3_dec_create(struct rpc_rqst *req, struct xdr_stream *xdr,
+ struct gss3_create_res *g3cr)
+{
+ u32 dummy, type;
+ __be32 *p;
+
+ p = xdr_inline_decode(xdr, 4);
+ if (unlikely(!p))
+ goto out_overflow;
+ g3cr->cr_hlen = be32_to_cpup(p++);
+
+ p = xdr_inline_decode(xdr, g3cr->cr_hlen + 16);
+ if (unlikely(!p))
+ goto out_overflow;
+
+ g3cr->cr_handle = kmemdup(p, g3cr->cr_hlen, GFP_KERNEL);
+ if (!g3cr->cr_handle)
+ goto out_err;
+
+ p += XDR_QUADLEN(g3cr->cr_hlen);
+
+ /* cr_mp_auth: not supported */
+ dummy = be32_to_cpup(p++);
+ if (dummy != 0) {
+ pr_warn("RPC gss3 create cr_mp_auth not supported\n");
+ goto out_free_handle;
+ }
+
+ /* cr_chan_bind: not supported */
+ dummy = be32_to_cpup(p++);
+ if (dummy != 0) {
+ pr_warn("RPC gss3 create cr_chan_bind not supported\n");
+ goto out_free_handle;
+ }
+
+ /* Support one label assertion */
+ g3cr->cr_num = be32_to_cpup(p++);
+ if (g3cr->cr_num != 1) {
+ pr_warn("RPC gss3 multiple assertions %d unspported\n",
+ g3cr->cr_num);
+ goto out_free_handle;
+ }
+
+ /* au_type */
+ type = be32_to_cpup(p++);
+ switch (type) {
+ case GSS3_LABEL:
+ if (gss3_dec_label(req, xdr, g3cr) != 0)
+ goto out_free_handle;
+ break;
+ case GSS3_PRIVS:
+ default:
+ pr_warn("RPC Unsupported gss3 create assertion %d\n", type);
+ goto out_free_handle;
+ }
+ return 0;
+
+out_free_handle:
+ kfree(g3cr->cr_handle);
+out_err:
+ return -EIO;
+out_overflow:
+ pr_warn("RPC %s End of receive buffer. Remaining len: %tu words.\n",
+ __func__, xdr->end - xdr->p);
+ goto out_err;
+}
+
+#define RPC_PROC_NULL 0
+
+struct rpc_procinfo gss3_label_assertion[] = {
+ [RPC_GSS_PROC_CREATE] = {
+ .p_proc = RPC_PROC_NULL,
+ .p_encode = (kxdreproc_t)gss3_enc_create,
+ .p_decode = (kxdrdproc_t)gss3_dec_create,
+ .p_arglen = GSS3_createargs_maxsz + GSS3_labelargs_maxsz,
+ .p_replen = GSS3_createres_maxsz + GSS3_labelres_maxsz,
+ .p_statidx = RPC_GSS_PROC_CREATE,
+ .p_timer = 0,
+ .p_name = "GSS_CREATE_LABEL",
+ },
+};
+
+/** GSSv3 RPC_GSS_PROC_CREATE operation
+ * Note: MUST use integrity or privacy security service.
+ * First pass; use rpc_gss_svc_none.
+ *
+ * scontext: returned from security_sid_to_scontext(u32 sid, char **scontext)
+ */
+static int
+gss3_create_label(struct rpc_cred *cred)
+{
+ struct gss_auth *gss_auth = container_of(cred->cr_auth, struct gss_auth,
+ rpc_auth);
+ struct gss_cl_ctx *ctx = gss_cred_get_ctx(cred);
+ struct rpc_task *task;
+ struct gss3_create_res cres = {
+ .cr_mp_auth = 0,
+ };
+ struct gss3_create_args *cargs = NULL;
+ struct gss3_label *gl;
+ int ret = -EINVAL;
+
+ if (!ctx)
+ goto out;
+ /**
+ * Take a reference to ensure the cred sticks around as we create
+ * a child context
+ * XXX does grabbing a reference to the context (gss_cred_get_ctx)
+ * also keep the cred from being removed?
+ * XXX do we need to keep this cred reference until the child context
+ * handle and associated assertion is removed?
+ */
+ get_rpccred(cred);
+
+ if (ctx->gc_v != RPC_GSS3_VERSION || !selinux_is_enabled())
+ goto out_err;
+
+ ret = -ENOMEM;
+ cargs = kzalloc(sizeof(*cargs), GFP_NOFS);
+ if (!cargs)
+ goto out_err;
+ cargs->ca_assertions = kzalloc(sizeof(*cargs->ca_assertions), GFP_NOFS);
+ if (!cargs->ca_assertions)
+ goto out_free_args;
+
+ /* NOTE: not setting la_lfs, la_pi. Do we even need them? */
+ cargs->ca_num = 1;
+ cargs->ca_assertions->au_type = GSS3_LABEL;
+ gl = &cargs->ca_assertions->u.au_label;
+
+ ret = security_current_sid_to_context((char **)&gl->la_label.data,
+ &gl->la_label.len);
+
+ ctx->gc_proc = RPC_GSS_PROC_CREATE;
+ cred->cr_ops = &gss_credops;
+
+ /* Want a sync rpc call */
+ task = rpc_call_null_payload(gss_auth->client, cred,
+ 0, cargs, &cres,
+ &gss3_label_assertion[RPC_GSS_PROC_CREATE]);
+ if (IS_ERR(task)) {
+ ret = PTR_ERR(task);
+ goto out_free_assert;
+ }
+ if (task->tk_status != 0) {
+ ret = task->tk_status;
+ goto out_free_assert;
+ }
+ rpc_put_task(task);
+
+out_free_assert:
+ kfree(cargs->ca_assertions);
+out_free_args:
+ kfree(cargs);
+out_err:
+ ctx->gc_proc = RPC_GSS_PROC_DATA;
+ gss_put_ctx(ctx);
+ put_rpccred(cred);
+out:
+ return ret;
+}
+
/*
* Refresh credentials. XXX - finish
*/
--
1.8.3.1


2016-12-23 16:06:34

by Andy Adamson

[permalink] [raw]
Subject: [PATCH Version 3 11/16] SUNRPC SVCAUTH_GSS reap the rsc cache entry on RPC_GSS_PROC_DESTROY

From: Neil Brown <[email protected]>

NOTE: This patch has been subitted upstream and may have a name
change as Neil has to review.

The rsc cache code operates in a read_lock/write_lock environment.
Changes to a cache entry should use the provided rsc_update
routine which takes the write_lock.

The current code sets the expiry_time and the CACHE_NEGATIVE flag
without taking the write_lock as it does not call rsc_update.
Without this patch, while cache_clean sees the entries to be
removed, it does not remove the rsc_entries. This is because
rsc_update sets other fields in the entry to properly trigger
cache_clean.

Add a new sunrpc_cache_unhash() function (by Neil Brown) to
reap the to be destroyed cache entry.

Signed-off-by: Neil Brown <[email protected]>
Reported-by: Andy Adamson <[email protected]>
Signed-off-by: Andy Adamson <[email protected]>
---
include/linux/sunrpc/cache.h | 1 +
net/sunrpc/auth_gss/svcauth_gss.c | 5 +++--
net/sunrpc/cache.c | 13 +++++++++++++
3 files changed, 17 insertions(+), 2 deletions(-)

diff --git a/include/linux/sunrpc/cache.h b/include/linux/sunrpc/cache.h
index 62a60ee..9dcf2c8 100644
--- a/include/linux/sunrpc/cache.h
+++ b/include/linux/sunrpc/cache.h
@@ -227,6 +227,7 @@ extern int cache_check(struct cache_detail *detail,
extern int sunrpc_cache_register_pipefs(struct dentry *parent, const char *,
umode_t, struct cache_detail *);
extern void sunrpc_cache_unregister_pipefs(struct cache_detail *);
+extern void sunrpc_cache_unhash(struct cache_detail *, struct cache_head *);

/* Must store cache_detail in seq_file->private if using next three functions */
extern void *cache_seq_start(struct seq_file *file, loff_t *pos);
diff --git a/net/sunrpc/auth_gss/svcauth_gss.c b/net/sunrpc/auth_gss/svcauth_gss.c
index edf71a0..5ac2503 100644
--- a/net/sunrpc/auth_gss/svcauth_gss.c
+++ b/net/sunrpc/auth_gss/svcauth_gss.c
@@ -1489,10 +1489,11 @@ static void destroy_use_gss_proxy_proc_entry(struct net *net) {}
case RPC_GSS_PROC_DESTROY:
if (gss_write_verf(rqstp, rsci->mechctx, gc->gc_seq))
goto auth_err;
- rsci->h.expiry_time = get_seconds();
- set_bit(CACHE_NEGATIVE, &rsci->h.flags);
+ /* Delete the entry from the cache_list and call cache_put */
+ sunrpc_cache_unhash(sn->rsc_cache, &rsci->h);
if (resv->iov_len + 4 > PAGE_SIZE)
goto drop;
+
svc_putnl(resv, RPC_SUCCESS);
goto complete;
case RPC_GSS_PROC_DATA:
diff --git a/net/sunrpc/cache.c b/net/sunrpc/cache.c
index 8aabe12..2bbbf3d 100644
--- a/net/sunrpc/cache.c
+++ b/net/sunrpc/cache.c
@@ -1855,3 +1855,16 @@ void sunrpc_cache_unregister_pipefs(struct cache_detail *cd)
}
EXPORT_SYMBOL_GPL(sunrpc_cache_unregister_pipefs);

+void sunrpc_cache_unhash(struct cache_detail *cd, struct cache_head *h)
+{
+ write_lock(&cd->hash_lock);
+ if (!hlist_unhashed(&h->cache_list)) {
+ hlist_del_init(&h->cache_list);
+ cd->entries--;
+ write_unlock(&cd->hash_lock);
+ cache_put(h, cd);
+ } else {
+ write_unlock(&cd->hash_lock);
+ }
+}
+EXPORT_SYMBOL_GPL(sunrpc_cache_unhash);
--
1.8.3.1


2016-12-23 16:06:33

by Andy Adamson

[permalink] [raw]
Subject: [PATCH Version 3 14/16] SUNRPC SVCAUTH_GSS gss3 create label

From: Andy Adamson <[email protected]>

The rsc cache now has two types of entries. The original entry type is
called the parent entry. The new entry type is called a child and holds
the results of a successful RPCSEC_GSS_CREATE operation.

The parent handle rca entries:
- do not use the parent_handle nor assertions fields.

The child handle rca entries:
- do not use the cred, seqdata, mechctx,num_ch, nor child_handles fields.
- XXX perhaps use the seqdata?
- the parent_handle field is used to lookup the parent context
- the assertions field holds the established GSSv3 assertion that the
child handle asserts.

Save assertions in rsc child
Share RPC_GSS_PROC_DATA with RPC_GSS_PROC_CREATE to enable wrap and unwrap.

Signed-off-by: Andy Adamson <[email protected]>
---
include/linux/sunrpc/auth_gss.h | 5 +
net/sunrpc/auth_gss/svcauth_gss.c | 268 +++++++++++++++++++++++++++++++++++++-
2 files changed, 267 insertions(+), 6 deletions(-)

diff --git a/include/linux/sunrpc/auth_gss.h b/include/linux/sunrpc/auth_gss.h
index 59469fc..150e4b7 100644
--- a/include/linux/sunrpc/auth_gss.h
+++ b/include/linux/sunrpc/auth_gss.h
@@ -138,6 +138,11 @@ struct gss3_assertion_u {
} u;
};

+struct gss3_svc_assert {
+ u32 sa_num;
+ struct gss3_assertion_u sa_assert;
+};
+
struct gss3_create_args {
struct gss3_mp_auth *ca_mp_auth;
struct gss3_chan_binding *ca_chan_bind;
diff --git a/net/sunrpc/auth_gss/svcauth_gss.c b/net/sunrpc/auth_gss/svcauth_gss.c
index 0d7f89b..7e675c2 100644
--- a/net/sunrpc/auth_gss/svcauth_gss.c
+++ b/net/sunrpc/auth_gss/svcauth_gss.c
@@ -55,6 +55,9 @@
# define RPCDBG_FACILITY RPCDBG_AUTH
#endif

+/* Global counter for context handles */
+static atomic64_t ctxhctr;
+
/* The rpcsec_init cache is used for mapping RPCSEC_GSS_{,CONT_}INIT requests
* into replies.
*
@@ -324,23 +327,46 @@ struct gss_svc_seq_data {
spinlock_t sd_lock;
};

+/**
+ * struct rca:
+ *
+ * Contains normal GSSv1 and GSSv3 RPCSEC_GSS_INIT established handle rca
+ * entries with related GSS security context information. For GSSv3 these
+ * are termed parent handle rca entries.
+ *
+ * The parent handle rca entries:
+ * - do not use the parent_handle nor assertions fields.
+ *
+ * The child handle rca entries:
+ * - do not use the cred, seqdata, nor mechctx fields.
+ * - XXX perhaps use the seqdata?
+ * - the parent_handle field is used to lookup the parent context
+ * - the assertions field holds the established GSSv3 assertion that the
+ * child handle asserts.
+ */
struct rsc {
struct cache_head h;
struct xdr_netobj handle;
+ struct xdr_netobj parent_handle;
struct svc_cred cred;
struct gss_svc_seq_data seqdata;
struct gss_ctx *mechctx;
+ struct gss3_svc_assert *assertions;
};

static struct rsc *rsc_update(struct cache_detail *cd, struct rsc *new, struct rsc *old);
static struct rsc *rsc_lookup(struct cache_detail *cd, struct rsc *item);
+static void gss3_free_svc_assert(struct gss3_svc_assert *g3a);

static void rsc_free(struct rsc *rsci)
{
kfree(rsci->handle.data);
+ kfree(rsci->parent_handle.data);
if (rsci->mechctx)
gss_delete_sec_context(&rsci->mechctx);
free_svc_cred(&rsci->cred);
+ if (rsci->assertions)
+ gss3_free_svc_assert(rsci->assertions);
}

static void rsc_put(struct kref *ref)
@@ -376,8 +402,15 @@ static void rsc_put(struct kref *ref)
tmp->handle.len = 0;
new->handle.data = tmp->handle.data;
tmp->handle.data = NULL;
+
+ new->parent_handle.len = tmp->handle.len;
+ tmp->parent_handle.len = 0;
+ new->parent_handle.data = tmp->handle.data;
+ tmp->parent_handle.data = NULL;
+
new->mechctx = NULL;
init_svc_cred(&new->cred);
+ new->assertions = NULL;
}

static void
@@ -388,9 +421,15 @@ static void rsc_put(struct kref *ref)

new->mechctx = tmp->mechctx;
tmp->mechctx = NULL;
+ new->parent_handle.len = tmp->parent_handle.len;
+ new->parent_handle.data = tmp->parent_handle.data;
+ tmp->parent_handle.len = 0;
+ tmp->parent_handle.data = NULL;
memset(&new->seqdata, 0, sizeof(new->seqdata));
spin_lock_init(&new->seqdata.sd_lock);
new->cred = tmp->cred;
+ new->assertions = tmp->assertions;
+ tmp->assertions = NULL;
init_svc_cred(&tmp->cred);
}

@@ -1203,7 +1242,6 @@ static int gss_proxy_save_rsc(struct cache_detail *cd,
uint64_t *handle)
{
struct rsc rsci, *rscp = NULL;
- static atomic64_t ctxhctr;
long long ctxh;
struct gss_api_mech *gm = NULL;
time_t expiry;
@@ -1447,13 +1485,199 @@ static void destroy_use_gss_proxy_proc_entry(struct net *net) {}

#endif /* CONFIG_PROC_FS */

+/**
+ * for now, support a single au_label per RPCSEC_GSS_CREATE
+ * no checks here, as the checks are in gss3_save_child
+ */
+static void gss3_free_svc_assert(struct gss3_svc_assert *g3a)
+{
+ struct gss3_label *glp = &g3a->sa_assert.u.au_label;
+
+ kfree(glp->la_label.data);
+ kfree(g3a);
+}
+
+/**
+ * gss3_save_child_rsc()
+ * Create a child handle, set the parent handle, assertions, and add to
+ * the rsc cache.
+ *
+ * @handle - output child handle data
+ * @phandle - input parent handle
+ * @expiry - input parent expiry
+ */
+static struct gss3_svc_assert *
+gss3_save_child_rsc(struct cache_detail *cd, uint64_t *handle,
+ struct xdr_netobj *phandle, time_t expiry,
+ struct kvec *argv)
+{
+ struct gss3_svc_assert *g3a, *ret = NULL;
+ struct gss3_label *glp;
+ struct rsc child, *rscp = NULL;
+ unsigned int len;
+ long dummy;
+ long long ctxh;
+
+ memset(&child, 0, sizeof(child));
+
+ /* context handle */
+ ctxh = atomic64_inc_return(&ctxhctr);
+
+ /* make a copy for the caller */
+ *handle = ctxh;
+
+ /* make a copy for the rsc cache */
+ if (dup_to_netobj(&child.handle, (char *)handle, sizeof(uint64_t)))
+ goto out;
+
+ rscp = rsc_lookup(cd, &child);
+ if (!rscp)
+ goto out;
+
+ if (dup_netobj(&child.parent_handle, phandle))
+ goto out;
+ child.h.expiry_time = expiry;
+
+ /* ca_mp_auth */
+ dummy = svc_getnl(argv);
+ if (dummy != 0)
+ goto out;
+
+ /* ca_chan_bind */
+ dummy = svc_getnl(argv);
+ if (dummy != 0)
+ goto out;
+
+ g3a = kmalloc(sizeof(*g3a), GFP_KERNEL);
+ if (!g3a)
+ goto out;
+ glp = &g3a->sa_assert.u.au_label;
+
+ /* for now support one assertion per RPCSEC_GSS_CREATE */
+ g3a->sa_num = svc_getnl(argv);
+ if (g3a->sa_num != 1) {
+ pr_warn("RPC Number gss3 assertions %d not 1\n",
+ g3a->sa_num);
+ goto out;
+ }
+
+ /** currently support only label assertion
+ * NOTE: will eventually switch on au_type
+ */
+ g3a->sa_assert.au_type = svc_getnl(argv);
+ if (g3a->sa_assert.au_type != GSS3_LABEL) {
+ pr_warn("RPC au_type %d not GSS3_LABEL\n",
+ g3a->sa_assert.au_type);
+ goto out;
+ }
+ /* XXX need to verify? */
+ glp->la_lfs = svc_getnl(argv);
+ glp->la_pi = svc_getnl(argv);
+
+ /**
+ * don't use svc_safe_getnetobj as this memory needs to live
+ * in the rsc cache past the nfsd thread request processing.
+ */
+ glp->la_label.len = svc_getnl(argv);
+ len = round_up_to_quad(glp->la_label.len);
+ if (argv->iov_len < len)
+ goto out;
+
+ if (dup_to_netobj(&glp->la_label, (char *)argv->iov_base,
+ glp->la_label.len))
+ goto out;
+ argv->iov_base += len;
+ argv->iov_len -= len;
+
+ child.assertions = g3a;
+ rscp = rsc_update(cd, &child, rscp);
+
+out:
+ rsc_free(&child);
+ if (rscp) {
+ ret = rscp->assertions;
+ cache_put(&rscp->h, cd);
+ }
+ return ret;
+}
+
+/**
+ * gss3_handle_create_req.
+ *
+ * Create a child rsc record
+ *
+ * Encode the RPCSEC_GSS_CREATE reply as follows:
+ * 4 RPC_SUCCESS
+ * 4 gss3_handle len
+ * 4 rcr_mp_auth
+ * 4 rcr_chan_bind_mic
+ * 4 gss3_num
+ * 4 au_type
+ * 4 la_lfs
+ * 4 la_pi
+ * 4 la_label length
+ *
+ * total encode length: 36 + gss_handlelen + label_len
+ */
+static int
+gss3_handle_create_req(struct kvec *resv, struct kvec *argv, struct rsc *rsci,
+ struct rpc_gss_wire_cred *gc, struct sunrpc_net *sn)
+{
+ struct gss3_svc_assert *g3a;
+ struct gss3_label *glp;
+ u64 c_handle;
+ struct xdr_netobj child_handle;
+ int enc_len, ret = 0;
+
+ g3a = gss3_save_child_rsc(sn->rsc_cache, &c_handle, &gc->gc_ctx,
+ rsci->h.expiry_time, argv);
+ if (!g3a)
+ goto auth_err;
+
+ glp = &g3a->sa_assert.u.au_label;
+
+ /* set child handle for encoding */
+ child_handle.data = (u8 *)&c_handle;
+ child_handle.len = sizeof(c_handle);
+
+ enc_len = 36 + child_handle.len + glp->la_label.len;
+ if (resv->iov_len + enc_len > PAGE_SIZE)
+ goto drop;
+
+ svc_putnl(resv, RPC_SUCCESS);
+
+ /* Encode the RPCSEC_GSS_CREATE payload */
+
+ if (svc_safe_putnetobj(resv, &child_handle))
+ goto auth_err;
+ svc_putnl(resv, 0); /* NULL rcr_mp_auth */
+ svc_putnl(resv, 0); /* NULL rcr_chan_bind_mic */
+ svc_putnl(resv, g3a->sa_num); /* the # of assertions (<>) */
+ svc_putnl(resv, g3a->sa_assert.au_type); /* GSS3_LABEL */
+ svc_putnl(resv, glp->la_lfs);
+ svc_putnl(resv, glp->la_pi);
+
+ if (svc_safe_putnetobj(resv, &glp->la_label))
+ goto auth_err;
+out:
+ return ret;
+auth_err:
+ ret = SVC_DENIED;
+ goto out;
+drop:
+ ret = SVC_DROP;
+ goto out;
+}
+
/*
* Accept an rpcsec packet.
* If context establishment, punt to user space
* If data exchange, verify/decrypt
* If context destruction, handle here
+ * If gssv3 RPCSEC_GSS_CREATE handle here
* In the context establishment and destruction case we encode
* response here and return SVC_COMPLETE.
+ * XXXX should punt to user space for RPCSEC_GSS_CREATE payloads.
*/
static int
svcauth_gss_accept(struct svc_rqst *rqstp, __be32 *authp)
@@ -1462,7 +1686,7 @@ static void destroy_use_gss_proxy_proc_entry(struct net *net) {}
struct kvec *resv = &rqstp->rq_res.head[0];
struct gss_svc_data *svcdata = rqstp->rq_auth_data;
struct rpc_gss_wire_cred *gc;
- struct rsc *rsci = NULL;
+ struct rsc *rsci = NULL, *rsci_ch = NULL;
__be32 *rpcstart;
__be32 *reject_stat = resv->iov_base + resv->iov_len;
int ret;
@@ -1519,11 +1743,25 @@ static void destroy_use_gss_proxy_proc_entry(struct net *net) {}
return svcauth_gss_legacy_init(rqstp, gc, authp);
case RPC_GSS_PROC_DATA:
case RPC_GSS_PROC_DESTROY:
+ case RPC_GSS_PROC_CREATE:
/* Look up the context, and check the verifier: */
*authp = rpcsec_gsserr_credproblem;
+
rsci = gss_svc_searchbyctx(sn->rsc_cache, &gc->gc_ctx);
- if (!rsci)
+ if (!rsci) {
+ pr_warn("RPC gc_ctx handle not found\n");
goto auth_err;
+ }
+ if (rsci->parent_handle.len != 0) { /* GSSv3 child handle */
+
+ rsci_ch = rsci;
+ rsci = gss_svc_searchbyctx(sn->rsc_cache,
+ &rsci_ch->parent_handle);
+ if (!rsci) {
+ pr_warn("RPC parent handle not found\n");
+ goto auth_err;
+ }
+ }
if (rsci->mechctx->gss_version != gc->gc_v) {
pr_warn("NFSD: RPCSEC_GSS version mismatch (%u:%u)\n",
rsci->mechctx->gss_version, gc->gc_v);
@@ -1556,6 +1794,7 @@ static void destroy_use_gss_proxy_proc_entry(struct net *net) {}
svc_putnl(resv, RPC_SUCCESS);
goto complete;
case RPC_GSS_PROC_DATA:
+ case RPC_GSS_PROC_CREATE:
*authp = rpcsec_gsserr_ctxproblem;
svcdata->verf_start = resv->iov_base + resv->iov_len;
if (gss_write_verf(rqstp, rsci->mechctx, gc, gc->gc_seq))
@@ -1593,8 +1832,22 @@ static void destroy_use_gss_proxy_proc_entry(struct net *net) {}
rsci->mechctx->mech_type,
GSS_C_QOP_DEFAULT,
gc->gc_svc);
- ret = SVC_OK;
- goto out;
+ /* RPC_GSS_PROC_DATA */
+ if (gc->gc_proc == RPC_GSS_PROC_DATA) {
+ ret = SVC_OK;
+ goto out;
+ }
+
+ /* RPC_GSS_PROC_CREATE */
+ ret = gss3_handle_create_req(resv, argv, rsci, gc, sn);
+ switch (ret) {
+ case 0:
+ goto out;
+ case SVC_DENIED:
+ goto auth_err;
+ case SVC_DROP:
+ goto drop;
+ }
}
garbage_args:
ret = SVC_GARBAGE;
@@ -1612,6 +1865,8 @@ static void destroy_use_gss_proxy_proc_entry(struct net *net) {}
out:
if (rsci)
cache_put(&rsci->h, sn->rsc_cache);
+ if (rsci_ch)
+ cache_put(&rsci_ch->h, sn->rsc_cache);
return ret;
}

@@ -1763,7 +2018,8 @@ static void destroy_use_gss_proxy_proc_entry(struct net *net) {}
int stat = -EINVAL;
struct sunrpc_net *sn = net_generic(rqstp->rq_xprt->xpt_net, sunrpc_net_id);

- if (gc->gc_proc != RPC_GSS_PROC_DATA)
+ if (!(gc->gc_proc == RPC_GSS_PROC_DATA ||
+ gc->gc_proc == RPC_GSS_PROC_CREATE))
goto out;
/* Release can be called twice, but we only wrap once. */
if (gsd->verf_start == NULL)
--
1.8.3.1


2016-12-23 16:06:33

by Andy Adamson

[permalink] [raw]
Subject: [PATCH Version 3 04/16] SUNRPC GSSv3: base definitions

From: Andy Adamson <[email protected]>

Signed-off-by: Andy Adamson <[email protected]>
---
include/linux/sunrpc/auth_gss.h | 10 ++++++++--
1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/include/linux/sunrpc/auth_gss.h b/include/linux/sunrpc/auth_gss.h
index 36eebc4..4ab63a9 100644
--- a/include/linux/sunrpc/auth_gss.h
+++ b/include/linux/sunrpc/auth_gss.h
@@ -18,6 +18,7 @@
#include <linux/sunrpc/gss_api.h>

#define RPC_GSS_VERSION 1
+#define RPC_GSS3_VERSION 3

#define MAXSEQ 0x80000000 /* maximum legal sequence number, from rfc 2203 */

@@ -25,13 +26,17 @@ enum rpc_gss_proc {
RPC_GSS_PROC_DATA = 0,
RPC_GSS_PROC_INIT = 1,
RPC_GSS_PROC_CONTINUE_INIT = 2,
- RPC_GSS_PROC_DESTROY = 3
+ RPC_GSS_PROC_DESTROY = 3,
+ RPC_GSS_PROC_BIND_CHANNEL = 4, /* GSS2, not used */
+ RPC_GSS_PROC_CREATE = 5, /* GSS3 */
+ RPC_GSS_PROC_LIST = 6 /* GSS3 */
};

enum rpc_gss_svc {
RPC_GSS_SVC_NONE = 1,
RPC_GSS_SVC_INTEGRITY = 2,
- RPC_GSS_SVC_PRIVACY = 3
+ RPC_GSS_SVC_PRIVACY = 3,
+ RPC_GSS_SVC_CHANNEL_PROT = 4 /* GSS2, not used */
};

/* on-the-wire gss cred: */
@@ -66,6 +71,7 @@ struct rpc_gss_init_res {

struct gss_cl_ctx {
atomic_t count;
+ u32 gc_v;
enum rpc_gss_proc gc_proc;
u32 gc_seq;
spinlock_t gc_seq_lock;
--
1.8.3.1


2016-12-23 16:06:35

by Andy Adamson

[permalink] [raw]
Subject: [PATCH Version 3 12/16] SUNRPC SVCAUTH_GSS allow RPCSEC_GSS version 1 or 3

From: Andy Adamson <[email protected]>

Store the version used in the context INIT phase with the context.
Check the incoming rpcsec gss version against the stored context version.

Signed-off-by: Andy Adamson <[email protected]>
---
include/linux/sunrpc/gss_api.h | 1 +
net/sunrpc/auth_gss/svcauth_gss.c | 20 ++++++++++++++------
2 files changed, 15 insertions(+), 6 deletions(-)

diff --git a/include/linux/sunrpc/gss_api.h b/include/linux/sunrpc/gss_api.h
index bbcb100..2d99688 100644
--- a/include/linux/sunrpc/gss_api.h
+++ b/include/linux/sunrpc/gss_api.h
@@ -30,6 +30,7 @@ struct gss3_assert {
/* The mechanism-independent gss-api context: */
struct gss_ctx {
struct gss_api_mech *mech_type;
+ u32 gss_version;
void *internal_ctx_id;
};

diff --git a/net/sunrpc/auth_gss/svcauth_gss.c b/net/sunrpc/auth_gss/svcauth_gss.c
index 5ac2503..1b6777d 100644
--- a/net/sunrpc/auth_gss/svcauth_gss.c
+++ b/net/sunrpc/auth_gss/svcauth_gss.c
@@ -976,8 +976,8 @@ struct gss_svc_data {
}

static inline int
-gss_write_init_verf(struct cache_detail *cd, struct svc_rqst *rqstp,
- struct xdr_netobj *out_handle, int *major_status)
+gss_write_init_verf(struct cache_detail *cd, struct svc_rqst *rqstp, u32 gssv,
+ struct xdr_netobj *out_handle, int *major_status)
{
struct rsc *rsci;
int rc;
@@ -989,6 +989,8 @@ struct gss_svc_data {
*major_status = GSS_S_NO_CONTEXT;
return gss_write_null_verf(rqstp);
}
+ /* set the RPCSEC_GSS version in the context */
+ rsci->mechctx->gss_version = gssv;
rc = gss_write_verf(rqstp, rsci->mechctx, GSS_SEQ_WIN);
cache_put(&rsci->h, cd);
return rc;
@@ -1130,7 +1132,7 @@ static int svcauth_gss_legacy_init(struct svc_rqst *rqstp,

ret = SVC_CLOSE;
/* Got an answer to the upcall; use it: */
- if (gss_write_init_verf(sn->rsc_cache, rqstp,
+ if (gss_write_init_verf(sn->rsc_cache, rqstp, gc->gc_v,
&rsip->out_handle, &rsip->major_status))
goto out;
if (gss_write_resv(resv, PAGE_SIZE,
@@ -1259,7 +1261,7 @@ static int svcauth_gss_proxy_init(struct svc_rqst *rqstp,
}

/* Got an answer to the upcall; use it: */
- if (gss_write_init_verf(sn->rsc_cache, rqstp,
+ if (gss_write_init_verf(sn->rsc_cache, rqstp, gc->gc_v,
&cli_handle, &ud.major_status))
goto out;
if (gss_write_resv(resv, PAGE_SIZE,
@@ -1435,14 +1437,15 @@ static void destroy_use_gss_proxy_proc_entry(struct net *net) {}
rpcstart -= 7;

/* credential is:
- * version(==1), proc(0,1,2,3), seq, service (1,2,3), handle
+ * version(==1 or 3), proc(0,1,2,3), seq, service (1,2,3), handle
* at least 5 u32s, and is preceded by length, so that makes 6.
*/

if (argv->iov_len < 5 * 4)
goto auth_err;
crlen = svc_getnl(argv);
- if (svc_getnl(argv) != RPC_GSS_VERSION)
+ gc->gc_v = svc_getnl(argv);
+ if ((gc->gc_v != RPC_GSS_VERSION) && (gc->gc_v != RPC_GSS3_VERSION))
goto auth_err;
gc->gc_proc = svc_getnl(argv);
gc->gc_seq = svc_getnl(argv);
@@ -1470,6 +1473,11 @@ static void destroy_use_gss_proxy_proc_entry(struct net *net) {}
rsci = gss_svc_searchbyctx(sn->rsc_cache, &gc->gc_ctx);
if (!rsci)
goto auth_err;
+ if (rsci->mechctx->gss_version != gc->gc_v) {
+ pr_warn("NFSD: RPCSEC_GSS version mismatch (%u:%u)\n",
+ rsci->mechctx->gss_version, gc->gc_v);
+ goto auth_err;
+ }
switch (gss_verify_header(rqstp, rsci, rpcstart, gc, authp)) {
case SVC_OK:
break;
--
1.8.3.1


2016-12-23 16:06:37

by Andy Adamson

[permalink] [raw]
Subject: [PATCH Version 3 13/16] SUNRPC SVCAUTH_GSS gss3 reply verifier

From: Andy Adamson <[email protected]>

The new GSS Version 3 reply verifier is taken over the same data as
the call verifier, caveat REPLY direction

verifier data

rpc_header (6 u32s)
XID (put32) rqstp->rq_xid
type REPLY (putnl) always a 1.
rpcvers u32
prog (getnl) rqstp->rq_prog
vers (getnl) rqstp->rq_vers
proc (getnl) rqstp->rq_proc

credential (7 u32s plus data)
flavor (getnl) (new rq_flav)
length (getnl) (new gc_crlen)
gss version gc_v u32 (svcauth_gss_accept)
gss proceedure u32 gc_proc
gss seq num u32 gc_seq
gss service u32 gc_svc
gss context length u32 gc_ctx->len

gss context gc_ctx->data

size is 13 + gc_ctx->len.

Signed-off-by: Andy Adamson <[email protected]>
---
include/linux/sunrpc/auth_gss.h | 1 +
net/sunrpc/auth_gss/svcauth_gss.c | 77 ++++++++++++++++++++++++++++++++-------
2 files changed, 65 insertions(+), 13 deletions(-)

diff --git a/include/linux/sunrpc/auth_gss.h b/include/linux/sunrpc/auth_gss.h
index 7f7b378..59469fc 100644
--- a/include/linux/sunrpc/auth_gss.h
+++ b/include/linux/sunrpc/auth_gss.h
@@ -41,6 +41,7 @@ enum rpc_gss_svc {

/* on-the-wire gss cred: */
struct rpc_gss_wire_cred {
+ u32 gc_crlen;
u32 gc_v; /* version */
u32 gc_proc; /* control procedure */
u32 gc_seq; /* sequence number */
diff --git a/net/sunrpc/auth_gss/svcauth_gss.c b/net/sunrpc/auth_gss/svcauth_gss.c
index 1b6777d..0d7f89b 100644
--- a/net/sunrpc/auth_gss/svcauth_gss.c
+++ b/net/sunrpc/auth_gss/svcauth_gss.c
@@ -715,8 +715,50 @@ static inline u32 round_up_to_quad(u32 i)
return 0;
}

+/**
+ * The new GSS Version 3 reply verifier is taken over the same data as
+ * the call verifier, caveat REPLY direction
+ */
+static void *
+gss3_svc_reply_verifier(struct svc_rqst *rqstp, struct rpc_gss_wire_cred *gc,
+ struct kvec *iov, u32 seq)
+{
+ void *gss3_buf = NULL;
+ __be32 *ptr = NULL;
+ int len;
+
+ /* freed in gss_write_verf */
+ len = (13 * 4) + gc->gc_ctx.len;
+ gss3_buf = kmalloc(len, GFP_KERNEL);
+ if (!gss3_buf)
+ return NULL;
+
+ iov->iov_len = 0;
+ iov->iov_base = gss3_buf;
+ /* 12 __be32's plus iov_len = 13 */
+ svc_putnl(iov, rqstp->rq_xid);
+ svc_putnl(iov, RPC_REPLY);
+ svc_putnl(iov, 2);
+ svc_putnl(iov, rqstp->rq_prog);
+ svc_putnl(iov, rqstp->rq_vers);
+ svc_putnl(iov, rqstp->rq_proc);
+ svc_putnl(iov, RPC_AUTH_GSS);
+ svc_putnl(iov, gc->gc_crlen);
+ svc_putnl(iov, gc->gc_v);
+ svc_putnl(iov, gc->gc_proc);
+ svc_putnl(iov, seq);
+ svc_putnl(iov, gc->gc_svc);
+ ptr = iov->iov_base + iov->iov_len;
+
+ ptr = xdr_encode_netobj(ptr, &gc->gc_ctx);
+ iov->iov_len += sizeof(__be32); /* for ctx length */
+ iov->iov_len += gc->gc_ctx.len;
+ return gss3_buf;
+}
+
static int
-gss_write_verf(struct svc_rqst *rqstp, struct gss_ctx *ctx_id, u32 seq)
+gss_write_verf(struct svc_rqst *rqstp, struct gss_ctx *ctx_id,
+ struct rpc_gss_wire_cred *gc, u32 seq)
{
__be32 *xdr_seq;
u32 maj_stat;
@@ -724,6 +766,7 @@ static inline u32 round_up_to_quad(u32 i)
struct xdr_netobj mic;
__be32 *p;
struct kvec iov;
+ void *g3_buf = NULL;
int err = -1;

svc_putnl(rqstp->rq_res.head, RPC_AUTH_GSS);
@@ -732,12 +775,20 @@ static inline u32 round_up_to_quad(u32 i)
return -1;
*xdr_seq = htonl(seq);

- iov.iov_base = xdr_seq;
- iov.iov_len = 4;
+ if (gc->gc_v == 1) {
+ iov.iov_base = &xdr_seq;
+ iov.iov_len = 4;
+ }
+ if (gc->gc_v == 3) {
+ g3_buf = gss3_svc_reply_verifier(rqstp, gc, &iov, seq);
+ if (!g3_buf)
+ return -1;
+ }
xdr_buf_from_iov(&iov, &verf_data);
p = rqstp->rq_res.head->iov_base + rqstp->rq_res.head->iov_len;
mic.data = (u8 *)(p + 1);
maj_stat = gss_get_mic(ctx_id, &verf_data, &mic);
+ kfree(g3_buf);
if (maj_stat != GSS_S_COMPLETE)
goto out;
*p++ = htonl(mic.len);
@@ -976,7 +1027,8 @@ struct gss_svc_data {
}

static inline int
-gss_write_init_verf(struct cache_detail *cd, struct svc_rqst *rqstp, u32 gssv,
+gss_write_init_verf(struct cache_detail *cd, struct svc_rqst *rqstp,
+ struct rpc_gss_wire_cred *gc,
struct xdr_netobj *out_handle, int *major_status)
{
struct rsc *rsci;
@@ -990,8 +1042,8 @@ struct gss_svc_data {
return gss_write_null_verf(rqstp);
}
/* set the RPCSEC_GSS version in the context */
- rsci->mechctx->gss_version = gssv;
- rc = gss_write_verf(rqstp, rsci->mechctx, GSS_SEQ_WIN);
+ rsci->mechctx->gss_version = gc->gc_v;
+ rc = gss_write_verf(rqstp, rsci->mechctx, gc, GSS_SEQ_WIN);
cache_put(&rsci->h, cd);
return rc;
}
@@ -1132,7 +1184,7 @@ static int svcauth_gss_legacy_init(struct svc_rqst *rqstp,

ret = SVC_CLOSE;
/* Got an answer to the upcall; use it: */
- if (gss_write_init_verf(sn->rsc_cache, rqstp, gc->gc_v,
+ if (gss_write_init_verf(sn->rsc_cache, rqstp, gc,
&rsip->out_handle, &rsip->major_status))
goto out;
if (gss_write_resv(resv, PAGE_SIZE,
@@ -1261,7 +1313,7 @@ static int svcauth_gss_proxy_init(struct svc_rqst *rqstp,
}

/* Got an answer to the upcall; use it: */
- if (gss_write_init_verf(sn->rsc_cache, rqstp, gc->gc_v,
+ if (gss_write_init_verf(sn->rsc_cache, rqstp, gc,
&cli_handle, &ud.major_status))
goto out;
if (gss_write_resv(resv, PAGE_SIZE,
@@ -1408,7 +1460,6 @@ static void destroy_use_gss_proxy_proc_entry(struct net *net) {}
{
struct kvec *argv = &rqstp->rq_arg.head[0];
struct kvec *resv = &rqstp->rq_res.head[0];
- u32 crlen;
struct gss_svc_data *svcdata = rqstp->rq_auth_data;
struct rpc_gss_wire_cred *gc;
struct rsc *rsci = NULL;
@@ -1443,7 +1494,7 @@ static void destroy_use_gss_proxy_proc_entry(struct net *net) {}

if (argv->iov_len < 5 * 4)
goto auth_err;
- crlen = svc_getnl(argv);
+ gc->gc_crlen = svc_getnl(argv);
gc->gc_v = svc_getnl(argv);
if ((gc->gc_v != RPC_GSS_VERSION) && (gc->gc_v != RPC_GSS3_VERSION))
goto auth_err;
@@ -1452,7 +1503,7 @@ static void destroy_use_gss_proxy_proc_entry(struct net *net) {}
gc->gc_svc = svc_getnl(argv);
if (svc_safe_getnetobj(argv, &gc->gc_ctx))
goto auth_err;
- if (crlen != round_up_to_quad(gc->gc_ctx.len) + 5 * 4)
+ if (gc->gc_crlen != round_up_to_quad(gc->gc_ctx.len) + 5 * 4)
goto auth_err;

if ((gc->gc_proc != RPC_GSS_PROC_DATA) && (rqstp->rq_proc != 0))
@@ -1495,7 +1546,7 @@ static void destroy_use_gss_proxy_proc_entry(struct net *net) {}
/* now act upon the command: */
switch (gc->gc_proc) {
case RPC_GSS_PROC_DESTROY:
- if (gss_write_verf(rqstp, rsci->mechctx, gc->gc_seq))
+ if (gss_write_verf(rqstp, rsci->mechctx, gc, gc->gc_seq))
goto auth_err;
/* Delete the entry from the cache_list and call cache_put */
sunrpc_cache_unhash(sn->rsc_cache, &rsci->h);
@@ -1507,7 +1558,7 @@ static void destroy_use_gss_proxy_proc_entry(struct net *net) {}
case RPC_GSS_PROC_DATA:
*authp = rpcsec_gsserr_ctxproblem;
svcdata->verf_start = resv->iov_base + resv->iov_len;
- if (gss_write_verf(rqstp, rsci->mechctx, gc->gc_seq))
+ if (gss_write_verf(rqstp, rsci->mechctx, gc, gc->gc_seq))
goto auth_err;
rqstp->rq_cred = rsci->cred;
get_group_info(rsci->cred.cr_group_info);
--
1.8.3.1


2016-12-23 16:06:31

by Andy Adamson

[permalink] [raw]
Subject: [PATCH Version 3 08/16] SUNRPC AUTH_GSS store and use gss3 label assertion

From: Andy Adamson <[email protected]>

Store gss3 assertions in the parent gss_cl_ctx. Choose to use a child
or parent context handle in gss_marshal and in gss3_reply_verifier.

Note: Need to add a test for full mode labeling. Current code only tests
for the GSS version == 3 and selinx enabled.

In gss_cred_set_ctx() after assigning a new gss context to a credential,
call gss3_create_label which will kick off an RPCSED_GSS_CREATE for the
thread's label if selinux is enabled.

In gss_match(), check if the current threads SeLinux label (sid) has
a matching label assertion. If not, kick off an RPCSEC_GSS_CREATE for
the thread's label.

Signed-off-by: Andy Adamson <[email protected]>
---
include/linux/sunrpc/auth_gss.h | 6 +++
include/linux/sunrpc/gss_api.h | 10 ++++
include/linux/sunrpc/svcauth_gss.h | 1 +
net/sunrpc/auth_gss/auth_gss.c | 105 ++++++++++++++++++++++++++++++++++---
net/sunrpc/auth_gss/svcauth_gss.c | 2 +-
5 files changed, 117 insertions(+), 7 deletions(-)

diff --git a/include/linux/sunrpc/auth_gss.h b/include/linux/sunrpc/auth_gss.h
index b2a5a61..7f7b378 100644
--- a/include/linux/sunrpc/auth_gss.h
+++ b/include/linux/sunrpc/auth_gss.h
@@ -63,6 +63,11 @@ struct rpc_gss_init_res {
struct xdr_netobj gr_token; /* token */
};

+struct gss3_assert_list {
+ struct list_head assert_list;
+ spinlock_t assert_lock;
+};
+
/* The gss_cl_ctx struct holds all the information the rpcsec_gss client
* code needs to know about a single security context. In particular,
* gc_gss_ctx is the context handle that is used to do gss-api calls, while
@@ -80,6 +85,7 @@ struct gss_cl_ctx {
struct xdr_netobj gc_acceptor;
u32 gc_win;
unsigned long gc_expiry;
+ struct gss3_assert_list gc_alist;
struct rcu_head gc_rcu;
};

diff --git a/include/linux/sunrpc/gss_api.h b/include/linux/sunrpc/gss_api.h
index 68ec78c..bbcb100 100644
--- a/include/linux/sunrpc/gss_api.h
+++ b/include/linux/sunrpc/gss_api.h
@@ -17,6 +17,16 @@
#include <linux/sunrpc/msg_prot.h>
#include <linux/uio.h>

+/* one gss3 assertion plus associated child context handle
+ * XXX more than one assertion per child context?
+ */
+struct gss3_assert {
+ struct list_head gss3_list; /* per context list of assertions */
+ struct xdr_netobj gss3_handle;
+ u32 gss3_num;
+ struct gss3_assertion_u *gss3_assertion;
+};
+
/* The mechanism-independent gss-api context: */
struct gss_ctx {
struct gss_api_mech *mech_type;
diff --git a/include/linux/sunrpc/svcauth_gss.h b/include/linux/sunrpc/svcauth_gss.h
index 726aff1..af45bff 100644
--- a/include/linux/sunrpc/svcauth_gss.h
+++ b/include/linux/sunrpc/svcauth_gss.h
@@ -22,6 +22,7 @@
void gss_svc_shutdown_net(struct net *net);
int svcauth_gss_register_pseudoflavor(u32 pseudoflavor, char * name);
u32 svcauth_gss_flavor(struct auth_domain *dom);
+int netobj_equal(struct xdr_netobj *a, struct xdr_netobj *b);

#endif /* __KERNEL__ */
#endif /* _LINUX_SUNRPC_SVCAUTH_GSS_H */
diff --git a/net/sunrpc/auth_gss/auth_gss.c b/net/sunrpc/auth_gss/auth_gss.c
index e39c1af..de0c9ef 100644
--- a/net/sunrpc/auth_gss/auth_gss.c
+++ b/net/sunrpc/auth_gss/auth_gss.c
@@ -57,6 +57,8 @@
#include "../netns.h"

static int gss3_create_label(struct rpc_cred *cred);
+static struct gss3_assert *gss3_use_child_handle(struct gss_cl_ctx *ctx);
+static struct gss3_assert *gss3_match_label(struct gss3_assert_list *in);

static const struct rpc_authops authgss_ops;

@@ -206,6 +208,8 @@ struct gss_auth {
ctx->gc_seq = 1; /* NetApp 6.4R1 doesn't accept seq. no. 0 */
spin_lock_init(&ctx->gc_seq_lock);
atomic_set(&ctx->count,1);
+ INIT_LIST_HEAD(&ctx->gc_alist.assert_list);
+ spin_lock_init(&ctx->gc_alist.assert_lock);
}
return ctx;
}
@@ -1457,6 +1461,7 @@ static void gss_pipe_free(struct gss_pipe *p)
{
struct gss_cred *gss_cred = container_of(rc, struct gss_cred, gc_base);
struct gss_cl_ctx *ctx;
+ struct gss3_assert *g3a;
int ret;

if (test_bit(RPCAUTH_CRED_NEW, &rc->cr_flags))
@@ -1494,6 +1499,13 @@ static void gss_pipe_free(struct gss_pipe *p)
/* tell NFS layer that key will expire soon */
set_bit(RPC_CRED_KEY_EXPIRE_SOON, &acred->ac_flags);
}
+ if (ret) {
+ ctx = gss_cred_get_ctx(rc);
+ g3a = gss3_match_label(&ctx->gc_alist);
+ if (!g3a)
+ gss3_create_label(rc);
+ gss_put_ctx(ctx);
+ }
return ret;
}

@@ -1514,6 +1526,7 @@ static void gss_pipe_free(struct gss_pipe *p)
struct xdr_netobj mic;
struct kvec iov;
struct xdr_buf verf_buf;
+ struct gss3_assert *g3a;

dprintk("RPC: %5u %s\n", task->tk_pid, __func__);

@@ -1528,7 +1541,11 @@ static void gss_pipe_free(struct gss_pipe *p)
*p++ = htonl((u32)ctx->gc_proc);
*p++ = htonl((u32)req->rq_seqno);
*p++ = htonl((u32)gss_cred->gc_service);
- p = xdr_encode_netobj(p, &ctx->gc_wire_ctx);
+ g3a = gss3_use_child_handle(ctx);
+ if (g3a)
+ p = xdr_encode_netobj(p, &g3a->gss3_handle);
+ else
+ p = xdr_encode_netobj(p, &ctx->gc_wire_ctx);
*cred_len = htonl((p - (cred_len + 1)) << 2);

/* We compute the checksum for the verifier over the xdr-encoded bytes
@@ -1597,6 +1614,73 @@ static int gss_cred_is_negative_entry(struct rpc_cred *cred)
}

/**
+ * The gss3_handle and gss3_assertions are allocated in gss3_dec_label
+ */
+static struct gss3_assert *
+gss3_alloc_init_assertion(struct gss3_create_res *cres)
+{
+ struct gss3_assert *ret;
+
+ ret = kzalloc(sizeof(*ret), GFP_NOFS);
+ if (!ret)
+ return ERR_PTR(-ENOMEM);
+
+ INIT_LIST_HEAD(&ret->gss3_list);
+ ret->gss3_handle.len = cres->cr_hlen;
+ ret->gss3_handle.data = cres->cr_handle;
+ ret->gss3_num = cres->cr_num;
+ ret->gss3_assertion = cres->cr_assertions;
+ return ret;
+}
+
+void
+gss3_insert_assertion(struct gss3_assert_list *alist, struct gss3_assert *g3a)
+{
+ spin_lock(&alist->assert_lock);
+ /* list_add_tail_rcu(new,head) inserts new before head */
+ list_add_tail_rcu(&g3a->gss3_list, &alist->assert_list);
+ spin_unlock(&alist->assert_lock);
+}
+
+static struct gss3_assert *
+gss3_match_label(struct gss3_assert_list *in)
+{
+ struct gss3_assert *found;
+ struct xdr_netobj label;
+
+ /* Need a Full Mode stanza in /etc/selinux/config to check */
+ if (!selinux_is_enabled())
+ return NULL;
+
+ /* grab the current threads subject label */
+ security_current_sid_to_context((char **)&label.data, &label.len);
+ rcu_read_lock();
+ list_for_each_entry_rcu(found, &in->assert_list, gss3_list) {
+ struct gss3_label *gl;
+
+ if (found->gss3_assertion->au_type != GSS3_LABEL)
+ continue;
+ gl = &found->gss3_assertion->u.au_label;
+ if (netobj_equal(&gl->la_label, &label))
+ goto out;
+ }
+ found = NULL;
+out:
+ rcu_read_lock();
+ return found;
+}
+
+static struct gss3_assert *
+gss3_use_child_handle(struct gss_cl_ctx *ctx)
+{
+ struct gss3_assert *g3a = NULL;
+
+ if (ctx->gc_v == RPC_GSS3_VERSION && ctx->gc_proc == RPC_GSS_PROC_DATA)
+ g3a = gss3_match_label(&ctx->gc_alist);
+ return g3a;
+}
+
+/**
* GSS3_createargs_maxsz and GSS3_createres_maxsz
* include no rgss3_assertion_u payload.
*
@@ -1691,10 +1775,6 @@ static int gss_cred_is_negative_entry(struct rpc_cred *cred)
gl->la_label.data = kmemdup(p, gl->la_label.len, GFP_KERNEL);
if (!gl->la_label.data)
goto out_free_assert;
- /**
- * Note: need to store the assertion with the child handle
- * in the gss context cache.
- */

g3cr->cr_assertions = g3a;

@@ -1811,6 +1891,7 @@ struct rpc_procinfo gss3_label_assertion[] = {
};
struct gss3_create_args *cargs = NULL;
struct gss3_label *gl;
+ struct gss3_assert *g3a = NULL;
int ret = -EINVAL;

if (!ctx)
@@ -1861,6 +1942,13 @@ struct rpc_procinfo gss3_label_assertion[] = {
}
rpc_put_task(task);

+ g3a = gss3_alloc_init_assertion(&cres);
+ if (IS_ERR(g3a)) {
+ ret = PTR_ERR(task);
+ goto out_free_assert;
+ }
+ gss3_insert_assertion(&ctx->gc_alist, g3a);
+
out_free_assert:
kfree(cargs->ca_assertions);
out_free_args:
@@ -1916,6 +2004,7 @@ struct rpc_procinfo gss3_label_assertion[] = {
{
struct gss_cred *g_cred = container_of(cred, struct gss_cred, gc_base);
void *gss3_buf = NULL;
+ struct gss3_assert *g3a;
__be32 *crlen, *ptr = NULL;
int len;

@@ -1942,7 +2031,11 @@ struct rpc_procinfo gss3_label_assertion[] = {
*ptr++ = htonl(ctx->gc_proc);
*ptr++ = *seq;
*ptr++ = htonl(g_cred->gc_service);
- ptr = xdr_encode_netobj(ptr, &ctx->gc_wire_ctx);
+ g3a = gss3_use_child_handle(ctx);
+ if (g3a)
+ ptr = xdr_encode_netobj(ptr, &g3a->gss3_handle);
+ else
+ ptr = xdr_encode_netobj(ptr, &ctx->gc_wire_ctx);

/* backfill cred length */
*crlen = htonl((ptr - (crlen + 1)) << 2);
diff --git a/net/sunrpc/auth_gss/svcauth_gss.c b/net/sunrpc/auth_gss/svcauth_gss.c
index 45662d7..edf71a0 100644
--- a/net/sunrpc/auth_gss/svcauth_gss.c
+++ b/net/sunrpc/auth_gss/svcauth_gss.c
@@ -63,7 +63,7 @@
*
*/

-static int netobj_equal(struct xdr_netobj *a, struct xdr_netobj *b)
+int netobj_equal(struct xdr_netobj *a, struct xdr_netobj *b)
{
return a->len == b->len && 0 == memcmp(a->data, b->data, a->len);
}
--
1.8.3.1


2016-12-23 16:06:45

by Andy Adamson

[permalink] [raw]
Subject: [PATCH Version 3 09/16] SUNRPC AUTH_GSS free assertions

From: Andy Adamson <[email protected]>

Signed-off-by: Andy Adamson <[email protected]>
---
net/sunrpc/auth_gss/auth_gss.c | 40 ++++++++++++++++++++++++++++++++++++++--
1 file changed, 38 insertions(+), 2 deletions(-)

diff --git a/net/sunrpc/auth_gss/auth_gss.c b/net/sunrpc/auth_gss/auth_gss.c
index de0c9ef..8445ff4 100644
--- a/net/sunrpc/auth_gss/auth_gss.c
+++ b/net/sunrpc/auth_gss/auth_gss.c
@@ -59,6 +59,7 @@
static int gss3_create_label(struct rpc_cred *cred);
static struct gss3_assert *gss3_use_child_handle(struct gss_cl_ctx *ctx);
static struct gss3_assert *gss3_match_label(struct gss3_assert_list *in);
+static void gss3_free_assertions(struct gss3_assert_list *in);

static const struct rpc_authops authgss_ops;

@@ -1262,11 +1263,10 @@ static void gss_pipe_free(struct gss_pipe *p)
static void
gss_do_free_ctx(struct gss_cl_ctx *ctx)
{
- dprintk("RPC: %s\n", __func__);
-
gss_delete_sec_context(&ctx->gc_gss_ctx);
kfree(ctx->gc_wire_ctx.data);
kfree(ctx->gc_acceptor.data);
+ gss3_free_assertions(&ctx->gc_alist);
kfree(ctx);
}

@@ -1642,6 +1642,41 @@ static int gss_cred_is_negative_entry(struct rpc_cred *cred)
spin_unlock(&alist->assert_lock);
}

+static void gss3_free_label(struct gss3_label *gl)
+{
+ kfree(gl->la_label.data);
+}
+
+/**
+ * Note: Currently, only support for one assertion so gss3_num always = 1
+ */
+static void gss3_free_assertions(struct gss3_assert_list *in)
+{
+ struct gss3_assert *found;
+
+ spin_lock(&in->assert_lock);
+
+ rcu_read_lock();
+ list_for_each_entry_rcu(found, &in->assert_list, gss3_list) {
+ list_del_rcu(&found->gss3_list);
+ /* allocated in gss3_dec_label */
+ kfree(found->gss3_handle.data);
+ /* gss3_num is always one for now */
+ switch (found->gss3_assertion->au_type) {
+ case GSS3_LABEL:
+ gss3_free_label(&found->gss3_assertion->u.au_label);
+ break;
+ case GSS3_PRIVS:
+ default:
+ pr_warn("RPC %s Can't free unsupported au_type %d\n",
+ __func__, found->gss3_assertion->au_type);
+ return;
+ }
+ }
+ rcu_read_unlock();
+ spin_unlock(&in->assert_lock);
+}
+
static struct gss3_assert *
gss3_match_label(struct gss3_assert_list *in)
{
@@ -1806,6 +1841,7 @@ static int gss_cred_is_negative_entry(struct rpc_cred *cred)
if (unlikely(!p))
goto out_overflow;

+ /* freed in gss3_free_assertions */
g3cr->cr_handle = kmemdup(p, g3cr->cr_hlen, GFP_KERNEL);
if (!g3cr->cr_handle)
goto out_err;
--
1.8.3.1


2016-12-23 16:06:47

by Andy Adamson

[permalink] [raw]
Subject: [PATCH Version 3 10/16] SUNRPC: AUTH_GSS add RPC_GSS_PROC_CREATE case for wrap and unwrap

From: Andy Adamson <[email protected]>

Signed-off-by: Andy Adamson <[email protected]>
---
net/sunrpc/auth_gss/auth_gss.c | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/net/sunrpc/auth_gss/auth_gss.c b/net/sunrpc/auth_gss/auth_gss.c
index 8445ff4..2b284e1 100644
--- a/net/sunrpc/auth_gss/auth_gss.c
+++ b/net/sunrpc/auth_gss/auth_gss.c
@@ -2325,7 +2325,8 @@ static void gss_wrap_req_encode(kxdreproc_t encode, struct rpc_rqst *rqstp,
int status = -EIO;

dprintk("RPC: %5u %s\n", task->tk_pid, __func__);
- if (ctx->gc_proc != RPC_GSS_PROC_DATA) {
+ if (!(ctx->gc_proc == RPC_GSS_PROC_DATA ||
+ ctx->gc_proc == RPC_GSS_PROC_CREATE)) {
/* The spec seems a little ambiguous here, but I think that not
* wrapping context destruction requests makes the most sense.
*/
@@ -2444,7 +2445,8 @@ static void gss_wrap_req_encode(kxdreproc_t encode, struct rpc_rqst *rqstp,
int savedlen = head->iov_len;
int status = -EIO;

- if (ctx->gc_proc != RPC_GSS_PROC_DATA)
+ if (!(ctx->gc_proc == RPC_GSS_PROC_DATA ||
+ ctx->gc_proc == RPC_GSS_PROC_CREATE))
goto out_decode;
switch (gss_cred->gc_service) {
case RPC_GSS_SVC_NONE:
--
1.8.3.1


2016-12-23 16:06:47

by Andy Adamson

[permalink] [raw]
Subject: [PATCH Version 3 15/16] SUNRPC SVCAUTH_GSS set gss3 label on nfsd thread

From: Andy Adamson <[email protected]>

Signed-off-by: Andy Adamson <[email protected]>
---
fs/nfsd/auth.c | 11 ++++++++++-
include/linux/sunrpc/svcauth.h | 1 +
net/sunrpc/auth_gss/svcauth_gss.c | 41 +++++++++++++++++++++++++++++++++++++++
3 files changed, 52 insertions(+), 1 deletion(-)

diff --git a/fs/nfsd/auth.c b/fs/nfsd/auth.c
index 62469c6..02756b2 100644
--- a/fs/nfsd/auth.c
+++ b/fs/nfsd/auth.c
@@ -1,6 +1,7 @@
/* Copyright (C) 1995, 1996 Olaf Kirch <[email protected]> */

#include <linux/sched.h>
+#include <linux/selinux.h>
#include "nfsd.h"
#include "auth.h"

@@ -22,7 +23,7 @@ int nfsd_setuser(struct svc_rqst *rqstp, struct svc_export *exp)
struct group_info *rqgi;
struct group_info *gi;
struct cred *new;
- int i;
+ int i, ret;
int flags = nfsexp_flags(rqstp, exp);

validate_process_creds();
@@ -77,6 +78,14 @@ int nfsd_setuser(struct svc_rqst *rqstp, struct svc_export *exp)
else
new->cap_effective = cap_raise_nfsd_set(new->cap_effective,
new->cap_permitted);
+
+ /* Need a test for FULL labeling.*/
+ if (selinux_is_enabled() && rqstp->rq_authop->set_label) {
+ ret = rqstp->rq_authop->set_label(rqstp, new);
+ if (ret < 0)
+ /* Should nfsd fail this request? */
+ pr_warn("%s set_label FAILED ret %d\n", __func__, ret);
+ }
validate_process_creds();
put_cred(override_creds(new));
put_cred(new);
diff --git a/include/linux/sunrpc/svcauth.h b/include/linux/sunrpc/svcauth.h
index d039320..eed6880 100644
--- a/include/linux/sunrpc/svcauth.h
+++ b/include/linux/sunrpc/svcauth.h
@@ -128,6 +128,7 @@ struct auth_ops {
int (*release)(struct svc_rqst *rq);
void (*domain_release)(struct auth_domain *);
int (*set_client)(struct svc_rqst *rq);
+ int (*set_label)(struct svc_rqst *rq, struct cred *new);
};

#define SVC_GARBAGE 1
diff --git a/net/sunrpc/auth_gss/svcauth_gss.c b/net/sunrpc/auth_gss/svcauth_gss.c
index 7e675c2..c89ecca 100644
--- a/net/sunrpc/auth_gss/svcauth_gss.c
+++ b/net/sunrpc/auth_gss/svcauth_gss.c
@@ -1065,6 +1065,46 @@ struct gss_svc_data {
return SVC_OK;
}

+/**
+ * the svcdata->rsci pointer is the parent context.
+ * the svcdata->cl_cred->gc_ctx may hold the child context handle
+ * assume one GSS3_LABEL per child context.
+ */
+static int
+svcauth_gss_set_label(struct svc_rqst *rqstp, struct cred *new)
+{
+ struct gss_svc_data *svcdata = rqstp->rq_auth_data;
+ struct rpc_gss_wire_cred *gc = &svcdata->clcred;
+ struct sunrpc_net *sn = net_generic(rqstp->rq_xprt->xpt_net,
+ sunrpc_net_id);
+ struct rsc *rsci;
+ struct gss3_svc_assert *g3a;
+ struct gss3_label *g3l;
+ int ret = -1;
+
+ rsci = gss_svc_searchbyctx(sn->rsc_cache, &gc->gc_ctx);
+ if (!rsci)
+ goto out;
+
+ if (rsci->parent_handle.len == 0 || !rsci->assertions)
+ goto out_put;
+
+ g3a = rsci->assertions;
+ g3l = &g3a->sa_assert.u.au_label;
+
+ if (g3a->sa_num != 1 || g3a->sa_assert.au_type != GSS3_LABEL ||
+ g3l->la_label.len == 0)
+ goto out_put;
+
+ /* Assume SeLinux - need to validate la_lfs and la_pi ? */
+ ret = set_security_override_from_ctx(new, (char *)g3l->la_label.data);
+
+out_put:
+ cache_put(&rsci->h, sn->rsc_cache);
+out:
+ return ret;
+}
+
static inline int
gss_write_init_verf(struct cache_detail *cd, struct svc_rqst *rqstp,
struct rpc_gss_wire_cred *gc,
@@ -2083,6 +2123,7 @@ static void gss3_free_svc_assert(struct gss3_svc_assert *g3a)
.release = svcauth_gss_release,
.domain_release = svcauth_gss_domain_release,
.set_client = svcauth_gss_set_client,
+ .set_label = svcauth_gss_set_label,
};

static int rsi_cache_create_net(struct net *net)
--
1.8.3.1


2016-12-23 16:06:48

by Andy Adamson

[permalink] [raw]
Subject: [PATCH Version 3 16/16] SUNRPC SVCAUTH_gss store gss3 child handles in parent rsc

From: Andy Adamson <[email protected]>

GSSv3 parent handles use the new children list field protected
by the new ch_lock to hold child handles created by successful
RPCSEC_GSS_CREATE calls. This list is used upon parent rsc entry
destruction to lookup child rsc cache entries so as to reap the
children. The net field is needed for the lookup.

Signed-off-by: Andy Adamson <[email protected]>
---
net/sunrpc/auth_gss/svcauth_gss.c | 125 ++++++++++++++++++++++++++++++++++++--
1 file changed, 119 insertions(+), 6 deletions(-)

diff --git a/net/sunrpc/auth_gss/svcauth_gss.c b/net/sunrpc/auth_gss/svcauth_gss.c
index c89ecca..d5d020f 100644
--- a/net/sunrpc/auth_gss/svcauth_gss.c
+++ b/net/sunrpc/auth_gss/svcauth_gss.c
@@ -336,9 +336,14 @@ struct gss_svc_seq_data {
*
* The parent handle rca entries:
* - do not use the parent_handle nor assertions fields.
+ * - GSSv3 parent handles uses the children list field to hold
+ * child handles created by successful RPCSEC_GSS_CREATE calls. This
+ * list is used upon parent rca record destruction to lookup child rsc
+ * cache entries so as to destroy the children. The net field is needed
+ * for the lookup.
*
* The child handle rca entries:
- * - do not use the cred, seqdata, nor mechctx fields.
+ * - do not use the cred, seqdata, mechctx, net, ch_lock, nor children fields.
* - XXX perhaps use the seqdata?
* - the parent_handle field is used to lookup the parent context
* - the assertions field holds the established GSSv3 assertion that the
@@ -352,11 +357,20 @@ struct rsc {
struct gss_svc_seq_data seqdata;
struct gss_ctx *mechctx;
struct gss3_svc_assert *assertions;
+ struct net *net;
+ spinlock_t ch_lock; /* for children */
+ struct list_head children;
+};
+
+struct rsc_child_entry {
+ struct list_head ce_list;
+ struct xdr_netobj ce_chandle;
};

static struct rsc *rsc_update(struct cache_detail *cd, struct rsc *new, struct rsc *old);
static struct rsc *rsc_lookup(struct cache_detail *cd, struct rsc *item);
static void gss3_free_svc_assert(struct gss3_svc_assert *g3a);
+static void gss3_free_rsc_children(struct rsc *rsci);

static void rsc_free(struct rsc *rsci)
{
@@ -367,6 +381,8 @@ static void rsc_free(struct rsc *rsci)
free_svc_cred(&rsci->cred);
if (rsci->assertions)
gss3_free_svc_assert(rsci->assertions);
+ if (!list_empty(&rsci->children))
+ gss3_free_rsc_children(rsci);
}

static void rsc_put(struct kref *ref)
@@ -393,6 +409,14 @@ static void rsc_put(struct kref *ref)
}

static void
+init_rsc(struct rsc *rsci)
+{
+ memset(rsci, 0, sizeof(struct rsc));
+ spin_lock_init(&rsci->ch_lock);
+ INIT_LIST_HEAD(&rsci->children);
+}
+
+static void
rsc_init(struct cache_head *cnew, struct cache_head *ctmp)
{
struct rsc *new = container_of(cnew, struct rsc, h);
@@ -411,6 +435,9 @@ static void rsc_put(struct kref *ref)
new->mechctx = NULL;
init_svc_cred(&new->cred);
new->assertions = NULL;
+ new->net = NULL;
+ spin_lock_init(&new->ch_lock);
+ INIT_LIST_HEAD(&new->children);
}

static void
@@ -431,6 +458,16 @@ static void rsc_put(struct kref *ref)
new->assertions = tmp->assertions;
tmp->assertions = NULL;
init_svc_cred(&tmp->cred);
+ new->net = tmp->net;
+ tmp->net = NULL;
+ spin_lock_init(&new->ch_lock);
+ INIT_LIST_HEAD(&new->children);
+ spin_lock(&tmp->ch_lock);
+ if (!list_empty(&tmp->children)) {
+ list_move(&tmp->children, &new->children);
+ INIT_LIST_HEAD(&tmp->children);
+ }
+ spin_unlock(&tmp->ch_lock);
}

static struct cache_head *
@@ -455,7 +492,7 @@ static int rsc_parse(struct cache_detail *cd,
int status = -EINVAL;
struct gss_api_mech *gm = NULL;

- memset(&rsci, 0, sizeof(rsci));
+ init_rsc(&rsci);
/* context handle */
len = qword_get(&mesg, buf, mlen);
if (len < 0) goto out;
@@ -607,7 +644,7 @@ static struct rsc *rsc_update(struct cache_detail *cd, struct rsc *new, struct r
struct rsc rsci;
struct rsc *found;

- memset(&rsci, 0, sizeof(rsci));
+ init_rsc(&rsci);
if (dup_to_netobj(&rsci.handle, handle->data, handle->len))
return NULL;
found = rsc_lookup(cd, &rsci);
@@ -1287,7 +1324,7 @@ static int gss_proxy_save_rsc(struct cache_detail *cd,
time_t expiry;
int status = -EINVAL;

- memset(&rsci, 0, sizeof(rsci));
+ init_rsc(&rsci);
/* context handle */
status = -ENOMEM;
/* the handle needs to be just a unique id,
@@ -1537,6 +1574,68 @@ static void gss3_free_svc_assert(struct gss3_svc_assert *g3a)
kfree(g3a);
}

+static void gss3_free_rsc_children(struct rsc *rsci)
+{
+ struct rsc_child_entry *cep, *tmp;
+ LIST_HEAD(free);
+ struct sunrpc_net *sn = net_generic(rsci->net, sunrpc_net_id);
+ struct rsc *child;
+
+ spin_lock(&rsci->ch_lock);
+
+ list_for_each_entry_safe(cep, tmp, &rsci->children, ce_list)
+ list_move(&cep->ce_list, &free);
+
+ spin_unlock(&rsci->ch_lock);
+
+ list_for_each_entry_safe(cep, tmp, &free, ce_list) {
+ list_del(&cep->ce_list);
+ child = gss_svc_searchbyctx(sn->rsc_cache, &cep->ce_chandle);
+ if (child) {
+ /* balance gss_svc_searchbyctx cache_get */
+ cache_put(&child->h, sn->rsc_cache);
+ /* reap the child */
+ sunrpc_cache_unhash(sn->rsc_cache, &child->h);
+ } else
+ pr_warn("RPC %s child in children list not found\n",
+ __func__);
+ }
+}
+
+/**
+ * After a gss3 child rsc is created, add it's context handle to the
+ * children list of the parent rsc.
+ * Required: gss_svc_searchbyctx has already been called on parent_rsc.
+ */
+static int
+gss3_add_child_rsc(struct cache_detail *cd, struct rsc *parent_rsc,
+ struct xdr_netobj *chandle)
+{
+ struct rsc_child_entry *cep;
+ int status = -ENOMEM;
+
+ cep = kmalloc(sizeof(*cep), GFP_KERNEL);
+ if (!cep)
+ goto out;
+
+ /* child handle */
+ if (dup_netobj(&cep->ce_chandle, chandle))
+ goto out_free;
+
+ parent_rsc->net = cd->net;
+ INIT_LIST_HEAD(&cep->ce_list);
+ spin_lock(&parent_rsc->ch_lock);
+ list_add(&cep->ce_list, &parent_rsc->children);
+ spin_unlock(&parent_rsc->ch_lock);
+
+ status = 0;
+out:
+ return status;
+out_free:
+ kfree(cep);
+ goto out;
+}
+
/**
* gss3_save_child_rsc()
* Create a child handle, set the parent handle, assertions, and add to
@@ -1558,8 +1657,7 @@ static void gss3_free_svc_assert(struct gss3_svc_assert *g3a)
long dummy;
long long ctxh;

- memset(&child, 0, sizeof(child));
-
+ init_rsc(&child);
/* context handle */
ctxh = atomic64_inc_return(&ctxhctr);

@@ -1680,6 +1778,21 @@ static void gss3_free_svc_assert(struct gss3_svc_assert *g3a)
child_handle.data = (u8 *)&c_handle;
child_handle.len = sizeof(c_handle);

+ ret = gss3_add_child_rsc(sn->rsc_cache, rsci, &child_handle);
+ if (ret < 0) {
+ struct rsc *child;
+
+ pr_warn("%s failed to add child rsc to parent\n", __func__);
+ /* delete child */
+ child = gss_svc_searchbyctx(sn->rsc_cache, &child_handle);
+ if (child) {
+ /* balance gss_svc_searchbyctx cache_get */
+ cache_put(&child->h, sn->rsc_cache);
+ /* reap the child */
+ sunrpc_cache_unhash(sn->rsc_cache, &child->h);
+ }
+ goto auth_err;
+ }
enc_len = 36 + child_handle.len + glp->la_label.len;
if (resv->iov_len + enc_len > PAGE_SIZE)
goto drop;
--
1.8.3.1


2016-12-23 18:07:52

by kernel test robot

[permalink] [raw]
Subject: Re: [PATCH Version 3 07/16] SUNRPC AUTH_GSS RPCSEC_GSS_CREATE with label payload

Hi Andy,

[auto build test ERROR on nfsd/nfsd-next]
[also build test ERROR on v4.9 next-20161223]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url: https://github.com/0day-ci/linux/commits/andros-netapp-com/RFC-RPCSEC_GSS-Version-3-prototype-Full-Mode-MAC/20161224-014029
base: git://linux-nfs.org/~bfields/linux.git nfsd-next
config: x86_64-randconfig-x000-201651 (attached as .config)
compiler: gcc-6 (Debian 6.2.0-3) 6.2.0 20160901
reproduce:
# save the attached .config to linux build tree
make ARCH=x86_64

All errors (new ones prefixed by >>):

net/sunrpc/auth_gss/auth_gss.c: In function 'gss3_create_label':
>> net/sunrpc/auth_gss/auth_gss.c:1839:8: error: implicit declaration of function 'security_current_sid_to_context' [-Werror=implicit-function-declaration]
ret = security_current_sid_to_context((char **)&gl->la_label.data,
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
cc1: some warnings being treated as errors

vim +/security_current_sid_to_context +1839 net/sunrpc/auth_gss/auth_gss.c

1833
1834 /* NOTE: not setting la_lfs, la_pi. Do we even need them? */
1835 cargs->ca_num = 1;
1836 cargs->ca_assertions->au_type = GSS3_LABEL;
1837 gl = &cargs->ca_assertions->u.au_label;
1838
> 1839 ret = security_current_sid_to_context((char **)&gl->la_label.data,
1840 &gl->la_label.len);
1841
1842 ctx->gc_proc = RPC_GSS_PROC_CREATE;

---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation


Attachments:
(No filename) (1.59 kB)
.config.gz (33.45 kB)
Download all attachments

2016-12-23 18:33:00

by kernel test robot

[permalink] [raw]
Subject: Re: [PATCH Version 3 01/16] SUNRPC handle unsupported RPC_GSS_SVC_CHANNEL_PROT

Hi Andy,

[auto build test ERROR on nfsd/nfsd-next]
[also build test ERROR on v4.9 next-20161223]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url: https://github.com/0day-ci/linux/commits/andros-netapp-com/RFC-RPCSEC_GSS-Version-3-prototype-Full-Mode-MAC/20161224-014029
base: git://linux-nfs.org/~bfields/linux.git nfsd-next
config: x86_64-acpi-redef (attached as .config)
compiler: gcc-6 (Debian 6.2.0-3) 6.2.0 20160901
reproduce:
# save the attached .config to linux build tree
make ARCH=x86_64

Note: the linux-review/andros-netapp-com/RFC-RPCSEC_GSS-Version-3-prototype-Full-Mode-MAC/20161224-014029 HEAD 1e08ecfb34721e70383610d313ba7b44abec0968 builds fine.
It only hurts bisectibility.

All errors (new ones prefixed by >>):

net/sunrpc/auth_gss/auth_gss.c: In function 'gss_wrap_req':
>> net/sunrpc/auth_gss/auth_gss.c:1867:7: error: 'RPC_GSS_SVC_CHANNEL_PROT' undeclared (first use in this function)
case RPC_GSS_SVC_CHANNEL_PROT:
^~~~~~~~~~~~~~~~~~~~~~~~
net/sunrpc/auth_gss/auth_gss.c:1867:7: note: each undeclared identifier is reported only once for each function it appears in
net/sunrpc/auth_gss/auth_gss.c: In function 'gss_unwrap_resp':
net/sunrpc/auth_gss/auth_gss.c:1982:7: error: 'RPC_GSS_SVC_CHANNEL_PROT' undeclared (first use in this function)
case RPC_GSS_SVC_CHANNEL_PROT:
^~~~~~~~~~~~~~~~~~~~~~~~

vim +/RPC_GSS_SVC_CHANNEL_PROT +1867 net/sunrpc/auth_gss/auth_gss.c

1861 case RPC_GSS_SVC_INTEGRITY:
1862 status = gss_wrap_req_integ(cred, ctx, encode, rqstp, p, obj);
1863 break;
1864 case RPC_GSS_SVC_PRIVACY:
1865 status = gss_wrap_req_priv(cred, ctx, encode, rqstp, p, obj);
1866 break;
> 1867 case RPC_GSS_SVC_CHANNEL_PROT:
1868 status = -EIO;
1869 pr_warn("RPC Unsupported service level %d\n",
1870 gss_cred->gc_service);

---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation


Attachments:
(No filename) (2.03 kB)
.config.gz (27.99 kB)
Download all attachments

2017-01-04 21:11:55

by Anna Schumaker

[permalink] [raw]
Subject: Re: [PATCH Version 3 01/16] SUNRPC handle unsupported RPC_GSS_SVC_CHANNEL_PROT

Hi Andy,

On 12/23/2016 11:04 AM, [email protected] wrote:
> From: Andy Adamson <[email protected]>
>
> Signed-off-by: Andy Adamson <[email protected]>
> ---
> net/sunrpc/auth_gss/auth_gss.c | 10 ++++++++++
> 1 file changed, 10 insertions(+)
>
> diff --git a/net/sunrpc/auth_gss/auth_gss.c b/net/sunrpc/auth_gss/auth_gss.c
> index 16cea00..96135da 100644
> --- a/net/sunrpc/auth_gss/auth_gss.c
> +++ b/net/sunrpc/auth_gss/auth_gss.c
> @@ -1869,6 +1869,11 @@ static void gss_wrap_req_encode(kxdreproc_t encode, struct rpc_rqst *rqstp,
> case RPC_GSS_SVC_PRIVACY:
> status = gss_wrap_req_priv(cred, ctx, encode, rqstp, p, obj);
> break;
> + case RPC_GSS_SVC_CHANNEL_PROT:

Would it make more sense to simply use "default:" here instead? That way we do the right thing if an invalid value is somehow set for gc_service.

> + status = -EIO;
> + pr_warn("RPC Unsupported service level %d\n",
> + gss_cred->gc_service);
> + break;
> }
> out:
> gss_put_ctx(ctx);
> @@ -1979,6 +1984,11 @@ static void gss_wrap_req_encode(kxdreproc_t encode, struct rpc_rqst *rqstp,
> if (status)
> goto out;
> break;
> + case RPC_GSS_SVC_CHANNEL_PROT:
> + status = -EIO;
> + pr_warn("RPC Unsupported service level %d\n",
> + gss_cred->gc_service);

Same question here.

Thanks,
Anna

> + goto out;
> }
> /* take into account extra slack for integrity and privacy cases: */
> cred->cr_auth->au_rslack = cred->cr_auth->au_verfsize + (p - savedp)
>

2017-01-04 21:47:02

by Anna Schumaker

[permalink] [raw]
Subject: Re: [PATCH Version 3 06/16] SUNRPC AUTH_GSS gss3 reply verifier

Hi Andy,

On 12/23/2016 11:04 AM, [email protected] wrote:
> From: Andy Adamson <[email protected]>
>
> The new GSS Version 3 reply verifier is taken over the same data as
> the call verifier, caveat REPLY direction
>
> Verifier Data
>
> xid tk_rqstp->rq_xid
> direction REPLY (always a 1) RPC_REPLY
> rpcvers RPC_VERSION
> prog clnt->cl_prog
> vers clnt->cl_vers
> proc tk_msg.rpc_proc->p_proc
> credential
> flavor RPC_AUTH_GSS
> length cred_len is in gss_marshal (new gv_crlen)
> gss version ctx->gc_v
> gss proc ctx->gv_proc
> gss seq tk_rqstp->rq_seqno
> gss svc gss_cred->gc_service
> gss ctx len ctx->gc_wire_ctx
> gss ctx data ctx->gc_wire_ctx
>
> Signed-off-by: Andy Adamson <[email protected]>
> ---
> net/sunrpc/auth_gss/auth_gss.c | 60 ++++++++++++++++++++++++++++++++++++++++--
> 1 file changed, 58 insertions(+), 2 deletions(-)
>
> diff --git a/net/sunrpc/auth_gss/auth_gss.c b/net/sunrpc/auth_gss/auth_gss.c
> index 9288cc2..d11f421 100644
> --- a/net/sunrpc/auth_gss/auth_gss.c
> +++ b/net/sunrpc/auth_gss/auth_gss.c
> @@ -1624,6 +1624,53 @@ static int gss_cred_is_negative_entry(struct rpc_cred *cred)
> return 0;
> }
>
> +/**
> + * gss3_reply_verifier: The new gssv3 verifier uses same data as call
> + * caveat REPLY direction - see rpc_encode_header
> + */
> +static void *
> +gss3_reply_verifier(struct rpc_cred *cred, struct gss_cl_ctx *ctx,
> + struct rpc_task *task, __be32 *seq, struct kvec *iov)
> +{
> + struct gss_cred *g_cred = container_of(cred, struct gss_cred, gc_base);
> + void *gss3_buf = NULL;
> + __be32 *crlen, *ptr = NULL;
> + int len;
> +
> + /* freed in gss_validate */
> + len = (13 * 4) + ctx->gc_wire_ctx.len;
> + gss3_buf = kmalloc(len, GFP_NOFS);
> + if (!gss3_buf) {
> + gss3_buf = ERR_PTR(-EIO);
> + goto out;
> + }
> + ptr = (__be32 *)gss3_buf;
> +
> + *ptr++ = htonl(task->tk_rqstp->rq_xid);
> + *ptr++ = htonl(RPC_REPLY);
> + *ptr++ = htonl(RPC_VERSION);
> + *ptr++ = htonl(task->tk_client->cl_prog);
> + *ptr++ = htonl(task->tk_client->cl_vers);
> + *ptr++ = htonl(task->tk_msg.rpc_proc->p_proc);
> + *ptr++ = htonl(RPC_AUTH_GSS);
> +
> + /* credential */
> + crlen = ptr++;
> + *ptr++ = htonl(ctx->gc_v);
> + *ptr++ = htonl(ctx->gc_proc);
> + *ptr++ = *seq;
> + *ptr++ = htonl(g_cred->gc_service);
> + ptr = xdr_encode_netobj(ptr, &ctx->gc_wire_ctx);
> +
> + /* backfill cred length */
> + *crlen = htonl((ptr - (crlen + 1)) << 2);
> +
> + iov->iov_base = gss3_buf;
> + iov->iov_len = (ptr - (__be32 *)gss3_buf) << 2;
> +out:
> + return gss3_buf;
> +}
> +
> static __be32 *
> gss_validate(struct rpc_task *task, __be32 *p)
> {
> @@ -1633,6 +1680,7 @@ static int gss_cred_is_negative_entry(struct rpc_cred *cred)
> struct kvec iov;
> struct xdr_buf verf_buf;
> struct xdr_netobj mic;
> + void *g3_buf = NULL;
> u32 flav,len;
> u32 maj_stat;
> __be32 *ret = ERR_PTR(-EIO);
> @@ -1648,14 +1696,22 @@ static int gss_cred_is_negative_entry(struct rpc_cred *cred)
> if (!seq)
> goto out_bad;
> *seq = htonl(task->tk_rqstp->rq_seqno);
> - iov.iov_base = seq;
> - iov.iov_len = 4;
> + if (ctx->gc_v == RPC_GSS_VERSION) {
> + iov.iov_base = seq;
> + iov.iov_len = 4;
> + }
> + if (ctx->gc_v == RPC_GSS3_VERSION) {

Can this be written as an else-if instead? I don't think it's likely for gc_v to have two values at once :)

Thanks,
Anna

> + g3_buf = gss3_reply_verifier(cred, ctx, task, seq, &iov);
> + if (IS_ERR(g3_buf))
> + goto out_bad;
> + }
> xdr_buf_from_iov(&iov, &verf_buf);
> mic.data = (u8 *)p;
> mic.len = len;
>
> ret = ERR_PTR(-EACCES);
> maj_stat = gss_verify_mic(ctx->gc_gss_ctx, &verf_buf, &mic);
> + kfree(g3_buf);
> if (maj_stat == GSS_S_CONTEXT_EXPIRED)
> clear_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags);
> if (maj_stat) {
>

2017-01-04 21:51:53

by Anna Schumaker

[permalink] [raw]
Subject: Re: [PATCH Version 3 07/16] SUNRPC AUTH_GSS RPCSEC_GSS_CREATE with label payload

Hey Andy,

On 12/23/2016 11:04 AM, [email protected] wrote:
> From: Andy Adamson <[email protected]>
>
> Signed-off-by: Andy Adamson <[email protected]>
> ---
> include/linux/sunrpc/auth_gss.h | 54 ++++++++
> net/sunrpc/auth_gss/auth_gss.c | 282 ++++++++++++++++++++++++++++++++++++++++
> 2 files changed, 336 insertions(+)
>
> diff --git a/include/linux/sunrpc/auth_gss.h b/include/linux/sunrpc/auth_gss.h
> index 4ab63a9..b2a5a61 100644
> --- a/include/linux/sunrpc/auth_gss.h
> +++ b/include/linux/sunrpc/auth_gss.h
> @@ -93,6 +93,60 @@ struct gss_cred {
> unsigned long gc_upcall_timestamp;
> };
>
> +/** GSS3 */
> +enum gss3_type {
> + GSS3_LABEL = 0,
> + GSS3_PRIVS = 1,
> +};
> +
> +struct gss3_chan_binding {
> + u32 cb_len;
> + void *cb_binding;
> +};
> +
> +struct gss3_mp_auth {
> + u32 mp_handle_len;
> + void *mp_handle;
> + u32 *mp_mic_len;
> + void *mp_mic; /* header mic */
> +};
> +
> +struct gss3_label {
> + u32 la_lfs;
> + u32 la_pi;
> + struct xdr_netobj la_label;
> +};
> +
> +struct gss3_privs {
> + char *pr_name;
> + u32 pr_num;
> + void *pr_data;
> +};
> +
> +struct gss3_assertion_u {
> + u32 au_type;
> + union {
> + struct gss3_label au_label;
> + struct gss3_privs au_privs;
> + } u;
> +};
> +
> +struct gss3_create_args {
> + struct gss3_mp_auth *ca_mp_auth;
> + struct gss3_chan_binding *ca_chan_bind;
> + u32 ca_num;
> + struct gss3_assertion_u *ca_assertions;
> +};
> +
> +struct gss3_create_res {
> + u32 cr_hlen;
> + void *cr_handle;
> + struct gss3_mp_auth *cr_mp_auth;
> + struct gss3_chan_binding *cr_chan_bind;
> + u32 cr_num;
> + struct gss3_assertion_u *cr_assertions;
> +};
> +
> #endif /* __KERNEL__ */
> #endif /* _LINUX_SUNRPC_AUTH_GSS_H */
>
> diff --git a/net/sunrpc/auth_gss/auth_gss.c b/net/sunrpc/auth_gss/auth_gss.c
> index d11f421..e39c1af 100644
> --- a/net/sunrpc/auth_gss/auth_gss.c
> +++ b/net/sunrpc/auth_gss/auth_gss.c
> @@ -52,9 +52,12 @@
> #include <linux/sunrpc/gss_api.h>
> #include <asm/uaccess.h>
> #include <linux/hashtable.h>
> +#include <linux/security.h>
>
> #include "../netns.h"
>
> +static int gss3_create_label(struct rpc_cred *cred);
> +
> static const struct rpc_authops authgss_ops;
>
> static const struct rpc_credops gss_credops;
> @@ -145,6 +148,8 @@ struct gss_auth {
> set_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags);
> smp_mb__before_atomic();
> clear_bit(RPCAUTH_CRED_NEW, &cred->cr_flags);
> + /* XXXX make void? check retunr ?? */
> + gss3_create_label(cred);
> }
>
> static const void *
> @@ -1591,6 +1596,283 @@ static int gss_cred_is_negative_entry(struct rpc_cred *cred)
> return 0;
> }
>
> +/**
> + * GSS3_createargs_maxsz and GSS3_createres_maxsz
> + * include no rgss3_assertion_u payload.
> + *
> + * GSS3_createargs_maxsz is currently for one label.
> + */
> +#define GSS3_createargs_maxsz (1 /* empty ca_mp_auth */ + \
> + 1 /* empty ca_chan_bind */ + \
> + 1 /* ca_num */ + \
> + 1 /* au_type */)
> +#define GSS3_labelargs_maxsz (1 /* la_lfs */ + \
> + 1 /* la_pi */ + \
> + 1 /* la_label.len */ + \
> + XDR_QUADLEN(1024) /* la_label.data */)
> +#define GSS3_createres_maxsz (1 /* cr_hlen */ + \
> + XDR_QUADLEN(1024) /* cr_handle*/ + \
> + GSS3_createargs_maxsz)
> +#define GSS3_labelres_maxsz GSS3_labelargs_maxsz
> +
> +#define GSS3_listargs_maxsz (1) /* dummy */
> +#define GSS3_listres_maxsz (1) /* dummy */
> +
> +static void
> +gss3_enc_label(struct rpc_rqst *req, struct xdr_stream *xdr,
> + const struct gss3_create_args *g3ca)
> +{
> + struct gss3_label *gl;
> + __be32 *p;
> +
> + gl = &g3ca->ca_assertions[0].u.au_label;
> +
> + dprintk("RPC: %5u encoding GSSv3 label %s:%d\n", req->rq_task->tk_pid,
> + (char *)gl->la_label.data, gl->la_label.len);
> +
> + p = xdr_reserve_space(xdr, GSS3_labelargs_maxsz << 2);
> + *p++ = cpu_to_be32(0); /* la_lfs */
> + *p++ = cpu_to_be32(0); /* la_pi */
> + p = xdr_encode_netobj(p, &gl->la_label);
> +}
> +
> +static void
> +gss3_enc_create(struct rpc_rqst *req, struct xdr_stream *xdr,
> + const struct gss3_create_args *g3ca)
> +{
> + u32 type;
> + __be32 *p;
> +
> + p = xdr_reserve_space(xdr, GSS3_createargs_maxsz << 2);
> + *p++ = cpu_to_be32(0); /* NULL ca_mp_auth */
> + *p++ = cpu_to_be32(0); /* NULL ca_chan_bind */
> + *p++ = cpu_to_be32(g3ca->ca_num);
> + /* the payload is a gss3_label */
> + type = cpu_to_be32(g3ca->ca_assertions->au_type);
> + *p++ = type;
> + switch (type) {
> + case GSS3_LABEL:
> + gss3_enc_label(req, xdr, g3ca);
> + break;
> + case GSS3_PRIVS:
> + default:
> + /* drop through to return */
> + pr_warn("RPC Unsupported gss3 create assertion %d\n", type);
> + }
> +}
> +
> +static int
> +gss3_dec_label(struct rpc_rqst *req, struct xdr_stream *xdr,
> + struct gss3_create_res *g3cr)
> +{
> + struct gss3_label *gl;
> + struct gss3_assertion_u *g3a;
> + __be32 *p;
> +
> + g3a = kzalloc(sizeof(*g3a), GFP_KERNEL);
> + if (!g3a)
> + goto out_err;
> +
> + g3a->au_type = GSS3_LABEL;
> + gl = &g3a->u.au_label;
> +
> + p = xdr_inline_decode(xdr, 12);
> + if (unlikely(!p))
> + goto out_overflow;
> +
> + gl->la_lfs = be32_to_cpup(p++);
> + gl->la_pi = be32_to_cpup(p++);
> + gl->la_label.len = be32_to_cpup(p++);
> +
> + p = xdr_inline_decode(xdr, gl->la_label.len);
> + if (unlikely(!p))
> + goto out_overflow;
> +
> + gl->la_label.data = kmemdup(p, gl->la_label.len, GFP_KERNEL);
> + if (!gl->la_label.data)
> + goto out_free_assert;
> + /**
> + * Note: need to store the assertion with the child handle
> + * in the gss context cache.
> + */
> +
> + g3cr->cr_assertions = g3a;
> +
> + return 0;
> +
> +out_free_assert:
> + kfree(g3a);
> +out_err:
> + return -EIO;
> +out_overflow:
> + pr_warn("RPC %s End of receive buffer. Remaining len: %tu words.\n",
> + __func__, xdr->end - xdr->p);
> + goto out_free_assert;
> +}
> +
> +static int
> +gss3_dec_create(struct rpc_rqst *req, struct xdr_stream *xdr,
> + struct gss3_create_res *g3cr)
> +{
> + u32 dummy, type;
> + __be32 *p;
> +
> + p = xdr_inline_decode(xdr, 4);
> + if (unlikely(!p))
> + goto out_overflow;
> + g3cr->cr_hlen = be32_to_cpup(p++);
> +
> + p = xdr_inline_decode(xdr, g3cr->cr_hlen + 16);
> + if (unlikely(!p))
> + goto out_overflow;
> +
> + g3cr->cr_handle = kmemdup(p, g3cr->cr_hlen, GFP_KERNEL);
> + if (!g3cr->cr_handle)
> + goto out_err;
> +
> + p += XDR_QUADLEN(g3cr->cr_hlen);
> +
> + /* cr_mp_auth: not supported */
> + dummy = be32_to_cpup(p++);
> + if (dummy != 0) {
> + pr_warn("RPC gss3 create cr_mp_auth not supported\n");
> + goto out_free_handle;
> + }
> +
> + /* cr_chan_bind: not supported */
> + dummy = be32_to_cpup(p++);
> + if (dummy != 0) {
> + pr_warn("RPC gss3 create cr_chan_bind not supported\n");
> + goto out_free_handle;
> + }
> +
> + /* Support one label assertion */
> + g3cr->cr_num = be32_to_cpup(p++);
> + if (g3cr->cr_num != 1) {
> + pr_warn("RPC gss3 multiple assertions %d unspported\n",
> + g3cr->cr_num);
> + goto out_free_handle;
> + }
> +
> + /* au_type */
> + type = be32_to_cpup(p++);
> + switch (type) {
> + case GSS3_LABEL:
> + if (gss3_dec_label(req, xdr, g3cr) != 0)
> + goto out_free_handle;
> + break;
> + case GSS3_PRIVS:
> + default:
> + pr_warn("RPC Unsupported gss3 create assertion %d\n", type);
> + goto out_free_handle;
> + }
> + return 0;
> +
> +out_free_handle:
> + kfree(g3cr->cr_handle);
> +out_err:
> + return -EIO;
> +out_overflow:
> + pr_warn("RPC %s End of receive buffer. Remaining len: %tu words.\n",
> + __func__, xdr->end - xdr->p);
> + goto out_err;
> +}
> +
> +#define RPC_PROC_NULL 0
> +
> +struct rpc_procinfo gss3_label_assertion[] = {
> + [RPC_GSS_PROC_CREATE] = {
> + .p_proc = RPC_PROC_NULL,
> + .p_encode = (kxdreproc_t)gss3_enc_create,
> + .p_decode = (kxdrdproc_t)gss3_dec_create,
> + .p_arglen = GSS3_createargs_maxsz + GSS3_labelargs_maxsz,
> + .p_replen = GSS3_createres_maxsz + GSS3_labelres_maxsz,
> + .p_statidx = RPC_GSS_PROC_CREATE,
> + .p_timer = 0,
> + .p_name = "GSS_CREATE_LABEL",
> + },
> +};
> +
> +/** GSSv3 RPC_GSS_PROC_CREATE operation
> + * Note: MUST use integrity or privacy security service.
> + * First pass; use rpc_gss_svc_none.
> + *
> + * scontext: returned from security_sid_to_scontext(u32 sid, char **scontext)
> + */
> +static int
> +gss3_create_label(struct rpc_cred *cred)
> +{
> + struct gss_auth *gss_auth = container_of(cred->cr_auth, struct gss_auth,
> + rpc_auth);
> + struct gss_cl_ctx *ctx = gss_cred_get_ctx(cred);
> + struct rpc_task *task;
> + struct gss3_create_res cres = {
> + .cr_mp_auth = 0,
> + };
> + struct gss3_create_args *cargs = NULL;
> + struct gss3_label *gl;
> + int ret = -EINVAL;
> +
> + if (!ctx)
> + goto out;
> + /**
> + * Take a reference to ensure the cred sticks around as we create
> + * a child context
> + * XXX does grabbing a reference to the context (gss_cred_get_ctx)
> + * also keep the cred from being removed?
> + * XXX do we need to keep this cred reference until the child context
> + * handle and associated assertion is removed?
> + */
> + get_rpccred(cred);
> +
> + if (ctx->gc_v != RPC_GSS3_VERSION || !selinux_is_enabled())
> + goto out_err;
> +
> + ret = -ENOMEM;
> + cargs = kzalloc(sizeof(*cargs), GFP_NOFS);
> + if (!cargs)
> + goto out_err;
> + cargs->ca_assertions = kzalloc(sizeof(*cargs->ca_assertions), GFP_NOFS);
> + if (!cargs->ca_assertions)
> + goto out_free_args;
> +
> + /* NOTE: not setting la_lfs, la_pi. Do we even need them? */
> + cargs->ca_num = 1;
> + cargs->ca_assertions->au_type = GSS3_LABEL;
> + gl = &cargs->ca_assertions->u.au_label;
> +
> + ret = security_current_sid_to_context((char **)&gl->la_label.data,
> + &gl->la_label.len);

This function only exists if CONFIG_SECURITY_SELINUX=y, as the kbuild bot pointed out. What is the right thing to do here if selinux isn't compiled in?

Also, do you need to check the value of "ret" here before going on?

Thanks,
Anna

> +
> + ctx->gc_proc = RPC_GSS_PROC_CREATE;
> + cred->cr_ops = &gss_credops;
> +
> + /* Want a sync rpc call */
> + task = rpc_call_null_payload(gss_auth->client, cred,
> + 0, cargs, &cres,
> + &gss3_label_assertion[RPC_GSS_PROC_CREATE]);
> + if (IS_ERR(task)) {
> + ret = PTR_ERR(task);
> + goto out_free_assert;
> + }
> + if (task->tk_status != 0) {
> + ret = task->tk_status;
> + goto out_free_assert;
> + }
> + rpc_put_task(task);
> +
> +out_free_assert:
> + kfree(cargs->ca_assertions);
> +out_free_args:
> + kfree(cargs);
> +out_err:
> + ctx->gc_proc = RPC_GSS_PROC_DATA;
> + gss_put_ctx(ctx);
> + put_rpccred(cred);
> +out:
> + return ret;
> +}
> +
> /*
> * Refresh credentials. XXX - finish
> */
>