[Version,5,08/17] SUNRPC AUTH_GSS RPCSEC_GSS_CREATE with label payload
diff mbox

Message ID 20170224221953.5502-9-andros@netapp.com
State New
Headers show

Commit Message

Andy Adamson Feb. 24, 2017, 10:19 p.m. UTC
From: Andy Adamson <andros@netapp.com>

Signed-off-by: Andy Adamson <andros@netapp.com>
---
 net/sunrpc/auth_gss/auth_gss.c | 140 ++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 138 insertions(+), 2 deletions(-)

Comments

Schumaker, Anna Feb. 27, 2017, 9:47 p.m. UTC | #1
Hi Andy,

On 02/24/2017 05:19 PM, andros@netapp.com wrote:
> From: Andy Adamson <andros@netapp.com>
> 
> Signed-off-by: Andy Adamson <andros@netapp.com>
> ---
>  net/sunrpc/auth_gss/auth_gss.c | 140 ++++++++++++++++++++++++++++++++++++++++-
>  1 file changed, 138 insertions(+), 2 deletions(-)
> 
> diff --git a/net/sunrpc/auth_gss/auth_gss.c b/net/sunrpc/auth_gss/auth_gss.c
> index 6ffb16d..98971cf 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 <linux/uaccess.h>
>  #include <linux/hashtable.h>
> +#include <linux/security.h>
>  
>  #include "../netns.h"
>  
> +static int gss3_create_label(struct rpc_cred *cred, int gss_vers);
> +
>  static const struct rpc_authops authgss_ops;
>  
>  static const struct rpc_credops gss_credops;
> @@ -128,6 +131,20 @@ gss_put_ctx(struct gss_cl_ctx *ctx)
>  		gss_free_ctx(ctx);
>  }
>  
> +/* gss3_label_enabled:
> + * Called to determine if Full Mode Mandatory Access Control (MAC)
> + * over a GSS connection is desired.
> + *
> + * Note:
> + * Currently Full Mode MAC is assuemed if SeLinux is enabled and
> + * RPCSEC_GSS version 3 is in use.
> + */
> +static inline bool
> +gss3_label_assertion_is_enabled(u32 rpcsec_version)
> +{
> +	return (rpcsec_version == RPC_GSS3_VERSION && selinux_is_enabled());
> +}
> +
>  /* gss_cred_set_ctx:
>   * called by gss_upcall_callback and gss_create_upcall in order
>   * to set the gss context. The actual exchange of an old context
> @@ -145,6 +162,7 @@ gss_cred_set_ctx(struct rpc_cred *cred, struct gss_cl_ctx *ctx)
>  	set_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags);
>  	smp_mb__before_atomic();
>  	clear_bit(RPCAUTH_CRED_NEW, &cred->cr_flags);
> +	gss3_create_label(cred, ctx->gc_v);
>  }
>  
>  static const void *
> @@ -1602,6 +1620,75 @@ static int gss_cred_is_negative_entry(struct rpc_cred *cred)
>  #define GSS3_createres_maxsz   (1 /* cr_hlen */ + \
>  				XDR_QUADLEN(1024) /* cr_handle*/ + \
>  				GSS3_createargs_maxsz)
> +#define GSS3_labelargs_maxsz	(1 /* la_lfs */ + \
> +				 1 /* la_pi */ + \
> +				 1 /* la_label.len */ + \
> +				 XDR_QUADLEN(1024) /* la_label.data */)
> +#define GSS3_labelres_maxsz	GSS3_labelargs_maxsz
> +
> +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 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;
> +
> +	/* Used to store assertion in parent gss_cl_ctx */
> +	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;
> +
> +	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 void
>  gss3_enc_create(struct rpc_rqst *req, struct xdr_stream *xdr,
> @@ -1618,6 +1705,8 @@ gss3_enc_create(struct rpc_rqst *req, struct xdr_stream *xdr,
>  	*p++ = type;
>  	switch (type) {
>  	case GSS3_LABEL:
> +		gss3_enc_label(req, xdr, g3ca);
> +		break;
>  	case GSS3_PRIVS:
>  	default:
>  		/* drop through to return */
> @@ -1673,6 +1762,9 @@ gss3_dec_create(struct rpc_rqst *req, struct xdr_stream *xdr,
>  	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);
> @@ -1692,13 +1784,16 @@ gss3_dec_create(struct rpc_rqst *req, struct xdr_stream *xdr,
>  
>  #define RPC_PROC_NULL 0
>  
> +#define GSS3_create_args_max	(GSS3_createargs_maxsz + GSS3_labelargs_maxsz)
> +#define GSS3_create_res_max	(GSS3_createres_maxsz + GSS3_labelres_maxsz)
> +
>  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,
> -		.p_replen	= GSS3_createres_maxsz,
> +		.p_arglen	= GSS3_create_args_max,
> +		.p_replen	= GSS3_create_res_max,
>  		.p_statidx	= RPC_GSS_PROC_CREATE,
>  		.p_timer	= 0,
>  		.p_name		= "GSS_PROC_CREATE",
> @@ -1773,6 +1868,47 @@ gss3_proc_create(struct rpc_cred *cred, struct gss3_assertion_u *asserts,
>  	return ret;
>  }
>  
> +/**
> + * GSS3 Label Assertion
> + *
> + * Support one label assertion
> + *
> + * XXX Return not checked. Should we fail nfs requests if
> + * a label fails to be created? I think the server enforcing
> + * Full Mode MAC will reject an NFS request that does not use
> + * a GSS3 (child) context with the correct label.
> + */
> +static int
> +gss3_create_label(struct rpc_cred *cred, int gss_vers)
> +{
> +	struct gss3_assertion_u *asserts;
> +	struct gss3_label *gl;
> +	int ret;
> +
> +	if (!gss3_label_assertion_is_enabled(gss_vers))
> +		return -EINVAL;
> +
> +	asserts = kzalloc(sizeof(*asserts), GFP_NOFS);
> +	if (!asserts)
> +		return -ENOMEM;
> +
> +	/* NOTE: not setting la_lfs, la_pi. Do we even need them? */
> +	asserts->au_type = GSS3_LABEL;
> +	gl = &asserts->u.au_label;
> +
> +	ret = -EINVAL;
> +	ret = security_current_sid_to_context((char **)&gl->la_label.data,
> +					      &gl->la_label.len);

I think you only need to assign "ret" once here

Thanks,
Anna

> +	if (ret)
> +		goto out_free_asserts;
> +
> +	return gss3_proc_create(cred, asserts, 1);
> +
> +out_free_asserts:
> +	kfree(asserts);
> +	return ret;
> +}
> +
>  /*
>  * Refresh credentials. XXX - finish
>  */
> 
--
To unsubscribe from this list: send the line "unsubscribe linux-nfs" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
J. Bruce Fields March 10, 2017, 5:31 p.m. UTC | #2
On Fri, Feb 24, 2017 at 05:19:44PM -0500, andros@netapp.com wrote:
> From: Andy Adamson <andros@netapp.com>
> 
> Signed-off-by: Andy Adamson <andros@netapp.com>
> ---
>  net/sunrpc/auth_gss/auth_gss.c | 140 ++++++++++++++++++++++++++++++++++++++++-
>  1 file changed, 138 insertions(+), 2 deletions(-)
> 
> diff --git a/net/sunrpc/auth_gss/auth_gss.c b/net/sunrpc/auth_gss/auth_gss.c
> index 6ffb16d..98971cf 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 <linux/uaccess.h>
>  #include <linux/hashtable.h>
> +#include <linux/security.h>
>  
>  #include "../netns.h"
>  
> +static int gss3_create_label(struct rpc_cred *cred, int gss_vers);
> +
>  static const struct rpc_authops authgss_ops;
>  
>  static const struct rpc_credops gss_credops;
> @@ -128,6 +131,20 @@ gss_put_ctx(struct gss_cl_ctx *ctx)
>  		gss_free_ctx(ctx);
>  }
>  
> +/* gss3_label_enabled:
> + * Called to determine if Full Mode Mandatory Access Control (MAC)
> + * over a GSS connection is desired.
> + *
> + * Note:
> + * Currently Full Mode MAC is assuemed if SeLinux is enabled and
> + * RPCSEC_GSS version 3 is in use.

Eventually I guess we may want support for GSSv3-enabled copy without
full MAC, so we'll want some way to configure this.

Also, do I understand right that currently it's gssd that decides
whether to enable GSSv3, by passing down the new version number?  How
will the user choose whether to enable GSSv3 or not?

Should that be a mount option?

The mount option could then cause some "use_gss3" flag to be added in
rpc_pipefs, in an info file or whatever.  I think that'd also provide
backwards compatibility (if you're not already handling that some other
way), since gssd could use the presence of that flag to decide whether
the kernel was new enough to support the new downcall.

--b.

> + */
> +static inline bool
> +gss3_label_assertion_is_enabled(u32 rpcsec_version)
> +{
> +	return (rpcsec_version == RPC_GSS3_VERSION && selinux_is_enabled());
> +}
> +
>  /* gss_cred_set_ctx:
>   * called by gss_upcall_callback and gss_create_upcall in order
>   * to set the gss context. The actual exchange of an old context
> @@ -145,6 +162,7 @@ gss_cred_set_ctx(struct rpc_cred *cred, struct gss_cl_ctx *ctx)
>  	set_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags);
>  	smp_mb__before_atomic();
>  	clear_bit(RPCAUTH_CRED_NEW, &cred->cr_flags);
> +	gss3_create_label(cred, ctx->gc_v);
>  }
>  
>  static const void *
> @@ -1602,6 +1620,75 @@ static int gss_cred_is_negative_entry(struct rpc_cred *cred)
>  #define GSS3_createres_maxsz   (1 /* cr_hlen */ + \
>  				XDR_QUADLEN(1024) /* cr_handle*/ + \
>  				GSS3_createargs_maxsz)
> +#define GSS3_labelargs_maxsz	(1 /* la_lfs */ + \
> +				 1 /* la_pi */ + \
> +				 1 /* la_label.len */ + \
> +				 XDR_QUADLEN(1024) /* la_label.data */)
> +#define GSS3_labelres_maxsz	GSS3_labelargs_maxsz
> +
> +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 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;
> +
> +	/* Used to store assertion in parent gss_cl_ctx */
> +	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;
> +
> +	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 void
>  gss3_enc_create(struct rpc_rqst *req, struct xdr_stream *xdr,
> @@ -1618,6 +1705,8 @@ gss3_enc_create(struct rpc_rqst *req, struct xdr_stream *xdr,
>  	*p++ = type;
>  	switch (type) {
>  	case GSS3_LABEL:
> +		gss3_enc_label(req, xdr, g3ca);
> +		break;
>  	case GSS3_PRIVS:
>  	default:
>  		/* drop through to return */
> @@ -1673,6 +1762,9 @@ gss3_dec_create(struct rpc_rqst *req, struct xdr_stream *xdr,
>  	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);
> @@ -1692,13 +1784,16 @@ gss3_dec_create(struct rpc_rqst *req, struct xdr_stream *xdr,
>  
>  #define RPC_PROC_NULL 0
>  
> +#define GSS3_create_args_max	(GSS3_createargs_maxsz + GSS3_labelargs_maxsz)
> +#define GSS3_create_res_max	(GSS3_createres_maxsz + GSS3_labelres_maxsz)
> +
>  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,
> -		.p_replen	= GSS3_createres_maxsz,
> +		.p_arglen	= GSS3_create_args_max,
> +		.p_replen	= GSS3_create_res_max,
>  		.p_statidx	= RPC_GSS_PROC_CREATE,
>  		.p_timer	= 0,
>  		.p_name		= "GSS_PROC_CREATE",
> @@ -1773,6 +1868,47 @@ gss3_proc_create(struct rpc_cred *cred, struct gss3_assertion_u *asserts,
>  	return ret;
>  }
>  
> +/**
> + * GSS3 Label Assertion
> + *
> + * Support one label assertion
> + *
> + * XXX Return not checked. Should we fail nfs requests if
> + * a label fails to be created? I think the server enforcing
> + * Full Mode MAC will reject an NFS request that does not use
> + * a GSS3 (child) context with the correct label.
> + */
> +static int
> +gss3_create_label(struct rpc_cred *cred, int gss_vers)
> +{
> +	struct gss3_assertion_u *asserts;
> +	struct gss3_label *gl;
> +	int ret;
> +
> +	if (!gss3_label_assertion_is_enabled(gss_vers))
> +		return -EINVAL;
> +
> +	asserts = kzalloc(sizeof(*asserts), GFP_NOFS);
> +	if (!asserts)
> +		return -ENOMEM;
> +
> +	/* NOTE: not setting la_lfs, la_pi. Do we even need them? */
> +	asserts->au_type = GSS3_LABEL;
> +	gl = &asserts->u.au_label;
> +
> +	ret = -EINVAL;
> +	ret = security_current_sid_to_context((char **)&gl->la_label.data,
> +					      &gl->la_label.len);
> +	if (ret)
> +		goto out_free_asserts;
> +
> +	return gss3_proc_create(cred, asserts, 1);
> +
> +out_free_asserts:
> +	kfree(asserts);
> +	return ret;
> +}
> +
>  /*
>  * Refresh credentials. XXX - finish
>  */
> -- 
> 2.9.3
--
To unsubscribe from this list: send the line "unsubscribe linux-nfs" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
J. Bruce Fields March 10, 2017, 5:33 p.m. UTC | #3
On Fri, Mar 10, 2017 at 12:31:07PM -0500, J. Bruce Fields wrote:
> On Fri, Feb 24, 2017 at 05:19:44PM -0500, andros@netapp.com wrote:
> > From: Andy Adamson <andros@netapp.com>
> > 
> > Signed-off-by: Andy Adamson <andros@netapp.com>
> > ---
> >  net/sunrpc/auth_gss/auth_gss.c | 140 ++++++++++++++++++++++++++++++++++++++++-
> >  1 file changed, 138 insertions(+), 2 deletions(-)
> > 
> > diff --git a/net/sunrpc/auth_gss/auth_gss.c b/net/sunrpc/auth_gss/auth_gss.c
> > index 6ffb16d..98971cf 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 <linux/uaccess.h>
> >  #include <linux/hashtable.h>
> > +#include <linux/security.h>
> >  
> >  #include "../netns.h"
> >  
> > +static int gss3_create_label(struct rpc_cred *cred, int gss_vers);
> > +
> >  static const struct rpc_authops authgss_ops;
> >  
> >  static const struct rpc_credops gss_credops;
> > @@ -128,6 +131,20 @@ gss_put_ctx(struct gss_cl_ctx *ctx)
> >  		gss_free_ctx(ctx);
> >  }
> >  
> > +/* gss3_label_enabled:
> > + * Called to determine if Full Mode Mandatory Access Control (MAC)
> > + * over a GSS connection is desired.
> > + *
> > + * Note:
> > + * Currently Full Mode MAC is assuemed if SeLinux is enabled and
> > + * RPCSEC_GSS version 3 is in use.
> 
> Eventually I guess we may want support for GSSv3-enabled copy without
> full MAC, so we'll want some way to configure this.
> 
> Also, do I understand right that currently it's gssd that decides
> whether to enable GSSv3, by passing down the new version number?  How
> will the user choose whether to enable GSSv3 or not?
> 
> Should that be a mount option?
> 
> The mount option could then cause some "use_gss3" flag to be added in
> rpc_pipefs, in an info file or whatever.  I think that'd also provide
> backwards compatibility (if you're not already handling that some other
> way), since gssd could use the presence of that flag to decide whether
> the kernel was new enough to support the new downcall.

By the way, I didn't notice on a quick skim of the rest of the patches:
what happens if the server doesn't support GSSv3?  Does gssd negotiate
down to GSSv2 automatically?

How do you configure this on the server?  (It probably needs to be
possible to turn of mac labeling on the server.)

--b.
--
To unsubscribe from this list: send the line "unsubscribe linux-nfs" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Patch
diff mbox

diff --git a/net/sunrpc/auth_gss/auth_gss.c b/net/sunrpc/auth_gss/auth_gss.c
index 6ffb16d..98971cf 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 <linux/uaccess.h>
 #include <linux/hashtable.h>
+#include <linux/security.h>
 
 #include "../netns.h"
 
+static int gss3_create_label(struct rpc_cred *cred, int gss_vers);
+
 static const struct rpc_authops authgss_ops;
 
 static const struct rpc_credops gss_credops;
@@ -128,6 +131,20 @@  gss_put_ctx(struct gss_cl_ctx *ctx)
 		gss_free_ctx(ctx);
 }
 
+/* gss3_label_enabled:
+ * Called to determine if Full Mode Mandatory Access Control (MAC)
+ * over a GSS connection is desired.
+ *
+ * Note:
+ * Currently Full Mode MAC is assuemed if SeLinux is enabled and
+ * RPCSEC_GSS version 3 is in use.
+ */
+static inline bool
+gss3_label_assertion_is_enabled(u32 rpcsec_version)
+{
+	return (rpcsec_version == RPC_GSS3_VERSION && selinux_is_enabled());
+}
+
 /* gss_cred_set_ctx:
  * called by gss_upcall_callback and gss_create_upcall in order
  * to set the gss context. The actual exchange of an old context
@@ -145,6 +162,7 @@  gss_cred_set_ctx(struct rpc_cred *cred, struct gss_cl_ctx *ctx)
 	set_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags);
 	smp_mb__before_atomic();
 	clear_bit(RPCAUTH_CRED_NEW, &cred->cr_flags);
+	gss3_create_label(cred, ctx->gc_v);
 }
 
 static const void *
@@ -1602,6 +1620,75 @@  static int gss_cred_is_negative_entry(struct rpc_cred *cred)
 #define GSS3_createres_maxsz   (1 /* cr_hlen */ + \
 				XDR_QUADLEN(1024) /* cr_handle*/ + \
 				GSS3_createargs_maxsz)
+#define GSS3_labelargs_maxsz	(1 /* la_lfs */ + \
+				 1 /* la_pi */ + \
+				 1 /* la_label.len */ + \
+				 XDR_QUADLEN(1024) /* la_label.data */)
+#define GSS3_labelres_maxsz	GSS3_labelargs_maxsz
+
+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 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;
+
+	/* Used to store assertion in parent gss_cl_ctx */
+	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;
+
+	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 void
 gss3_enc_create(struct rpc_rqst *req, struct xdr_stream *xdr,
@@ -1618,6 +1705,8 @@  gss3_enc_create(struct rpc_rqst *req, struct xdr_stream *xdr,
 	*p++ = type;
 	switch (type) {
 	case GSS3_LABEL:
+		gss3_enc_label(req, xdr, g3ca);
+		break;
 	case GSS3_PRIVS:
 	default:
 		/* drop through to return */
@@ -1673,6 +1762,9 @@  gss3_dec_create(struct rpc_rqst *req, struct xdr_stream *xdr,
 	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);
@@ -1692,13 +1784,16 @@  gss3_dec_create(struct rpc_rqst *req, struct xdr_stream *xdr,
 
 #define RPC_PROC_NULL 0
 
+#define GSS3_create_args_max	(GSS3_createargs_maxsz + GSS3_labelargs_maxsz)
+#define GSS3_create_res_max	(GSS3_createres_maxsz + GSS3_labelres_maxsz)
+
 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,
-		.p_replen	= GSS3_createres_maxsz,
+		.p_arglen	= GSS3_create_args_max,
+		.p_replen	= GSS3_create_res_max,
 		.p_statidx	= RPC_GSS_PROC_CREATE,
 		.p_timer	= 0,
 		.p_name		= "GSS_PROC_CREATE",
@@ -1773,6 +1868,47 @@  gss3_proc_create(struct rpc_cred *cred, struct gss3_assertion_u *asserts,
 	return ret;
 }
 
+/**
+ * GSS3 Label Assertion
+ *
+ * Support one label assertion
+ *
+ * XXX Return not checked. Should we fail nfs requests if
+ * a label fails to be created? I think the server enforcing
+ * Full Mode MAC will reject an NFS request that does not use
+ * a GSS3 (child) context with the correct label.
+ */
+static int
+gss3_create_label(struct rpc_cred *cred, int gss_vers)
+{
+	struct gss3_assertion_u *asserts;
+	struct gss3_label *gl;
+	int ret;
+
+	if (!gss3_label_assertion_is_enabled(gss_vers))
+		return -EINVAL;
+
+	asserts = kzalloc(sizeof(*asserts), GFP_NOFS);
+	if (!asserts)
+		return -ENOMEM;
+
+	/* NOTE: not setting la_lfs, la_pi. Do we even need them? */
+	asserts->au_type = GSS3_LABEL;
+	gl = &asserts->u.au_label;
+
+	ret = -EINVAL;
+	ret = security_current_sid_to_context((char **)&gl->la_label.data,
+					      &gl->la_label.len);
+	if (ret)
+		goto out_free_asserts;
+
+	return gss3_proc_create(cred, asserts, 1);
+
+out_free_asserts:
+	kfree(asserts);
+	return ret;
+}
+
 /*
 * Refresh credentials. XXX - finish
 */