Commit 9568c5e9 authored by Chuck Lever's avatar Chuck Lever Committed by Trond Myklebust

SUNRPC: Introduce rpcauth_get_pseudoflavor()

A SECINFO reply may contain flavors whose kernel module is not
yet loaded by the client's kernel.  A new RPC client API, called
rpcauth_get_pseudoflavor(), is introduced to do proper checking
for support of a security flavor.

When this API is invoked, the RPC client now tries to load the
module for each flavor first before performing the "is this
supported?" check.  This means if a module is available on the
client, but has not been loaded yet, it will be loaded and
registered automatically when the SECINFO reply is processed.

The new API can take a full GSS tuple (OID, QoP, and service).
Previously only the OID and service were considered.

nfs_find_best_sec() is updated to verify all flavors requested in a
SECINFO reply, including AUTH_NULL and AUTH_UNIX.  Previously these
two flavors were simply assumed to be supported without consulting
the RPC client.

Note that the replaced version of nfs_find_best_sec() can return
RPC_AUTH_MAXFLAVOR if the server returns a recognized OID but an
unsupported "service" value.  nfs_find_best_sec() now returns
RPC_AUTH_UNIX in this case.
Signed-off-by: default avatarChuck Lever <chuck.lever@oracle.com>
Signed-off-by: default avatarTrond Myklebust <Trond.Myklebust@netapp.com>
parent fb15b26f
...@@ -134,33 +134,38 @@ static size_t nfs_parse_server_name(char *string, size_t len, ...@@ -134,33 +134,38 @@ static size_t nfs_parse_server_name(char *string, size_t len,
return ret; return ret;
} }
/**
* nfs_find_best_sec - Find a security mechanism supported locally
* @flavors: List of security tuples returned by SECINFO procedure
*
* Return the pseudoflavor of the first security mechanism in
* "flavors" that is locally supported. Return RPC_AUTH_UNIX if
* no matching flavor is found in the array. The "flavors" array
* is searched in the order returned from the server, per RFC 3530
* recommendation.
*/
rpc_authflavor_t nfs_find_best_sec(struct nfs4_secinfo_flavors *flavors) rpc_authflavor_t nfs_find_best_sec(struct nfs4_secinfo_flavors *flavors)
{ {
struct gss_api_mech *mech; rpc_authflavor_t pseudoflavor;
struct xdr_netobj oid; struct nfs4_secinfo4 *secinfo;
unsigned int i; unsigned int i;
rpc_authflavor_t pseudoflavor = RPC_AUTH_UNIX;
for (i = 0; i < flavors->num_flavors; i++) { for (i = 0; i < flavors->num_flavors; i++) {
struct nfs4_secinfo4 *flavor = &flavors->flavors[i]; secinfo = &flavors->flavors[i];
if (flavor->flavor == RPC_AUTH_NULL || flavor->flavor == RPC_AUTH_UNIX) { switch (secinfo->flavor) {
pseudoflavor = flavor->flavor; case RPC_AUTH_NULL:
break; case RPC_AUTH_UNIX:
} else if (flavor->flavor == RPC_AUTH_GSS) { case RPC_AUTH_GSS:
oid.len = flavor->flavor_info.oid.len; pseudoflavor = rpcauth_get_pseudoflavor(secinfo->flavor,
oid.data = flavor->flavor_info.oid.data; &secinfo->flavor_info);
mech = gss_mech_get_by_OID(&oid); if (pseudoflavor != RPC_AUTH_MAXFLAVOR)
if (!mech) return pseudoflavor;
continue;
pseudoflavor = gss_svc_to_pseudoflavor(mech,
flavor->flavor_info.service);
gss_mech_put(mech);
break; break;
} }
} }
return pseudoflavor; return RPC_AUTH_UNIX;
} }
static rpc_authflavor_t nfs4_negotiate_security(struct inode *inode, struct qstr *name) static rpc_authflavor_t nfs4_negotiate_security(struct inode *inode, struct qstr *name)
......
...@@ -22,6 +22,8 @@ ...@@ -22,6 +22,8 @@
/* size of the nodename buffer */ /* size of the nodename buffer */
#define UNX_MAXNODENAME 32 #define UNX_MAXNODENAME 32
struct rpcsec_gss_info;
/* Work around the lack of a VFS credential */ /* Work around the lack of a VFS credential */
struct auth_cred { struct auth_cred {
kuid_t uid; kuid_t uid;
...@@ -103,6 +105,7 @@ struct rpc_authops { ...@@ -103,6 +105,7 @@ struct rpc_authops {
int (*pipes_create)(struct rpc_auth *); int (*pipes_create)(struct rpc_auth *);
void (*pipes_destroy)(struct rpc_auth *); void (*pipes_destroy)(struct rpc_auth *);
int (*list_pseudoflavors)(rpc_authflavor_t *, int); int (*list_pseudoflavors)(rpc_authflavor_t *, int);
rpc_authflavor_t (*info2flavor)(struct rpcsec_gss_info *);
}; };
struct rpc_credops { struct rpc_credops {
...@@ -137,6 +140,8 @@ int rpcauth_register(const struct rpc_authops *); ...@@ -137,6 +140,8 @@ int rpcauth_register(const struct rpc_authops *);
int rpcauth_unregister(const struct rpc_authops *); int rpcauth_unregister(const struct rpc_authops *);
struct rpc_auth * rpcauth_create(rpc_authflavor_t, struct rpc_clnt *); struct rpc_auth * rpcauth_create(rpc_authflavor_t, struct rpc_clnt *);
void rpcauth_release(struct rpc_auth *); void rpcauth_release(struct rpc_auth *);
rpc_authflavor_t rpcauth_get_pseudoflavor(rpc_authflavor_t,
struct rpcsec_gss_info *);
int rpcauth_list_flavors(rpc_authflavor_t *, int); int rpcauth_list_flavors(rpc_authflavor_t *, int);
struct rpc_cred * rpcauth_lookup_credcache(struct rpc_auth *, struct auth_cred *, 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 *); void rpcauth_init_cred(struct rpc_cred *, const struct auth_cred *, struct rpc_auth *, const struct rpc_credops *);
......
...@@ -127,9 +127,8 @@ struct gss_api_ops { ...@@ -127,9 +127,8 @@ struct gss_api_ops {
int gss_mech_register(struct gss_api_mech *); int gss_mech_register(struct gss_api_mech *);
void gss_mech_unregister(struct gss_api_mech *); void gss_mech_unregister(struct gss_api_mech *);
/* returns a mechanism descriptor given an OID, and increments the mechanism's /* Given a GSS security tuple, look up a pseudoflavor */
* reference count. */ rpc_authflavor_t gss_mech_info2flavor(struct rpcsec_gss_info *);
struct gss_api_mech * gss_mech_get_by_OID(struct xdr_netobj *);
/* Returns a reference to a mechanism, given a name like "krb5" etc. */ /* Returns a reference to a mechanism, given a name like "krb5" etc. */
struct gss_api_mech *gss_mech_get_by_name(const char *); struct gss_api_mech *gss_mech_get_by_name(const char *);
......
...@@ -123,6 +123,41 @@ rpcauth_unregister(const struct rpc_authops *ops) ...@@ -123,6 +123,41 @@ rpcauth_unregister(const struct rpc_authops *ops)
} }
EXPORT_SYMBOL_GPL(rpcauth_unregister); EXPORT_SYMBOL_GPL(rpcauth_unregister);
/**
* rpcauth_get_pseudoflavor - check if security flavor is supported
* @flavor: a security flavor
* @info: a GSS mech OID, quality of protection, and service value
*
* Verifies that an appropriate kernel module is available or already loaded.
* Returns an equivalent pseudoflavor, or RPC_AUTH_MAXFLAVOR if "flavor" is
* not supported locally.
*/
rpc_authflavor_t
rpcauth_get_pseudoflavor(rpc_authflavor_t flavor, struct rpcsec_gss_info *info)
{
const struct rpc_authops *ops;
rpc_authflavor_t pseudoflavor;
ops = auth_flavors[flavor];
if (ops == 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 RPC_AUTH_MAXFLAVOR;
}
spin_unlock(&rpc_authflavor_lock);
pseudoflavor = flavor;
if (ops->info2flavor != NULL)
pseudoflavor = ops->info2flavor(info);
module_put(ops->owner);
return pseudoflavor;
}
EXPORT_SYMBOL_GPL(rpcauth_get_pseudoflavor);
/** /**
* rpcauth_list_flavors - discover registered flavors and pseudoflavors * rpcauth_list_flavors - discover registered flavors and pseudoflavors
* @array: array to fill in * @array: array to fill in
......
...@@ -1641,6 +1641,7 @@ static const struct rpc_authops authgss_ops = { ...@@ -1641,6 +1641,7 @@ static const struct rpc_authops authgss_ops = {
.pipes_create = gss_pipes_dentries_create, .pipes_create = gss_pipes_dentries_create,
.pipes_destroy = gss_pipes_dentries_destroy, .pipes_destroy = gss_pipes_dentries_destroy,
.list_pseudoflavors = gss_mech_list_pseudoflavors, .list_pseudoflavors = gss_mech_list_pseudoflavors,
.info2flavor = gss_mech_info2flavor,
}; };
static const struct rpc_credops gss_credops = { static const struct rpc_credops gss_credops = {
......
...@@ -171,8 +171,7 @@ struct gss_api_mech * gss_mech_get_by_name(const char *name) ...@@ -171,8 +171,7 @@ struct gss_api_mech * gss_mech_get_by_name(const char *name)
} }
EXPORT_SYMBOL_GPL(gss_mech_get_by_name); EXPORT_SYMBOL_GPL(gss_mech_get_by_name);
struct gss_api_mech * static struct gss_api_mech *gss_mech_get_by_OID(struct rpcsec_gss_oid *obj)
gss_mech_get_by_OID(struct xdr_netobj *obj)
{ {
struct gss_api_mech *pos, *gm = NULL; struct gss_api_mech *pos, *gm = NULL;
...@@ -188,11 +187,8 @@ gss_mech_get_by_OID(struct xdr_netobj *obj) ...@@ -188,11 +187,8 @@ gss_mech_get_by_OID(struct xdr_netobj *obj)
} }
spin_unlock(&registered_mechs_lock); spin_unlock(&registered_mechs_lock);
return gm; return gm;
} }
EXPORT_SYMBOL_GPL(gss_mech_get_by_OID);
static inline int static inline int
mech_supports_pseudoflavor(struct gss_api_mech *gm, u32 pseudoflavor) mech_supports_pseudoflavor(struct gss_api_mech *gm, u32 pseudoflavor)
{ {
...@@ -282,6 +278,28 @@ gss_svc_to_pseudoflavor(struct gss_api_mech *gm, u32 service) ...@@ -282,6 +278,28 @@ gss_svc_to_pseudoflavor(struct gss_api_mech *gm, u32 service)
} }
EXPORT_SYMBOL_GPL(gss_svc_to_pseudoflavor); EXPORT_SYMBOL_GPL(gss_svc_to_pseudoflavor);
/**
* gss_mech_info2flavor - look up a pseudoflavor given a GSS tuple
* @info: a GSS mech OID, quality of protection, and service value
*
* Returns a matching pseudoflavor, or RPC_AUTH_MAXFLAVOR if the tuple is
* not supported.
*/
rpc_authflavor_t gss_mech_info2flavor(struct rpcsec_gss_info *info)
{
rpc_authflavor_t pseudoflavor;
struct gss_api_mech *gm;
gm = gss_mech_get_by_OID(&info->oid);
if (gm == NULL)
return RPC_AUTH_MAXFLAVOR;
pseudoflavor = gss_svc_to_pseudoflavor(gm, info->service);
gss_mech_put(gm);
return pseudoflavor;
}
u32 u32
gss_pseudoflavor_to_service(struct gss_api_mech *gm, u32 pseudoflavor) gss_pseudoflavor_to_service(struct gss_api_mech *gm, u32 pseudoflavor)
{ {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment