2013-01-28 20:31:24

by Chuck Lever III

[permalink] [raw]
Subject: [PATCH 1 0/5] Dynamically load security flavors by OID

Hi-

Currently the RPC client is able to dynamically load GSS security
flavor support by name or pseudoflavor number, but not by
[OID, qop, service] tuple. Such a tuple is provided by a server in
a SECINFO reply.

This means that if a server lists a tuple for, say, krb5p, in a
SECINFO reply, the client will pretend it doesn't support it if the
kernel module for krb5p is available on the system but does not
happen to be loaded at that moment.

This series implements support for loading the correct GSS module
before searching by GSS tuple. The series has seen some successful
light testing, so it's time for wider review. Suggestions about how
to test this series are welcome.

Trond, back when we discussed this change, there was also some
hand-waving about how to deal with WRONG_SEC on the pseudo-fs root.
Any thoughts or recollection about that?

I have 'But we have to worry about WRONG_SEC on the root. "Fixed in
4.1, but fucking bug in 4.0.' and then 'For NFSv4.0 root FS, we can
use a fixed array of mandated flavors, and the new "try to autoload
pseudo flavor" API'.

Somehow that's going to have to be folded into the 4.0 trunking
detection logic.

---

Chuck Lever (5):
SUNRPC: Consider qop when looking up pseudoflavors
SUNRPC: Find kernel module by OID
SUNRPC: Introduce rpcauth_lookup_gss_pseudoflavor()
SUNRPC: Define rpcsec_gss_info structure
NFS: Remove unneeded forward declaration


fs/nfs/nfs4namespace.c | 48 +++++++++++++++++------------
fs/nfs/nfs4xdr.c | 20 +++++++-----
include/linux/nfs_xdr.h | 24 +++-----------
include/linux/sunrpc/auth.h | 4 ++
include/linux/sunrpc/auth_gss.h | 6 ----
include/linux/sunrpc/gss_api.h | 25 +++++++++++++--
net/sunrpc/Kconfig | 1 +
net/sunrpc/auth.c | 31 +++++++++++++++++++
net/sunrpc/auth_gss/auth_gss.c | 1 +
net/sunrpc/auth_gss/gss_krb5_mech.c | 4 ++
net/sunrpc/auth_gss/gss_mech_switch.c | 55 ++++++++++++++++++++++++++++++---
11 files changed, 158 insertions(+), 61 deletions(-)

--
Chuck Lever


2013-01-28 20:31:34

by Chuck Lever III

[permalink] [raw]
Subject: [PATCH 1 1/5] NFS: Remove unneeded forward declaration

I've built with NFSv4 enabled and disabled. This forward
declaration does not seem to be required.

Signed-off-by: Chuck Lever <[email protected]>
---

include/linux/nfs_xdr.h | 3 ---
1 files changed, 0 insertions(+), 3 deletions(-)

diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h
index 29adb12..467167402 100644
--- a/include/linux/nfs_xdr.h
+++ b/include/linux/nfs_xdr.h
@@ -14,9 +14,6 @@
#define NFS_DEF_FILE_IO_SIZE (4096U)
#define NFS_MIN_FILE_IO_SIZE (1024U)

-/* Forward declaration for NFS v3 */
-struct nfs4_secinfo_flavors;
-
struct nfs4_string {
unsigned int len;
char *data;


2013-01-28 20:31:42

by Chuck Lever III

[permalink] [raw]
Subject: [PATCH 1 2/5] SUNRPC: Define rpcsec_gss_info structure

rpcsec_gss_info is a collection of information used by the NFSv4
SECINFO procedure. For simplicity and efficiency, I'd like to
return this structure from the NFSv4 XDR layer, and pass it
straight into the RPC client.

Define an RPC client structure that can be shared between NFS and
RPC.

Signed-off-by: Chuck Lever <[email protected]>
---

fs/nfs/nfs4namespace.c | 12 ++++++------
fs/nfs/nfs4xdr.c | 20 +++++++++++---------
include/linux/nfs_xdr.h | 21 +++++----------------
include/linux/sunrpc/auth_gss.h | 6 ------
include/linux/sunrpc/gss_api.h | 18 ++++++++++++++++++
5 files changed, 40 insertions(+), 37 deletions(-)

diff --git a/fs/nfs/nfs4namespace.c b/fs/nfs/nfs4namespace.c
index 1e09eb7..f97b0a8 100644
--- a/fs/nfs/nfs4namespace.c
+++ b/fs/nfs/nfs4namespace.c
@@ -137,23 +137,23 @@ rpc_authflavor_t nfs_find_best_sec(struct nfs4_secinfo_flavors *flavors)
{
struct gss_api_mech *mech;
struct xdr_netobj oid;
- int i;
+ unsigned int i;
rpc_authflavor_t pseudoflavor = RPC_AUTH_UNIX;

for (i = 0; i < flavors->num_flavors; i++) {
- struct nfs4_secinfo_flavor *flavor;
- flavor = &flavors->flavors[i];
+ struct nfs4_secinfo4 *flavor = &flavors->flavors[i];

if (flavor->flavor == RPC_AUTH_NULL || flavor->flavor == RPC_AUTH_UNIX) {
pseudoflavor = flavor->flavor;
break;
} else if (flavor->flavor == RPC_AUTH_GSS) {
- oid.len = flavor->gss.sec_oid4.len;
- oid.data = flavor->gss.sec_oid4.data;
+ oid.len = flavor->flavor_info.oid.len;
+ oid.data = flavor->flavor_info.oid.data;
mech = gss_mech_get_by_OID(&oid);
if (!mech)
continue;
- pseudoflavor = gss_svc_to_pseudoflavor(mech, flavor->gss.service);
+ pseudoflavor = gss_svc_to_pseudoflavor(mech,
+ flavor->flavor_info.service);
gss_mech_put(mech);
break;
}
diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c
index 26b1439..c445b8c 100644
--- a/fs/nfs/nfs4xdr.c
+++ b/fs/nfs/nfs4xdr.c
@@ -5209,27 +5209,29 @@ static int decode_delegreturn(struct xdr_stream *xdr)
return decode_op_hdr(xdr, OP_DELEGRETURN);
}

-static int decode_secinfo_gss(struct xdr_stream *xdr, struct nfs4_secinfo_flavor *flavor)
+static int decode_secinfo_gss(struct xdr_stream *xdr, struct nfs4_secinfo4 *flavor)
{
+ u32 oid_len;
__be32 *p;

p = xdr_inline_decode(xdr, 4);
if (unlikely(!p))
goto out_overflow;
- flavor->gss.sec_oid4.len = be32_to_cpup(p);
- if (flavor->gss.sec_oid4.len > GSS_OID_MAX_LEN)
+ oid_len = be32_to_cpup(p);
+ if (oid_len > GSS_OID_MAX_LEN)
goto out_err;

- p = xdr_inline_decode(xdr, flavor->gss.sec_oid4.len);
+ p = xdr_inline_decode(xdr, oid_len);
if (unlikely(!p))
goto out_overflow;
- memcpy(flavor->gss.sec_oid4.data, p, flavor->gss.sec_oid4.len);
+ memcpy(flavor->flavor_info.oid.data, p, oid_len);
+ flavor->flavor_info.oid.len = oid_len;

p = xdr_inline_decode(xdr, 8);
if (unlikely(!p))
goto out_overflow;
- flavor->gss.qop4 = be32_to_cpup(p++);
- flavor->gss.service = be32_to_cpup(p);
+ flavor->flavor_info.qop = be32_to_cpup(p++);
+ flavor->flavor_info.service = be32_to_cpup(p);

return 0;

@@ -5242,10 +5244,10 @@ out_err:

static int decode_secinfo_common(struct xdr_stream *xdr, struct nfs4_secinfo_res *res)
{
- struct nfs4_secinfo_flavor *sec_flavor;
+ struct nfs4_secinfo4 *sec_flavor;
+ unsigned int i, num_flavors;
int status;
__be32 *p;
- int i, num_flavors;

p = xdr_inline_decode(xdr, 4);
if (unlikely(!p))
diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h
index 467167402..92a6e7c 100644
--- a/include/linux/nfs_xdr.h
+++ b/include/linux/nfs_xdr.h
@@ -1046,25 +1046,14 @@ struct nfs4_fs_locations_res {
struct nfs4_fs_locations *fs_locations;
};

-struct nfs4_secinfo_oid {
- unsigned int len;
- char data[GSS_OID_MAX_LEN];
-};
-
-struct nfs4_secinfo_gss {
- struct nfs4_secinfo_oid sec_oid4;
- unsigned int qop4;
- unsigned int service;
-};
-
-struct nfs4_secinfo_flavor {
- unsigned int flavor;
- struct nfs4_secinfo_gss gss;
+struct nfs4_secinfo4 {
+ u32 flavor;
+ struct rpcsec_gss_info flavor_info;
};

struct nfs4_secinfo_flavors {
- unsigned int num_flavors;
- struct nfs4_secinfo_flavor flavors[0];
+ unsigned int num_flavors;
+ struct nfs4_secinfo4 flavors[0];
};

struct nfs4_secinfo_arg {
diff --git a/include/linux/sunrpc/auth_gss.h b/include/linux/sunrpc/auth_gss.h
index f1cfd4c..39de1f30 100644
--- a/include/linux/sunrpc/auth_gss.h
+++ b/include/linux/sunrpc/auth_gss.h
@@ -28,12 +28,6 @@ enum rpc_gss_proc {
RPC_GSS_PROC_DESTROY = 3
};

-enum rpc_gss_svc {
- RPC_GSS_SVC_NONE = 1,
- RPC_GSS_SVC_INTEGRITY = 2,
- RPC_GSS_SVC_PRIVACY = 3
-};
-
/* on-the-wire gss cred: */
struct rpc_gss_wire_cred {
u32 gc_v; /* version */
diff --git a/include/linux/sunrpc/gss_api.h b/include/linux/sunrpc/gss_api.h
index a19e254..6381b5b 100644
--- a/include/linux/sunrpc/gss_api.h
+++ b/include/linux/sunrpc/gss_api.h
@@ -27,8 +27,26 @@ struct gss_ctx {
#define GSS_C_NO_CONTEXT ((struct gss_ctx *) 0)
#define GSS_C_NULL_OID ((struct xdr_netobj) 0)

+/* RPCSEC_GSS services, from RFC 2203, section 5 */
+enum rpc_gss_svc {
+ /* The enumerated value for 0 is reserved */
+ RPC_GSS_SVC_NONE = 1,
+ RPC_GSS_SVC_INTEGRITY = 2,
+ RPC_GSS_SVC_PRIVACY = 3
+};
+
/*XXX arbitrary length - is this set somewhere? */
#define GSS_OID_MAX_LEN 32
+struct rpcsec_gss_oid {
+ unsigned int len;
+ u8 data[GSS_OID_MAX_LEN];
+};
+
+struct rpcsec_gss_info {
+ struct rpcsec_gss_oid oid;
+ u32 qop;
+ u32 service;
+};

/* gss-api prototypes; note that these are somewhat simplified versions of
* the prototypes specified in RFC 2744. */


2013-01-29 19:40:10

by J. Bruce Fields

[permalink] [raw]
Subject: Re: [PATCH 1 4/5] SUNRPC: Find kernel module by OID

On Mon, Jan 28, 2013 at 03:31:57PM -0500, Chuck Lever wrote:
> The current GSS mech switch can find and load GSS pseudoflavor
> modules by name or pseudoflavor number, but cannot find them by OID.
>
> This is important when dealing with a SECINFO request, especially
> early in the boot process. A SECINFO reply has a list of flavors
> the server can support, but they are listed by OID. Our client
> currently can choose only flavors that are already loaded. We want
> to be able to choose flavors where the module is available but not
> yet loaded.
>
> Add a way for the GSS mech switch to load GSS pseudoflavor support
> by OID.

OK, and I was wondering how you'd set up the module aliases--I didn't
know about sprint_oid. Looks nice.

--b.

>
> Signed-off-by: Chuck Lever <[email protected]>
> ---
>
> net/sunrpc/Kconfig | 1 +
> net/sunrpc/auth_gss/gss_krb5_mech.c | 1 +
> net/sunrpc/auth_gss/gss_mech_switch.c | 7 +++++++
> 3 files changed, 9 insertions(+), 0 deletions(-)
>
> diff --git a/net/sunrpc/Kconfig b/net/sunrpc/Kconfig
> index 03d03e3..f97f555 100644
> --- a/net/sunrpc/Kconfig
> +++ b/net/sunrpc/Kconfig
> @@ -3,6 +3,7 @@ config SUNRPC
>
> config SUNRPC_GSS
> tristate
> + select OID_REGISTRY
>
> config SUNRPC_BACKCHANNEL
> bool
> diff --git a/net/sunrpc/auth_gss/gss_krb5_mech.c b/net/sunrpc/auth_gss/gss_krb5_mech.c
> index d3611f1..8b1005d 100644
> --- a/net/sunrpc/auth_gss/gss_krb5_mech.c
> +++ b/net/sunrpc/auth_gss/gss_krb5_mech.c
> @@ -750,6 +750,7 @@ MODULE_ALIAS("rpc-auth-gss-krb5p");
> MODULE_ALIAS("rpc-auth-gss-390003");
> MODULE_ALIAS("rpc-auth-gss-390004");
> MODULE_ALIAS("rpc-auth-gss-390005");
> +MODULE_ALIAS("rpc-auth-gss-1.2.840.113554.1.2.2");
>
> static struct gss_api_mech gss_kerberos_mech = {
> .gm_name = "krb5",
> diff --git a/net/sunrpc/auth_gss/gss_mech_switch.c b/net/sunrpc/auth_gss/gss_mech_switch.c
> index 883b5b8..64dbf07a 100644
> --- a/net/sunrpc/auth_gss/gss_mech_switch.c
> +++ b/net/sunrpc/auth_gss/gss_mech_switch.c
> @@ -36,6 +36,7 @@
> #include <linux/types.h>
> #include <linux/slab.h>
> #include <linux/module.h>
> +#include <linux/oid_registry.h>
> #include <linux/sunrpc/msg_prot.h>
> #include <linux/sunrpc/gss_asn1.h>
> #include <linux/sunrpc/auth_gss.h>
> @@ -174,6 +175,12 @@ EXPORT_SYMBOL_GPL(gss_mech_get_by_name);
> static struct gss_api_mech *gss_mech_get_by_OID(struct rpcsec_gss_oid *obj)
> {
> struct gss_api_mech *pos, *gm = NULL;
> + char buf[32];
> +
> + if (sprint_oid(obj->data, obj->len, buf, sizeof(buf)) < 0)
> + return NULL;
> + dprintk("RPC: %s: oid: %s\n", __func__, buf);
> + request_module("rpc-auth-gss-%s", buf);
>
> spin_lock(&registered_mechs_lock);
> list_for_each_entry(pos, &registered_mechs, gm_list) {
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-nfs" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html

2013-01-28 20:31:50

by Chuck Lever III

[permalink] [raw]
Subject: [PATCH 1 3/5] SUNRPC: Introduce rpcauth_lookup_gss_pseudoflavor()

Calling gss_foo() in the NFS client without first ensuring the GSS
kernel module is pinned is not safe. Introduce an rpcauth API that
pins the GSS module properly before performing the pseudoflavor
lookup.

The refactored code addresses a bug: If nfs_find_best_sec() matches
on an OID, but the "service" part of the tuple is not supported by
our RPC client, nfs_find_best_sec() currently returns
RPC_AUTH_MAXFLAVOR, but should return RPC_AUTH_UNIX.

Signed-off-by: Chuck Lever <[email protected]>
---

fs/nfs/nfs4namespace.c | 44 ++++++++++++++++++++-------------
include/linux/sunrpc/auth.h | 4 +++
include/linux/sunrpc/gss_api.h | 5 ++--
net/sunrpc/auth.c | 31 +++++++++++++++++++++++
net/sunrpc/auth_gss/auth_gss.c | 1 +
net/sunrpc/auth_gss/gss_mech_switch.c | 28 +++++++++++++++++----
6 files changed, 88 insertions(+), 25 deletions(-)

diff --git a/fs/nfs/nfs4namespace.c b/fs/nfs/nfs4namespace.c
index f97b0a8..7ab7ab7 100644
--- a/fs/nfs/nfs4namespace.c
+++ b/fs/nfs/nfs4namespace.c
@@ -133,33 +133,43 @@ static size_t nfs_parse_server_name(char *string, size_t len,
return ret;
}

+/**
+ * nfs_find_best_sec - Find a security mechanism supported locally
+ * @flavors: List of security tuples returned by SECINFO procedure
+ *
+ * The "flavors" array is searched in the order returned from the server,
+ * per RFC 3530. Return the pseudoflavor of the first security mechanism
+ * in the array we support. RPC_AUTH_UNIX is returned if no matching
+ * flavor is found in the array.
+ */
rpc_authflavor_t nfs_find_best_sec(struct nfs4_secinfo_flavors *flavors)
{
- struct gss_api_mech *mech;
- struct xdr_netobj oid;
unsigned int i;
- rpc_authflavor_t pseudoflavor = RPC_AUTH_UNIX;

for (i = 0; i < flavors->num_flavors; i++) {
struct nfs4_secinfo4 *flavor = &flavors->flavors[i];
-
- if (flavor->flavor == RPC_AUTH_NULL || flavor->flavor == RPC_AUTH_UNIX) {
- pseudoflavor = flavor->flavor;
- break;
- } else if (flavor->flavor == RPC_AUTH_GSS) {
- oid.len = flavor->flavor_info.oid.len;
- oid.data = flavor->flavor_info.oid.data;
- mech = gss_mech_get_by_OID(&oid);
- if (!mech)
- continue;
- pseudoflavor = gss_svc_to_pseudoflavor(mech,
- flavor->flavor_info.service);
- gss_mech_put(mech);
+ rpc_authflavor_t pseudoflavor;
+
+ switch (flavor->flavor) {
+ case RPC_AUTH_NULL:
+ case RPC_AUTH_UNIX:
+ dprintk("%s: Using supported flavor %d\n",
+ __func__, flavor->flavor);
+ return flavor->flavor;
+ case RPC_AUTH_GSS:
+ pseudoflavor = rpcauth_lookup_gss_pseudoflavor(
+ &flavor->flavor_info);
+ if (pseudoflavor != RPC_AUTH_MAXFLAVOR) {
+ dprintk("%s: Using supported GSS flavor %d\n",
+ __func__, pseudoflavor);
+ return pseudoflavor;
+ }
break;
}
}

- return pseudoflavor;
+ dprintk("%s: No supported flavor found, using AUTH_UNIX\n", __func__);
+ return RPC_AUTH_UNIX;
}

static rpc_authflavor_t nfs4_negotiate_security(struct inode *inode, struct qstr *name)
diff --git a/include/linux/sunrpc/auth.h b/include/linux/sunrpc/auth.h
index f25ba92..b1a49a5 100644
--- a/include/linux/sunrpc/auth.h
+++ b/include/linux/sunrpc/auth.h
@@ -21,6 +21,8 @@
/* size of the nodename buffer */
#define UNX_MAXNODENAME 32

+struct rpcsec_gss_info;
+
/* Work around the lack of a VFS credential */
struct auth_cred {
uid_t uid;
@@ -102,6 +104,7 @@ struct rpc_authops {
int (*pipes_create)(struct rpc_auth *);
void (*pipes_destroy)(struct rpc_auth *);
int (*list_pseudoflavors)(rpc_authflavor_t *, int);
+ rpc_authflavor_t (*lookup_pseudoflavor)(struct rpcsec_gss_info *);
};

struct rpc_credops {
@@ -136,6 +139,7 @@ int rpcauth_register(const struct rpc_authops *);
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_lookup_gss_pseudoflavor(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 6381b5b..a7bbe96 100644
--- a/include/linux/sunrpc/gss_api.h
+++ b/include/linux/sunrpc/gss_api.h
@@ -135,9 +135,8 @@ struct gss_api_ops {
int gss_mech_register(struct gss_api_mech *);
void gss_mech_unregister(struct gss_api_mech *);

-/* returns a mechanism descriptor given an OID, and increments the mechanism's
- * reference count. */
-struct gss_api_mech * gss_mech_get_by_OID(struct xdr_netobj *);
+/* Given a GSS security tuple, look up a pseudoflavor */
+rpc_authflavor_t gss_mech_lookup_pseudoflavor(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 b5c067b..5e160c9 100644
--- a/net/sunrpc/auth.c
+++ b/net/sunrpc/auth.c
@@ -124,6 +124,37 @@ rpcauth_unregister(const struct rpc_authops *ops)
EXPORT_SYMBOL_GPL(rpcauth_unregister);

/**
+ * rpcauth_lookup_gss_pseudoflavor - find pseudoflavor matching 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 locally.
+ */
+rpc_authflavor_t
+rpcauth_lookup_gss_pseudoflavor(struct rpcsec_gss_info *info)
+{
+ const struct rpc_authops *ops;
+ rpc_authflavor_t pseudoflavor;
+
+ if ((ops = auth_flavors[RPC_AUTH_GSS]) == NULL)
+ request_module("rpc-auth-%u", RPC_AUTH_GSS);
+ spin_lock(&rpc_authflavor_lock);
+ ops = auth_flavors[RPC_AUTH_GSS];
+ if (ops == NULL || !try_module_get(ops->owner)) {
+ spin_unlock(&rpc_authflavor_lock);
+ dprintk("RPC: %s: failed to pin GSS module\n", __func__);
+ return RPC_AUTH_MAXFLAVOR;
+ }
+ spin_unlock(&rpc_authflavor_lock);
+
+ pseudoflavor = ops->lookup_pseudoflavor(info);
+
+ module_put(ops->owner);
+ return pseudoflavor;
+}
+EXPORT_SYMBOL_GPL(rpcauth_lookup_gss_pseudoflavor);
+
+/**
* 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 6e5c824..f2e8f45 100644
--- a/net/sunrpc/auth_gss/auth_gss.c
+++ b/net/sunrpc/auth_gss/auth_gss.c
@@ -1629,6 +1629,7 @@ static const struct rpc_authops authgss_ops = {
.pipes_create = gss_pipes_dentries_create,
.pipes_destroy = gss_pipes_dentries_destroy,
.list_pseudoflavors = gss_mech_list_pseudoflavors,
+ .lookup_pseudoflavor = gss_mech_lookup_pseudoflavor,
};

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 b174fcd..883b5b8 100644
--- a/net/sunrpc/auth_gss/gss_mech_switch.c
+++ b/net/sunrpc/auth_gss/gss_mech_switch.c
@@ -171,8 +171,7 @@ struct gss_api_mech * gss_mech_get_by_name(const char *name)
}
EXPORT_SYMBOL_GPL(gss_mech_get_by_name);

-struct gss_api_mech *
-gss_mech_get_by_OID(struct xdr_netobj *obj)
+static struct gss_api_mech *gss_mech_get_by_OID(struct rpcsec_gss_oid *obj)
{
struct gss_api_mech *pos, *gm = NULL;

@@ -188,11 +187,8 @@ gss_mech_get_by_OID(struct xdr_netobj *obj)
}
spin_unlock(&registered_mechs_lock);
return gm;
-
}

-EXPORT_SYMBOL_GPL(gss_mech_get_by_OID);
-
static inline int
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)
}
EXPORT_SYMBOL_GPL(gss_svc_to_pseudoflavor);

+/**
+ * gss_mech_lookup_pseudoflavor - 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_lookup_pseudoflavor(struct rpcsec_gss_info *info)
+{
+ rpc_authflavor_t pseudoflavor;
+ struct gss_api_mech *mech;
+
+ mech = gss_mech_get_by_OID(&info->oid);
+ if (mech == NULL)
+ return RPC_AUTH_MAXFLAVOR;
+
+ pseudoflavor = gss_svc_to_pseudoflavor(mech, info->service);
+
+ gss_mech_put(mech);
+ return pseudoflavor;
+}
+
u32
gss_pseudoflavor_to_service(struct gss_api_mech *gm, u32 pseudoflavor)
{


2013-01-28 20:32:07

by Chuck Lever III

[permalink] [raw]
Subject: [PATCH 1 5/5] SUNRPC: Consider qop when looking up pseudoflavors

A list of GSS tuples are returned in response to a SECINFO request.
The client is supposed to pick a pseudoflavor it supports that
corresponds to one of the tuples returned by the server.

Currently we ignore a GSS tuple's "qop" value. A pseudoflavor is
chosen based on the OID and service. NFSv4 appears to support
only one qop value: zero. So this omission has not had much effect.

However, if a server returns something other than zero in that
field, we won't catch it, and may behave in incorrect or unexpected
ways.

Signed-off-by: Chuck Lever <[email protected]>
---

include/linux/sunrpc/gss_api.h | 2 ++
net/sunrpc/auth_gss/gss_krb5_mech.c | 3 +++
net/sunrpc/auth_gss/gss_mech_switch.c | 22 +++++++++++++++++++++-
3 files changed, 26 insertions(+), 1 deletions(-)

diff --git a/include/linux/sunrpc/gss_api.h b/include/linux/sunrpc/gss_api.h
index a7bbe96..7daacea 100644
--- a/include/linux/sunrpc/gss_api.h
+++ b/include/linux/sunrpc/gss_api.h
@@ -26,6 +26,7 @@ struct gss_ctx {
#define GSS_C_NO_BUFFER ((struct xdr_netobj) 0)
#define GSS_C_NO_CONTEXT ((struct gss_ctx *) 0)
#define GSS_C_NULL_OID ((struct xdr_netobj) 0)
+#define GSS_C_QOP_DEFAULT (0)

/* RPCSEC_GSS services, from RFC 2203, section 5 */
enum rpc_gss_svc {
@@ -82,6 +83,7 @@ char *gss_service_to_auth_domain_name(struct gss_api_mech *, u32 service);

struct pf_desc {
u32 pseudoflavor;
+ u32 qop;
u32 service;
char *name;
char *auth_domain_name;
diff --git a/net/sunrpc/auth_gss/gss_krb5_mech.c b/net/sunrpc/auth_gss/gss_krb5_mech.c
index 8b1005d..9d99f3a 100644
--- a/net/sunrpc/auth_gss/gss_krb5_mech.c
+++ b/net/sunrpc/auth_gss/gss_krb5_mech.c
@@ -729,16 +729,19 @@ static const struct gss_api_ops gss_kerberos_ops = {
static struct pf_desc gss_kerberos_pfs[] = {
[0] = {
.pseudoflavor = RPC_AUTH_GSS_KRB5,
+ .qop = GSS_C_QOP_DEFAULT,
.service = RPC_GSS_SVC_NONE,
.name = "krb5",
},
[1] = {
.pseudoflavor = RPC_AUTH_GSS_KRB5I,
+ .qop = GSS_C_QOP_DEFAULT,
.service = RPC_GSS_SVC_INTEGRITY,
.name = "krb5i",
},
[2] = {
.pseudoflavor = RPC_AUTH_GSS_KRB5P,
+ .qop = GSS_C_QOP_DEFAULT,
.service = RPC_GSS_SVC_PRIVACY,
.name = "krb5p",
},
diff --git a/net/sunrpc/auth_gss/gss_mech_switch.c b/net/sunrpc/auth_gss/gss_mech_switch.c
index 64dbf07a..731e298 100644
--- a/net/sunrpc/auth_gss/gss_mech_switch.c
+++ b/net/sunrpc/auth_gss/gss_mech_switch.c
@@ -285,6 +285,25 @@ gss_svc_to_pseudoflavor(struct gss_api_mech *gm, u32 service)
}
EXPORT_SYMBOL_GPL(gss_svc_to_pseudoflavor);

+/*
+ * Same as gss_svc_to_pseudoflavor, but includes a search on the qop
+ * as well.
+ */
+static rpc_authflavor_t gss_qop_svc_to_pseudoflavor(struct gss_api_mech *gm,
+ u32 qop, u32 service)
+{
+ int i;
+
+ for (i = 0; i < gm->gm_pf_num; i++) {
+ if (gm->gm_pfs[i].qop == qop &&
+ gm->gm_pfs[i].service == service) {
+ return gm->gm_pfs[i].pseudoflavor;
+ }
+ }
+
+ return RPC_AUTH_MAXFLAVOR;
+}
+
/**
* gss_mech_lookup_pseudoflavor - look up a pseudoflavor given a GSS tuple
* @info: a GSS mech OID, quality of protection, and service value
@@ -301,7 +320,8 @@ rpc_authflavor_t gss_mech_lookup_pseudoflavor(struct rpcsec_gss_info *info)
if (mech == NULL)
return RPC_AUTH_MAXFLAVOR;

- pseudoflavor = gss_svc_to_pseudoflavor(mech, info->service);
+ pseudoflavor = gss_qop_svc_to_pseudoflavor(mech,
+ info->qop, info->service);

gss_mech_put(mech);
return pseudoflavor;


2013-01-28 20:31:59

by Chuck Lever III

[permalink] [raw]
Subject: [PATCH 1 4/5] SUNRPC: Find kernel module by OID

The current GSS mech switch can find and load GSS pseudoflavor
modules by name or pseudoflavor number, but cannot find them by OID.

This is important when dealing with a SECINFO request, especially
early in the boot process. A SECINFO reply has a list of flavors
the server can support, but they are listed by OID. Our client
currently can choose only flavors that are already loaded. We want
to be able to choose flavors where the module is available but not
yet loaded.

Add a way for the GSS mech switch to load GSS pseudoflavor support
by OID.

Signed-off-by: Chuck Lever <[email protected]>
---

net/sunrpc/Kconfig | 1 +
net/sunrpc/auth_gss/gss_krb5_mech.c | 1 +
net/sunrpc/auth_gss/gss_mech_switch.c | 7 +++++++
3 files changed, 9 insertions(+), 0 deletions(-)

diff --git a/net/sunrpc/Kconfig b/net/sunrpc/Kconfig
index 03d03e3..f97f555 100644
--- a/net/sunrpc/Kconfig
+++ b/net/sunrpc/Kconfig
@@ -3,6 +3,7 @@ config SUNRPC

config SUNRPC_GSS
tristate
+ select OID_REGISTRY

config SUNRPC_BACKCHANNEL
bool
diff --git a/net/sunrpc/auth_gss/gss_krb5_mech.c b/net/sunrpc/auth_gss/gss_krb5_mech.c
index d3611f1..8b1005d 100644
--- a/net/sunrpc/auth_gss/gss_krb5_mech.c
+++ b/net/sunrpc/auth_gss/gss_krb5_mech.c
@@ -750,6 +750,7 @@ MODULE_ALIAS("rpc-auth-gss-krb5p");
MODULE_ALIAS("rpc-auth-gss-390003");
MODULE_ALIAS("rpc-auth-gss-390004");
MODULE_ALIAS("rpc-auth-gss-390005");
+MODULE_ALIAS("rpc-auth-gss-1.2.840.113554.1.2.2");

static struct gss_api_mech gss_kerberos_mech = {
.gm_name = "krb5",
diff --git a/net/sunrpc/auth_gss/gss_mech_switch.c b/net/sunrpc/auth_gss/gss_mech_switch.c
index 883b5b8..64dbf07a 100644
--- a/net/sunrpc/auth_gss/gss_mech_switch.c
+++ b/net/sunrpc/auth_gss/gss_mech_switch.c
@@ -36,6 +36,7 @@
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/module.h>
+#include <linux/oid_registry.h>
#include <linux/sunrpc/msg_prot.h>
#include <linux/sunrpc/gss_asn1.h>
#include <linux/sunrpc/auth_gss.h>
@@ -174,6 +175,12 @@ EXPORT_SYMBOL_GPL(gss_mech_get_by_name);
static struct gss_api_mech *gss_mech_get_by_OID(struct rpcsec_gss_oid *obj)
{
struct gss_api_mech *pos, *gm = NULL;
+ char buf[32];
+
+ if (sprint_oid(obj->data, obj->len, buf, sizeof(buf)) < 0)
+ return NULL;
+ dprintk("RPC: %s: oid: %s\n", __func__, buf);
+ request_module("rpc-auth-gss-%s", buf);

spin_lock(&registered_mechs_lock);
list_for_each_entry(pos, &registered_mechs, gm_list) {