libtommath: Fix possible integer overflow CVE-2023-36328
[heimdal.git] / kdc / gss_preauth.c
blob24663deb03ad6d1462f0b5977927592987b2dc93
1 /*
2 * Copyright (c) 2021, PADL Software Pty Ltd.
3 * All rights reserved.
5 * Portions Copyright (c) 2019 Kungliga Tekniska Högskolan
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
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.
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.
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.
35 #include "kdc_locl.h"
37 #include <gssapi/gssapi.h>
38 #include <gssapi_mech.h>
40 #include <gss-preauth-protos.h>
41 #include <gss-preauth-private.h>
43 #include "gss_preauth_authorizer_plugin.h"
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;
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);
63 static void
64 pa_gss_display_name(gss_name_t name,
65 gss_buffer_t namebuf,
66 gss_const_buffer_t *namebuf_p);
68 static void HEIM_CALLCONV
69 pa_gss_dealloc_client_params(void *ptr);
72 * Create a checksum over KDC-REQ-BODY (without the nonce), used to
73 * assert the request is invariant within the preauth conversation.
75 static krb5_error_code
76 pa_gss_create_req_body_checksum(astgs_request_t r,
77 krb5_checksum *checksum)
79 krb5_error_code ret;
80 KDC_REQ_BODY b = r->req.req_body;
81 krb5_data data;
82 size_t size;
84 b.nonce = 0;
86 ASN1_MALLOC_ENCODE(KDC_REQ_BODY, data.data, data.length, &b, &size, ret);
87 heim_assert(ret || data.length,
88 "internal asn1 encoder error");
90 ret = krb5_create_checksum(r->context, NULL, 0, CKSUMTYPE_SHA256,
91 data.data, data.length, checksum);
92 krb5_data_free(&data);
94 return ret;
98 * Verify a checksum over KDC-REQ-BODY (without the nonce), used to
99 * assert the request is invariant within the preauth conversation.
101 static krb5_error_code
102 pa_gss_verify_req_body_checksum(astgs_request_t r,
103 krb5_checksum *checksum)
105 krb5_error_code ret;
106 KDC_REQ_BODY b = r->req.req_body;
107 krb5_data data;
108 size_t size;
110 b.nonce = 0;
112 ASN1_MALLOC_ENCODE(KDC_REQ_BODY, data.data, data.length, &b, &size, ret);
113 heim_assert(ret || data.length,
114 "internal asn1 encoder error");
116 ret = _kdc_verify_checksum(r->context, NULL, 0, &data, checksum);
117 krb5_data_free(&data);
119 return ret;
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.
127 static krb5_error_code
128 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)
133 krb5_error_code ret;
134 krb5_storage *sp;
135 size_t cksumsize;
136 krb5_data data;
137 int32_t cksumtype;
139 memset(req_body_checksum, 0, sizeof(*req_body_checksum));
140 sec_context_token->length = 0;
141 sec_context_token->value = NULL;
143 krb5_data_zero(&data);
145 sp = krb5_storage_from_readonly_mem(state->data, state->length);
146 if (sp == NULL) {
147 ret = krb5_enomem(r->context);
148 goto out;
151 krb5_storage_set_eof_code(sp, KRB5_BAD_MSIZE);
152 krb5_storage_set_byteorder(sp, KRB5_STORAGE_BYTEORDER_PACKED);
154 ret = krb5_ret_data(sp, &data);
155 if (ret)
156 goto out;
158 ret = krb5_ret_int32(sp, &cksumtype);
159 if (ret)
160 goto out;
162 req_body_checksum->cksumtype = (CKSUMTYPE)cksumtype;
164 if (req_body_checksum->cksumtype == CKSUMTYPE_NONE ||
165 krb5_checksum_is_keyed(r->context, req_body_checksum->cksumtype)) {
166 ret = KRB5KDC_ERR_SUMTYPE_NOSUPP;
167 goto out;
170 ret = krb5_checksumsize(r->context, req_body_checksum->cksumtype,
171 &cksumsize);
172 if (ret)
173 goto out;
175 req_body_checksum->checksum.data = malloc(cksumsize);
176 if (req_body_checksum->checksum.data == NULL) {
177 ret = krb5_enomem(r->context);
178 goto out;
181 if (krb5_storage_read(sp, req_body_checksum->checksum.data,
182 cksumsize) != cksumsize) {
183 ret = KRB5_BAD_MSIZE;
184 goto out;
187 req_body_checksum->checksum.length = cksumsize;
189 _krb5_gss_data_to_buffer(&data, sec_context_token);
191 out:
192 if (ret) {
193 krb5_data_free(&data);
194 free_Checksum(req_body_checksum);
195 memset(req_body_checksum, 0, sizeof(*req_body_checksum));
197 krb5_storage_free(sp);
199 return ret;
203 * Deserialize a GSS-API security context from the FAST cookie.
205 static krb5_error_code
206 pa_gss_get_context_state(astgs_request_t r,
207 gss_client_params *gcp)
209 int idx = 0;
210 PA_DATA *fast_pa;
211 krb5_error_code ret;
213 OM_uint32 major, minor;
214 gss_buffer_desc sec_context_token;
216 fast_pa = krb5_find_padata(r->fast.fast_state.val,
217 r->fast.fast_state.len,
218 KRB5_PADATA_GSS, &idx);
219 if (fast_pa == NULL)
220 return 0;
222 ret = pa_gss_decode_context_state(r, &fast_pa->padata_value,
223 &sec_context_token,
224 &gcp->req_body_checksum);
225 if (ret)
226 return ret;
228 ret = pa_gss_verify_req_body_checksum(r, &gcp->req_body_checksum);
229 if (ret) {
230 gss_release_buffer(&minor, &sec_context_token);
231 return ret;
234 major = gss_import_sec_context(&minor, &sec_context_token,
235 &gcp->context_handle);
236 if (GSS_ERROR(major)) {
237 pa_gss_display_status(r, major, minor, gcp,
238 "Failed to import GSS pre-authentication context");
239 ret = _krb5_gss_map_error(major, minor);
240 } else
241 ret = 0;
243 gss_release_buffer(&minor, &sec_context_token);
245 return ret;
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.
253 static krb5_error_code
254 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)
259 krb5_error_code ret;
260 krb5_storage *sp;
261 krb5_data data;
263 krb5_data_zero(state);
265 sp = krb5_storage_emem();
266 if (sp == NULL) {
267 ret = krb5_enomem(r->context);
268 goto out;
271 krb5_storage_set_byteorder(sp, KRB5_STORAGE_BYTEORDER_PACKED);
273 _krb5_gss_buffer_to_data(sec_context_token, &data);
275 ret = krb5_store_data(sp, data);
276 if (ret)
277 goto out;
279 ret = krb5_store_int32(sp, (int32_t)req_body_checksum->cksumtype);
280 if (ret)
281 goto out;
283 ret = krb5_store_bytes(sp, req_body_checksum->checksum.data,
284 req_body_checksum->checksum.length);
285 if (ret)
286 goto out;
288 ret = krb5_storage_to_data(sp, state);
289 if (ret)
290 goto out;
292 out:
293 krb5_storage_free(sp);
295 return ret;
299 * Serialize a GSS-API security context into a FAST cookie.
301 static krb5_error_code
302 pa_gss_set_context_state(astgs_request_t r,
303 gss_client_params *gcp)
305 krb5_error_code ret;
306 PA_DATA *fast_pa;
307 int idx = 0;
308 krb5_data state;
310 OM_uint32 major, minor;
311 gss_buffer_desc sec_context_token = GSS_C_EMPTY_BUFFER;
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.
318 if (gcp->req_body_checksum.cksumtype == CKSUMTYPE_NONE) {
319 ret = pa_gss_create_req_body_checksum(r, &gcp->req_body_checksum);
320 if (ret)
321 return ret;
324 major = gss_export_sec_context(&minor, &gcp->context_handle,
325 &sec_context_token);
326 if (GSS_ERROR(major)) {
327 pa_gss_display_status(r, major, minor, gcp,
328 "Failed to export GSS pre-authentication context");
329 return _krb5_gss_map_error(major, minor);
332 ret = pa_gss_encode_context_state(r, &sec_context_token,
333 &gcp->req_body_checksum, &state);
334 gss_release_buffer(&minor, &sec_context_token);
335 if (ret)
336 return ret;
338 fast_pa = krb5_find_padata(r->fast.fast_state.val,
339 r->fast.fast_state.len,
340 KRB5_PADATA_GSS, &idx);
341 if (fast_pa) {
342 krb5_data_free(&fast_pa->padata_value);
343 fast_pa->padata_value = state;
344 } else {
345 ret = krb5_padata_add(r->context, &r->fast.fast_state,
346 KRB5_PADATA_GSS,
347 state.data, state.length);
348 if (ret)
349 krb5_data_free(&state);
352 return ret;
355 static krb5_error_code
356 pa_gss_acquire_acceptor_cred(astgs_request_t r,
357 gss_client_params *gcp,
358 gss_cred_id_t *cred)
360 krb5_error_code ret;
361 krb5_principal tgs_name;
363 OM_uint32 major, minor;
364 gss_name_t target_name = GSS_C_NO_NAME;
365 gss_buffer_desc display_name = GSS_C_EMPTY_BUFFER;
366 gss_const_buffer_t display_name_p;
368 *cred = GSS_C_NO_CREDENTIAL;
370 ret = krb5_make_principal(r->context, &tgs_name, r->req.req_body.realm,
371 KRB5_TGS_NAME, r->req.req_body.realm, NULL);
372 if (ret)
373 return ret;
375 ret = _krb5_gss_pa_unparse_name(r->context, tgs_name, &target_name);
376 krb5_free_principal(r->context, tgs_name);
377 if (ret)
378 return ret;
380 pa_gss_display_name(target_name, &display_name, &display_name_p);
382 kdc_log(r->context, r->config, 4,
383 "Acquiring GSS acceptor credential for %.*s",
384 (int)display_name_p->length, (char *)display_name_p->value);
386 major = gss_acquire_cred(&minor, target_name, GSS_C_INDEFINITE,
387 r->config->gss_mechanisms_allowed,
388 GSS_C_ACCEPT, cred, NULL, NULL);
389 ret = _krb5_gss_map_error(major, minor);
391 if (ret)
392 pa_gss_display_status(r, major, minor, gcp,
393 "Failed to acquire GSS acceptor credential");
395 gss_release_buffer(&minor, &display_name);
396 gss_release_name(&minor, &target_name);
398 return ret;
401 krb5_error_code
402 _kdc_gss_rd_padata(astgs_request_t r,
403 const PA_DATA *pa,
404 gss_client_params **pgcp,
405 int *open)
407 krb5_error_code ret;
409 OM_uint32 minor;
410 gss_client_params *gcp = NULL;
411 gss_cred_id_t cred = GSS_C_NO_CREDENTIAL;
412 gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
413 struct gss_channel_bindings_struct cb;
415 memset(&cb, 0, sizeof(cb));
417 *pgcp = NULL;
419 if (!r->config->enable_gss_preauth) {
420 ret = KRB5KDC_ERR_POLICY;
421 goto out;
424 if (pa->padata_value.length == 0) {
425 ret = KRB5KDC_ERR_PREAUTH_FAILED;
426 goto out;
429 gcp = kdc_object_alloc(sizeof(*gcp), "pa-gss-client-params", pa_gss_dealloc_client_params);
430 if (gcp == NULL) {
431 ret = krb5_enomem(r->context);
432 goto out;
435 /* errors are fast fail until gss_accept_sec_context() is called */
436 gcp->major = GSS_S_NO_CONTEXT;
438 ret = pa_gss_get_context_state(r, gcp);
439 if (ret)
440 goto out;
442 ret = pa_gss_acquire_acceptor_cred(r, gcp, &cred);
443 if (ret)
444 goto out;
446 _krb5_gss_data_to_buffer(&pa->padata_value, &input_token);
447 _krb5_gss_data_to_buffer(&r->req.req_body._save, &cb.application_data);
449 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 &gcp->output_token,
457 &gcp->flags,
458 &gcp->lifetime,
459 NULL); /* delegated_cred_handle */
461 ret = _krb5_gss_map_error(gcp->major, gcp->minor);
463 if (GSS_ERROR(gcp->major)) {
464 pa_gss_display_status(r, gcp->major, gcp->minor, gcp,
465 "Failed to accept GSS security context");
466 } else if ((gcp->flags & GSS_C_ANON_FLAG) && !_kdc_is_anon_request(&r->req)) {
467 kdc_log(r->context, r->config, 2,
468 "Anonymous GSS pre-authentication request w/o anonymous flag");
469 ret = KRB5KDC_ERR_BADOPTION;
470 } else
471 *open = (gcp->major == GSS_S_COMPLETE);
473 out:
474 gss_release_cred(&minor, &cred);
476 if (gcp && gcp->major != GSS_S_NO_CONTEXT)
477 *pgcp = gcp;
478 else
479 kdc_object_release(gcp);
481 return ret;
484 krb5_timestamp
485 _kdc_gss_endtime(astgs_request_t r,
486 gss_client_params *gcp)
488 krb5_timestamp endtime;
490 if (gcp->lifetime == GSS_C_INDEFINITE)
491 endtime = 0;
492 else
493 endtime = kdc_time + gcp->lifetime;
495 kdc_log(r->context, r->config, 10,
496 "GSS pre-authentication endtime is %ld", (long)endtime);
498 return endtime;
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;
508 static krb5_error_code KRB5_LIB_CALL
509 pa_gss_authorize_cb(krb5_context context,
510 const void *plug,
511 void *plugctx,
512 void *userctx)
514 const krb5plugin_gss_preauth_authorizer_ftable *authorizer = plug;
515 struct pa_gss_authorize_plugin_ctx *pa_gss_authorize_plugin_ctx = userctx;
517 return authorizer->authorize(plugctx,
518 pa_gss_authorize_plugin_ctx->r,
519 pa_gss_authorize_plugin_ctx->gcp->initiator_name,
520 pa_gss_authorize_plugin_ctx->gcp->mech_type,
521 pa_gss_authorize_plugin_ctx->gcp->flags,
522 &pa_gss_authorize_plugin_ctx->authorized,
523 &pa_gss_authorize_plugin_ctx->initiator_princ);
526 static const char *plugin_deps[] = {
527 "kdc",
528 "hdb",
529 "gssapi",
530 "krb5",
531 NULL
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
543 static krb5_error_code
544 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)
550 krb5_error_code ret;
551 struct pa_gss_authorize_plugin_ctx ctx;
553 ctx.r = r;
554 ctx.gcp = gcp;
555 ctx.authorized = 0;
556 ctx.initiator_princ = NULL;
558 krb5_clear_error_message(r->context);
559 ret = _krb5_plugin_run_f(r->context, &gss_preauth_authorizer_data,
560 0, &ctx, pa_gss_authorize_cb);
562 if (ret != KRB5_PLUGIN_NO_HANDLE) {
563 const char *msg = krb5_get_error_message(r->context, ret);
565 kdc_log(r->context, r->config, 7,
566 "GSS authz plugin %sauthorize%s %s initiator %.*s: %s",
567 ctx.authorized ? "" : "did not " ,
568 ctx.authorized ? "d" : "",
569 gss_oid_to_name(gcp->mech_type),
570 (int)display_name->length, (char *)display_name->value,
571 msg);
572 krb5_free_error_message(r->context, msg);
575 *authorized = ctx.authorized;
576 *initiator_princ = ctx.initiator_princ;
578 return ret;
581 static krb5_error_code
582 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)
588 krb5_error_code ret;
589 krb5_principal principal;
590 krb5_const_realm realm = r->server->principal->realm;
591 int flags = 0, cross_realm_allowed = 0, unauth_anon;
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.
601 if (r->config->gss_cross_realm_mechanisms_allowed) {
602 OM_uint32 minor;
604 gss_test_oid_set_member(&minor, gcp->mech_type,
605 r->config->gss_cross_realm_mechanisms_allowed,
606 &cross_realm_allowed);
609 kdc_log(r->context, r->config, 10,
610 "Initiator %.*s will be mapped to %s",
611 (int)display_name->length, (char *)display_name->value,
612 cross_realm_allowed ? "nt-principal" : "nt-enterprise-principal");
614 if (!cross_realm_allowed)
615 flags |= KRB5_PRINCIPAL_PARSE_ENTERPRISE | KRB5_PRINCIPAL_PARSE_NO_REALM;
617 ret = _krb5_gss_pa_parse_name(r->context, gcp->initiator_name,
618 flags, &principal);
619 if (ret) {
620 const char *msg = krb5_get_error_message(r->context, ret);
622 kdc_log(r->context, r->config, 2,
623 "Failed to parse %s initiator name %.*s: %s",
624 gss_oid_to_name(gcp->mech_type),
625 (int)display_name->length, (char *)display_name->value, msg);
626 krb5_free_error_message(r->context, msg);
628 return ret;
632 * GSS_C_ANON_FLAG indicates the client requested anonymous authentication
633 * (it is validated against the request-anonymous flag).
635 * _kdc_is_anonymous_pkinit() returns TRUE if the principal contains both
636 * the well known anonymous name and realm.
638 unauth_anon = (gcp->flags & GSS_C_ANON_FLAG) &&
639 _kdc_is_anonymous_pkinit(r->context, principal);
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.
646 if (unauth_anon || (flags & KRB5_PRINCIPAL_PARSE_ENTERPRISE)) {
647 ret = krb5_principal_set_realm(r->context, principal, realm);
648 if (ret) {
649 krb5_free_principal(r->context, principal);
650 return ret;
654 if (unauth_anon) {
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.
663 krb5_principal anon_princ;
665 ret = krb5_copy_principal(r->context, principal, &anon_princ);
666 if (ret)
667 return ret;
669 krb5_free_principal(r->context, r->client_princ);
670 r->client_princ = anon_princ;
673 *authorized = TRUE;
674 *initiator_princ = principal;
676 return 0;
679 krb5_error_code
680 _kdc_gss_check_client(astgs_request_t r,
681 gss_client_params *gcp,
682 char **client_name)
684 krb5_error_code ret;
685 krb5_principal initiator_princ = NULL;
686 hdb_entry *initiator = NULL;
687 krb5_boolean authorized = FALSE;
688 HDB *clientdb = r->clientdb;
690 OM_uint32 minor;
691 gss_buffer_desc display_name = GSS_C_EMPTY_BUFFER;
692 gss_const_buffer_t display_name_p;
694 *client_name = NULL;
696 pa_gss_display_name(gcp->initiator_name, &display_name, &display_name_p);
699 * If no plugins handled the authorization request, then all clients
700 * are authorized as the directly corresponding Kerberos principal.
702 ret = pa_gss_authorize_plugin(r, gcp, display_name_p,
703 &authorized, &initiator_princ);
704 if (ret == KRB5_PLUGIN_NO_HANDLE)
705 ret = pa_gss_authorize_default(r, gcp, display_name_p,
706 &authorized, &initiator_princ);
707 if (ret == 0 && !authorized)
708 ret = KRB5_KDC_ERR_CLIENT_NAME_MISMATCH;
709 if (ret)
710 goto out;
712 ret = krb5_unparse_name(r->context, initiator_princ, client_name);
713 if (ret)
714 goto out;
716 kdc_log(r->context, r->config, 4,
717 "Mapped GSS %s initiator %.*s to principal %s",
718 gss_oid_to_name(gcp->mech_type),
719 (int)display_name_p->length, (char *)display_name_p->value,
720 *client_name);
722 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 if (ret) {
731 const char *msg = krb5_get_error_message(r->context, ret);
733 kdc_log(r->context, r->config, 4, "UNKNOWN -- %s: %s",
734 *client_name, msg);
735 krb5_free_error_message(r->context, msg);
737 goto out;
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.
746 if (krb5_principal_is_federated(r->context, r->client->principal)) {
747 initiator->flags.force_canonicalize = 1;
749 _kdc_free_ent(r->context, clientdb, r->client);
750 r->client = initiator;
751 initiator = NULL;
752 } else if (!krb5_principal_compare(r->context,
753 r->client->principal,
754 initiator->principal)) {
755 kdc_log(r->context, r->config, 2,
756 "GSS %s initiator %.*s does not match principal %s",
757 gss_oid_to_name(gcp->mech_type),
758 (int)display_name_p->length, (char *)display_name_p->value,
759 r->cname);
760 ret = KRB5_KDC_ERR_CLIENT_NAME_MISMATCH;
761 goto out;
764 out:
765 krb5_free_principal(r->context, initiator_princ);
766 if (initiator)
767 _kdc_free_ent(r->context, r->clientdb, initiator);
768 gss_release_buffer(&minor, &display_name);
770 return ret;
773 krb5_error_code
774 _kdc_gss_mk_pa_reply(astgs_request_t r,
775 gss_client_params *gcp)
777 krb5_error_code ret;
778 const KDC_REQ *req = &r->req;
780 if (gcp->major == GSS_S_COMPLETE) {
781 krb5_enctype enctype;
782 uint32_t kfe = 0;
783 krb5_keyblock *reply_key = NULL;
785 if (krb5_principal_is_krbtgt(r->context, r->server_princ))
786 kfe |= KFE_IS_TGS;
788 ret = _kdc_find_etype(r, kfe, req->req_body.etype.val,
789 req->req_body.etype.len, &enctype, NULL, NULL);
790 if (ret)
791 return ret;
793 ret = _krb5_gss_pa_derive_key(r->context, gcp->context_handle,
794 req->req_body.nonce,
795 enctype, &reply_key);
796 if (ret) {
797 kdc_log(r->context, r->config, 10,
798 "Failed to derive GSS reply key: %d", ret);
799 return ret;
802 krb5_free_keyblock_contents(r->context, &r->reply_key);
803 r->reply_key = *reply_key;
804 free(reply_key);
805 } else if (gcp->major == GSS_S_CONTINUE_NEEDED) {
806 ret = pa_gss_set_context_state(r, gcp);
807 if (ret)
808 return ret;
811 /* only return padata in error case if we have an error token */
812 if (!GSS_ERROR(gcp->major) || gcp->output_token.length) {
813 ret = krb5_padata_add(r->context, r->rep.padata, KRB5_PADATA_GSS,
814 gcp->output_token.value, gcp->output_token.length);
815 if (ret)
816 return ret;
818 /* token is now owned by r->rep.padata */
819 gcp->output_token.length = 0;
820 gcp->output_token.value = NULL;
823 if (gcp->major == GSS_S_CONTINUE_NEEDED)
824 ret = KRB5_KDC_ERR_MORE_PREAUTH_DATA_REQUIRED;
825 else
826 ret = _krb5_gss_map_error(gcp->major, gcp->minor);
828 return ret;
831 krb5_error_code
832 _kdc_gss_mk_composite_name_ad(astgs_request_t r,
833 gss_client_params *gcp)
835 krb5_error_code ret;
836 krb5_data data;
838 OM_uint32 major, minor;
839 gss_buffer_desc namebuf = GSS_C_EMPTY_BUFFER;
841 if (!r->config->enable_gss_auth_data || (gcp->flags & GSS_C_ANON_FLAG))
842 return 0;
844 major = gss_export_name_composite(&minor, gcp->initiator_name, &namebuf);
845 if (major == GSS_S_COMPLETE) {
846 _krb5_gss_buffer_to_data(&namebuf, &data);
848 ret = _kdc_tkt_add_if_relevant_ad(r->context, &r->et,
849 KRB5_AUTHDATA_GSS_COMPOSITE_NAME,
850 &data);
851 } else if (major != GSS_S_UNAVAILABLE)
852 ret = _krb5_gss_map_error(major, minor);
853 else
854 ret = 0;
856 gss_release_buffer(&minor, &namebuf);
858 return ret;
861 static void HEIM_CALLCONV
862 pa_gss_dealloc_client_params(void *ptr)
864 gss_client_params *gcp = ptr;
865 OM_uint32 minor;
867 if (gcp == NULL)
868 return;
870 gss_delete_sec_context(&minor, &gcp->context_handle, GSS_C_NO_BUFFER);
871 gss_release_name(&minor, &gcp->initiator_name);
872 gss_release_buffer(&minor, &gcp->output_token);
873 free_Checksum(&gcp->req_body_checksum);
874 memset(gcp, 0, sizeof(*gcp));
877 krb5_error_code
878 _kdc_gss_get_mechanism_config(krb5_context context,
879 const char *section,
880 const char *key,
881 gss_OID_set *oidsp)
883 krb5_error_code ret;
884 char **mechs, **mechp;
886 gss_OID_set oids = GSS_C_NO_OID_SET;
887 OM_uint32 major, minor;
889 mechs = krb5_config_get_strings(context, NULL, section, key, NULL);
890 if (mechs == NULL)
891 return 0;
893 major = gss_create_empty_oid_set(&minor, &oids);
894 if (GSS_ERROR(major)) {
895 krb5_config_free_strings(mechs);
896 return _krb5_gss_map_error(major, minor);
899 for (mechp = mechs; *mechp; mechp++) {
900 gss_OID oid = gss_name_to_oid(*mechp);
901 if (oid == GSS_C_NO_OID)
902 continue;
904 major = gss_add_oid_set_member(&minor, oid, &oids);
905 if (GSS_ERROR(major))
906 break;
909 ret = _krb5_gss_map_error(major, minor);
910 if (ret == 0)
911 *oidsp = oids;
912 else
913 gss_release_oid_set(&minor, &oids);
915 krb5_config_free_strings(mechs);
917 return ret;
920 static void
921 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)
927 krb5_error_code ret = _krb5_gss_map_error(major, minor);
928 gss_buffer_desc buf = GSS_C_EMPTY_BUFFER;
929 OM_uint32 dmaj, dmin;
930 OM_uint32 more = 0;
931 char *gmmsg = NULL;
932 char *gmsg = NULL;
933 char *s = NULL;
935 do {
936 gss_release_buffer(&dmin, &buf);
937 dmaj = gss_display_status(&dmin, major, GSS_C_GSS_CODE, GSS_C_NO_OID,
938 &more, &buf);
939 if (GSS_ERROR(dmaj) ||
940 buf.length >= INT_MAX ||
941 asprintf(&s, "%s%s%.*s", gmsg ? gmsg : "", gmsg ? ": " : "",
942 (int)buf.length, (char *)buf.value) == -1 ||
943 s == NULL) {
944 free(gmsg);
945 gmsg = NULL;
946 break;
948 gmsg = s;
949 s = NULL;
950 } while (!GSS_ERROR(dmaj) && more);
952 if (gcp->mech_type != GSS_C_NO_OID) {
953 do {
954 gss_release_buffer(&dmin, &buf);
955 dmaj = gss_display_status(&dmin, major, GSS_C_MECH_CODE,
956 gcp->mech_type, &more, &buf);
957 if (GSS_ERROR(dmaj) ||
958 asprintf(&s, "%s%s%.*s", gmmsg ? gmmsg : "", gmmsg ? ": " : "",
959 (int)buf.length, (char *)buf.value) == -1 ||
960 s == NULL) {
961 free(gmmsg);
962 gmmsg = NULL;
963 break;
965 gmmsg = s;
966 s = NULL;
967 } while (!GSS_ERROR(dmaj) && more);
970 if (gmsg == NULL)
971 krb5_set_error_message(r->context, ENOMEM,
972 "Error displaying GSS-API status");
973 else
974 krb5_set_error_message(r->context, ret, "%s%s%s%s", gmsg,
975 gmmsg ? " (" : "", gmmsg ? gmmsg : "",
976 gmmsg ? ")" : "");
977 krb5_prepend_error_message(r->context, ret, "%s", msg);
979 kdc_log(r->context, r->config, 1,
980 "%s: %s%s%s%s",
981 msg, gmsg, gmmsg ? " (" : "", gmmsg ? gmmsg : "",
982 gmmsg ? ")" : "");
984 free(gmmsg);
985 free(gmsg);
988 static const gss_buffer_desc
989 gss_pa_unknown_display_name = {
990 sizeof("<unknown name>") - 1,
991 "<unknown name>"
994 static void
995 pa_gss_display_name(gss_name_t name,
996 gss_buffer_t namebuf,
997 gss_const_buffer_t *namebuf_p)
999 OM_uint32 major, minor;
1001 major = gss_display_name(&minor, name, namebuf, NULL);
1002 if (GSS_ERROR(major))
1003 *namebuf_p = &gss_pa_unknown_display_name;
1004 else
1005 *namebuf_p = namebuf;
1008 static krb5_error_code KRB5_LIB_CALL
1009 pa_gss_finalize_pac_cb(krb5_context context,
1010 const void *plug,
1011 void *plugctx,
1012 void *userctx)
1014 const krb5plugin_gss_preauth_authorizer_ftable *authorizer = plug;
1016 return authorizer->finalize_pac(plugctx, userctx);
1020 krb5_error_code
1021 _kdc_gss_finalize_pac(astgs_request_t r,
1022 gss_client_params *gcp)
1024 krb5_error_code ret;
1026 krb5_clear_error_message(r->context);
1027 ret = _krb5_plugin_run_f(r->context, &gss_preauth_authorizer_data,
1028 0, r, pa_gss_finalize_pac_cb);
1030 if (ret == KRB5_PLUGIN_NO_HANDLE)
1031 ret = 0;
1033 return ret;