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