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