s4:torture: Adapt KDC canon test to Heimdal upstream changes
[Samba.git] / source4 / heimdal / kdc / gss_preauth.c
blobce62a29afccc3e70217e94c64b6690c78c4eb78c
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;
54 krb5_data pac_data;
57 static void
58 pa_gss_display_status(astgs_request_t r,
59 OM_uint32 major,
60 OM_uint32 minor,
61 gss_client_params *gcp,
62 const char *msg);
64 static void
65 pa_gss_display_name(gss_name_t name,
66 gss_buffer_t namebuf,
67 gss_const_buffer_t *namebuf_p);
70 * Create a checksum over KDC-REQ-BODY (without the nonce), used to
71 * assert the request is invariant within the preauth conversation.
73 static krb5_error_code
74 pa_gss_create_req_body_checksum(astgs_request_t r,
75 krb5_checksum *checksum)
77 krb5_error_code ret;
78 KDC_REQ_BODY b = r->req.req_body;
79 krb5_data data;
80 size_t size;
82 b.nonce = 0;
84 ASN1_MALLOC_ENCODE(KDC_REQ_BODY, data.data, data.length, &b, &size, ret);
85 heim_assert(ret || data.length,
86 "internal asn1 encoder error");
88 ret = krb5_create_checksum(r->context, NULL, 0, CKSUMTYPE_SHA256,
89 data.data, data.length, checksum);
90 krb5_data_free(&data);
92 return ret;
96 * Verify a checksum over KDC-REQ-BODY (without the nonce), used to
97 * assert the request is invariant within the preauth conversation.
99 static krb5_error_code
100 pa_gss_verify_req_body_checksum(astgs_request_t r,
101 krb5_checksum *checksum)
103 krb5_error_code ret;
104 KDC_REQ_BODY b = r->req.req_body;
105 krb5_data data;
106 size_t size;
108 b.nonce = 0;
110 ASN1_MALLOC_ENCODE(KDC_REQ_BODY, data.data, data.length, &b, &size, ret);
111 heim_assert(ret || data.length,
112 "internal asn1 encoder error");
114 ret = _kdc_verify_checksum(r->context, NULL, 0, &data, checksum);
115 krb5_data_free(&data);
117 return ret;
121 * Decode the FX-COOKIE context state, consisting of the exported
122 * GSS context token concatenated with the checksum of the initial
123 * KDC-REQ-BODY.
125 static krb5_error_code
126 pa_gss_decode_context_state(astgs_request_t r,
127 const krb5_data *state,
128 gss_buffer_t sec_context_token,
129 krb5_checksum *req_body_checksum)
131 krb5_error_code ret;
132 krb5_storage *sp;
133 size_t cksumsize;
134 krb5_data data;
136 memset(req_body_checksum, 0, sizeof(*req_body_checksum));
137 sec_context_token->length = 0;
138 sec_context_token->value = NULL;
140 krb5_data_zero(&data);
142 sp = krb5_storage_from_readonly_mem(state->data, state->length);
143 if (sp == NULL) {
144 ret = krb5_enomem(r->context);
145 goto out;
148 krb5_storage_set_eof_code(sp, KRB5_BAD_MSIZE);
149 krb5_storage_set_byteorder(sp, KRB5_STORAGE_BYTEORDER_PACKED);
151 ret = krb5_ret_data(sp, &data);
152 if (ret)
153 goto out;
155 ret = krb5_ret_int32(sp, &req_body_checksum->cksumtype);
156 if (ret)
157 goto out;
159 if (req_body_checksum->cksumtype == CKSUMTYPE_NONE ||
160 krb5_checksum_is_keyed(r->context, req_body_checksum->cksumtype)) {
161 ret = KRB5KDC_ERR_SUMTYPE_NOSUPP;
162 goto out;
165 ret = krb5_checksumsize(r->context, req_body_checksum->cksumtype,
166 &cksumsize);
167 if (ret)
168 goto out;
170 req_body_checksum->checksum.data = malloc(cksumsize);
171 if (req_body_checksum->checksum.data == NULL) {
172 ret = krb5_enomem(r->context);
173 goto out;
176 if (krb5_storage_read(sp, req_body_checksum->checksum.data,
177 cksumsize) != cksumsize) {
178 ret = KRB5_BAD_MSIZE;
179 goto out;
182 req_body_checksum->checksum.length = cksumsize;
184 _krb5_gss_data_to_buffer(&data, sec_context_token);
186 out:
187 if (ret) {
188 krb5_data_free(&data);
189 free_Checksum(req_body_checksum);
190 memset(req_body_checksum, 0, sizeof(*req_body_checksum));
192 krb5_storage_free(sp);
194 return ret;
198 * Deserialize a GSS-API security context from the FAST cookie.
200 static krb5_error_code
201 pa_gss_get_context_state(astgs_request_t r,
202 gss_client_params *gcp)
204 int idx = 0;
205 PA_DATA *fast_pa;
206 krb5_error_code ret;
208 OM_uint32 major, minor;
209 gss_buffer_desc sec_context_token;
211 fast_pa = krb5_find_padata(r->fast.fast_state.val,
212 r->fast.fast_state.len,
213 KRB5_PADATA_GSS, &idx);
214 if (fast_pa == NULL)
215 return 0;
217 ret = pa_gss_decode_context_state(r, &fast_pa->padata_value,
218 &sec_context_token,
219 &gcp->req_body_checksum);
220 if (ret)
221 return ret;
223 ret = pa_gss_verify_req_body_checksum(r, &gcp->req_body_checksum);
224 if (ret) {
225 gss_release_buffer(&minor, &sec_context_token);
226 return ret;
229 major = gss_import_sec_context(&minor, &sec_context_token,
230 &gcp->context_handle);
231 if (GSS_ERROR(major)) {
232 pa_gss_display_status(r, major, minor, gcp,
233 "Failed to import GSS pre-authentication context");
234 ret = _krb5_gss_map_error(major, minor);
235 } else
236 ret = 0;
238 gss_release_buffer(&minor, &sec_context_token);
240 return ret;
244 * Encode the FX-COOKIE context state, consisting of the exported
245 * GSS context token concatenated with the checksum of the initial
246 * KDC-REQ-BODY.
248 static krb5_error_code
249 pa_gss_encode_context_state(astgs_request_t r,
250 gss_const_buffer_t sec_context_token,
251 const krb5_checksum *req_body_checksum,
252 krb5_data *state)
254 krb5_error_code ret;
255 krb5_storage *sp;
256 krb5_data data;
258 krb5_data_zero(state);
260 sp = krb5_storage_emem();
261 if (sp == NULL) {
262 ret = krb5_enomem(r->context);
263 goto out;
266 krb5_storage_set_byteorder(sp, KRB5_STORAGE_BYTEORDER_PACKED);
268 _krb5_gss_buffer_to_data(sec_context_token, &data);
270 ret = krb5_store_data(sp, data);
271 if (ret)
272 goto out;
274 ret = krb5_store_int32(sp, req_body_checksum->cksumtype);
275 if (ret)
276 goto out;
278 ret = krb5_store_bytes(sp, req_body_checksum->checksum.data,
279 req_body_checksum->checksum.length);
280 if (ret)
281 goto out;
283 ret = krb5_storage_to_data(sp, state);
284 if (ret)
285 goto out;
287 out:
288 krb5_storage_free(sp);
290 return ret;
294 * Serialize a GSS-API security context into a FAST cookie.
296 static krb5_error_code
297 pa_gss_set_context_state(astgs_request_t r,
298 gss_client_params *gcp)
300 krb5_error_code ret;
301 PA_DATA *fast_pa;
302 int idx = 0;
303 krb5_data state;
305 OM_uint32 major, minor;
306 gss_buffer_desc sec_context_token = GSS_C_EMPTY_BUFFER;
309 * On second and subsequent responses, we can recycle the checksum
310 * from the request as it is validated and invariant. This saves
311 * re-encoding the request body again.
313 if (gcp->req_body_checksum.cksumtype == CKSUMTYPE_NONE) {
314 ret = pa_gss_create_req_body_checksum(r, &gcp->req_body_checksum);
315 if (ret)
316 return ret;
319 major = gss_export_sec_context(&minor, &gcp->context_handle,
320 &sec_context_token);
321 if (GSS_ERROR(major)) {
322 pa_gss_display_status(r, major, minor, gcp,
323 "Failed to export GSS pre-authentication context");
324 return _krb5_gss_map_error(major, minor);
327 ret = pa_gss_encode_context_state(r, &sec_context_token,
328 &gcp->req_body_checksum, &state);
329 gss_release_buffer(&minor, &sec_context_token);
330 if (ret)
331 return ret;
333 fast_pa = krb5_find_padata(r->fast.fast_state.val,
334 r->fast.fast_state.len,
335 KRB5_PADATA_GSS, &idx);
336 if (fast_pa) {
337 krb5_data_free(&fast_pa->padata_value);
338 fast_pa->padata_value = state;
339 } else {
340 ret = krb5_padata_add(r->context, &r->fast.fast_state,
341 KRB5_PADATA_GSS,
342 state.data, state.length);
343 if (ret)
344 krb5_data_free(&state);
347 return ret;
350 static krb5_error_code
351 pa_gss_acquire_acceptor_cred(astgs_request_t r,
352 gss_client_params *gcp,
353 gss_cred_id_t *cred)
355 krb5_error_code ret;
356 krb5_principal tgs_name;
358 OM_uint32 major, minor;
359 gss_name_t target_name = GSS_C_NO_NAME;
360 gss_buffer_desc display_name = GSS_C_EMPTY_BUFFER;
361 gss_const_buffer_t display_name_p;
363 *cred = GSS_C_NO_CREDENTIAL;
365 ret = krb5_make_principal(r->context, &tgs_name, r->req.req_body.realm,
366 KRB5_TGS_NAME, r->req.req_body.realm, NULL);
367 if (ret)
368 return ret;
370 ret = _krb5_gss_pa_unparse_name(r->context, tgs_name, &target_name);
371 krb5_free_principal(r->context, tgs_name);
372 if (ret)
373 return ret;
375 pa_gss_display_name(target_name, &display_name, &display_name_p);
377 kdc_log(r->context, r->config, 4,
378 "Acquiring GSS acceptor credential for %.*s",
379 (int)display_name_p->length, (char *)display_name_p->value);
381 major = gss_acquire_cred(&minor, target_name, GSS_C_INDEFINITE,
382 r->config->gss_mechanisms_allowed,
383 GSS_C_ACCEPT, cred, NULL, NULL);
384 ret = _krb5_gss_map_error(major, minor);
386 if (ret)
387 pa_gss_display_status(r, major, minor, gcp,
388 "Failed to acquire GSS acceptor credential");
390 gss_release_buffer(&minor, &display_name);
391 gss_release_name(&minor, &target_name);
393 return ret;
396 krb5_error_code
397 _kdc_gss_rd_padata(astgs_request_t r,
398 const PA_DATA *pa,
399 gss_client_params **pgcp,
400 int *open)
402 krb5_error_code ret;
404 OM_uint32 minor;
405 gss_client_params *gcp = NULL;
406 gss_cred_id_t cred = GSS_C_NO_CREDENTIAL;
407 gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
408 struct gss_channel_bindings_struct cb;
410 memset(&cb, 0, sizeof(cb));
412 *pgcp = NULL;
414 if (!r->config->enable_gss_preauth) {
415 ret = KRB5KDC_ERR_POLICY;
416 goto out;
419 if (pa->padata_value.length == 0) {
420 ret = KRB5KDC_ERR_PREAUTH_FAILED;
421 goto out;
424 gcp = calloc(1, sizeof(*gcp));
425 if (gcp == NULL) {
426 ret = krb5_enomem(r->context);
427 goto out;
430 /* errors are fast fail until gss_accept_sec_context() is called */
431 gcp->major = GSS_S_NO_CONTEXT;
433 ret = pa_gss_get_context_state(r, gcp);
434 if (ret)
435 goto out;
437 ret = pa_gss_acquire_acceptor_cred(r, gcp, &cred);
438 if (ret)
439 goto out;
441 _krb5_gss_data_to_buffer(&pa->padata_value, &input_token);
442 _krb5_gss_data_to_buffer(&r->req.req_body._save, &cb.application_data);
444 gcp->major = gss_accept_sec_context(&gcp->minor,
445 &gcp->context_handle,
446 cred,
447 &input_token,
448 &cb,
449 &gcp->initiator_name,
450 &gcp->mech_type,
451 &gcp->output_token,
452 &gcp->flags,
453 &gcp->lifetime,
454 NULL); /* delegated_cred_handle */
456 ret = _krb5_gss_map_error(gcp->major, gcp->minor);
458 if (GSS_ERROR(gcp->major)) {
459 pa_gss_display_status(r, gcp->major, gcp->minor, gcp,
460 "Failed to accept GSS security context");
461 } else if ((gcp->flags & GSS_C_ANON_FLAG) && !_kdc_is_anon_request(&r->req)) {
462 kdc_log(r->context, r->config, 2,
463 "Anonymous GSS pre-authentication request w/o anonymous flag");
464 ret = KRB5KDC_ERR_BADOPTION;
465 } else
466 *open = (gcp->major == GSS_S_COMPLETE);
468 out:
469 gss_release_cred(&minor, &cred);
471 if (gcp && gcp->major != GSS_S_NO_CONTEXT)
472 *pgcp = gcp;
473 else
474 _kdc_gss_free_client_param(r, gcp);
476 return ret;
479 krb5_timestamp
480 _kdc_gss_endtime(astgs_request_t r,
481 gss_client_params *gcp)
483 krb5_timestamp endtime;
485 if (gcp->lifetime == GSS_C_INDEFINITE)
486 endtime = 0;
487 else
488 endtime = kdc_time + gcp->lifetime;
490 kdc_log(r->context, r->config, 10,
491 "GSS pre-authentication endtime is %ld", endtime);
493 return endtime;
496 struct pa_gss_authorize_plugin_ctx {
497 astgs_request_t r;
498 struct gss_client_params *gcp;
499 krb5_boolean authorized;
500 krb5_principal initiator_princ;
501 krb5_data pac_data;
504 static krb5_error_code KRB5_LIB_CALL
505 pa_gss_authorize_cb(krb5_context context,
506 const void *plug,
507 void *plugctx,
508 void *userctx)
510 const krb5plugin_gss_preauth_authorizer_ftable *authorizer = plug;
511 struct pa_gss_authorize_plugin_ctx *pa_gss_authorize_plugin_ctx = userctx;
513 return authorizer->authorize(plugctx,
514 pa_gss_authorize_plugin_ctx->r,
515 pa_gss_authorize_plugin_ctx->gcp->initiator_name,
516 pa_gss_authorize_plugin_ctx->gcp->mech_type,
517 pa_gss_authorize_plugin_ctx->gcp->flags,
518 &pa_gss_authorize_plugin_ctx->authorized,
519 &pa_gss_authorize_plugin_ctx->initiator_princ,
520 &pa_gss_authorize_plugin_ctx->pac_data);
523 static const char *plugin_deps[] = {
524 "kdc",
525 "hdb",
526 "gssapi",
527 "krb5",
528 NULL
531 static struct heim_plugin_data
532 gss_preauth_authorizer_data = {
533 "kdc",
534 KDC_GSS_PREAUTH_AUTHORIZER,
535 KDC_GSS_PREAUTH_AUTHORIZER_VERSION_1,
536 plugin_deps,
537 kdc_get_instance
540 static krb5_error_code
541 pa_gss_authorize_plugin(astgs_request_t r,
542 struct gss_client_params *gcp,
543 gss_const_buffer_t display_name,
544 krb5_boolean *authorized,
545 krb5_principal *initiator_princ,
546 krb5_data *pac_data)
548 krb5_error_code ret;
549 struct pa_gss_authorize_plugin_ctx ctx;
551 ctx.r = r;
552 ctx.gcp = gcp;
553 ctx.authorized = 0;
554 ctx.initiator_princ = NULL;
555 krb5_data_zero(&ctx.pac_data);
557 krb5_clear_error_message(r->context);
558 ret = _krb5_plugin_run_f(r->context, &gss_preauth_authorizer_data,
559 0, &ctx, pa_gss_authorize_cb);
561 if (ret != KRB5_PLUGIN_NO_HANDLE) {
562 const char *msg = krb5_get_error_message(r->context, ret);
564 kdc_log(r->context, r->config, 7,
565 "GSS authz plugin %sauthorize%s %s initiator %.*s: %s",
566 ctx.authorized ? "" : "did not " ,
567 ctx.authorized ? "d" : "",
568 gss_oid_to_name(gcp->mech_type),
569 (int)display_name->length, (char *)display_name->value,
570 msg);
571 krb5_free_error_message(r->context, msg);
574 *authorized = ctx.authorized;
575 *initiator_princ = ctx.initiator_princ;
576 *pac_data = ctx.pac_data;
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,
587 krb5_data *pac_data)
589 krb5_error_code ret;
590 krb5_principal principal;
591 krb5_const_realm realm = r->server->entry.principal->realm;
592 int flags = 0, cross_realm_allowed = 0, unauth_anon;
595 * gss_cross_realm_mechanisms_allowed is a list of GSS-API mechanisms
596 * that are allowed to map directly to Kerberos principals in any
597 * realm. If the authenticating mechanism is not on the list, then
598 * the initiator will be mapped to an enterprise principal in the
599 * service realm. This is useful to stop synthetic principals in
600 * foreign realms being conflated with true cross-realm principals.
602 if (r->config->gss_cross_realm_mechanisms_allowed) {
603 OM_uint32 minor;
605 gss_test_oid_set_member(&minor, gcp->mech_type,
606 r->config->gss_cross_realm_mechanisms_allowed,
607 &cross_realm_allowed);
610 kdc_log(r->context, r->config, 10,
611 "Initiator %.*s will be mapped to %s",
612 (int)display_name->length, (char *)display_name->value,
613 cross_realm_allowed ? "nt-principal" : "nt-enterprise-principal");
615 if (!cross_realm_allowed)
616 flags |= KRB5_PRINCIPAL_PARSE_ENTERPRISE | KRB5_PRINCIPAL_PARSE_NO_REALM;
618 ret = _krb5_gss_pa_parse_name(r->context, gcp->initiator_name,
619 flags, &principal);
620 if (ret) {
621 const char *msg = krb5_get_error_message(r->context, ret);
623 kdc_log(r->context, r->config, 2,
624 "Failed to parse %s initiator name %.*s: %s",
625 gss_oid_to_name(gcp->mech_type),
626 (int)display_name->length, (char *)display_name->value, msg);
627 krb5_free_error_message(r->context, msg);
629 return ret;
633 * GSS_C_ANON_FLAG indicates the client requested anonymous authentication
634 * (it is validated against the request-anonymous flag).
636 * _kdc_is_anonymous_pkinit() returns TRUE if the principal contains both
637 * the well known anonymous name and realm.
639 unauth_anon = (gcp->flags & GSS_C_ANON_FLAG) &&
640 _kdc_is_anonymous_pkinit(r->context, principal);
643 * Always use the anonymous entry created in our HDB, i.e. with the local
644 * realm, for authorizing anonymous requests. This matches PKINIT behavior
645 * as anonymous PKINIT requests include the KDC realm in the request.
647 if (unauth_anon || (flags & KRB5_PRINCIPAL_PARSE_ENTERPRISE)) {
648 ret = krb5_principal_set_realm(r->context, principal, realm);
649 if (ret) {
650 krb5_free_principal(r->context, principal);
651 return ret;
655 if (unauth_anon) {
657 * Special case to avoid changing _kdc_as_rep(). If the initiator is
658 * the unauthenticated anonymous principal, r->client_princ also needs
659 * to be set in order to force the AS-REP realm to be set to the well-
660 * known anonymous identity. This is because (unlike anonymous PKINIT)
661 * we only require the anonymous flag, not the anonymous name, in the
662 * client AS-REQ.
664 krb5_principal anon_princ;
666 ret = krb5_copy_principal(r->context, principal, &anon_princ);
667 if (ret)
668 return ret;
670 krb5_free_principal(r->context, r->client_princ);
671 r->client_princ = anon_princ;
674 *authorized = TRUE;
675 *initiator_princ = principal;
677 return 0;
680 krb5_error_code
681 _kdc_gss_check_client(astgs_request_t r,
682 gss_client_params *gcp,
683 char **client_name)
685 krb5_error_code ret;
686 krb5_principal initiator_princ = NULL;
687 hdb_entry_ex *initiator = NULL;
688 krb5_boolean authorized = FALSE;
689 krb5_data pac_data;
691 OM_uint32 minor;
692 gss_buffer_desc display_name = GSS_C_EMPTY_BUFFER;
693 gss_const_buffer_t display_name_p;
695 *client_name = NULL;
696 krb5_data_zero(&pac_data);
698 pa_gss_display_name(gcp->initiator_name, &display_name, &display_name_p);
701 * If no plugins handled the authorization request, then all clients
702 * are authorized as the directly corresponding Kerberos principal.
704 ret = pa_gss_authorize_plugin(r, gcp, display_name_p,
705 &authorized, &initiator_princ, &pac_data);
706 if (ret == KRB5_PLUGIN_NO_HANDLE)
707 ret = pa_gss_authorize_default(r, gcp, display_name_p,
708 &authorized, &initiator_princ, &pac_data);
709 if (ret == 0 && !authorized)
710 ret = KRB5_KDC_ERR_CLIENT_NAME_MISMATCH;
711 if (ret)
712 goto out;
714 ret = krb5_unparse_name(r->context, initiator_princ, client_name);
715 if (ret)
716 goto out;
718 kdc_log(r->context, r->config, 4,
719 "Mapped GSS %s initiator %.*s to principal %s",
720 gss_oid_to_name(gcp->mech_type),
721 (int)display_name_p->length, (char *)display_name_p->value,
722 *client_name);
724 ret = _kdc_db_fetch(r->context,
725 r->config,
726 initiator_princ,
727 HDB_F_FOR_AS_REQ | HDB_F_GET_CLIENT |
728 HDB_F_CANON | HDB_F_SYNTHETIC_OK,
729 NULL,
730 &r->clientdb,
731 &initiator);
732 if (ret) {
733 const char *msg = krb5_get_error_message(r->context, ret);
735 kdc_log(r->context, r->config, 4, "UNKNOWN -- %s: %s",
736 *client_name, msg);
737 krb5_free_error_message(r->context, msg);
739 goto out;
743 * If the AS-REQ client name was the well-known federated name, then
744 * replace the client name with the initiator name. Otherwise, the
745 * two principals must match, noting that GSS pre-authentication is
746 * for authentication, not general purpose impersonation.
748 if (krb5_principal_is_federated(r->context, r->client->entry.principal)) {
749 initiator->entry.flags.force_canonicalize = 1;
751 _kdc_free_ent(r->context, r->client);
752 r->client = initiator;
753 initiator = NULL;
754 } else if (!krb5_principal_compare(r->context,
755 r->client->entry.principal,
756 initiator->entry.principal)) {
757 kdc_log(r->context, r->config, 2,
758 "GSS %s initiator %.*s does not match principal %s",
759 gss_oid_to_name(gcp->mech_type),
760 (int)display_name_p->length, (char *)display_name_p->value,
761 r->cname);
762 ret = KRB5_KDC_ERR_CLIENT_NAME_MISMATCH;
763 goto out;
766 gcp->pac_data = pac_data;
767 krb5_data_zero(&pac_data);
769 out:
770 krb5_free_principal(r->context, initiator_princ);
771 if (initiator)
772 _kdc_free_ent(r->context, initiator);
773 krb5_data_free(&pac_data);
774 gss_release_buffer(&minor, &display_name);
776 return ret;
779 krb5_error_code
780 _kdc_gss_mk_pa_reply(astgs_request_t r,
781 gss_client_params *gcp)
783 krb5_error_code ret;
784 const KDC_REQ *req = &r->req;
786 if (gcp->major == GSS_S_COMPLETE) {
787 krb5_enctype enctype;
788 uint32_t kfe = 0;
789 krb5_keyblock *reply_key = NULL;
791 if (krb5_principal_is_krbtgt(r->context, r->server_princ))
792 kfe |= KFE_IS_TGS;
794 ret = _kdc_find_etype(r, kfe, req->req_body.etype.val,
795 req->req_body.etype.len, &enctype, NULL, NULL);
796 if (ret)
797 return ret;
799 ret = _krb5_gss_pa_derive_key(r->context, gcp->context_handle,
800 req->req_body.nonce,
801 enctype, &reply_key);
802 if (ret) {
803 kdc_log(r->context, r->config, 10,
804 "Failed to derive GSS reply key: %d", ret);
805 return ret;
808 krb5_free_keyblock_contents(r->context, &r->reply_key);
809 r->reply_key = *reply_key;
810 free(reply_key);
811 } else if (gcp->major == GSS_S_CONTINUE_NEEDED) {
812 ret = pa_gss_set_context_state(r, gcp);
813 if (ret)
814 return ret;
817 /* only return padata in error case if we have an error token */
818 if (!GSS_ERROR(gcp->major) || gcp->output_token.length) {
819 ret = krb5_padata_add(r->context, r->rep.padata, KRB5_PADATA_GSS,
820 gcp->output_token.value, gcp->output_token.length);
821 if (ret)
822 return ret;
824 /* token is now owned by r->rep.padata */
825 gcp->output_token.length = 0;
826 gcp->output_token.value = NULL;
829 if (gcp->major == GSS_S_CONTINUE_NEEDED)
830 ret = KRB5_KDC_ERR_MORE_PREAUTH_DATA_REQUIRED;
831 else
832 ret = _krb5_gss_map_error(gcp->major, gcp->minor);
834 return ret;
837 krb5_error_code
838 _kdc_gss_mk_composite_name_ad(astgs_request_t r,
839 gss_client_params *gcp)
841 krb5_error_code ret;
842 krb5_data data;
844 OM_uint32 major, minor;
845 gss_buffer_desc namebuf = GSS_C_EMPTY_BUFFER;
847 if (!r->config->enable_gss_auth_data || (gcp->flags & GSS_C_ANON_FLAG))
848 return 0;
850 major = gss_export_name_composite(&minor, gcp->initiator_name, &namebuf);
851 if (major == GSS_S_COMPLETE) {
852 _krb5_gss_buffer_to_data(&namebuf, &data);
854 ret = _kdc_tkt_add_if_relevant_ad(r->context, &r->et,
855 KRB5_AUTHDATA_GSS_COMPOSITE_NAME,
856 &data);
857 } else if (major != GSS_S_UNAVAILABLE)
858 ret = _krb5_gss_map_error(major, minor);
859 else
860 ret = 0;
862 gss_release_buffer(&minor, &namebuf);
864 return ret;
867 void
868 _kdc_gss_free_client_param(astgs_request_t r,
869 gss_client_params *gcp)
871 OM_uint32 minor;
873 if (gcp == NULL)
874 return;
876 gss_delete_sec_context(&minor, &gcp->context_handle, GSS_C_NO_BUFFER);
877 gss_release_name(&minor, &gcp->initiator_name);
878 gss_release_buffer(&minor, &gcp->output_token);
879 free_Checksum(&gcp->req_body_checksum);
880 krb5_data_free(&gcp->pac_data);
881 memset(gcp, 0, sizeof(*gcp));
882 free(gcp);
885 krb5_error_code
886 _kdc_gss_get_mechanism_config(krb5_context context,
887 const char *section,
888 const char *key,
889 gss_OID_set *oidsp)
891 krb5_error_code ret;
892 char **mechs, **mechp;
894 gss_OID_set oids = GSS_C_NO_OID_SET;
895 OM_uint32 major, minor;
897 mechs = krb5_config_get_strings(context, NULL, section, key, NULL);
898 if (mechs == NULL)
899 return 0;
901 major = gss_create_empty_oid_set(&minor, &oids);
902 if (GSS_ERROR(major)) {
903 krb5_config_free_strings(mechs);
904 return _krb5_gss_map_error(major, minor);
907 for (mechp = mechs; *mechp; mechp++) {
908 gss_OID oid = gss_name_to_oid(*mechp);
909 if (oid == GSS_C_NO_OID)
910 continue;
912 major = gss_add_oid_set_member(&minor, oid, &oids);
913 if (GSS_ERROR(major))
914 break;
917 ret = _krb5_gss_map_error(major, minor);
918 if (ret == 0)
919 *oidsp = oids;
920 else
921 gss_release_oid_set(&minor, &oids);
923 krb5_config_free_strings(mechs);
925 return ret;
928 static void
929 pa_gss_display_status(astgs_request_t r,
930 OM_uint32 major,
931 OM_uint32 minor,
932 gss_client_params *gcp,
933 const char *msg)
935 krb5_error_code ret = _krb5_gss_map_error(major, minor);
936 gss_buffer_desc buf = GSS_C_EMPTY_BUFFER;
937 OM_uint32 dmaj, dmin;
938 OM_uint32 more = 0;
939 char *gmmsg = NULL;
940 char *gmsg = NULL;
941 char *s = NULL;
943 do {
944 gss_release_buffer(&dmin, &buf);
945 dmaj = gss_display_status(&dmin, major, GSS_C_GSS_CODE, GSS_C_NO_OID,
946 &more, &buf);
947 if (GSS_ERROR(dmaj) ||
948 buf.length >= INT_MAX ||
949 asprintf(&s, "%s%s%.*s", gmsg ? gmsg : "", gmsg ? ": " : "",
950 (int)buf.length, (char *)buf.value) == -1 ||
951 s == NULL) {
952 free(gmsg);
953 gmsg = NULL;
954 break;
956 gmsg = s;
957 s = NULL;
958 } while (!GSS_ERROR(dmaj) && more);
960 if (gcp->mech_type != GSS_C_NO_OID) {
961 do {
962 gss_release_buffer(&dmin, &buf);
963 dmaj = gss_display_status(&dmin, major, GSS_C_MECH_CODE,
964 gcp->mech_type, &more, &buf);
965 if (GSS_ERROR(dmaj) ||
966 asprintf(&s, "%s%s%.*s", gmmsg ? gmmsg : "", gmmsg ? ": " : "",
967 (int)buf.length, (char *)buf.value) == -1 ||
968 s == NULL) {
969 free(gmmsg);
970 gmmsg = NULL;
971 break;
973 gmmsg = s;
974 s = NULL;
975 } while (!GSS_ERROR(dmaj) && more);
978 if (gmsg == NULL)
979 krb5_set_error_message(r->context, ENOMEM,
980 "Error displaying GSS-API status");
981 else
982 krb5_set_error_message(r->context, ret, "%s%s%s%s", gmsg,
983 gmmsg ? " (" : "", gmmsg ? gmmsg : "",
984 gmmsg ? ")" : "");
985 krb5_prepend_error_message(r->context, ret, "%s", msg);
987 kdc_log(r->context, r->config, 1,
988 "%s: %s%s%s%s",
989 msg, gmsg, gmmsg ? " (" : "", gmmsg ? gmmsg : "",
990 gmmsg ? ")" : "");
992 free(gmmsg);
993 free(gmsg);
996 static const gss_buffer_desc
997 gss_pa_unknown_display_name = {
998 sizeof("<unknown name>") - 1,
999 "<unknown name>"
1002 static void
1003 pa_gss_display_name(gss_name_t name,
1004 gss_buffer_t namebuf,
1005 gss_const_buffer_t *namebuf_p)
1007 OM_uint32 major, minor;
1009 major = gss_display_name(&minor, name, namebuf, NULL);
1010 if (GSS_ERROR(major))
1011 *namebuf_p = &gss_pa_unknown_display_name;
1012 else
1013 *namebuf_p = namebuf;
1016 struct pa_gss_finalize_pac_plugin_ctx {
1017 astgs_request_t r;
1018 krb5_data *pac_data;
1021 static krb5_error_code KRB5_LIB_CALL
1022 pa_gss_finalize_pac_cb(krb5_context context,
1023 const void *plug,
1024 void *plugctx,
1025 void *userctx)
1027 const krb5plugin_gss_preauth_authorizer_ftable *authorizer = plug;
1028 struct pa_gss_finalize_pac_plugin_ctx *pa_gss_finalize_pac_ctx = userctx;
1030 return authorizer->finalize_pac(plugctx,
1031 pa_gss_finalize_pac_ctx->r,
1032 pa_gss_finalize_pac_ctx->pac_data);
1036 krb5_error_code
1037 _kdc_gss_finalize_pac(astgs_request_t r,
1038 gss_client_params *gcp)
1040 krb5_error_code ret;
1041 struct pa_gss_finalize_pac_plugin_ctx ctx;
1043 ctx.r = r;
1044 ctx.pac_data = &gcp->pac_data;
1046 krb5_clear_error_message(r->context);
1047 ret = _krb5_plugin_run_f(r->context, &gss_preauth_authorizer_data,
1048 0, &ctx, pa_gss_finalize_pac_cb);
1050 if (ret == KRB5_PLUGIN_NO_HANDLE)
1051 ret = 0;
1053 return ret;