diff options
author | Chuck Lever <chuck.lever@oracle.com> | 2013-03-16 15:54:43 -0400 |
---|---|---|
committer | Trond Myklebust <Trond.Myklebust@netapp.com> | 2013-03-29 15:43:07 -0400 |
commit | 9568c5e9a61de49f67f524404a27a1014a8d7f1e (patch) | |
tree | 5bc8c5496bae61b6e5dfac11e620f7aef18a58b6 | |
parent | fb15b26f8ba3ff629a052faf3f4a4744585ca2dc (diff) |
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: Chuck Lever <chuck.lever@oracle.com>
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
-rw-r--r-- | fs/nfs/nfs4namespace.c | 41 | ||||
-rw-r--r-- | include/linux/sunrpc/auth.h | 5 | ||||
-rw-r--r-- | include/linux/sunrpc/gss_api.h | 5 | ||||
-rw-r--r-- | net/sunrpc/auth.c | 35 | ||||
-rw-r--r-- | net/sunrpc/auth_gss/auth_gss.c | 1 | ||||
-rw-r--r-- | net/sunrpc/auth_gss/gss_mech_switch.c | 28 |
6 files changed, 89 insertions, 26 deletions
diff --git a/fs/nfs/nfs4namespace.c b/fs/nfs/nfs4namespace.c index 88231c92317c..cdb0b41a4810 100644 --- a/fs/nfs/nfs4namespace.c +++ b/fs/nfs/nfs4namespace.c | |||
@@ -134,33 +134,38 @@ static size_t nfs_parse_server_name(char *string, size_t len, | |||
134 | return ret; | 134 | return ret; |
135 | } | 135 | } |
136 | 136 | ||
137 | /** | ||
138 | * nfs_find_best_sec - Find a security mechanism supported locally | ||
139 | * @flavors: List of security tuples returned by SECINFO procedure | ||
140 | * | ||
141 | * Return the pseudoflavor of the first security mechanism in | ||
142 | * "flavors" that is locally supported. Return RPC_AUTH_UNIX if | ||
143 | * no matching flavor is found in the array. The "flavors" array | ||
144 | * is searched in the order returned from the server, per RFC 3530 | ||
145 | * recommendation. | ||
146 | */ | ||
137 | rpc_authflavor_t nfs_find_best_sec(struct nfs4_secinfo_flavors *flavors) | 147 | rpc_authflavor_t nfs_find_best_sec(struct nfs4_secinfo_flavors *flavors) |
138 | { | 148 | { |
139 | struct gss_api_mech *mech; | 149 | rpc_authflavor_t pseudoflavor; |
140 | struct xdr_netobj oid; | 150 | struct nfs4_secinfo4 *secinfo; |
141 | unsigned int i; | 151 | unsigned int i; |
142 | rpc_authflavor_t pseudoflavor = RPC_AUTH_UNIX; | ||
143 | 152 | ||
144 | for (i = 0; i < flavors->num_flavors; i++) { | 153 | for (i = 0; i < flavors->num_flavors; i++) { |
145 | struct nfs4_secinfo4 *flavor = &flavors->flavors[i]; | 154 | secinfo = &flavors->flavors[i]; |
146 | 155 | ||
147 | if (flavor->flavor == RPC_AUTH_NULL || flavor->flavor == RPC_AUTH_UNIX) { | 156 | switch (secinfo->flavor) { |
148 | pseudoflavor = flavor->flavor; | 157 | case RPC_AUTH_NULL: |
149 | break; | 158 | case RPC_AUTH_UNIX: |
150 | } else if (flavor->flavor == RPC_AUTH_GSS) { | 159 | case RPC_AUTH_GSS: |
151 | oid.len = flavor->flavor_info.oid.len; | 160 | pseudoflavor = rpcauth_get_pseudoflavor(secinfo->flavor, |
152 | oid.data = flavor->flavor_info.oid.data; | 161 | &secinfo->flavor_info); |
153 | mech = gss_mech_get_by_OID(&oid); | 162 | if (pseudoflavor != RPC_AUTH_MAXFLAVOR) |
154 | if (!mech) | 163 | return pseudoflavor; |
155 | continue; | ||
156 | pseudoflavor = gss_svc_to_pseudoflavor(mech, | ||
157 | flavor->flavor_info.service); | ||
158 | gss_mech_put(mech); | ||
159 | break; | 164 | break; |
160 | } | 165 | } |
161 | } | 166 | } |
162 | 167 | ||
163 | return pseudoflavor; | 168 | return RPC_AUTH_UNIX; |
164 | } | 169 | } |
165 | 170 | ||
166 | static rpc_authflavor_t nfs4_negotiate_security(struct inode *inode, struct qstr *name) | 171 | 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 58fda1c3c783..6851da4cb416 100644 --- a/include/linux/sunrpc/auth.h +++ b/include/linux/sunrpc/auth.h | |||
@@ -22,6 +22,8 @@ | |||
22 | /* size of the nodename buffer */ | 22 | /* size of the nodename buffer */ |
23 | #define UNX_MAXNODENAME 32 | 23 | #define UNX_MAXNODENAME 32 |
24 | 24 | ||
25 | struct rpcsec_gss_info; | ||
26 | |||
25 | /* Work around the lack of a VFS credential */ | 27 | /* Work around the lack of a VFS credential */ |
26 | struct auth_cred { | 28 | struct auth_cred { |
27 | kuid_t uid; | 29 | kuid_t uid; |
@@ -103,6 +105,7 @@ struct rpc_authops { | |||
103 | int (*pipes_create)(struct rpc_auth *); | 105 | int (*pipes_create)(struct rpc_auth *); |
104 | void (*pipes_destroy)(struct rpc_auth *); | 106 | void (*pipes_destroy)(struct rpc_auth *); |
105 | int (*list_pseudoflavors)(rpc_authflavor_t *, int); | 107 | int (*list_pseudoflavors)(rpc_authflavor_t *, int); |
108 | rpc_authflavor_t (*info2flavor)(struct rpcsec_gss_info *); | ||
106 | }; | 109 | }; |
107 | 110 | ||
108 | struct rpc_credops { | 111 | struct rpc_credops { |
@@ -137,6 +140,8 @@ int rpcauth_register(const struct rpc_authops *); | |||
137 | int rpcauth_unregister(const struct rpc_authops *); | 140 | int rpcauth_unregister(const struct rpc_authops *); |
138 | struct rpc_auth * rpcauth_create(rpc_authflavor_t, struct rpc_clnt *); | 141 | struct rpc_auth * rpcauth_create(rpc_authflavor_t, struct rpc_clnt *); |
139 | void rpcauth_release(struct rpc_auth *); | 142 | void rpcauth_release(struct rpc_auth *); |
143 | rpc_authflavor_t rpcauth_get_pseudoflavor(rpc_authflavor_t, | ||
144 | struct rpcsec_gss_info *); | ||
140 | int rpcauth_list_flavors(rpc_authflavor_t *, int); | 145 | int rpcauth_list_flavors(rpc_authflavor_t *, int); |
141 | struct rpc_cred * rpcauth_lookup_credcache(struct rpc_auth *, struct auth_cred *, int); | 146 | struct rpc_cred * rpcauth_lookup_credcache(struct rpc_auth *, struct auth_cred *, int); |
142 | void rpcauth_init_cred(struct rpc_cred *, const struct auth_cred *, struct rpc_auth *, const struct rpc_credops *); | 147 | 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 98950e5a8877..aba7687ca884 100644 --- a/include/linux/sunrpc/gss_api.h +++ b/include/linux/sunrpc/gss_api.h | |||
@@ -127,9 +127,8 @@ struct gss_api_ops { | |||
127 | int gss_mech_register(struct gss_api_mech *); | 127 | int gss_mech_register(struct gss_api_mech *); |
128 | void gss_mech_unregister(struct gss_api_mech *); | 128 | void gss_mech_unregister(struct gss_api_mech *); |
129 | 129 | ||
130 | /* returns a mechanism descriptor given an OID, and increments the mechanism's | 130 | /* Given a GSS security tuple, look up a pseudoflavor */ |
131 | * reference count. */ | 131 | rpc_authflavor_t gss_mech_info2flavor(struct rpcsec_gss_info *); |
132 | struct gss_api_mech * gss_mech_get_by_OID(struct xdr_netobj *); | ||
133 | 132 | ||
134 | /* Returns a reference to a mechanism, given a name like "krb5" etc. */ | 133 | /* Returns a reference to a mechanism, given a name like "krb5" etc. */ |
135 | struct gss_api_mech *gss_mech_get_by_name(const char *); | 134 | struct gss_api_mech *gss_mech_get_by_name(const char *); |
diff --git a/net/sunrpc/auth.c b/net/sunrpc/auth.c index f5294047df77..9b81be8d9946 100644 --- a/net/sunrpc/auth.c +++ b/net/sunrpc/auth.c | |||
@@ -124,6 +124,41 @@ rpcauth_unregister(const struct rpc_authops *ops) | |||
124 | EXPORT_SYMBOL_GPL(rpcauth_unregister); | 124 | EXPORT_SYMBOL_GPL(rpcauth_unregister); |
125 | 125 | ||
126 | /** | 126 | /** |
127 | * rpcauth_get_pseudoflavor - check if security flavor is supported | ||
128 | * @flavor: a security flavor | ||
129 | * @info: a GSS mech OID, quality of protection, and service value | ||
130 | * | ||
131 | * Verifies that an appropriate kernel module is available or already loaded. | ||
132 | * Returns an equivalent pseudoflavor, or RPC_AUTH_MAXFLAVOR if "flavor" is | ||
133 | * not supported locally. | ||
134 | */ | ||
135 | rpc_authflavor_t | ||
136 | rpcauth_get_pseudoflavor(rpc_authflavor_t flavor, struct rpcsec_gss_info *info) | ||
137 | { | ||
138 | const struct rpc_authops *ops; | ||
139 | rpc_authflavor_t pseudoflavor; | ||
140 | |||
141 | ops = auth_flavors[flavor]; | ||
142 | if (ops == NULL) | ||
143 | request_module("rpc-auth-%u", flavor); | ||
144 | spin_lock(&rpc_authflavor_lock); | ||
145 | ops = auth_flavors[flavor]; | ||
146 | if (ops == NULL || !try_module_get(ops->owner)) { | ||
147 | spin_unlock(&rpc_authflavor_lock); | ||
148 | return RPC_AUTH_MAXFLAVOR; | ||
149 | } | ||
150 | spin_unlock(&rpc_authflavor_lock); | ||
151 | |||
152 | pseudoflavor = flavor; | ||
153 | if (ops->info2flavor != NULL) | ||
154 | pseudoflavor = ops->info2flavor(info); | ||
155 | |||
156 | module_put(ops->owner); | ||
157 | return pseudoflavor; | ||
158 | } | ||
159 | EXPORT_SYMBOL_GPL(rpcauth_get_pseudoflavor); | ||
160 | |||
161 | /** | ||
127 | * rpcauth_list_flavors - discover registered flavors and pseudoflavors | 162 | * rpcauth_list_flavors - discover registered flavors and pseudoflavors |
128 | * @array: array to fill in | 163 | * @array: array to fill in |
129 | * @size: size of "array" | 164 | * @size: size of "array" |
diff --git a/net/sunrpc/auth_gss/auth_gss.c b/net/sunrpc/auth_gss/auth_gss.c index 282dfb14db05..a7420076ef39 100644 --- a/net/sunrpc/auth_gss/auth_gss.c +++ b/net/sunrpc/auth_gss/auth_gss.c | |||
@@ -1641,6 +1641,7 @@ static const struct rpc_authops authgss_ops = { | |||
1641 | .pipes_create = gss_pipes_dentries_create, | 1641 | .pipes_create = gss_pipes_dentries_create, |
1642 | .pipes_destroy = gss_pipes_dentries_destroy, | 1642 | .pipes_destroy = gss_pipes_dentries_destroy, |
1643 | .list_pseudoflavors = gss_mech_list_pseudoflavors, | 1643 | .list_pseudoflavors = gss_mech_list_pseudoflavors, |
1644 | .info2flavor = gss_mech_info2flavor, | ||
1644 | }; | 1645 | }; |
1645 | 1646 | ||
1646 | static const struct rpc_credops gss_credops = { | 1647 | 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 f0f4eee63a35..4db66f5f490e 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) | |||
171 | } | 171 | } |
172 | EXPORT_SYMBOL_GPL(gss_mech_get_by_name); | 172 | EXPORT_SYMBOL_GPL(gss_mech_get_by_name); |
173 | 173 | ||
174 | struct gss_api_mech * | 174 | static struct gss_api_mech *gss_mech_get_by_OID(struct rpcsec_gss_oid *obj) |
175 | gss_mech_get_by_OID(struct xdr_netobj *obj) | ||
176 | { | 175 | { |
177 | struct gss_api_mech *pos, *gm = NULL; | 176 | struct gss_api_mech *pos, *gm = NULL; |
178 | 177 | ||
@@ -188,11 +187,8 @@ gss_mech_get_by_OID(struct xdr_netobj *obj) | |||
188 | } | 187 | } |
189 | spin_unlock(®istered_mechs_lock); | 188 | spin_unlock(®istered_mechs_lock); |
190 | return gm; | 189 | return gm; |
191 | |||
192 | } | 190 | } |
193 | 191 | ||
194 | EXPORT_SYMBOL_GPL(gss_mech_get_by_OID); | ||
195 | |||
196 | static inline int | 192 | static inline int |
197 | mech_supports_pseudoflavor(struct gss_api_mech *gm, u32 pseudoflavor) | 193 | mech_supports_pseudoflavor(struct gss_api_mech *gm, u32 pseudoflavor) |
198 | { | 194 | { |
@@ -282,6 +278,28 @@ gss_svc_to_pseudoflavor(struct gss_api_mech *gm, u32 service) | |||
282 | } | 278 | } |
283 | EXPORT_SYMBOL_GPL(gss_svc_to_pseudoflavor); | 279 | EXPORT_SYMBOL_GPL(gss_svc_to_pseudoflavor); |
284 | 280 | ||
281 | /** | ||
282 | * gss_mech_info2flavor - look up a pseudoflavor given a GSS tuple | ||
283 | * @info: a GSS mech OID, quality of protection, and service value | ||
284 | * | ||
285 | * Returns a matching pseudoflavor, or RPC_AUTH_MAXFLAVOR if the tuple is | ||
286 | * not supported. | ||
287 | */ | ||
288 | rpc_authflavor_t gss_mech_info2flavor(struct rpcsec_gss_info *info) | ||
289 | { | ||
290 | rpc_authflavor_t pseudoflavor; | ||
291 | struct gss_api_mech *gm; | ||
292 | |||
293 | gm = gss_mech_get_by_OID(&info->oid); | ||
294 | if (gm == NULL) | ||
295 | return RPC_AUTH_MAXFLAVOR; | ||
296 | |||
297 | pseudoflavor = gss_svc_to_pseudoflavor(gm, info->service); | ||
298 | |||
299 | gss_mech_put(gm); | ||
300 | return pseudoflavor; | ||
301 | } | ||
302 | |||
285 | u32 | 303 | u32 |
286 | gss_pseudoflavor_to_service(struct gss_api_mech *gm, u32 pseudoflavor) | 304 | gss_pseudoflavor_to_service(struct gss_api_mech *gm, u32 pseudoflavor) |
287 | { | 305 | { |