diff mbox

SUNRPC: Refactor nfsd4_do_encode_secinfo()

Message ID 20130201220308.13518.82164.stgit@seurat.1015granger.net (mailing list archive)
State New, archived
Headers show

Commit Message

Chuck Lever III Feb. 1, 2013, 10:43 p.m. UTC
Clean up.  This matches a similar API for the client side, and
keeps ULP fingers out the of the GSS mech switch.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
Acked-by: J. Bruce Fields <bfields@redhat.com>
---

Bruce-

This version of the patch follows the existing logic in
nfsd4_do_encode_secinfo(): If the RPC layer can't find GSS info
that matches an export security flavor, it assumes the flavor is
not a GSS pseudoflavor, and simply puts it on the wire.

However, if the below XDR encoding logic is given a legitimate GSS
pseudoflavor but the RPC layer says it does not support that
pseudoflavor for some reason, then we leak GSS pseudoflavor numbers
onto the wire.

I confirmed this happens by blacklisting rpcsec_gss_krb5, then
attempted a client transition from the pseudo-fs to a Kerberos-only
share.  The client received a flavor list containing the Kerberos
pseudoflavor numbers, rather than GSS tuples.

The encoder logic can check that each pseudoflavor is less than
MAXFLAVOR before writing it into the buffer, to prevent this.  But
after "nflavs" is written into the XDR buffer, the encoder can't
skip writing flavor information into the buffer when it discovers
the RPC layer doesn't support that flavor.

Is there some way of writing "nflavs" into the XDR buffer after
the loop that writes the flavor information is complete?

What if "nflavs" turns out to be zero?  RFC 3530bis, 15.33.4 does
not explicitly say what should be done if the array of security
mechanisms for that client and file handle contains zero items.

Or is there a better way to handle this situation?  Also, should we
care about this scenario?


 fs/nfsd/nfs4xdr.c                     |   24 +++++++++++------------
 include/linux/sunrpc/auth.h           |    2 ++
 include/linux/sunrpc/gss_api.h        |    3 +++
 net/sunrpc/auth.c                     |   34 +++++++++++++++++++++++++++++++++
 net/sunrpc/auth_gss/auth_gss.c        |    1 +
 net/sunrpc/auth_gss/gss_mech_switch.c |   34 +++++++++++++++++++++++++++++++--
 6 files changed, 83 insertions(+), 15 deletions(-)


--
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

Comments

J. Bruce Fields Feb. 7, 2013, 3:02 p.m. UTC | #1
On Fri, Feb 01, 2013 at 05:43:44PM -0500, Chuck Lever wrote:
> Clean up.  This matches a similar API for the client side, and
> keeps ULP fingers out the of the GSS mech switch.
> 
> Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
> Acked-by: J. Bruce Fields <bfields@redhat.com>
> ---
> 
> Bruce-
> 
> This version of the patch follows the existing logic in
> nfsd4_do_encode_secinfo(): If the RPC layer can't find GSS info
> that matches an export security flavor, it assumes the flavor is
> not a GSS pseudoflavor, and simply puts it on the wire.
> 
> However, if the below XDR encoding logic is given a legitimate GSS
> pseudoflavor but the RPC layer says it does not support that
> pseudoflavor for some reason, then we leak GSS pseudoflavor numbers
> onto the wire.
> 
> I confirmed this happens by blacklisting rpcsec_gss_krb5, then
> attempted a client transition from the pseudo-fs to a Kerberos-only
> share.  The client received a flavor list containing the Kerberos
> pseudoflavor numbers, rather than GSS tuples.
> 
> The encoder logic can check that each pseudoflavor is less than
> MAXFLAVOR before writing it into the buffer, to prevent this.  But
> after "nflavs" is written into the XDR buffer, the encoder can't
> skip writing flavor information into the buffer when it discovers
> the RPC layer doesn't support that flavor.
> 
> Is there some way of writing "nflavs" into the XDR buffer after
> the loop that writes the flavor information is complete?

Yes, you can save a pointer and then go back and fill that in--see
encode_fattr for an example.

--b.

> 
> What if "nflavs" turns out to be zero?  RFC 3530bis, 15.33.4 does
> not explicitly say what should be done if the array of security
> mechanisms for that client and file handle contains zero items.
> 
> Or is there a better way to handle this situation?  Also, should we
> care about this scenario?
> 
> 
>  fs/nfsd/nfs4xdr.c                     |   24 +++++++++++------------
>  include/linux/sunrpc/auth.h           |    2 ++
>  include/linux/sunrpc/gss_api.h        |    3 +++
>  net/sunrpc/auth.c                     |   34 +++++++++++++++++++++++++++++++++
>  net/sunrpc/auth_gss/auth_gss.c        |    1 +
>  net/sunrpc/auth_gss/gss_mech_switch.c |   34 +++++++++++++++++++++++++++++++--
>  6 files changed, 83 insertions(+), 15 deletions(-)
> 
> diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
> index 0dc1158..424f5a2 100644
> --- a/fs/nfsd/nfs4xdr.c
> +++ b/fs/nfsd/nfs4xdr.c
> @@ -3127,10 +3127,9 @@ nfsd4_encode_rename(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_
>  
>  static __be32
>  nfsd4_do_encode_secinfo(struct nfsd4_compoundres *resp,
> -			 __be32 nfserr,struct svc_export *exp)
> +			 __be32 nfserr, struct svc_export *exp)
>  {
> -	int i = 0;
> -	u32 nflavs;
> +	u32 i, nflavs;
>  	struct exp_flavor_info *flavs;
>  	struct exp_flavor_info def_flavs[2];
>  	__be32 *p;
> @@ -3161,30 +3160,29 @@ nfsd4_do_encode_secinfo(struct nfsd4_compoundres *resp,
>  	WRITE32(nflavs);
>  	ADJUST_ARGS();
>  	for (i = 0; i < nflavs; i++) {
> -		u32 flav = flavs[i].pseudoflavor;
> -		struct gss_api_mech *gm = gss_mech_get_by_pseudoflavor(flav);
> +		struct rpcsec_gss_info info;
>  
> -		if (gm) {
> +		if (rpcauth_get_gssinfo(flavs[i].pseudoflavor, &info) == 0) {
>  			RESERVE_SPACE(4);
>  			WRITE32(RPC_AUTH_GSS);
>  			ADJUST_ARGS();
> -			RESERVE_SPACE(4 + gm->gm_oid.len);
> -			WRITE32(gm->gm_oid.len);
> -			WRITEMEM(gm->gm_oid.data, gm->gm_oid.len);
> +			RESERVE_SPACE(4 + info.oid.len);
> +			WRITE32(info.oid.len);
> +			WRITEMEM(info.oid.data, info.oid.len);
>  			ADJUST_ARGS();
>  			RESERVE_SPACE(4);
> -			WRITE32(0); /* qop */
> +			WRITE32(info.qop);
>  			ADJUST_ARGS();
>  			RESERVE_SPACE(4);
> -			WRITE32(gss_pseudoflavor_to_service(gm, flav));
> +			WRITE32(info.service);
>  			ADJUST_ARGS();
> -			gss_mech_put(gm);
>  		} else {
>  			RESERVE_SPACE(4);
> -			WRITE32(flav);
> +			WRITE32(flavs[i].pseudoflavor);
>  			ADJUST_ARGS();
>  		}
>  	}
> +
>  out:
>  	if (exp)
>  		exp_put(exp);
> diff --git a/include/linux/sunrpc/auth.h b/include/linux/sunrpc/auth.h
> index 476e97c..c34ac17 100644
> --- a/include/linux/sunrpc/auth.h
> +++ b/include/linux/sunrpc/auth.h
> @@ -105,6 +105,7 @@ struct rpc_authops {
>  	void			(*pipes_destroy)(struct rpc_auth *);
>  	int			(*list_pseudoflavors)(rpc_authflavor_t *, int);
>  	rpc_authflavor_t	(*info2flavor)(struct rpcsec_gss_info *);
> +	int			(*flavor2info)(rpc_authflavor_t, struct rpcsec_gss_info *);
>  };
>  
>  struct rpc_credops {
> @@ -140,6 +141,7 @@ int			rpcauth_unregister(const struct rpc_authops *);
>  struct rpc_auth *	rpcauth_create(rpc_authflavor_t, struct rpc_clnt *);
>  void			rpcauth_release(struct rpc_auth *);
>  rpc_authflavor_t	rpcauth_get_pseudoflavor(rpc_authflavor_t, struct rpcsec_gss_info *);
> +int			rpcauth_get_gssinfo(rpc_authflavor_t, struct rpcsec_gss_info *);
>  int			rpcauth_list_flavors(rpc_authflavor_t *, int);
>  struct rpc_cred *	rpcauth_lookup_credcache(struct rpc_auth *, struct auth_cred *, int);
>  void			rpcauth_init_cred(struct rpc_cred *, const struct auth_cred *, struct rpc_auth *, const struct rpc_credops *);
> diff --git a/include/linux/sunrpc/gss_api.h b/include/linux/sunrpc/gss_api.h
> index e8299b2..dc1e51b 100644
> --- a/include/linux/sunrpc/gss_api.h
> +++ b/include/linux/sunrpc/gss_api.h
> @@ -132,6 +132,9 @@ void gss_mech_unregister(struct gss_api_mech *);
>  /* Given a GSS security tuple, look up a pseudoflavor */
>  rpc_authflavor_t gss_mech_info2flavor(struct rpcsec_gss_info *);
>  
> +/* Given a pseudoflavor, look up a GSS security tuple */
> +int gss_mech_flavor2info(rpc_authflavor_t, struct rpcsec_gss_info *);
> +
>  /* Returns a reference to a mechanism, given a name like "krb5" etc. */
>  struct gss_api_mech *gss_mech_get_by_name(const char *);
>  
> diff --git a/net/sunrpc/auth.c b/net/sunrpc/auth.c
> index ec18827..4c551f9 100644
> --- a/net/sunrpc/auth.c
> +++ b/net/sunrpc/auth.c
> @@ -158,6 +158,40 @@ rpcauth_get_pseudoflavor(rpc_authflavor_t flavor, struct rpcsec_gss_info *info)
>  EXPORT_SYMBOL_GPL(rpcauth_get_pseudoflavor);
>  
>  /**
> + * rpcauth_get_gssinfo - find GSS tuple matching a GSS pseudoflavor
> + * @pseudoflavor: GSS pseudoflavor to match
> + * @info: rpcsec_gss_info structure to fill in
> + *
> + * Returns zero and fills in "info" if pseudoflavor matches a
> + * supported mechanism.
> + */
> +int
> +rpcauth_get_gssinfo(rpc_authflavor_t pseudoflavor, struct rpcsec_gss_info *info)
> +{
> +	rpc_authflavor_t flavor = pseudoflavor_to_flavor(pseudoflavor);
> +	const struct rpc_authops *ops;
> +	int result;
> +
> +	if ((ops = auth_flavors[flavor]) == NULL)
> +		request_module("rpc-auth-%u", flavor);
> +	spin_lock(&rpc_authflavor_lock);
> +	ops = auth_flavors[flavor];
> +	if (ops == NULL || !try_module_get(ops->owner)) {
> +		spin_unlock(&rpc_authflavor_lock);
> +		return -ENOENT;
> +	}
> +	spin_unlock(&rpc_authflavor_lock);
> +
> +	result = -ENOENT;
> +	if (ops->flavor2info != NULL)
> +		result = ops->flavor2info(pseudoflavor, info);
> +
> +	module_put(ops->owner);
> +	return result;
> +}
> +EXPORT_SYMBOL_GPL(rpcauth_get_gssinfo);
> +
> +/**
>   * rpcauth_list_flavors - discover registered flavors and pseudoflavors
>   * @array: array to fill in
>   * @size: size of "array"
> diff --git a/net/sunrpc/auth_gss/auth_gss.c b/net/sunrpc/auth_gss/auth_gss.c
> index 4522297..ade24b2 100644
> --- a/net/sunrpc/auth_gss/auth_gss.c
> +++ b/net/sunrpc/auth_gss/auth_gss.c
> @@ -1630,6 +1630,7 @@ static const struct rpc_authops authgss_ops = {
>  	.pipes_destroy	= gss_pipes_dentries_destroy,
>  	.list_pseudoflavors = gss_mech_list_pseudoflavors,
>  	.info2flavor	= gss_mech_info2flavor,
> +	.flavor2info	= gss_mech_flavor2info,
>  };
>  
>  static const struct rpc_credops gss_credops = {
> diff --git a/net/sunrpc/auth_gss/gss_mech_switch.c b/net/sunrpc/auth_gss/gss_mech_switch.c
> index c8da89e..a8d6ee5 100644
> --- a/net/sunrpc/auth_gss/gss_mech_switch.c
> +++ b/net/sunrpc/auth_gss/gss_mech_switch.c
> @@ -240,8 +240,6 @@ gss_mech_get_by_pseudoflavor(u32 pseudoflavor)
>  	return gm;
>  }
>  
> -EXPORT_SYMBOL_GPL(gss_mech_get_by_pseudoflavor);
> -
>  /**
>   * gss_mech_list_pseudoflavors - Discover registered GSS pseudoflavors
>   * @array: array to fill in
> @@ -314,6 +312,38 @@ rpc_authflavor_t gss_mech_info2flavor(struct rpcsec_gss_info *info)
>  	return pseudoflavor;
>  }
>  
> +/**
> + * gss_mech_flavor2info - look up a GSS tuple for a given pseudoflavor
> + * @pseudoflavor: GSS pseudoflavor to match
> + * @info: rpcsec_gss_info structure to fill in
> + *
> + * Returns zero and fills in "info" if pseudoflavor matches a
> + * supported mechanism.  Otherwise a negative errno is returned.
> + */
> +int gss_mech_flavor2info(rpc_authflavor_t pseudoflavor, struct rpcsec_gss_info *info)
> +{
> +	struct gss_api_mech *gm;
> +	int i;
> +
> +	gm = gss_mech_get_by_pseudoflavor(pseudoflavor);
> +	if (gm == NULL)
> +		return -ENOENT;
> +
> +	for (i = 0; i < gm->gm_pf_num; i++) {
> +		if (gm->gm_pfs[i].pseudoflavor == pseudoflavor) {
> +			memcpy(info->oid.data, gm->gm_oid.data, gm->gm_oid.len);
> +			info->oid.len = gm->gm_oid.len;
> +			info->qop = gm->gm_pfs[i].qop;
> +			info->service = gm->gm_pfs[i].service;
> +			gss_mech_put(gm);
> +			return 0;
> +		}
> +	}
> +
> +	gss_mech_put(gm);
> +	return -ENOENT;
> +}
> +
>  u32
>  gss_pseudoflavor_to_service(struct gss_api_mech *gm, u32 pseudoflavor)
>  {
> 
> --
> 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
--
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
Chuck Lever III Feb. 7, 2013, 3:58 p.m. UTC | #2
On Feb 7, 2013, at 10:02 AM, J. Bruce Fields <bfields@fieldses.org> wrote:

> On Fri, Feb 01, 2013 at 05:43:44PM -0500, Chuck Lever wrote:
>> Clean up.  This matches a similar API for the client side, and
>> keeps ULP fingers out the of the GSS mech switch.
>> 
>> Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
>> Acked-by: J. Bruce Fields <bfields@redhat.com>
>> ---
>> 
>> Bruce-
>> 
>> This version of the patch follows the existing logic in
>> nfsd4_do_encode_secinfo(): If the RPC layer can't find GSS info
>> that matches an export security flavor, it assumes the flavor is
>> not a GSS pseudoflavor, and simply puts it on the wire.
>> 
>> However, if the below XDR encoding logic is given a legitimate GSS
>> pseudoflavor but the RPC layer says it does not support that
>> pseudoflavor for some reason, then we leak GSS pseudoflavor numbers
>> onto the wire.
>> 
>> I confirmed this happens by blacklisting rpcsec_gss_krb5, then
>> attempted a client transition from the pseudo-fs to a Kerberos-only
>> share.  The client received a flavor list containing the Kerberos
>> pseudoflavor numbers, rather than GSS tuples.
>> 
>> The encoder logic can check that each pseudoflavor is less than
>> MAXFLAVOR before writing it into the buffer, to prevent this.  But
>> after "nflavs" is written into the XDR buffer, the encoder can't
>> skip writing flavor information into the buffer when it discovers
>> the RPC layer doesn't support that flavor.
>> 
>> Is there some way of writing "nflavs" into the XDR buffer after
>> the loop that writes the flavor information is complete?
> 
> Yes, you can save a pointer and then go back and fill that in--see
> encode_fattr for an example.

Thanks, I will submit an additional patch that describes this issue and fixes it.

I asked David Noveck, as one of the authors of RFC 3530, whether an NFS server should return a zero-length flavor list or an error if SECINFO can't find any flavors a client is allowed to use. His opinion was to return NFS4_OK and a zero-length flavor list.


> 
> --b.
> 
>> 
>> What if "nflavs" turns out to be zero?  RFC 3530bis, 15.33.4 does
>> not explicitly say what should be done if the array of security
>> mechanisms for that client and file handle contains zero items.
>> 
>> Or is there a better way to handle this situation?  Also, should we
>> care about this scenario?
>> 
>> 
>> fs/nfsd/nfs4xdr.c                     |   24 +++++++++++------------
>> include/linux/sunrpc/auth.h           |    2 ++
>> include/linux/sunrpc/gss_api.h        |    3 +++
>> net/sunrpc/auth.c                     |   34 +++++++++++++++++++++++++++++++++
>> net/sunrpc/auth_gss/auth_gss.c        |    1 +
>> net/sunrpc/auth_gss/gss_mech_switch.c |   34 +++++++++++++++++++++++++++++++--
>> 6 files changed, 83 insertions(+), 15 deletions(-)
>> 
>> diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
>> index 0dc1158..424f5a2 100644
>> --- a/fs/nfsd/nfs4xdr.c
>> +++ b/fs/nfsd/nfs4xdr.c
>> @@ -3127,10 +3127,9 @@ nfsd4_encode_rename(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_
>> 
>> static __be32
>> nfsd4_do_encode_secinfo(struct nfsd4_compoundres *resp,
>> -			 __be32 nfserr,struct svc_export *exp)
>> +			 __be32 nfserr, struct svc_export *exp)
>> {
>> -	int i = 0;
>> -	u32 nflavs;
>> +	u32 i, nflavs;
>> 	struct exp_flavor_info *flavs;
>> 	struct exp_flavor_info def_flavs[2];
>> 	__be32 *p;
>> @@ -3161,30 +3160,29 @@ nfsd4_do_encode_secinfo(struct nfsd4_compoundres *resp,
>> 	WRITE32(nflavs);
>> 	ADJUST_ARGS();
>> 	for (i = 0; i < nflavs; i++) {
>> -		u32 flav = flavs[i].pseudoflavor;
>> -		struct gss_api_mech *gm = gss_mech_get_by_pseudoflavor(flav);
>> +		struct rpcsec_gss_info info;
>> 
>> -		if (gm) {
>> +		if (rpcauth_get_gssinfo(flavs[i].pseudoflavor, &info) == 0) {
>> 			RESERVE_SPACE(4);
>> 			WRITE32(RPC_AUTH_GSS);
>> 			ADJUST_ARGS();
>> -			RESERVE_SPACE(4 + gm->gm_oid.len);
>> -			WRITE32(gm->gm_oid.len);
>> -			WRITEMEM(gm->gm_oid.data, gm->gm_oid.len);
>> +			RESERVE_SPACE(4 + info.oid.len);
>> +			WRITE32(info.oid.len);
>> +			WRITEMEM(info.oid.data, info.oid.len);
>> 			ADJUST_ARGS();
>> 			RESERVE_SPACE(4);
>> -			WRITE32(0); /* qop */
>> +			WRITE32(info.qop);
>> 			ADJUST_ARGS();
>> 			RESERVE_SPACE(4);
>> -			WRITE32(gss_pseudoflavor_to_service(gm, flav));
>> +			WRITE32(info.service);
>> 			ADJUST_ARGS();
>> -			gss_mech_put(gm);
>> 		} else {
>> 			RESERVE_SPACE(4);
>> -			WRITE32(flav);
>> +			WRITE32(flavs[i].pseudoflavor);
>> 			ADJUST_ARGS();
>> 		}
>> 	}
>> +
>> out:
>> 	if (exp)
>> 		exp_put(exp);
>> diff --git a/include/linux/sunrpc/auth.h b/include/linux/sunrpc/auth.h
>> index 476e97c..c34ac17 100644
>> --- a/include/linux/sunrpc/auth.h
>> +++ b/include/linux/sunrpc/auth.h
>> @@ -105,6 +105,7 @@ struct rpc_authops {
>> 	void			(*pipes_destroy)(struct rpc_auth *);
>> 	int			(*list_pseudoflavors)(rpc_authflavor_t *, int);
>> 	rpc_authflavor_t	(*info2flavor)(struct rpcsec_gss_info *);
>> +	int			(*flavor2info)(rpc_authflavor_t, struct rpcsec_gss_info *);
>> };
>> 
>> struct rpc_credops {
>> @@ -140,6 +141,7 @@ int			rpcauth_unregister(const struct rpc_authops *);
>> struct rpc_auth *	rpcauth_create(rpc_authflavor_t, struct rpc_clnt *);
>> void			rpcauth_release(struct rpc_auth *);
>> rpc_authflavor_t	rpcauth_get_pseudoflavor(rpc_authflavor_t, struct rpcsec_gss_info *);
>> +int			rpcauth_get_gssinfo(rpc_authflavor_t, struct rpcsec_gss_info *);
>> int			rpcauth_list_flavors(rpc_authflavor_t *, int);
>> struct rpc_cred *	rpcauth_lookup_credcache(struct rpc_auth *, struct auth_cred *, int);
>> void			rpcauth_init_cred(struct rpc_cred *, const struct auth_cred *, struct rpc_auth *, const struct rpc_credops *);
>> diff --git a/include/linux/sunrpc/gss_api.h b/include/linux/sunrpc/gss_api.h
>> index e8299b2..dc1e51b 100644
>> --- a/include/linux/sunrpc/gss_api.h
>> +++ b/include/linux/sunrpc/gss_api.h
>> @@ -132,6 +132,9 @@ void gss_mech_unregister(struct gss_api_mech *);
>> /* Given a GSS security tuple, look up a pseudoflavor */
>> rpc_authflavor_t gss_mech_info2flavor(struct rpcsec_gss_info *);
>> 
>> +/* Given a pseudoflavor, look up a GSS security tuple */
>> +int gss_mech_flavor2info(rpc_authflavor_t, struct rpcsec_gss_info *);
>> +
>> /* Returns a reference to a mechanism, given a name like "krb5" etc. */
>> struct gss_api_mech *gss_mech_get_by_name(const char *);
>> 
>> diff --git a/net/sunrpc/auth.c b/net/sunrpc/auth.c
>> index ec18827..4c551f9 100644
>> --- a/net/sunrpc/auth.c
>> +++ b/net/sunrpc/auth.c
>> @@ -158,6 +158,40 @@ rpcauth_get_pseudoflavor(rpc_authflavor_t flavor, struct rpcsec_gss_info *info)
>> EXPORT_SYMBOL_GPL(rpcauth_get_pseudoflavor);
>> 
>> /**
>> + * rpcauth_get_gssinfo - find GSS tuple matching a GSS pseudoflavor
>> + * @pseudoflavor: GSS pseudoflavor to match
>> + * @info: rpcsec_gss_info structure to fill in
>> + *
>> + * Returns zero and fills in "info" if pseudoflavor matches a
>> + * supported mechanism.
>> + */
>> +int
>> +rpcauth_get_gssinfo(rpc_authflavor_t pseudoflavor, struct rpcsec_gss_info *info)
>> +{
>> +	rpc_authflavor_t flavor = pseudoflavor_to_flavor(pseudoflavor);
>> +	const struct rpc_authops *ops;
>> +	int result;
>> +
>> +	if ((ops = auth_flavors[flavor]) == NULL)
>> +		request_module("rpc-auth-%u", flavor);
>> +	spin_lock(&rpc_authflavor_lock);
>> +	ops = auth_flavors[flavor];
>> +	if (ops == NULL || !try_module_get(ops->owner)) {
>> +		spin_unlock(&rpc_authflavor_lock);
>> +		return -ENOENT;
>> +	}
>> +	spin_unlock(&rpc_authflavor_lock);
>> +
>> +	result = -ENOENT;
>> +	if (ops->flavor2info != NULL)
>> +		result = ops->flavor2info(pseudoflavor, info);
>> +
>> +	module_put(ops->owner);
>> +	return result;
>> +}
>> +EXPORT_SYMBOL_GPL(rpcauth_get_gssinfo);
>> +
>> +/**
>>  * rpcauth_list_flavors - discover registered flavors and pseudoflavors
>>  * @array: array to fill in
>>  * @size: size of "array"
>> diff --git a/net/sunrpc/auth_gss/auth_gss.c b/net/sunrpc/auth_gss/auth_gss.c
>> index 4522297..ade24b2 100644
>> --- a/net/sunrpc/auth_gss/auth_gss.c
>> +++ b/net/sunrpc/auth_gss/auth_gss.c
>> @@ -1630,6 +1630,7 @@ static const struct rpc_authops authgss_ops = {
>> 	.pipes_destroy	= gss_pipes_dentries_destroy,
>> 	.list_pseudoflavors = gss_mech_list_pseudoflavors,
>> 	.info2flavor	= gss_mech_info2flavor,
>> +	.flavor2info	= gss_mech_flavor2info,
>> };
>> 
>> static const struct rpc_credops gss_credops = {
>> diff --git a/net/sunrpc/auth_gss/gss_mech_switch.c b/net/sunrpc/auth_gss/gss_mech_switch.c
>> index c8da89e..a8d6ee5 100644
>> --- a/net/sunrpc/auth_gss/gss_mech_switch.c
>> +++ b/net/sunrpc/auth_gss/gss_mech_switch.c
>> @@ -240,8 +240,6 @@ gss_mech_get_by_pseudoflavor(u32 pseudoflavor)
>> 	return gm;
>> }
>> 
>> -EXPORT_SYMBOL_GPL(gss_mech_get_by_pseudoflavor);
>> -
>> /**
>>  * gss_mech_list_pseudoflavors - Discover registered GSS pseudoflavors
>>  * @array: array to fill in
>> @@ -314,6 +312,38 @@ rpc_authflavor_t gss_mech_info2flavor(struct rpcsec_gss_info *info)
>> 	return pseudoflavor;
>> }
>> 
>> +/**
>> + * gss_mech_flavor2info - look up a GSS tuple for a given pseudoflavor
>> + * @pseudoflavor: GSS pseudoflavor to match
>> + * @info: rpcsec_gss_info structure to fill in
>> + *
>> + * Returns zero and fills in "info" if pseudoflavor matches a
>> + * supported mechanism.  Otherwise a negative errno is returned.
>> + */
>> +int gss_mech_flavor2info(rpc_authflavor_t pseudoflavor, struct rpcsec_gss_info *info)
>> +{
>> +	struct gss_api_mech *gm;
>> +	int i;
>> +
>> +	gm = gss_mech_get_by_pseudoflavor(pseudoflavor);
>> +	if (gm == NULL)
>> +		return -ENOENT;
>> +
>> +	for (i = 0; i < gm->gm_pf_num; i++) {
>> +		if (gm->gm_pfs[i].pseudoflavor == pseudoflavor) {
>> +			memcpy(info->oid.data, gm->gm_oid.data, gm->gm_oid.len);
>> +			info->oid.len = gm->gm_oid.len;
>> +			info->qop = gm->gm_pfs[i].qop;
>> +			info->service = gm->gm_pfs[i].service;
>> +			gss_mech_put(gm);
>> +			return 0;
>> +		}
>> +	}
>> +
>> +	gss_mech_put(gm);
>> +	return -ENOENT;
>> +}
>> +
>> u32
>> gss_pseudoflavor_to_service(struct gss_api_mech *gm, u32 pseudoflavor)
>> {
>> 
>> --
>> 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
> --
> 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 Feb. 7, 2013, 4:23 p.m. UTC | #3
On Thu, Feb 07, 2013 at 10:58:25AM -0500, Chuck Lever wrote:
> 
> On Feb 7, 2013, at 10:02 AM, J. Bruce Fields <bfields@fieldses.org> wrote:
> 
> > On Fri, Feb 01, 2013 at 05:43:44PM -0500, Chuck Lever wrote:
> >> Clean up.  This matches a similar API for the client side, and
> >> keeps ULP fingers out the of the GSS mech switch.
> >> 
> >> Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
> >> Acked-by: J. Bruce Fields <bfields@redhat.com>
> >> ---
> >> 
> >> Bruce-
> >> 
> >> This version of the patch follows the existing logic in
> >> nfsd4_do_encode_secinfo(): If the RPC layer can't find GSS info
> >> that matches an export security flavor, it assumes the flavor is
> >> not a GSS pseudoflavor, and simply puts it on the wire.
> >> 
> >> However, if the below XDR encoding logic is given a legitimate GSS
> >> pseudoflavor but the RPC layer says it does not support that
> >> pseudoflavor for some reason, then we leak GSS pseudoflavor numbers
> >> onto the wire.
> >> 
> >> I confirmed this happens by blacklisting rpcsec_gss_krb5, then
> >> attempted a client transition from the pseudo-fs to a Kerberos-only
> >> share.  The client received a flavor list containing the Kerberos
> >> pseudoflavor numbers, rather than GSS tuples.
> >> 
> >> The encoder logic can check that each pseudoflavor is less than
> >> MAXFLAVOR before writing it into the buffer, to prevent this.  But
> >> after "nflavs" is written into the XDR buffer, the encoder can't
> >> skip writing flavor information into the buffer when it discovers
> >> the RPC layer doesn't support that flavor.
> >> 
> >> Is there some way of writing "nflavs" into the XDR buffer after
> >> the loop that writes the flavor information is complete?
> > 
> > Yes, you can save a pointer and then go back and fill that in--see
> > encode_fattr for an example.
> 
> Thanks, I will submit an additional patch that describes this issue and fixes it.
> 
> I asked David Noveck, as one of the authors of RFC 3530, whether an NFS server should return a zero-length flavor list or an error if SECINFO can't find any flavors a client is allowed to use. His opinion was to return NFS4_OK and a zero-length flavor list.

Fine with me for this code.

(In practice though we should probably be warning somewhere (exportfs?)
if somebody creates an export like that.)

--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
Chuck Lever III Feb. 7, 2013, 4:26 p.m. UTC | #4
On Feb 7, 2013, at 11:23 AM, "J. Bruce Fields" <bfields@fieldses.org> wrote:

> On Thu, Feb 07, 2013 at 10:58:25AM -0500, Chuck Lever wrote:
>> 
>> On Feb 7, 2013, at 10:02 AM, J. Bruce Fields <bfields@fieldses.org> wrote:
>> 
>>> On Fri, Feb 01, 2013 at 05:43:44PM -0500, Chuck Lever wrote:
>>>> Clean up.  This matches a similar API for the client side, and
>>>> keeps ULP fingers out the of the GSS mech switch.
>>>> 
>>>> Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
>>>> Acked-by: J. Bruce Fields <bfields@redhat.com>
>>>> ---
>>>> 
>>>> Bruce-
>>>> 
>>>> This version of the patch follows the existing logic in
>>>> nfsd4_do_encode_secinfo(): If the RPC layer can't find GSS info
>>>> that matches an export security flavor, it assumes the flavor is
>>>> not a GSS pseudoflavor, and simply puts it on the wire.
>>>> 
>>>> However, if the below XDR encoding logic is given a legitimate GSS
>>>> pseudoflavor but the RPC layer says it does not support that
>>>> pseudoflavor for some reason, then we leak GSS pseudoflavor numbers
>>>> onto the wire.
>>>> 
>>>> I confirmed this happens by blacklisting rpcsec_gss_krb5, then
>>>> attempted a client transition from the pseudo-fs to a Kerberos-only
>>>> share.  The client received a flavor list containing the Kerberos
>>>> pseudoflavor numbers, rather than GSS tuples.
>>>> 
>>>> The encoder logic can check that each pseudoflavor is less than
>>>> MAXFLAVOR before writing it into the buffer, to prevent this.  But
>>>> after "nflavs" is written into the XDR buffer, the encoder can't
>>>> skip writing flavor information into the buffer when it discovers
>>>> the RPC layer doesn't support that flavor.
>>>> 
>>>> Is there some way of writing "nflavs" into the XDR buffer after
>>>> the loop that writes the flavor information is complete?
>>> 
>>> Yes, you can save a pointer and then go back and fill that in--see
>>> encode_fattr for an example.
>> 
>> Thanks, I will submit an additional patch that describes this issue and fixes it.
>> 
>> I asked David Noveck, as one of the authors of RFC 3530, whether an NFS server should return a zero-length flavor list or an error if SECINFO can't find any flavors a client is allowed to use. His opinion was to return NFS4_OK and a zero-length flavor list.
> 
> Fine with me for this code.

OK, will go with that.

> (In practice though we should probably be warning somewhere (exportfs?)
> if somebody creates an export like that.)

The problem can also arise because gssd isn't running or auth_rpcgss.ko or rpcsec_gss_krb5.ko are not loadable for some reason.  In other words, an empty flavor list might also be the result of a transient server misconfiguration.
J. Bruce Fields Feb. 7, 2013, 4:55 p.m. UTC | #5
On Thu, Feb 07, 2013 at 11:26:43AM -0500, Chuck Lever wrote:
> 
> On Feb 7, 2013, at 11:23 AM, "J. Bruce Fields" <bfields@fieldses.org> wrote:
> 
> > On Thu, Feb 07, 2013 at 10:58:25AM -0500, Chuck Lever wrote:
> >> 
> >> On Feb 7, 2013, at 10:02 AM, J. Bruce Fields <bfields@fieldses.org> wrote:
> >> 
> >>> On Fri, Feb 01, 2013 at 05:43:44PM -0500, Chuck Lever wrote:
> >>>> Clean up.  This matches a similar API for the client side, and
> >>>> keeps ULP fingers out the of the GSS mech switch.
> >>>> 
> >>>> Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
> >>>> Acked-by: J. Bruce Fields <bfields@redhat.com>
> >>>> ---
> >>>> 
> >>>> Bruce-
> >>>> 
> >>>> This version of the patch follows the existing logic in
> >>>> nfsd4_do_encode_secinfo(): If the RPC layer can't find GSS info
> >>>> that matches an export security flavor, it assumes the flavor is
> >>>> not a GSS pseudoflavor, and simply puts it on the wire.
> >>>> 
> >>>> However, if the below XDR encoding logic is given a legitimate GSS
> >>>> pseudoflavor but the RPC layer says it does not support that
> >>>> pseudoflavor for some reason, then we leak GSS pseudoflavor numbers
> >>>> onto the wire.
> >>>> 
> >>>> I confirmed this happens by blacklisting rpcsec_gss_krb5, then
> >>>> attempted a client transition from the pseudo-fs to a Kerberos-only
> >>>> share.  The client received a flavor list containing the Kerberos
> >>>> pseudoflavor numbers, rather than GSS tuples.
> >>>> 
> >>>> The encoder logic can check that each pseudoflavor is less than
> >>>> MAXFLAVOR before writing it into the buffer, to prevent this.  But
> >>>> after "nflavs" is written into the XDR buffer, the encoder can't
> >>>> skip writing flavor information into the buffer when it discovers
> >>>> the RPC layer doesn't support that flavor.
> >>>> 
> >>>> Is there some way of writing "nflavs" into the XDR buffer after
> >>>> the loop that writes the flavor information is complete?
> >>> 
> >>> Yes, you can save a pointer and then go back and fill that in--see
> >>> encode_fattr for an example.
> >> 
> >> Thanks, I will submit an additional patch that describes this issue and fixes it.
> >> 
> >> I asked David Noveck, as one of the authors of RFC 3530, whether an NFS server should return a zero-length flavor list or an error if SECINFO can't find any flavors a client is allowed to use. His opinion was to return NFS4_OK and a zero-length flavor list.
> > 
> > Fine with me for this code.
> 
> OK, will go with that.
> 
> > (In practice though we should probably be warning somewhere (exportfs?)
> > if somebody creates an export like that.)
> 
> The problem can also arise because gssd isn't running or auth_rpcgss.ko or rpcsec_gss_krb5.ko are not loadable for some reason.  In other words, an empty flavor list might also be the result of a transient server misconfiguration.

OK.  Do you think the kernel could help by providing a once-only warning
in such a case?  (Or in the case when we're not able to find support for
a security flavor set on the export.)

--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
Chuck Lever III Feb. 7, 2013, 5:03 p.m. UTC | #6
On Feb 7, 2013, at 11:55 AM, J. Bruce Fields <bfields@fieldses.org> wrote:

> On Thu, Feb 07, 2013 at 11:26:43AM -0500, Chuck Lever wrote:
>> 
>> On Feb 7, 2013, at 11:23 AM, "J. Bruce Fields" <bfields@fieldses.org> wrote:
>> 
>>> On Thu, Feb 07, 2013 at 10:58:25AM -0500, Chuck Lever wrote:
>>>> 
>>>> On Feb 7, 2013, at 10:02 AM, J. Bruce Fields <bfields@fieldses.org> wrote:
>>>> 
>>>>> On Fri, Feb 01, 2013 at 05:43:44PM -0500, Chuck Lever wrote:
>>>>>> Clean up.  This matches a similar API for the client side, and
>>>>>> keeps ULP fingers out the of the GSS mech switch.
>>>>>> 
>>>>>> Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
>>>>>> Acked-by: J. Bruce Fields <bfields@redhat.com>
>>>>>> ---
>>>>>> 
>>>>>> Bruce-
>>>>>> 
>>>>>> This version of the patch follows the existing logic in
>>>>>> nfsd4_do_encode_secinfo(): If the RPC layer can't find GSS info
>>>>>> that matches an export security flavor, it assumes the flavor is
>>>>>> not a GSS pseudoflavor, and simply puts it on the wire.
>>>>>> 
>>>>>> However, if the below XDR encoding logic is given a legitimate GSS
>>>>>> pseudoflavor but the RPC layer says it does not support that
>>>>>> pseudoflavor for some reason, then we leak GSS pseudoflavor numbers
>>>>>> onto the wire.
>>>>>> 
>>>>>> I confirmed this happens by blacklisting rpcsec_gss_krb5, then
>>>>>> attempted a client transition from the pseudo-fs to a Kerberos-only
>>>>>> share.  The client received a flavor list containing the Kerberos
>>>>>> pseudoflavor numbers, rather than GSS tuples.
>>>>>> 
>>>>>> The encoder logic can check that each pseudoflavor is less than
>>>>>> MAXFLAVOR before writing it into the buffer, to prevent this.  But
>>>>>> after "nflavs" is written into the XDR buffer, the encoder can't
>>>>>> skip writing flavor information into the buffer when it discovers
>>>>>> the RPC layer doesn't support that flavor.
>>>>>> 
>>>>>> Is there some way of writing "nflavs" into the XDR buffer after
>>>>>> the loop that writes the flavor information is complete?
>>>>> 
>>>>> Yes, you can save a pointer and then go back and fill that in--see
>>>>> encode_fattr for an example.
>>>> 
>>>> Thanks, I will submit an additional patch that describes this issue and fixes it.
>>>> 
>>>> I asked David Noveck, as one of the authors of RFC 3530, whether an NFS server should return a zero-length flavor list or an error if SECINFO can't find any flavors a client is allowed to use. His opinion was to return NFS4_OK and a zero-length flavor list.
>>> 
>>> Fine with me for this code.
>> 
>> OK, will go with that.
>> 
>>> (In practice though we should probably be warning somewhere (exportfs?)
>>> if somebody creates an export like that.)
>> 
>> The problem can also arise because gssd isn't running or auth_rpcgss.ko or rpcsec_gss_krb5.ko are not loadable for some reason.  In other words, an empty flavor list might also be the result of a transient server misconfiguration.
> 
> OK.  Do you think the kernel could help by providing a once-only warning
> in such a case?  (Or in the case when we're not able to find support for
> a security flavor set on the export.)

I've thought a little about that.  There is already a logged warning if a gssd upcall times out.  But it's hard to tell inside the kernel why a module doesn't load.  These all look pretty much the same to the RPC layer:

   -  module is blacklisted or not installed?
   -  GSS support wasn't built?
   -  filesystem corruption?
   -  export specifies a security flavor the RPC client doesn't recognize?

I suppose I could add something here, but I wonder about the false alarms.
Chuck Lever III Feb. 7, 2013, 5:12 p.m. UTC | #7
On Feb 7, 2013, at 12:03 PM, Chuck Lever <chuck.lever@oracle.com> wrote:

> 
> On Feb 7, 2013, at 11:55 AM, J. Bruce Fields <bfields@fieldses.org> wrote:
> 
>> On Thu, Feb 07, 2013 at 11:26:43AM -0500, Chuck Lever wrote:
>>> 
>>> On Feb 7, 2013, at 11:23 AM, "J. Bruce Fields" <bfields@fieldses.org> wrote:
>>> 
>>>> On Thu, Feb 07, 2013 at 10:58:25AM -0500, Chuck Lever wrote:
>>>>> 
>>>>> On Feb 7, 2013, at 10:02 AM, J. Bruce Fields <bfields@fieldses.org> wrote:
>>>>> 
>>>>>> On Fri, Feb 01, 2013 at 05:43:44PM -0500, Chuck Lever wrote:
>>>>>>> Clean up.  This matches a similar API for the client side, and
>>>>>>> keeps ULP fingers out the of the GSS mech switch.
>>>>>>> 
>>>>>>> Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
>>>>>>> Acked-by: J. Bruce Fields <bfields@redhat.com>
>>>>>>> ---
>>>>>>> 
>>>>>>> Bruce-
>>>>>>> 
>>>>>>> This version of the patch follows the existing logic in
>>>>>>> nfsd4_do_encode_secinfo(): If the RPC layer can't find GSS info
>>>>>>> that matches an export security flavor, it assumes the flavor is
>>>>>>> not a GSS pseudoflavor, and simply puts it on the wire.
>>>>>>> 
>>>>>>> However, if the below XDR encoding logic is given a legitimate GSS
>>>>>>> pseudoflavor but the RPC layer says it does not support that
>>>>>>> pseudoflavor for some reason, then we leak GSS pseudoflavor numbers
>>>>>>> onto the wire.
>>>>>>> 
>>>>>>> I confirmed this happens by blacklisting rpcsec_gss_krb5, then
>>>>>>> attempted a client transition from the pseudo-fs to a Kerberos-only
>>>>>>> share.  The client received a flavor list containing the Kerberos
>>>>>>> pseudoflavor numbers, rather than GSS tuples.
>>>>>>> 
>>>>>>> The encoder logic can check that each pseudoflavor is less than
>>>>>>> MAXFLAVOR before writing it into the buffer, to prevent this.  But
>>>>>>> after "nflavs" is written into the XDR buffer, the encoder can't
>>>>>>> skip writing flavor information into the buffer when it discovers
>>>>>>> the RPC layer doesn't support that flavor.
>>>>>>> 
>>>>>>> Is there some way of writing "nflavs" into the XDR buffer after
>>>>>>> the loop that writes the flavor information is complete?
>>>>>> 
>>>>>> Yes, you can save a pointer and then go back and fill that in--see
>>>>>> encode_fattr for an example.
>>>>> 
>>>>> Thanks, I will submit an additional patch that describes this issue and fixes it.
>>>>> 
>>>>> I asked David Noveck, as one of the authors of RFC 3530, whether an NFS server should return a zero-length flavor list or an error if SECINFO can't find any flavors a client is allowed to use. His opinion was to return NFS4_OK and a zero-length flavor list.
>>>> 
>>>> Fine with me for this code.
>>> 
>>> OK, will go with that.
>>> 
>>>> (In practice though we should probably be warning somewhere (exportfs?)
>>>> if somebody creates an export like that.)
>>> 
>>> The problem can also arise because gssd isn't running or auth_rpcgss.ko or rpcsec_gss_krb5.ko are not loadable for some reason.  In other words, an empty flavor list might also be the result of a transient server misconfiguration.
>> 
>> OK.  Do you think the kernel could help by providing a once-only warning
>> in such a case?  (Or in the case when we're not able to find support for
>> a security flavor set on the export.)
> 
> I've thought a little about that.  There is already a logged warning if a gssd upcall times out.  But it's hard to tell inside the kernel why a module doesn't load.  These all look pretty much the same to the RPC layer:
> 
>   -  module is blacklisted or not installed?
>   -  GSS support wasn't built?
>   -  filesystem corruption?
>   -  export specifies a security flavor the RPC client doesn't recognize?
> 
> I suppose I could add something here, but I wonder about the false alarms.

How about this:

  "Warning: export specifies a security flavor that isn't supported"
J. Bruce Fields Feb. 7, 2013, 5:14 p.m. UTC | #8
On Thu, Feb 07, 2013 at 12:12:24PM -0500, Chuck Lever wrote:
> 
> On Feb 7, 2013, at 12:03 PM, Chuck Lever <chuck.lever@oracle.com> wrote:
> > I've thought a little about that.  There is already a logged warning if a gssd upcall times out.  But it's hard to tell inside the kernel why a module doesn't load.  These all look pretty much the same to the RPC layer:
> > 
> >   -  module is blacklisted or not installed?
> >   -  GSS support wasn't built?
> >   -  filesystem corruption?
> >   -  export specifies a security flavor the RPC client doesn't recognize?
> > 
> > I suppose I could add something here, but I wonder about the false alarms.
> 
> How about this:
> 
>   "Warning: export specifies a security flavor that isn't supported"

Sounds fine.  With a guard so we just say it once.

Bonus if we could say which export or which flavor?

--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
diff mbox

Patch

diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
index 0dc1158..424f5a2 100644
--- a/fs/nfsd/nfs4xdr.c
+++ b/fs/nfsd/nfs4xdr.c
@@ -3127,10 +3127,9 @@  nfsd4_encode_rename(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_
 
 static __be32
 nfsd4_do_encode_secinfo(struct nfsd4_compoundres *resp,
-			 __be32 nfserr,struct svc_export *exp)
+			 __be32 nfserr, struct svc_export *exp)
 {
-	int i = 0;
-	u32 nflavs;
+	u32 i, nflavs;
 	struct exp_flavor_info *flavs;
 	struct exp_flavor_info def_flavs[2];
 	__be32 *p;
@@ -3161,30 +3160,29 @@  nfsd4_do_encode_secinfo(struct nfsd4_compoundres *resp,
 	WRITE32(nflavs);
 	ADJUST_ARGS();
 	for (i = 0; i < nflavs; i++) {
-		u32 flav = flavs[i].pseudoflavor;
-		struct gss_api_mech *gm = gss_mech_get_by_pseudoflavor(flav);
+		struct rpcsec_gss_info info;
 
-		if (gm) {
+		if (rpcauth_get_gssinfo(flavs[i].pseudoflavor, &info) == 0) {
 			RESERVE_SPACE(4);
 			WRITE32(RPC_AUTH_GSS);
 			ADJUST_ARGS();
-			RESERVE_SPACE(4 + gm->gm_oid.len);
-			WRITE32(gm->gm_oid.len);
-			WRITEMEM(gm->gm_oid.data, gm->gm_oid.len);
+			RESERVE_SPACE(4 + info.oid.len);
+			WRITE32(info.oid.len);
+			WRITEMEM(info.oid.data, info.oid.len);
 			ADJUST_ARGS();
 			RESERVE_SPACE(4);
-			WRITE32(0); /* qop */
+			WRITE32(info.qop);
 			ADJUST_ARGS();
 			RESERVE_SPACE(4);
-			WRITE32(gss_pseudoflavor_to_service(gm, flav));
+			WRITE32(info.service);
 			ADJUST_ARGS();
-			gss_mech_put(gm);
 		} else {
 			RESERVE_SPACE(4);
-			WRITE32(flav);
+			WRITE32(flavs[i].pseudoflavor);
 			ADJUST_ARGS();
 		}
 	}
+
 out:
 	if (exp)
 		exp_put(exp);
diff --git a/include/linux/sunrpc/auth.h b/include/linux/sunrpc/auth.h
index 476e97c..c34ac17 100644
--- a/include/linux/sunrpc/auth.h
+++ b/include/linux/sunrpc/auth.h
@@ -105,6 +105,7 @@  struct rpc_authops {
 	void			(*pipes_destroy)(struct rpc_auth *);
 	int			(*list_pseudoflavors)(rpc_authflavor_t *, int);
 	rpc_authflavor_t	(*info2flavor)(struct rpcsec_gss_info *);
+	int			(*flavor2info)(rpc_authflavor_t, struct rpcsec_gss_info *);
 };
 
 struct rpc_credops {
@@ -140,6 +141,7 @@  int			rpcauth_unregister(const struct rpc_authops *);
 struct rpc_auth *	rpcauth_create(rpc_authflavor_t, struct rpc_clnt *);
 void			rpcauth_release(struct rpc_auth *);
 rpc_authflavor_t	rpcauth_get_pseudoflavor(rpc_authflavor_t, struct rpcsec_gss_info *);
+int			rpcauth_get_gssinfo(rpc_authflavor_t, struct rpcsec_gss_info *);
 int			rpcauth_list_flavors(rpc_authflavor_t *, int);
 struct rpc_cred *	rpcauth_lookup_credcache(struct rpc_auth *, struct auth_cred *, int);
 void			rpcauth_init_cred(struct rpc_cred *, const struct auth_cred *, struct rpc_auth *, const struct rpc_credops *);
diff --git a/include/linux/sunrpc/gss_api.h b/include/linux/sunrpc/gss_api.h
index e8299b2..dc1e51b 100644
--- a/include/linux/sunrpc/gss_api.h
+++ b/include/linux/sunrpc/gss_api.h
@@ -132,6 +132,9 @@  void gss_mech_unregister(struct gss_api_mech *);
 /* Given a GSS security tuple, look up a pseudoflavor */
 rpc_authflavor_t gss_mech_info2flavor(struct rpcsec_gss_info *);
 
+/* Given a pseudoflavor, look up a GSS security tuple */
+int gss_mech_flavor2info(rpc_authflavor_t, struct rpcsec_gss_info *);
+
 /* Returns a reference to a mechanism, given a name like "krb5" etc. */
 struct gss_api_mech *gss_mech_get_by_name(const char *);
 
diff --git a/net/sunrpc/auth.c b/net/sunrpc/auth.c
index ec18827..4c551f9 100644
--- a/net/sunrpc/auth.c
+++ b/net/sunrpc/auth.c
@@ -158,6 +158,40 @@  rpcauth_get_pseudoflavor(rpc_authflavor_t flavor, struct rpcsec_gss_info *info)
 EXPORT_SYMBOL_GPL(rpcauth_get_pseudoflavor);
 
 /**
+ * rpcauth_get_gssinfo - find GSS tuple matching a GSS pseudoflavor
+ * @pseudoflavor: GSS pseudoflavor to match
+ * @info: rpcsec_gss_info structure to fill in
+ *
+ * Returns zero and fills in "info" if pseudoflavor matches a
+ * supported mechanism.
+ */
+int
+rpcauth_get_gssinfo(rpc_authflavor_t pseudoflavor, struct rpcsec_gss_info *info)
+{
+	rpc_authflavor_t flavor = pseudoflavor_to_flavor(pseudoflavor);
+	const struct rpc_authops *ops;
+	int result;
+
+	if ((ops = auth_flavors[flavor]) == NULL)
+		request_module("rpc-auth-%u", flavor);
+	spin_lock(&rpc_authflavor_lock);
+	ops = auth_flavors[flavor];
+	if (ops == NULL || !try_module_get(ops->owner)) {
+		spin_unlock(&rpc_authflavor_lock);
+		return -ENOENT;
+	}
+	spin_unlock(&rpc_authflavor_lock);
+
+	result = -ENOENT;
+	if (ops->flavor2info != NULL)
+		result = ops->flavor2info(pseudoflavor, info);
+
+	module_put(ops->owner);
+	return result;
+}
+EXPORT_SYMBOL_GPL(rpcauth_get_gssinfo);
+
+/**
  * rpcauth_list_flavors - discover registered flavors and pseudoflavors
  * @array: array to fill in
  * @size: size of "array"
diff --git a/net/sunrpc/auth_gss/auth_gss.c b/net/sunrpc/auth_gss/auth_gss.c
index 4522297..ade24b2 100644
--- a/net/sunrpc/auth_gss/auth_gss.c
+++ b/net/sunrpc/auth_gss/auth_gss.c
@@ -1630,6 +1630,7 @@  static const struct rpc_authops authgss_ops = {
 	.pipes_destroy	= gss_pipes_dentries_destroy,
 	.list_pseudoflavors = gss_mech_list_pseudoflavors,
 	.info2flavor	= gss_mech_info2flavor,
+	.flavor2info	= gss_mech_flavor2info,
 };
 
 static const struct rpc_credops gss_credops = {
diff --git a/net/sunrpc/auth_gss/gss_mech_switch.c b/net/sunrpc/auth_gss/gss_mech_switch.c
index c8da89e..a8d6ee5 100644
--- a/net/sunrpc/auth_gss/gss_mech_switch.c
+++ b/net/sunrpc/auth_gss/gss_mech_switch.c
@@ -240,8 +240,6 @@  gss_mech_get_by_pseudoflavor(u32 pseudoflavor)
 	return gm;
 }
 
-EXPORT_SYMBOL_GPL(gss_mech_get_by_pseudoflavor);
-
 /**
  * gss_mech_list_pseudoflavors - Discover registered GSS pseudoflavors
  * @array: array to fill in
@@ -314,6 +312,38 @@  rpc_authflavor_t gss_mech_info2flavor(struct rpcsec_gss_info *info)
 	return pseudoflavor;
 }
 
+/**
+ * gss_mech_flavor2info - look up a GSS tuple for a given pseudoflavor
+ * @pseudoflavor: GSS pseudoflavor to match
+ * @info: rpcsec_gss_info structure to fill in
+ *
+ * Returns zero and fills in "info" if pseudoflavor matches a
+ * supported mechanism.  Otherwise a negative errno is returned.
+ */
+int gss_mech_flavor2info(rpc_authflavor_t pseudoflavor, struct rpcsec_gss_info *info)
+{
+	struct gss_api_mech *gm;
+	int i;
+
+	gm = gss_mech_get_by_pseudoflavor(pseudoflavor);
+	if (gm == NULL)
+		return -ENOENT;
+
+	for (i = 0; i < gm->gm_pf_num; i++) {
+		if (gm->gm_pfs[i].pseudoflavor == pseudoflavor) {
+			memcpy(info->oid.data, gm->gm_oid.data, gm->gm_oid.len);
+			info->oid.len = gm->gm_oid.len;
+			info->qop = gm->gm_pfs[i].qop;
+			info->service = gm->gm_pfs[i].service;
+			gss_mech_put(gm);
+			return 0;
+		}
+	}
+
+	gss_mech_put(gm);
+	return -ENOENT;
+}
+
 u32
 gss_pseudoflavor_to_service(struct gss_api_mech *gm, u32 pseudoflavor)
 {