]>
Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * linux/net/sunrpc/gss_mech_switch.c | |
3 | * | |
4 | * Copyright (c) 2001 The Regents of the University of Michigan. | |
5 | * All rights reserved. | |
6 | * | |
7 | * J. Bruce Fields <bfields@umich.edu> | |
8 | * | |
cca5172a | 9 | * Redistribution and use in source and binary forms, with or without |
1da177e4 LT |
10 | * modification, are permitted provided that the following conditions |
11 | * are met: | |
12 | * | |
13 | * 1. Redistributions of source code must retain the above copyright | |
14 | * notice, this list of conditions and the following disclaimer. | |
15 | * 2. Redistributions in binary form must reproduce the above copyright | |
cca5172a | 16 | * notice, this list of conditions and the following disclaimer in the |
1da177e4 LT |
17 | * documentation and/or other materials provided with the distribution. |
18 | * 3. Neither the name of the University nor the names of its | |
19 | * contributors may be used to endorse or promote products derived | |
20 | * from this software without specific prior written permission. | |
21 | * | |
22 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED | |
23 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | |
24 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |
25 | * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | |
26 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | |
27 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | |
28 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR | |
29 | * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | |
30 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | |
31 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |
32 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
33 | * | |
34 | */ | |
35 | ||
36 | #include <linux/types.h> | |
37 | #include <linux/slab.h> | |
1da177e4 | 38 | #include <linux/module.h> |
f783288f | 39 | #include <linux/oid_registry.h> |
1da177e4 LT |
40 | #include <linux/sunrpc/msg_prot.h> |
41 | #include <linux/sunrpc/gss_asn1.h> | |
42 | #include <linux/sunrpc/auth_gss.h> | |
43 | #include <linux/sunrpc/svcauth_gss.h> | |
44 | #include <linux/sunrpc/gss_err.h> | |
45 | #include <linux/sunrpc/sched.h> | |
46 | #include <linux/sunrpc/gss_api.h> | |
47 | #include <linux/sunrpc/clnt.h> | |
48 | ||
f895b252 | 49 | #if IS_ENABLED(CONFIG_SUNRPC_DEBUG) |
1da177e4 LT |
50 | # define RPCDBG_FACILITY RPCDBG_AUTH |
51 | #endif | |
52 | ||
53 | static LIST_HEAD(registered_mechs); | |
54 | static DEFINE_SPINLOCK(registered_mechs_lock); | |
55 | ||
56 | static void | |
57 | gss_mech_free(struct gss_api_mech *gm) | |
58 | { | |
59 | struct pf_desc *pf; | |
60 | int i; | |
61 | ||
62 | for (i = 0; i < gm->gm_pf_num; i++) { | |
63 | pf = &gm->gm_pfs[i]; | |
a51482bd | 64 | kfree(pf->auth_domain_name); |
1da177e4 LT |
65 | pf->auth_domain_name = NULL; |
66 | } | |
67 | } | |
68 | ||
69 | static inline char * | |
70 | make_auth_domain_name(char *name) | |
71 | { | |
72 | static char *prefix = "gss/"; | |
73 | char *new; | |
74 | ||
75 | new = kmalloc(strlen(name) + strlen(prefix) + 1, GFP_KERNEL); | |
76 | if (new) { | |
77 | strcpy(new, prefix); | |
78 | strcat(new, name); | |
79 | } | |
80 | return new; | |
81 | } | |
82 | ||
83 | static int | |
84 | gss_mech_svc_setup(struct gss_api_mech *gm) | |
85 | { | |
86 | struct pf_desc *pf; | |
87 | int i, status; | |
88 | ||
89 | for (i = 0; i < gm->gm_pf_num; i++) { | |
90 | pf = &gm->gm_pfs[i]; | |
91 | pf->auth_domain_name = make_auth_domain_name(pf->name); | |
92 | status = -ENOMEM; | |
93 | if (pf->auth_domain_name == NULL) | |
94 | goto out; | |
95 | status = svcauth_gss_register_pseudoflavor(pf->pseudoflavor, | |
96 | pf->auth_domain_name); | |
97 | if (status) | |
98 | goto out; | |
99 | } | |
100 | return 0; | |
101 | out: | |
102 | gss_mech_free(gm); | |
103 | return status; | |
104 | } | |
105 | ||
5007220b CL |
106 | /** |
107 | * gss_mech_register - register a GSS mechanism | |
108 | * @gm: GSS mechanism handle | |
109 | * | |
110 | * Returns zero if successful, or a negative errno. | |
111 | */ | |
112 | int gss_mech_register(struct gss_api_mech *gm) | |
1da177e4 LT |
113 | { |
114 | int status; | |
115 | ||
116 | status = gss_mech_svc_setup(gm); | |
117 | if (status) | |
118 | return status; | |
119 | spin_lock(®istered_mechs_lock); | |
120 | list_add(&gm->gm_list, ®istered_mechs); | |
121 | spin_unlock(®istered_mechs_lock); | |
8885cb36 | 122 | dprintk("RPC: registered gss mechanism %s\n", gm->gm_name); |
1da177e4 LT |
123 | return 0; |
124 | } | |
7bd88269 | 125 | EXPORT_SYMBOL_GPL(gss_mech_register); |
1da177e4 | 126 | |
5007220b CL |
127 | /** |
128 | * gss_mech_unregister - release a GSS mechanism | |
129 | * @gm: GSS mechanism handle | |
130 | * | |
131 | */ | |
132 | void gss_mech_unregister(struct gss_api_mech *gm) | |
1da177e4 LT |
133 | { |
134 | spin_lock(®istered_mechs_lock); | |
135 | list_del(&gm->gm_list); | |
136 | spin_unlock(®istered_mechs_lock); | |
8885cb36 | 137 | dprintk("RPC: unregistered gss mechanism %s\n", gm->gm_name); |
1da177e4 LT |
138 | gss_mech_free(gm); |
139 | } | |
7bd88269 | 140 | EXPORT_SYMBOL_GPL(gss_mech_unregister); |
1da177e4 | 141 | |
0dc1531a | 142 | struct gss_api_mech *gss_mech_get(struct gss_api_mech *gm) |
1da177e4 LT |
143 | { |
144 | __module_get(gm->gm_owner); | |
145 | return gm; | |
146 | } | |
0dc1531a | 147 | EXPORT_SYMBOL(gss_mech_get); |
1da177e4 | 148 | |
c5f5e9c5 | 149 | static struct gss_api_mech * |
058c5c99 | 150 | _gss_mech_get_by_name(const char *name) |
1da177e4 LT |
151 | { |
152 | struct gss_api_mech *pos, *gm = NULL; | |
153 | ||
154 | spin_lock(®istered_mechs_lock); | |
155 | list_for_each_entry(pos, ®istered_mechs, gm_list) { | |
156 | if (0 == strcmp(name, pos->gm_name)) { | |
157 | if (try_module_get(pos->gm_owner)) | |
158 | gm = pos; | |
159 | break; | |
160 | } | |
161 | } | |
162 | spin_unlock(®istered_mechs_lock); | |
163 | return gm; | |
164 | ||
165 | } | |
166 | ||
058c5c99 BF |
167 | struct gss_api_mech * gss_mech_get_by_name(const char *name) |
168 | { | |
169 | struct gss_api_mech *gm = NULL; | |
170 | ||
171 | gm = _gss_mech_get_by_name(name); | |
172 | if (!gm) { | |
173 | request_module("rpc-auth-gss-%s", name); | |
174 | gm = _gss_mech_get_by_name(name); | |
175 | } | |
176 | return gm; | |
177 | } | |
1da177e4 | 178 | |
b1df7637 | 179 | struct gss_api_mech *gss_mech_get_by_OID(struct rpcsec_gss_oid *obj) |
7ebb9315 BS |
180 | { |
181 | struct gss_api_mech *pos, *gm = NULL; | |
f783288f CL |
182 | char buf[32]; |
183 | ||
184 | if (sprint_oid(obj->data, obj->len, buf, sizeof(buf)) < 0) | |
185 | return NULL; | |
186 | dprintk("RPC: %s(%s)\n", __func__, buf); | |
187 | request_module("rpc-auth-gss-%s", buf); | |
7ebb9315 BS |
188 | |
189 | spin_lock(®istered_mechs_lock); | |
190 | list_for_each_entry(pos, ®istered_mechs, gm_list) { | |
191 | if (obj->len == pos->gm_oid.len) { | |
192 | if (0 == memcmp(obj->data, pos->gm_oid.data, obj->len)) { | |
193 | if (try_module_get(pos->gm_owner)) | |
194 | gm = pos; | |
195 | break; | |
196 | } | |
197 | } | |
198 | } | |
199 | spin_unlock(®istered_mechs_lock); | |
200 | return gm; | |
7ebb9315 BS |
201 | } |
202 | ||
1da177e4 LT |
203 | static inline int |
204 | mech_supports_pseudoflavor(struct gss_api_mech *gm, u32 pseudoflavor) | |
205 | { | |
206 | int i; | |
207 | ||
208 | for (i = 0; i < gm->gm_pf_num; i++) { | |
209 | if (gm->gm_pfs[i].pseudoflavor == pseudoflavor) | |
210 | return 1; | |
211 | } | |
212 | return 0; | |
213 | } | |
214 | ||
c5f5e9c5 | 215 | static struct gss_api_mech *_gss_mech_get_by_pseudoflavor(u32 pseudoflavor) |
1da177e4 | 216 | { |
058c5c99 | 217 | struct gss_api_mech *gm = NULL, *pos; |
1da177e4 LT |
218 | |
219 | spin_lock(®istered_mechs_lock); | |
220 | list_for_each_entry(pos, ®istered_mechs, gm_list) { | |
7a9a7b77 | 221 | if (!mech_supports_pseudoflavor(pos, pseudoflavor)) |
1da177e4 | 222 | continue; |
1da177e4 LT |
223 | if (try_module_get(pos->gm_owner)) |
224 | gm = pos; | |
225 | break; | |
226 | } | |
227 | spin_unlock(®istered_mechs_lock); | |
228 | return gm; | |
229 | } | |
230 | ||
058c5c99 BF |
231 | struct gss_api_mech * |
232 | gss_mech_get_by_pseudoflavor(u32 pseudoflavor) | |
233 | { | |
234 | struct gss_api_mech *gm; | |
235 | ||
236 | gm = _gss_mech_get_by_pseudoflavor(pseudoflavor); | |
237 | ||
238 | if (!gm) { | |
239 | request_module("rpc-auth-gss-%u", pseudoflavor); | |
240 | gm = _gss_mech_get_by_pseudoflavor(pseudoflavor); | |
241 | } | |
242 | return gm; | |
243 | } | |
244 | ||
6a1a1e34 CL |
245 | /** |
246 | * gss_mech_list_pseudoflavors - Discover registered GSS pseudoflavors | |
247 | * @array: array to fill in | |
248 | * @size: size of "array" | |
249 | * | |
250 | * Returns the number of array items filled in, or a negative errno. | |
251 | * | |
252 | * The returned array is not sorted by any policy. Callers should not | |
253 | * rely on the order of the items in the returned array. | |
254 | */ | |
255 | int gss_mech_list_pseudoflavors(rpc_authflavor_t *array_ptr, int size) | |
8f70e95f BS |
256 | { |
257 | struct gss_api_mech *pos = NULL; | |
7bdf7415 | 258 | int j, i = 0; |
8f70e95f BS |
259 | |
260 | spin_lock(®istered_mechs_lock); | |
261 | list_for_each_entry(pos, ®istered_mechs, gm_list) { | |
6a1a1e34 | 262 | for (j = 0; j < pos->gm_pf_num; j++) { |
013448c5 TM |
263 | if (i >= size) { |
264 | spin_unlock(®istered_mechs_lock); | |
6a1a1e34 | 265 | return -ENOMEM; |
013448c5 | 266 | } |
7bdf7415 SD |
267 | array_ptr[i++] = pos->gm_pfs[j].pseudoflavor; |
268 | } | |
8f70e95f BS |
269 | } |
270 | spin_unlock(®istered_mechs_lock); | |
271 | return i; | |
272 | } | |
273 | ||
83523d08 CL |
274 | /** |
275 | * gss_svc_to_pseudoflavor - map a GSS service number to a pseudoflavor | |
276 | * @gm: GSS mechanism handle | |
277 | * @qop: GSS quality-of-protection value | |
278 | * @service: GSS service value | |
279 | * | |
280 | * Returns a matching security flavor, or RPC_AUTH_MAXFLAVOR if none is found. | |
281 | */ | |
282 | rpc_authflavor_t gss_svc_to_pseudoflavor(struct gss_api_mech *gm, u32 qop, | |
283 | u32 service) | |
c4170583 AA |
284 | { |
285 | int i; | |
286 | ||
287 | for (i = 0; i < gm->gm_pf_num; i++) { | |
83523d08 CL |
288 | if (gm->gm_pfs[i].qop == qop && |
289 | gm->gm_pfs[i].service == service) { | |
c4170583 AA |
290 | return gm->gm_pfs[i].pseudoflavor; |
291 | } | |
292 | } | |
83523d08 | 293 | return RPC_AUTH_MAXFLAVOR; |
c4170583 | 294 | } |
c4170583 | 295 | |
9568c5e9 CL |
296 | /** |
297 | * gss_mech_info2flavor - look up a pseudoflavor given a GSS tuple | |
298 | * @info: a GSS mech OID, quality of protection, and service value | |
299 | * | |
300 | * Returns a matching pseudoflavor, or RPC_AUTH_MAXFLAVOR if the tuple is | |
301 | * not supported. | |
302 | */ | |
303 | rpc_authflavor_t gss_mech_info2flavor(struct rpcsec_gss_info *info) | |
304 | { | |
305 | rpc_authflavor_t pseudoflavor; | |
306 | struct gss_api_mech *gm; | |
307 | ||
308 | gm = gss_mech_get_by_OID(&info->oid); | |
309 | if (gm == NULL) | |
310 | return RPC_AUTH_MAXFLAVOR; | |
311 | ||
83523d08 | 312 | pseudoflavor = gss_svc_to_pseudoflavor(gm, info->qop, info->service); |
9568c5e9 CL |
313 | |
314 | gss_mech_put(gm); | |
315 | return pseudoflavor; | |
316 | } | |
317 | ||
a77c806f CL |
318 | /** |
319 | * gss_mech_flavor2info - look up a GSS tuple for a given pseudoflavor | |
320 | * @pseudoflavor: GSS pseudoflavor to match | |
321 | * @info: rpcsec_gss_info structure to fill in | |
322 | * | |
323 | * Returns zero and fills in "info" if pseudoflavor matches a | |
324 | * supported mechanism. Otherwise a negative errno is returned. | |
325 | */ | |
326 | int gss_mech_flavor2info(rpc_authflavor_t pseudoflavor, | |
327 | struct rpcsec_gss_info *info) | |
328 | { | |
329 | struct gss_api_mech *gm; | |
330 | int i; | |
331 | ||
332 | gm = gss_mech_get_by_pseudoflavor(pseudoflavor); | |
333 | if (gm == NULL) | |
334 | return -ENOENT; | |
335 | ||
336 | for (i = 0; i < gm->gm_pf_num; i++) { | |
337 | if (gm->gm_pfs[i].pseudoflavor == pseudoflavor) { | |
338 | memcpy(info->oid.data, gm->gm_oid.data, gm->gm_oid.len); | |
339 | info->oid.len = gm->gm_oid.len; | |
340 | info->qop = gm->gm_pfs[i].qop; | |
341 | info->service = gm->gm_pfs[i].service; | |
342 | gss_mech_put(gm); | |
343 | return 0; | |
344 | } | |
345 | } | |
346 | ||
347 | gss_mech_put(gm); | |
348 | return -ENOENT; | |
c4170583 | 349 | } |
c4170583 | 350 | |
1da177e4 LT |
351 | u32 |
352 | gss_pseudoflavor_to_service(struct gss_api_mech *gm, u32 pseudoflavor) | |
353 | { | |
354 | int i; | |
355 | ||
356 | for (i = 0; i < gm->gm_pf_num; i++) { | |
357 | if (gm->gm_pfs[i].pseudoflavor == pseudoflavor) | |
358 | return gm->gm_pfs[i].service; | |
359 | } | |
360 | return 0; | |
361 | } | |
0dc1531a | 362 | EXPORT_SYMBOL(gss_pseudoflavor_to_service); |
1da177e4 | 363 | |
1da177e4 LT |
364 | char * |
365 | gss_service_to_auth_domain_name(struct gss_api_mech *gm, u32 service) | |
366 | { | |
367 | int i; | |
368 | ||
369 | for (i = 0; i < gm->gm_pf_num; i++) { | |
370 | if (gm->gm_pfs[i].service == service) | |
371 | return gm->gm_pfs[i].auth_domain_name; | |
372 | } | |
373 | return NULL; | |
374 | } | |
375 | ||
1da177e4 LT |
376 | void |
377 | gss_mech_put(struct gss_api_mech * gm) | |
378 | { | |
1df0cada BF |
379 | if (gm) |
380 | module_put(gm->gm_owner); | |
1da177e4 | 381 | } |
0dc1531a | 382 | EXPORT_SYMBOL(gss_mech_put); |
1da177e4 | 383 | |
1da177e4 LT |
384 | /* The mech could probably be determined from the token instead, but it's just |
385 | * as easy for now to pass it in. */ | |
386 | int | |
387 | gss_import_sec_context(const void *input_token, size_t bufsize, | |
388 | struct gss_api_mech *mech, | |
1f4c86c0 | 389 | struct gss_ctx **ctx_id, |
400f26b5 | 390 | time_t *endtime, |
1f4c86c0 | 391 | gfp_t gfp_mask) |
1da177e4 | 392 | { |
1f4c86c0 | 393 | if (!(*ctx_id = kzalloc(sizeof(**ctx_id), gfp_mask))) |
b891e4a0 | 394 | return -ENOMEM; |
1da177e4 LT |
395 | (*ctx_id)->mech_type = gss_mech_get(mech); |
396 | ||
400f26b5 SS |
397 | return mech->gm_ops->gss_import_sec_context(input_token, bufsize, |
398 | *ctx_id, endtime, gfp_mask); | |
1da177e4 LT |
399 | } |
400 | ||
401 | /* gss_get_mic: compute a mic over message and return mic_token. */ | |
402 | ||
403 | u32 | |
404 | gss_get_mic(struct gss_ctx *context_handle, | |
1da177e4 LT |
405 | struct xdr_buf *message, |
406 | struct xdr_netobj *mic_token) | |
407 | { | |
408 | return context_handle->mech_type->gm_ops | |
409 | ->gss_get_mic(context_handle, | |
1da177e4 LT |
410 | message, |
411 | mic_token); | |
412 | } | |
413 | ||
414 | /* gss_verify_mic: check whether the provided mic_token verifies message. */ | |
415 | ||
416 | u32 | |
417 | gss_verify_mic(struct gss_ctx *context_handle, | |
418 | struct xdr_buf *message, | |
00fd6e14 | 419 | struct xdr_netobj *mic_token) |
1da177e4 LT |
420 | { |
421 | return context_handle->mech_type->gm_ops | |
422 | ->gss_verify_mic(context_handle, | |
423 | message, | |
00fd6e14 | 424 | mic_token); |
1da177e4 LT |
425 | } |
426 | ||
7561042f KC |
427 | /* |
428 | * This function is called from both the client and server code. | |
429 | * Each makes guarantees about how much "slack" space is available | |
430 | * for the underlying function in "buf"'s head and tail while | |
431 | * performing the wrap. | |
432 | * | |
433 | * The client and server code allocate RPC_MAX_AUTH_SIZE extra | |
434 | * space in both the head and tail which is available for use by | |
435 | * the wrap function. | |
436 | * | |
437 | * Underlying functions should verify they do not use more than | |
438 | * RPC_MAX_AUTH_SIZE of extra space in either the head or tail | |
439 | * when performing the wrap. | |
440 | */ | |
293f1eb5 BF |
441 | u32 |
442 | gss_wrap(struct gss_ctx *ctx_id, | |
293f1eb5 BF |
443 | int offset, |
444 | struct xdr_buf *buf, | |
445 | struct page **inpages) | |
446 | { | |
447 | return ctx_id->mech_type->gm_ops | |
00fd6e14 | 448 | ->gss_wrap(ctx_id, offset, buf, inpages); |
293f1eb5 BF |
449 | } |
450 | ||
451 | u32 | |
452 | gss_unwrap(struct gss_ctx *ctx_id, | |
293f1eb5 BF |
453 | int offset, |
454 | struct xdr_buf *buf) | |
455 | { | |
456 | return ctx_id->mech_type->gm_ops | |
00fd6e14 | 457 | ->gss_unwrap(ctx_id, offset, buf); |
293f1eb5 BF |
458 | } |
459 | ||
460 | ||
1da177e4 LT |
461 | /* gss_delete_sec_context: free all resources associated with context_handle. |
462 | * Note this differs from the RFC 2744-specified prototype in that we don't | |
463 | * bother returning an output token, since it would never be used anyway. */ | |
464 | ||
465 | u32 | |
466 | gss_delete_sec_context(struct gss_ctx **context_handle) | |
467 | { | |
8885cb36 | 468 | dprintk("RPC: gss_delete_sec_context deleting %p\n", |
1da177e4 LT |
469 | *context_handle); |
470 | ||
471 | if (!*context_handle) | |
a02cec21 | 472 | return GSS_S_NO_CONTEXT; |
27724426 | 473 | if ((*context_handle)->internal_ctx_id) |
1da177e4 LT |
474 | (*context_handle)->mech_type->gm_ops |
475 | ->gss_delete_sec_context((*context_handle) | |
476 | ->internal_ctx_id); | |
1df0cada | 477 | gss_mech_put((*context_handle)->mech_type); |
1da177e4 LT |
478 | kfree(*context_handle); |
479 | *context_handle=NULL; | |
480 | return GSS_S_COMPLETE; | |
481 | } |