Line data Source code
1 : /*
2 : * Copyright (c) 2021, PADL Software Pty Ltd.
3 : * All rights reserved.
4 : *
5 : * Portions Copyright (c) 2019 Kungliga Tekniska Högskolan
6 : *
7 : * Redistribution and use in source and binary forms, with or without
8 : * modification, are permitted provided that the following conditions
9 : * are met:
10 : *
11 : * 1. Redistributions of source code must retain the above copyright
12 : * notice, this list of conditions and the following disclaimer.
13 : *
14 : * 2. Redistributions in binary form must reproduce the above copyright
15 : * notice, this list of conditions and the following disclaimer in the
16 : * documentation and/or other materials provided with the distribution.
17 : *
18 : * 3. Neither the name of PADL Software nor the names of its contributors
19 : * may be used to endorse or promote products derived from this software
20 : * without specific prior written permission.
21 : *
22 : * THIS SOFTWARE IS PROVIDED BY PADL SOFTWARE AND CONTRIBUTORS ``AS IS'' AND
23 : * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 : * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 : * ARE DISCLAIMED. IN NO EVENT SHALL PADL SOFTWARE OR CONTRIBUTORS BE LIABLE
26 : * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 : * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 : * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 : * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 : * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 : * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 : * SUCH DAMAGE.
33 : */
34 :
35 : #include "kdc_locl.h"
36 :
37 : #include <gssapi/gssapi.h>
38 : #include <gssapi_mech.h>
39 :
40 : #include <gss-preauth-protos.h>
41 : #include <gss-preauth-private.h>
42 :
43 : #include "gss_preauth_authorizer_plugin.h"
44 :
45 : struct gss_client_params {
46 : OM_uint32 major, minor;
47 : gss_ctx_id_t context_handle;
48 : gss_name_t initiator_name;
49 : gss_OID mech_type;
50 : gss_buffer_desc output_token;
51 : OM_uint32 flags;
52 : OM_uint32 lifetime;
53 : krb5_checksum req_body_checksum;
54 : };
55 :
56 : static void
57 : pa_gss_display_status(astgs_request_t r,
58 : OM_uint32 major,
59 : OM_uint32 minor,
60 : gss_client_params *gcp,
61 : const char *msg);
62 :
63 : static void
64 : pa_gss_display_name(gss_name_t name,
65 : gss_buffer_t namebuf,
66 : gss_const_buffer_t *namebuf_p);
67 :
68 : static void HEIM_CALLCONV
69 : pa_gss_dealloc_client_params(void *ptr);
70 :
71 : /*
72 : * Create a checksum over KDC-REQ-BODY (without the nonce), used to
73 : * assert the request is invariant within the preauth conversation.
74 : */
75 : static krb5_error_code
76 0 : pa_gss_create_req_body_checksum(astgs_request_t r,
77 : krb5_checksum *checksum)
78 : {
79 0 : krb5_error_code ret;
80 0 : KDC_REQ_BODY b = r->req.req_body;
81 0 : krb5_data data;
82 0 : size_t size;
83 :
84 0 : b.nonce = 0;
85 :
86 0 : ASN1_MALLOC_ENCODE(KDC_REQ_BODY, data.data, data.length, &b, &size, ret);
87 0 : heim_assert(ret || data.length,
88 : "internal asn1 encoder error");
89 :
90 0 : ret = krb5_create_checksum(r->context, NULL, 0, CKSUMTYPE_SHA256,
91 : data.data, data.length, checksum);
92 0 : krb5_data_free(&data);
93 :
94 0 : return ret;
95 : }
96 :
97 : /*
98 : * Verify a checksum over KDC-REQ-BODY (without the nonce), used to
99 : * assert the request is invariant within the preauth conversation.
100 : */
101 : static krb5_error_code
102 0 : pa_gss_verify_req_body_checksum(astgs_request_t r,
103 : krb5_checksum *checksum)
104 : {
105 0 : krb5_error_code ret;
106 0 : KDC_REQ_BODY b = r->req.req_body;
107 0 : krb5_data data;
108 0 : size_t size;
109 :
110 0 : b.nonce = 0;
111 :
112 0 : ASN1_MALLOC_ENCODE(KDC_REQ_BODY, data.data, data.length, &b, &size, ret);
113 0 : heim_assert(ret || data.length,
114 : "internal asn1 encoder error");
115 :
116 0 : ret = _kdc_verify_checksum(r->context, NULL, 0, &data, checksum);
117 0 : krb5_data_free(&data);
118 :
119 0 : return ret;
120 : }
121 :
122 : /*
123 : * Decode the FX-COOKIE context state, consisting of the exported
124 : * GSS context token concatenated with the checksum of the initial
125 : * KDC-REQ-BODY.
126 : */
127 : static krb5_error_code
128 0 : pa_gss_decode_context_state(astgs_request_t r,
129 : const krb5_data *state,
130 : gss_buffer_t sec_context_token,
131 : krb5_checksum *req_body_checksum)
132 : {
133 0 : krb5_error_code ret;
134 0 : krb5_storage *sp;
135 0 : size_t cksumsize;
136 0 : krb5_data data;
137 0 : int32_t cksumtype;
138 :
139 0 : memset(req_body_checksum, 0, sizeof(*req_body_checksum));
140 0 : sec_context_token->length = 0;
141 0 : sec_context_token->value = NULL;
142 :
143 0 : krb5_data_zero(&data);
144 :
145 0 : sp = krb5_storage_from_readonly_mem(state->data, state->length);
146 0 : if (sp == NULL) {
147 0 : ret = krb5_enomem(r->context);
148 0 : goto out;
149 : }
150 :
151 0 : krb5_storage_set_eof_code(sp, KRB5_BAD_MSIZE);
152 0 : krb5_storage_set_byteorder(sp, KRB5_STORAGE_BYTEORDER_PACKED);
153 :
154 0 : ret = krb5_ret_data(sp, &data);
155 0 : if (ret)
156 0 : goto out;
157 :
158 0 : ret = krb5_ret_int32(sp, &cksumtype);
159 0 : if (ret)
160 0 : goto out;
161 :
162 0 : req_body_checksum->cksumtype = (CKSUMTYPE)cksumtype;
163 :
164 0 : if (req_body_checksum->cksumtype == CKSUMTYPE_NONE ||
165 0 : krb5_checksum_is_keyed(r->context, req_body_checksum->cksumtype)) {
166 0 : ret = KRB5KDC_ERR_SUMTYPE_NOSUPP;
167 0 : goto out;
168 : }
169 :
170 0 : ret = krb5_checksumsize(r->context, req_body_checksum->cksumtype,
171 : &cksumsize);
172 0 : if (ret)
173 0 : goto out;
174 :
175 0 : req_body_checksum->checksum.data = malloc(cksumsize);
176 0 : if (req_body_checksum->checksum.data == NULL) {
177 0 : ret = krb5_enomem(r->context);
178 0 : goto out;
179 : }
180 :
181 0 : if (krb5_storage_read(sp, req_body_checksum->checksum.data,
182 0 : cksumsize) != cksumsize) {
183 0 : ret = KRB5_BAD_MSIZE;
184 0 : goto out;
185 : }
186 :
187 0 : req_body_checksum->checksum.length = cksumsize;
188 :
189 0 : _krb5_gss_data_to_buffer(&data, sec_context_token);
190 :
191 0 : out:
192 0 : if (ret) {
193 0 : krb5_data_free(&data);
194 0 : free_Checksum(req_body_checksum);
195 0 : memset(req_body_checksum, 0, sizeof(*req_body_checksum));
196 : }
197 0 : krb5_storage_free(sp);
198 :
199 0 : return ret;
200 : }
201 :
202 : /*
203 : * Deserialize a GSS-API security context from the FAST cookie.
204 : */
205 : static krb5_error_code
206 0 : pa_gss_get_context_state(astgs_request_t r,
207 : gss_client_params *gcp)
208 : {
209 0 : int idx = 0;
210 0 : PA_DATA *fast_pa;
211 0 : krb5_error_code ret;
212 :
213 0 : OM_uint32 major, minor;
214 0 : gss_buffer_desc sec_context_token;
215 :
216 0 : fast_pa = krb5_find_padata(r->fast.fast_state.val,
217 : r->fast.fast_state.len,
218 : KRB5_PADATA_GSS, &idx);
219 0 : if (fast_pa == NULL)
220 0 : return 0;
221 :
222 0 : ret = pa_gss_decode_context_state(r, &fast_pa->padata_value,
223 : &sec_context_token,
224 : &gcp->req_body_checksum);
225 0 : if (ret)
226 0 : return ret;
227 :
228 0 : ret = pa_gss_verify_req_body_checksum(r, &gcp->req_body_checksum);
229 0 : if (ret) {
230 0 : gss_release_buffer(&minor, &sec_context_token);
231 0 : return ret;
232 : }
233 :
234 0 : major = gss_import_sec_context(&minor, &sec_context_token,
235 : &gcp->context_handle);
236 0 : if (GSS_ERROR(major)) {
237 0 : pa_gss_display_status(r, major, minor, gcp,
238 : "Failed to import GSS pre-authentication context");
239 0 : ret = _krb5_gss_map_error(major, minor);
240 : } else
241 0 : ret = 0;
242 :
243 0 : gss_release_buffer(&minor, &sec_context_token);
244 :
245 0 : return ret;
246 : }
247 :
248 : /*
249 : * Encode the FX-COOKIE context state, consisting of the exported
250 : * GSS context token concatenated with the checksum of the initial
251 : * KDC-REQ-BODY.
252 : */
253 : static krb5_error_code
254 0 : pa_gss_encode_context_state(astgs_request_t r,
255 : gss_const_buffer_t sec_context_token,
256 : const krb5_checksum *req_body_checksum,
257 : krb5_data *state)
258 : {
259 0 : krb5_error_code ret;
260 0 : krb5_storage *sp;
261 0 : krb5_data data;
262 :
263 0 : krb5_data_zero(state);
264 :
265 0 : sp = krb5_storage_emem();
266 0 : if (sp == NULL) {
267 0 : ret = krb5_enomem(r->context);
268 0 : goto out;
269 : }
270 :
271 0 : krb5_storage_set_byteorder(sp, KRB5_STORAGE_BYTEORDER_PACKED);
272 :
273 0 : _krb5_gss_buffer_to_data(sec_context_token, &data);
274 :
275 0 : ret = krb5_store_data(sp, data);
276 0 : if (ret)
277 0 : goto out;
278 :
279 0 : ret = krb5_store_int32(sp, (int32_t)req_body_checksum->cksumtype);
280 0 : if (ret)
281 0 : goto out;
282 :
283 0 : ret = krb5_store_bytes(sp, req_body_checksum->checksum.data,
284 0 : req_body_checksum->checksum.length);
285 0 : if (ret)
286 0 : goto out;
287 :
288 0 : ret = krb5_storage_to_data(sp, state);
289 0 : if (ret)
290 0 : goto out;
291 :
292 0 : out:
293 0 : krb5_storage_free(sp);
294 :
295 0 : return ret;
296 : }
297 :
298 : /*
299 : * Serialize a GSS-API security context into a FAST cookie.
300 : */
301 : static krb5_error_code
302 0 : pa_gss_set_context_state(astgs_request_t r,
303 : gss_client_params *gcp)
304 : {
305 0 : krb5_error_code ret;
306 0 : PA_DATA *fast_pa;
307 0 : int idx = 0;
308 0 : krb5_data state;
309 :
310 0 : OM_uint32 major, minor;
311 0 : gss_buffer_desc sec_context_token = GSS_C_EMPTY_BUFFER;
312 :
313 : /*
314 : * On second and subsequent responses, we can recycle the checksum
315 : * from the request as it is validated and invariant. This saves
316 : * re-encoding the request body again.
317 : */
318 0 : if (gcp->req_body_checksum.cksumtype == CKSUMTYPE_NONE) {
319 0 : ret = pa_gss_create_req_body_checksum(r, &gcp->req_body_checksum);
320 0 : if (ret)
321 0 : return ret;
322 : }
323 :
324 0 : major = gss_export_sec_context(&minor, &gcp->context_handle,
325 : &sec_context_token);
326 0 : if (GSS_ERROR(major)) {
327 0 : pa_gss_display_status(r, major, minor, gcp,
328 : "Failed to export GSS pre-authentication context");
329 0 : return _krb5_gss_map_error(major, minor);
330 : }
331 :
332 0 : ret = pa_gss_encode_context_state(r, &sec_context_token,
333 0 : &gcp->req_body_checksum, &state);
334 0 : gss_release_buffer(&minor, &sec_context_token);
335 0 : if (ret)
336 0 : return ret;
337 :
338 0 : fast_pa = krb5_find_padata(r->fast.fast_state.val,
339 : r->fast.fast_state.len,
340 : KRB5_PADATA_GSS, &idx);
341 0 : if (fast_pa) {
342 0 : krb5_data_free(&fast_pa->padata_value);
343 0 : fast_pa->padata_value = state;
344 : } else {
345 0 : ret = krb5_padata_add(r->context, &r->fast.fast_state,
346 : KRB5_PADATA_GSS,
347 : state.data, state.length);
348 0 : if (ret)
349 0 : krb5_data_free(&state);
350 : }
351 :
352 0 : return ret;
353 : }
354 :
355 : static krb5_error_code
356 0 : pa_gss_acquire_acceptor_cred(astgs_request_t r,
357 : gss_client_params *gcp,
358 : gss_cred_id_t *cred)
359 : {
360 0 : krb5_error_code ret;
361 0 : krb5_principal tgs_name;
362 :
363 0 : OM_uint32 major, minor;
364 0 : gss_name_t target_name = GSS_C_NO_NAME;
365 0 : gss_buffer_desc display_name = GSS_C_EMPTY_BUFFER;
366 0 : gss_const_buffer_t display_name_p;
367 :
368 0 : *cred = GSS_C_NO_CREDENTIAL;
369 :
370 0 : ret = krb5_make_principal(r->context, &tgs_name, r->req.req_body.realm,
371 : KRB5_TGS_NAME, r->req.req_body.realm, NULL);
372 0 : if (ret)
373 0 : return ret;
374 :
375 0 : ret = _krb5_gss_pa_unparse_name(r->context, tgs_name, &target_name);
376 0 : krb5_free_principal(r->context, tgs_name);
377 0 : if (ret)
378 0 : return ret;
379 :
380 0 : pa_gss_display_name(target_name, &display_name, &display_name_p);
381 :
382 0 : kdc_log(r->context, r->config, 4,
383 : "Acquiring GSS acceptor credential for %.*s",
384 0 : (int)display_name_p->length, (char *)display_name_p->value);
385 :
386 0 : major = gss_acquire_cred(&minor, target_name, GSS_C_INDEFINITE,
387 0 : r->config->gss_mechanisms_allowed,
388 : GSS_C_ACCEPT, cred, NULL, NULL);
389 0 : ret = _krb5_gss_map_error(major, minor);
390 :
391 0 : if (ret)
392 0 : pa_gss_display_status(r, major, minor, gcp,
393 : "Failed to acquire GSS acceptor credential");
394 :
395 0 : gss_release_buffer(&minor, &display_name);
396 0 : gss_release_name(&minor, &target_name);
397 :
398 0 : return ret;
399 : }
400 :
401 : krb5_error_code
402 0 : _kdc_gss_rd_padata(astgs_request_t r,
403 : const PA_DATA *pa,
404 : gss_client_params **pgcp,
405 : int *open)
406 : {
407 0 : krb5_error_code ret;
408 :
409 0 : OM_uint32 minor;
410 0 : gss_client_params *gcp = NULL;
411 0 : gss_cred_id_t cred = GSS_C_NO_CREDENTIAL;
412 0 : gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
413 0 : struct gss_channel_bindings_struct cb;
414 :
415 0 : memset(&cb, 0, sizeof(cb));
416 :
417 0 : *pgcp = NULL;
418 :
419 0 : if (!r->config->enable_gss_preauth) {
420 0 : ret = KRB5KDC_ERR_POLICY;
421 0 : goto out;
422 : }
423 :
424 0 : if (pa->padata_value.length == 0) {
425 0 : ret = KRB5KDC_ERR_PREAUTH_FAILED;
426 0 : goto out;
427 : }
428 :
429 0 : gcp = kdc_object_alloc(sizeof(*gcp), "pa-gss-client-params", pa_gss_dealloc_client_params);
430 0 : if (gcp == NULL) {
431 0 : ret = krb5_enomem(r->context);
432 0 : goto out;
433 : }
434 :
435 : /* errors are fast fail until gss_accept_sec_context() is called */
436 0 : gcp->major = GSS_S_NO_CONTEXT;
437 :
438 0 : ret = pa_gss_get_context_state(r, gcp);
439 0 : if (ret)
440 0 : goto out;
441 :
442 0 : ret = pa_gss_acquire_acceptor_cred(r, gcp, &cred);
443 0 : if (ret)
444 0 : goto out;
445 :
446 0 : _krb5_gss_data_to_buffer(&pa->padata_value, &input_token);
447 0 : _krb5_gss_data_to_buffer(&r->req.req_body._save, &cb.application_data);
448 :
449 0 : gcp->major = gss_accept_sec_context(&gcp->minor,
450 : &gcp->context_handle,
451 : cred,
452 : &input_token,
453 : &cb,
454 : &gcp->initiator_name,
455 : &gcp->mech_type,
456 0 : &gcp->output_token,
457 : &gcp->flags,
458 : &gcp->lifetime,
459 : NULL); /* delegated_cred_handle */
460 :
461 0 : ret = _krb5_gss_map_error(gcp->major, gcp->minor);
462 :
463 0 : if (GSS_ERROR(gcp->major)) {
464 0 : pa_gss_display_status(r, gcp->major, gcp->minor, gcp,
465 : "Failed to accept GSS security context");
466 0 : } else if ((gcp->flags & GSS_C_ANON_FLAG) && !_kdc_is_anon_request(&r->req)) {
467 0 : kdc_log(r->context, r->config, 2,
468 : "Anonymous GSS pre-authentication request w/o anonymous flag");
469 0 : ret = KRB5KDC_ERR_BADOPTION;
470 : } else
471 0 : *open = (gcp->major == GSS_S_COMPLETE);
472 :
473 0 : out:
474 0 : gss_release_cred(&minor, &cred);
475 :
476 0 : if (gcp && gcp->major != GSS_S_NO_CONTEXT)
477 0 : *pgcp = gcp;
478 : else
479 0 : kdc_object_release(gcp);
480 :
481 0 : return ret;
482 : }
483 :
484 : krb5_timestamp
485 0 : _kdc_gss_endtime(astgs_request_t r,
486 : gss_client_params *gcp)
487 : {
488 0 : krb5_timestamp endtime;
489 :
490 0 : if (gcp->lifetime == GSS_C_INDEFINITE)
491 0 : endtime = 0;
492 : else
493 0 : endtime = kdc_time + gcp->lifetime;
494 :
495 0 : kdc_log(r->context, r->config, 10,
496 : "GSS pre-authentication endtime is %ld", (long)endtime);
497 :
498 0 : return endtime;
499 : }
500 :
501 : struct pa_gss_authorize_plugin_ctx {
502 : astgs_request_t r;
503 : struct gss_client_params *gcp;
504 : krb5_boolean authorized;
505 : krb5_principal initiator_princ;
506 : };
507 :
508 : static krb5_error_code KRB5_LIB_CALL
509 0 : pa_gss_authorize_cb(krb5_context context,
510 : const void *plug,
511 : void *plugctx,
512 : void *userctx)
513 : {
514 0 : const krb5plugin_gss_preauth_authorizer_ftable *authorizer = plug;
515 0 : struct pa_gss_authorize_plugin_ctx *pa_gss_authorize_plugin_ctx = userctx;
516 :
517 0 : return authorizer->authorize(plugctx,
518 : pa_gss_authorize_plugin_ctx->r,
519 0 : pa_gss_authorize_plugin_ctx->gcp->initiator_name,
520 0 : pa_gss_authorize_plugin_ctx->gcp->mech_type,
521 0 : pa_gss_authorize_plugin_ctx->gcp->flags,
522 : &pa_gss_authorize_plugin_ctx->authorized,
523 : &pa_gss_authorize_plugin_ctx->initiator_princ);
524 : }
525 :
526 : static const char *plugin_deps[] = {
527 : "kdc",
528 : "hdb",
529 : "gssapi",
530 : "krb5",
531 : NULL
532 : };
533 :
534 : static struct heim_plugin_data
535 : gss_preauth_authorizer_data = {
536 : "kdc",
537 : KDC_GSS_PREAUTH_AUTHORIZER,
538 : KDC_GSS_PREAUTH_AUTHORIZER_VERSION_1,
539 : plugin_deps,
540 : kdc_get_instance
541 : };
542 :
543 : static krb5_error_code
544 0 : pa_gss_authorize_plugin(astgs_request_t r,
545 : struct gss_client_params *gcp,
546 : gss_const_buffer_t display_name,
547 : krb5_boolean *authorized,
548 : krb5_principal *initiator_princ)
549 : {
550 0 : krb5_error_code ret;
551 0 : struct pa_gss_authorize_plugin_ctx ctx;
552 :
553 0 : ctx.r = r;
554 0 : ctx.gcp = gcp;
555 0 : ctx.authorized = 0;
556 0 : ctx.initiator_princ = NULL;
557 :
558 0 : krb5_clear_error_message(r->context);
559 0 : ret = _krb5_plugin_run_f(r->context, &gss_preauth_authorizer_data,
560 : 0, &ctx, pa_gss_authorize_cb);
561 :
562 0 : if (ret != KRB5_PLUGIN_NO_HANDLE) {
563 0 : const char *msg = krb5_get_error_message(r->context, ret);
564 :
565 0 : kdc_log(r->context, r->config, 7,
566 : "GSS authz plugin %sauthorize%s %s initiator %.*s: %s",
567 0 : ctx.authorized ? "" : "did not " ,
568 0 : ctx.authorized ? "d" : "",
569 0 : gss_oid_to_name(gcp->mech_type),
570 0 : (int)display_name->length, (char *)display_name->value,
571 : msg);
572 0 : krb5_free_error_message(r->context, msg);
573 : }
574 :
575 0 : *authorized = ctx.authorized;
576 0 : *initiator_princ = ctx.initiator_princ;
577 :
578 0 : return ret;
579 : }
580 :
581 : static krb5_error_code
582 0 : pa_gss_authorize_default(astgs_request_t r,
583 : struct gss_client_params *gcp,
584 : gss_const_buffer_t display_name,
585 : krb5_boolean *authorized,
586 : krb5_principal *initiator_princ)
587 : {
588 0 : krb5_error_code ret;
589 0 : krb5_principal principal;
590 0 : krb5_const_realm realm = r->server->principal->realm;
591 0 : int flags = 0, cross_realm_allowed = 0, unauth_anon;
592 :
593 : /*
594 : * gss_cross_realm_mechanisms_allowed is a list of GSS-API mechanisms
595 : * that are allowed to map directly to Kerberos principals in any
596 : * realm. If the authenticating mechanism is not on the list, then
597 : * the initiator will be mapped to an enterprise principal in the
598 : * service realm. This is useful to stop synthetic principals in
599 : * foreign realms being conflated with true cross-realm principals.
600 : */
601 0 : if (r->config->gss_cross_realm_mechanisms_allowed) {
602 0 : OM_uint32 minor;
603 :
604 0 : gss_test_oid_set_member(&minor, gcp->mech_type,
605 0 : r->config->gss_cross_realm_mechanisms_allowed,
606 : &cross_realm_allowed);
607 : }
608 :
609 0 : kdc_log(r->context, r->config, 10,
610 : "Initiator %.*s will be mapped to %s",
611 0 : (int)display_name->length, (char *)display_name->value,
612 0 : cross_realm_allowed ? "nt-principal" : "nt-enterprise-principal");
613 :
614 0 : if (!cross_realm_allowed)
615 0 : flags |= KRB5_PRINCIPAL_PARSE_ENTERPRISE | KRB5_PRINCIPAL_PARSE_NO_REALM;
616 :
617 0 : ret = _krb5_gss_pa_parse_name(r->context, gcp->initiator_name,
618 : flags, &principal);
619 0 : if (ret) {
620 0 : const char *msg = krb5_get_error_message(r->context, ret);
621 :
622 0 : kdc_log(r->context, r->config, 2,
623 : "Failed to parse %s initiator name %.*s: %s",
624 0 : gss_oid_to_name(gcp->mech_type),
625 0 : (int)display_name->length, (char *)display_name->value, msg);
626 0 : krb5_free_error_message(r->context, msg);
627 :
628 0 : return ret;
629 : }
630 :
631 : /*
632 : * GSS_C_ANON_FLAG indicates the client requested anonymous authentication
633 : * (it is validated against the request-anonymous flag).
634 : *
635 : * _kdc_is_anonymous_pkinit() returns TRUE if the principal contains both
636 : * the well known anonymous name and realm.
637 : */
638 0 : unauth_anon = (gcp->flags & GSS_C_ANON_FLAG) &&
639 0 : _kdc_is_anonymous_pkinit(r->context, principal);
640 :
641 : /*
642 : * Always use the anonymous entry created in our HDB, i.e. with the local
643 : * realm, for authorizing anonymous requests. This matches PKINIT behavior
644 : * as anonymous PKINIT requests include the KDC realm in the request.
645 : */
646 0 : if (unauth_anon || (flags & KRB5_PRINCIPAL_PARSE_ENTERPRISE)) {
647 0 : ret = krb5_principal_set_realm(r->context, principal, realm);
648 0 : if (ret) {
649 0 : krb5_free_principal(r->context, principal);
650 0 : return ret;
651 : }
652 : }
653 :
654 0 : if (unauth_anon) {
655 : /*
656 : * Special case to avoid changing _kdc_as_rep(). If the initiator is
657 : * the unauthenticated anonymous principal, r->client_princ also needs
658 : * to be set in order to force the AS-REP realm to be set to the well-
659 : * known anonymous identity. This is because (unlike anonymous PKINIT)
660 : * we only require the anonymous flag, not the anonymous name, in the
661 : * client AS-REQ.
662 : */
663 0 : krb5_principal anon_princ;
664 :
665 0 : ret = krb5_copy_principal(r->context, principal, &anon_princ);
666 0 : if (ret)
667 0 : return ret;
668 :
669 0 : krb5_free_principal(r->context, r->client_princ);
670 0 : r->client_princ = anon_princ;
671 : }
672 :
673 0 : *authorized = TRUE;
674 0 : *initiator_princ = principal;
675 :
676 0 : return 0;
677 : }
678 :
679 : krb5_error_code
680 0 : _kdc_gss_check_client(astgs_request_t r,
681 : gss_client_params *gcp,
682 : char **client_name)
683 : {
684 0 : krb5_error_code ret;
685 0 : krb5_principal initiator_princ = NULL;
686 0 : hdb_entry *initiator = NULL;
687 0 : krb5_boolean authorized = FALSE;
688 0 : HDB *clientdb = r->clientdb;
689 :
690 0 : OM_uint32 minor;
691 0 : gss_buffer_desc display_name = GSS_C_EMPTY_BUFFER;
692 0 : gss_const_buffer_t display_name_p;
693 :
694 0 : *client_name = NULL;
695 :
696 0 : pa_gss_display_name(gcp->initiator_name, &display_name, &display_name_p);
697 :
698 : /*
699 : * If no plugins handled the authorization request, then all clients
700 : * are authorized as the directly corresponding Kerberos principal.
701 : */
702 0 : ret = pa_gss_authorize_plugin(r, gcp, display_name_p,
703 : &authorized, &initiator_princ);
704 0 : if (ret == KRB5_PLUGIN_NO_HANDLE)
705 0 : ret = pa_gss_authorize_default(r, gcp, display_name_p,
706 : &authorized, &initiator_princ);
707 0 : if (ret == 0 && !authorized)
708 0 : ret = KRB5_KDC_ERR_CLIENT_NAME_MISMATCH;
709 0 : if (ret)
710 0 : goto out;
711 :
712 0 : ret = krb5_unparse_name(r->context, initiator_princ, client_name);
713 0 : if (ret)
714 0 : goto out;
715 :
716 0 : kdc_log(r->context, r->config, 4,
717 : "Mapped GSS %s initiator %.*s to principal %s",
718 0 : gss_oid_to_name(gcp->mech_type),
719 0 : (int)display_name_p->length, (char *)display_name_p->value,
720 : *client_name);
721 :
722 0 : ret = _kdc_db_fetch(r->context,
723 : r->config,
724 : initiator_princ,
725 : HDB_F_FOR_AS_REQ | HDB_F_GET_CLIENT |
726 : HDB_F_CANON | HDB_F_SYNTHETIC_OK,
727 : NULL,
728 : &r->clientdb,
729 : &initiator);
730 0 : if (ret) {
731 0 : const char *msg = krb5_get_error_message(r->context, ret);
732 :
733 0 : kdc_log(r->context, r->config, 4, "UNKNOWN -- %s: %s",
734 : *client_name, msg);
735 0 : krb5_free_error_message(r->context, msg);
736 :
737 0 : goto out;
738 : }
739 :
740 : /*
741 : * If the AS-REQ client name was the well-known federated name, then
742 : * replace the client name with the initiator name. Otherwise, the
743 : * two principals must match, noting that GSS pre-authentication is
744 : * for authentication, not general purpose impersonation.
745 : */
746 0 : if (krb5_principal_is_federated(r->context, r->client->principal)) {
747 0 : initiator->flags.force_canonicalize = 1;
748 :
749 0 : _kdc_free_ent(r->context, clientdb, r->client);
750 0 : r->client = initiator;
751 0 : initiator = NULL;
752 0 : } else if (!krb5_principal_compare(r->context,
753 0 : r->client->principal,
754 0 : initiator->principal)) {
755 0 : kdc_log(r->context, r->config, 2,
756 : "GSS %s initiator %.*s does not match principal %s",
757 0 : gss_oid_to_name(gcp->mech_type),
758 0 : (int)display_name_p->length, (char *)display_name_p->value,
759 : r->cname);
760 0 : ret = KRB5_KDC_ERR_CLIENT_NAME_MISMATCH;
761 0 : goto out;
762 : }
763 :
764 0 : out:
765 0 : krb5_free_principal(r->context, initiator_princ);
766 0 : if (initiator)
767 0 : _kdc_free_ent(r->context, r->clientdb, initiator);
768 0 : gss_release_buffer(&minor, &display_name);
769 :
770 0 : return ret;
771 : }
772 :
773 : krb5_error_code
774 0 : _kdc_gss_mk_pa_reply(astgs_request_t r,
775 : gss_client_params *gcp)
776 : {
777 0 : krb5_error_code ret;
778 0 : const KDC_REQ *req = &r->req;
779 :
780 0 : if (gcp->major == GSS_S_COMPLETE) {
781 0 : krb5_enctype enctype;
782 0 : uint32_t kfe = 0;
783 0 : krb5_keyblock *reply_key = NULL;
784 :
785 0 : if (krb5_principal_is_krbtgt(r->context, r->server_princ))
786 0 : kfe |= KFE_IS_TGS;
787 :
788 0 : ret = _kdc_find_etype(r, kfe, req->req_body.etype.val,
789 0 : req->req_body.etype.len, &enctype, NULL, NULL);
790 0 : if (ret)
791 0 : return ret;
792 :
793 0 : ret = _krb5_gss_pa_derive_key(r->context, gcp->context_handle,
794 0 : req->req_body.nonce,
795 : enctype, &reply_key);
796 0 : if (ret) {
797 0 : kdc_log(r->context, r->config, 10,
798 : "Failed to derive GSS reply key: %d", ret);
799 0 : return ret;
800 : }
801 :
802 0 : krb5_free_keyblock_contents(r->context, &r->reply_key);
803 0 : r->reply_key = *reply_key;
804 0 : free(reply_key);
805 0 : } else if (gcp->major == GSS_S_CONTINUE_NEEDED) {
806 0 : ret = pa_gss_set_context_state(r, gcp);
807 0 : if (ret)
808 0 : return ret;
809 : }
810 :
811 : /* only return padata in error case if we have an error token */
812 0 : if (!GSS_ERROR(gcp->major) || gcp->output_token.length) {
813 0 : ret = krb5_padata_add(r->context, r->rep.padata, KRB5_PADATA_GSS,
814 : gcp->output_token.value, gcp->output_token.length);
815 0 : if (ret)
816 0 : return ret;
817 :
818 : /* token is now owned by r->rep.padata */
819 0 : gcp->output_token.length = 0;
820 0 : gcp->output_token.value = NULL;
821 : }
822 :
823 0 : if (gcp->major == GSS_S_CONTINUE_NEEDED)
824 0 : ret = KRB5_KDC_ERR_MORE_PREAUTH_DATA_REQUIRED;
825 : else
826 0 : ret = _krb5_gss_map_error(gcp->major, gcp->minor);
827 :
828 0 : return ret;
829 : }
830 :
831 : krb5_error_code
832 0 : _kdc_gss_mk_composite_name_ad(astgs_request_t r,
833 : gss_client_params *gcp)
834 : {
835 0 : krb5_error_code ret;
836 0 : krb5_data data;
837 :
838 0 : OM_uint32 major, minor;
839 0 : gss_buffer_desc namebuf = GSS_C_EMPTY_BUFFER;
840 :
841 0 : if (!r->config->enable_gss_auth_data || (gcp->flags & GSS_C_ANON_FLAG))
842 0 : return 0;
843 :
844 0 : major = gss_export_name_composite(&minor, gcp->initiator_name, &namebuf);
845 0 : if (major == GSS_S_COMPLETE) {
846 0 : _krb5_gss_buffer_to_data(&namebuf, &data);
847 :
848 0 : ret = _kdc_tkt_add_if_relevant_ad(r->context, &r->et,
849 : KRB5_AUTHDATA_GSS_COMPOSITE_NAME,
850 : &data);
851 0 : } else if (major != GSS_S_UNAVAILABLE)
852 0 : ret = _krb5_gss_map_error(major, minor);
853 : else
854 0 : ret = 0;
855 :
856 0 : gss_release_buffer(&minor, &namebuf);
857 :
858 0 : return ret;
859 : }
860 :
861 : static void HEIM_CALLCONV
862 0 : pa_gss_dealloc_client_params(void *ptr)
863 : {
864 0 : gss_client_params *gcp = ptr;
865 0 : OM_uint32 minor;
866 :
867 0 : if (gcp == NULL)
868 0 : return;
869 :
870 0 : gss_delete_sec_context(&minor, &gcp->context_handle, GSS_C_NO_BUFFER);
871 0 : gss_release_name(&minor, &gcp->initiator_name);
872 0 : gss_release_buffer(&minor, &gcp->output_token);
873 0 : free_Checksum(&gcp->req_body_checksum);
874 0 : memset(gcp, 0, sizeof(*gcp));
875 : }
876 :
877 : krb5_error_code
878 186 : _kdc_gss_get_mechanism_config(krb5_context context,
879 : const char *section,
880 : const char *key,
881 : gss_OID_set *oidsp)
882 : {
883 16 : krb5_error_code ret;
884 16 : char **mechs, **mechp;
885 :
886 186 : gss_OID_set oids = GSS_C_NO_OID_SET;
887 16 : OM_uint32 major, minor;
888 :
889 186 : mechs = krb5_config_get_strings(context, NULL, section, key, NULL);
890 186 : if (mechs == NULL)
891 170 : return 0;
892 :
893 0 : major = gss_create_empty_oid_set(&minor, &oids);
894 0 : if (GSS_ERROR(major)) {
895 0 : krb5_config_free_strings(mechs);
896 0 : return _krb5_gss_map_error(major, minor);
897 : }
898 :
899 0 : for (mechp = mechs; *mechp; mechp++) {
900 0 : gss_OID oid = gss_name_to_oid(*mechp);
901 0 : if (oid == GSS_C_NO_OID)
902 0 : continue;
903 :
904 0 : major = gss_add_oid_set_member(&minor, oid, &oids);
905 0 : if (GSS_ERROR(major))
906 0 : break;
907 : }
908 :
909 0 : ret = _krb5_gss_map_error(major, minor);
910 0 : if (ret == 0)
911 0 : *oidsp = oids;
912 : else
913 0 : gss_release_oid_set(&minor, &oids);
914 :
915 0 : krb5_config_free_strings(mechs);
916 :
917 0 : return ret;
918 : }
919 :
920 : static void
921 0 : pa_gss_display_status(astgs_request_t r,
922 : OM_uint32 major,
923 : OM_uint32 minor,
924 : gss_client_params *gcp,
925 : const char *msg)
926 : {
927 0 : krb5_error_code ret = _krb5_gss_map_error(major, minor);
928 0 : gss_buffer_desc buf = GSS_C_EMPTY_BUFFER;
929 0 : OM_uint32 dmaj, dmin;
930 0 : OM_uint32 more = 0;
931 0 : char *gmmsg = NULL;
932 0 : char *gmsg = NULL;
933 0 : char *s = NULL;
934 :
935 0 : do {
936 0 : gss_release_buffer(&dmin, &buf);
937 0 : dmaj = gss_display_status(&dmin, major, GSS_C_GSS_CODE, GSS_C_NO_OID,
938 : &more, &buf);
939 0 : if (GSS_ERROR(dmaj) ||
940 0 : buf.length >= INT_MAX ||
941 0 : asprintf(&s, "%s%s%.*s", gmsg ? gmsg : "", gmsg ? ": " : "",
942 0 : (int)buf.length, (char *)buf.value) == -1 ||
943 0 : s == NULL) {
944 0 : free(gmsg);
945 0 : gmsg = NULL;
946 0 : break;
947 : }
948 0 : gmsg = s;
949 0 : s = NULL;
950 0 : } while (!GSS_ERROR(dmaj) && more);
951 :
952 0 : if (gcp->mech_type != GSS_C_NO_OID) {
953 0 : do {
954 0 : gss_release_buffer(&dmin, &buf);
955 0 : dmaj = gss_display_status(&dmin, major, GSS_C_MECH_CODE,
956 : gcp->mech_type, &more, &buf);
957 0 : if (GSS_ERROR(dmaj) ||
958 0 : asprintf(&s, "%s%s%.*s", gmmsg ? gmmsg : "", gmmsg ? ": " : "",
959 0 : (int)buf.length, (char *)buf.value) == -1 ||
960 0 : s == NULL) {
961 0 : free(gmmsg);
962 0 : gmmsg = NULL;
963 0 : break;
964 : }
965 0 : gmmsg = s;
966 0 : s = NULL;
967 0 : } while (!GSS_ERROR(dmaj) && more);
968 : }
969 :
970 0 : if (gmsg == NULL)
971 0 : krb5_set_error_message(r->context, ENOMEM,
972 : "Error displaying GSS-API status");
973 : else
974 0 : krb5_set_error_message(r->context, ret, "%s%s%s%s", gmsg,
975 : gmmsg ? " (" : "", gmmsg ? gmmsg : "",
976 : gmmsg ? ")" : "");
977 0 : krb5_prepend_error_message(r->context, ret, "%s", msg);
978 :
979 0 : kdc_log(r->context, r->config, 1,
980 : "%s: %s%s%s%s",
981 : msg, gmsg, gmmsg ? " (" : "", gmmsg ? gmmsg : "",
982 : gmmsg ? ")" : "");
983 :
984 0 : free(gmmsg);
985 0 : free(gmsg);
986 0 : }
987 :
988 : static const gss_buffer_desc
989 : gss_pa_unknown_display_name = {
990 : sizeof("<unknown name>") - 1,
991 : "<unknown name>"
992 : };
993 :
994 : static void
995 0 : pa_gss_display_name(gss_name_t name,
996 : gss_buffer_t namebuf,
997 : gss_const_buffer_t *namebuf_p)
998 : {
999 0 : OM_uint32 major, minor;
1000 :
1001 0 : major = gss_display_name(&minor, name, namebuf, NULL);
1002 0 : if (GSS_ERROR(major))
1003 0 : *namebuf_p = &gss_pa_unknown_display_name;
1004 : else
1005 0 : *namebuf_p = namebuf;
1006 0 : }
1007 :
1008 : static krb5_error_code KRB5_LIB_CALL
1009 0 : pa_gss_finalize_pac_cb(krb5_context context,
1010 : const void *plug,
1011 : void *plugctx,
1012 : void *userctx)
1013 : {
1014 0 : const krb5plugin_gss_preauth_authorizer_ftable *authorizer = plug;
1015 :
1016 0 : return authorizer->finalize_pac(plugctx, userctx);
1017 : }
1018 :
1019 :
1020 : krb5_error_code
1021 0 : _kdc_gss_finalize_pac(astgs_request_t r,
1022 : gss_client_params *gcp)
1023 : {
1024 0 : krb5_error_code ret;
1025 :
1026 0 : krb5_clear_error_message(r->context);
1027 0 : ret = _krb5_plugin_run_f(r->context, &gss_preauth_authorizer_data,
1028 : 0, r, pa_gss_finalize_pac_cb);
1029 :
1030 0 : if (ret == KRB5_PLUGIN_NO_HANDLE)
1031 0 : ret = 0;
1032 :
1033 0 : return ret;
1034 : }
|