lib/krb5: unparse_name_fixed ERANGE if zero buffer len
[heimdal.git] / kdc / gss_preauth.c
blob3b145fa54a42b263adf177a1beff8ec7207af543
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
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;
138 memset(req_body_checksum, 0, sizeof(*req_body_checksum));
139 sec_context_token->length = 0;
140 sec_context_token->value = NULL;
142 krb5_data_zero(&data);
144 sp = krb5_storage_from_readonly_mem(state->data, state->length);
145 if (sp == NULL) {
146 ret = krb5_enomem(r->context);
147 goto out;
150 krb5_storage_set_eof_code(sp, KRB5_BAD_MSIZE);
151 krb5_storage_set_byteorder(sp, KRB5_STORAGE_BYTEORDER_PACKED);
153 ret = krb5_ret_data(sp, &data);
154 if (ret)
155 goto out;
157 ret = krb5_ret_int32(sp, &req_body_checksum->cksumtype);
158 if (ret)
159 goto out;
161 if (req_body_checksum->cksumtype == CKSUMTYPE_NONE ||
162 krb5_checksum_is_keyed(r->context, req_body_checksum->cksumtype)) {
163 ret = KRB5KDC_ERR_SUMTYPE_NOSUPP;
164 goto out;
167 ret = krb5_checksumsize(r->context, req_body_checksum->cksumtype,
168 &cksumsize);
169 if (ret)
170 goto out;
172 req_body_checksum->checksum.data = malloc(cksumsize);
173 if (req_body_checksum->checksum.data == NULL) {
174 ret = krb5_enomem(r->context);
175 goto out;
178 if (krb5_storage_read(sp, req_body_checksum->checksum.data,
179 cksumsize) != cksumsize) {
180 ret = KRB5_BAD_MSIZE;
181 goto out;
184 req_body_checksum->checksum.length = cksumsize;
186 _krb5_gss_data_to_buffer(&data, sec_context_token);
188 out:
189 if (ret) {
190 krb5_data_free(&data);
191 free_Checksum(req_body_checksum);
192 memset(req_body_checksum, 0, sizeof(*req_body_checksum));
194 krb5_storage_free(sp);
196 return ret;
200 * Deserialize a GSS-API security context from the FAST cookie.
202 static krb5_error_code
203 pa_gss_get_context_state(astgs_request_t r,
204 gss_client_params *gcp)
206 int idx = 0;
207 PA_DATA *fast_pa;
208 krb5_error_code ret;
210 OM_uint32 major, minor;
211 gss_buffer_desc sec_context_token;
213 fast_pa = krb5_find_padata(r->fast.fast_state.val,
214 r->fast.fast_state.len,
215 KRB5_PADATA_GSS, &idx);
216 if (fast_pa == NULL)
217 return 0;
219 ret = pa_gss_decode_context_state(r, &fast_pa->padata_value,
220 &sec_context_token,
221 &gcp->req_body_checksum);
222 if (ret)
223 return ret;
225 ret = pa_gss_verify_req_body_checksum(r, &gcp->req_body_checksum);
226 if (ret) {
227 gss_release_buffer(&minor, &sec_context_token);
228 return ret;
231 major = gss_import_sec_context(&minor, &sec_context_token,
232 &gcp->context_handle);
233 if (GSS_ERROR(major)) {
234 pa_gss_display_status(r, major, minor, gcp,
235 "Failed to import GSS pre-authentication context");
236 ret = _krb5_gss_map_error(major, minor);
237 } else
238 ret = 0;
240 gss_release_buffer(&minor, &sec_context_token);
242 return ret;
246 * Encode the FX-COOKIE context state, consisting of the exported
247 * GSS context token concatenated with the checksum of the initial
248 * KDC-REQ-BODY.
250 static krb5_error_code
251 pa_gss_encode_context_state(astgs_request_t r,
252 gss_const_buffer_t sec_context_token,
253 const krb5_checksum *req_body_checksum,
254 krb5_data *state)
256 krb5_error_code ret;
257 krb5_storage *sp;
258 krb5_data data;
260 krb5_data_zero(state);
262 sp = krb5_storage_emem();
263 if (sp == NULL) {
264 ret = krb5_enomem(r->context);
265 goto out;
268 krb5_storage_set_byteorder(sp, KRB5_STORAGE_BYTEORDER_PACKED);
270 _krb5_gss_buffer_to_data(sec_context_token, &data);
272 ret = krb5_store_data(sp, data);
273 if (ret)
274 goto out;
276 ret = krb5_store_int32(sp, req_body_checksum->cksumtype);
277 if (ret)
278 goto out;
280 ret = krb5_store_bytes(sp, req_body_checksum->checksum.data,
281 req_body_checksum->checksum.length);
282 if (ret)
283 goto out;
285 ret = krb5_storage_to_data(sp, state);
286 if (ret)
287 goto out;
289 out:
290 krb5_storage_free(sp);
292 return ret;
296 * Serialize a GSS-API security context into a FAST cookie.
298 static krb5_error_code
299 pa_gss_set_context_state(astgs_request_t r,
300 gss_client_params *gcp)
302 krb5_error_code ret;
303 PA_DATA *fast_pa;
304 int idx = 0;
305 krb5_data state;
307 OM_uint32 major, minor;
308 gss_buffer_desc sec_context_token = GSS_C_EMPTY_BUFFER;
311 * On second and subsequent responses, we can recycle the checksum
312 * from the request as it is validated and invariant. This saves
313 * re-encoding the request body again.
315 if (gcp->req_body_checksum.cksumtype == CKSUMTYPE_NONE) {
316 ret = pa_gss_create_req_body_checksum(r, &gcp->req_body_checksum);
317 if (ret)
318 return ret;
321 major = gss_export_sec_context(&minor, &gcp->context_handle,
322 &sec_context_token);
323 if (GSS_ERROR(major)) {
324 pa_gss_display_status(r, major, minor, gcp,
325 "Failed to export GSS pre-authentication context");
326 return _krb5_gss_map_error(major, minor);
329 ret = pa_gss_encode_context_state(r, &sec_context_token,
330 &gcp->req_body_checksum, &state);
331 gss_release_buffer(&minor, &sec_context_token);
332 if (ret)
333 return ret;
335 fast_pa = krb5_find_padata(r->fast.fast_state.val,
336 r->fast.fast_state.len,
337 KRB5_PADATA_GSS, &idx);
338 if (fast_pa) {
339 krb5_data_free(&fast_pa->padata_value);
340 fast_pa->padata_value = state;
341 } else {
342 ret = krb5_padata_add(r->context, &r->fast.fast_state,
343 KRB5_PADATA_GSS,
344 state.data, state.length);
345 if (ret)
346 krb5_data_free(&state);
349 return ret;
352 static krb5_error_code
353 pa_gss_acquire_acceptor_cred(astgs_request_t r,
354 gss_client_params *gcp,
355 gss_cred_id_t *cred)
357 krb5_error_code ret;
358 krb5_principal tgs_name;
360 OM_uint32 major, minor;
361 gss_name_t target_name = GSS_C_NO_NAME;
362 gss_buffer_desc display_name = GSS_C_EMPTY_BUFFER;
363 gss_const_buffer_t display_name_p;
365 *cred = GSS_C_NO_CREDENTIAL;
367 ret = krb5_make_principal(r->context, &tgs_name, r->req.req_body.realm,
368 KRB5_TGS_NAME, r->req.req_body.realm, NULL);
369 if (ret)
370 return ret;
372 ret = _krb5_gss_pa_unparse_name(r->context, tgs_name, &target_name);
373 krb5_free_principal(r->context, tgs_name);
374 if (ret)
375 return ret;
377 pa_gss_display_name(target_name, &display_name, &display_name_p);
379 kdc_log(r->context, r->config, 4,
380 "Acquiring GSS acceptor credential for %.*s",
381 (int)display_name_p->length, (char *)display_name_p->value);
383 major = gss_acquire_cred(&minor, target_name, GSS_C_INDEFINITE,
384 r->config->gss_mechanisms_allowed,
385 GSS_C_ACCEPT, cred, NULL, NULL);
386 ret = _krb5_gss_map_error(major, minor);
388 if (ret)
389 pa_gss_display_status(r, major, minor, gcp,
390 "Failed to acquire GSS acceptor credential");
392 gss_release_buffer(&minor, &display_name);
393 gss_release_name(&minor, &target_name);
395 return ret;
398 krb5_error_code
399 _kdc_gss_rd_padata(astgs_request_t r,
400 const PA_DATA *pa,
401 gss_client_params **pgcp,
402 int *open)
404 krb5_error_code ret;
406 OM_uint32 minor;
407 gss_client_params *gcp = NULL;
408 gss_cred_id_t cred = GSS_C_NO_CREDENTIAL;
409 gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
410 struct gss_channel_bindings_struct cb;
412 memset(&cb, 0, sizeof(cb));
414 *pgcp = NULL;
416 if (!r->config->enable_gss_preauth) {
417 ret = KRB5KDC_ERR_POLICY;
418 goto out;
421 if (pa->padata_value.length == 0) {
422 ret = KRB5KDC_ERR_PREAUTH_FAILED;
423 goto out;
426 gcp = heim_alloc(sizeof(*gcp), "pa-gss-client-params", pa_gss_dealloc_client_params);
427 if (gcp == NULL) {
428 ret = krb5_enomem(r->context);
429 goto out;
432 /* errors are fast fail until gss_accept_sec_context() is called */
433 gcp->major = GSS_S_NO_CONTEXT;
435 ret = pa_gss_get_context_state(r, gcp);
436 if (ret)
437 goto out;
439 ret = pa_gss_acquire_acceptor_cred(r, gcp, &cred);
440 if (ret)
441 goto out;
443 _krb5_gss_data_to_buffer(&pa->padata_value, &input_token);
444 _krb5_gss_data_to_buffer(&r->req.req_body._save, &cb.application_data);
446 gcp->major = gss_accept_sec_context(&gcp->minor,
447 &gcp->context_handle,
448 cred,
449 &input_token,
450 &cb,
451 &gcp->initiator_name,
452 &gcp->mech_type,
453 &gcp->output_token,
454 &gcp->flags,
455 &gcp->lifetime,
456 NULL); /* delegated_cred_handle */
458 ret = _krb5_gss_map_error(gcp->major, gcp->minor);
460 if (GSS_ERROR(gcp->major)) {
461 pa_gss_display_status(r, gcp->major, gcp->minor, gcp,
462 "Failed to accept GSS security context");
463 } else if ((gcp->flags & GSS_C_ANON_FLAG) && !_kdc_is_anon_request(&r->req)) {
464 kdc_log(r->context, r->config, 2,
465 "Anonymous GSS pre-authentication request w/o anonymous flag");
466 ret = KRB5KDC_ERR_BADOPTION;
467 } else
468 *open = (gcp->major == GSS_S_COMPLETE);
470 out:
471 gss_release_cred(&minor, &cred);
473 if (gcp && gcp->major != GSS_S_NO_CONTEXT)
474 *pgcp = gcp;
475 else
476 heim_release(gcp);
478 return ret;
481 krb5_timestamp
482 _kdc_gss_endtime(astgs_request_t r,
483 gss_client_params *gcp)
485 krb5_timestamp endtime;
487 if (gcp->lifetime == GSS_C_INDEFINITE)
488 endtime = 0;
489 else
490 endtime = kdc_time + gcp->lifetime;
492 kdc_log(r->context, r->config, 10,
493 "GSS pre-authentication endtime is %ld", endtime);
495 return endtime;
498 struct pa_gss_authorize_plugin_ctx {
499 astgs_request_t r;
500 struct gss_client_params *gcp;
501 krb5_boolean authorized;
502 krb5_principal initiator_princ;
505 static krb5_error_code KRB5_LIB_CALL
506 pa_gss_authorize_cb(krb5_context context,
507 const void *plug,
508 void *plugctx,
509 void *userctx)
511 const krb5plugin_gss_preauth_authorizer_ftable *authorizer = plug;
512 struct pa_gss_authorize_plugin_ctx *pa_gss_authorize_plugin_ctx = userctx;
514 return authorizer->authorize(plugctx,
515 pa_gss_authorize_plugin_ctx->r,
516 pa_gss_authorize_plugin_ctx->gcp->initiator_name,
517 pa_gss_authorize_plugin_ctx->gcp->mech_type,
518 pa_gss_authorize_plugin_ctx->gcp->flags,
519 &pa_gss_authorize_plugin_ctx->authorized,
520 &pa_gss_authorize_plugin_ctx->initiator_princ);
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)
547 krb5_error_code ret;
548 struct pa_gss_authorize_plugin_ctx ctx;
550 ctx.r = r;
551 ctx.gcp = gcp;
552 ctx.authorized = 0;
553 ctx.initiator_princ = NULL;
555 krb5_clear_error_message(r->context);
556 ret = _krb5_plugin_run_f(r->context, &gss_preauth_authorizer_data,
557 0, &ctx, pa_gss_authorize_cb);
559 if (ret != KRB5_PLUGIN_NO_HANDLE) {
560 const char *msg = krb5_get_error_message(r->context, ret);
562 kdc_log(r->context, r->config, 7,
563 "GSS authz plugin %sauthorize%s %s initiator %.*s: %s",
564 ctx.authorized ? "" : "did not " ,
565 ctx.authorized ? "d" : "",
566 gss_oid_to_name(gcp->mech_type),
567 (int)display_name->length, (char *)display_name->value,
568 msg);
569 krb5_free_error_message(r->context, msg);
572 *authorized = ctx.authorized;
573 *initiator_princ = ctx.initiator_princ;
575 return ret;
578 static krb5_error_code
579 pa_gss_authorize_default(astgs_request_t r,
580 struct gss_client_params *gcp,
581 gss_const_buffer_t display_name,
582 krb5_boolean *authorized,
583 krb5_principal *initiator_princ)
585 krb5_error_code ret;
586 krb5_principal principal;
587 krb5_const_realm realm = r->server->principal->realm;
588 int flags = 0, cross_realm_allowed = 0, unauth_anon;
591 * gss_cross_realm_mechanisms_allowed is a list of GSS-API mechanisms
592 * that are allowed to map directly to Kerberos principals in any
593 * realm. If the authenticating mechanism is not on the list, then
594 * the initiator will be mapped to an enterprise principal in the
595 * service realm. This is useful to stop synthetic principals in
596 * foreign realms being conflated with true cross-realm principals.
598 if (r->config->gss_cross_realm_mechanisms_allowed) {
599 OM_uint32 minor;
601 gss_test_oid_set_member(&minor, gcp->mech_type,
602 r->config->gss_cross_realm_mechanisms_allowed,
603 &cross_realm_allowed);
606 kdc_log(r->context, r->config, 10,
607 "Initiator %.*s will be mapped to %s",
608 (int)display_name->length, (char *)display_name->value,
609 cross_realm_allowed ? "nt-principal" : "nt-enterprise-principal");
611 if (!cross_realm_allowed)
612 flags |= KRB5_PRINCIPAL_PARSE_ENTERPRISE | KRB5_PRINCIPAL_PARSE_NO_REALM;
614 ret = _krb5_gss_pa_parse_name(r->context, gcp->initiator_name,
615 flags, &principal);
616 if (ret) {
617 const char *msg = krb5_get_error_message(r->context, ret);
619 kdc_log(r->context, r->config, 2,
620 "Failed to parse %s initiator name %.*s: %s",
621 gss_oid_to_name(gcp->mech_type),
622 (int)display_name->length, (char *)display_name->value, msg);
623 krb5_free_error_message(r->context, msg);
625 return ret;
629 * GSS_C_ANON_FLAG indicates the client requested anonymous authentication
630 * (it is validated against the request-anonymous flag).
632 * _kdc_is_anonymous_pkinit() returns TRUE if the principal contains both
633 * the well known anonymous name and realm.
635 unauth_anon = (gcp->flags & GSS_C_ANON_FLAG) &&
636 _kdc_is_anonymous_pkinit(r->context, principal);
639 * Always use the anonymous entry created in our HDB, i.e. with the local
640 * realm, for authorizing anonymous requests. This matches PKINIT behavior
641 * as anonymous PKINIT requests include the KDC realm in the request.
643 if (unauth_anon || (flags & KRB5_PRINCIPAL_PARSE_ENTERPRISE)) {
644 ret = krb5_principal_set_realm(r->context, principal, realm);
645 if (ret) {
646 krb5_free_principal(r->context, principal);
647 return ret;
651 if (unauth_anon) {
653 * Special case to avoid changing _kdc_as_rep(). If the initiator is
654 * the unauthenticated anonymous principal, r->client_princ also needs
655 * to be set in order to force the AS-REP realm to be set to the well-
656 * known anonymous identity. This is because (unlike anonymous PKINIT)
657 * we only require the anonymous flag, not the anonymous name, in the
658 * client AS-REQ.
660 krb5_principal anon_princ;
662 ret = krb5_copy_principal(r->context, principal, &anon_princ);
663 if (ret)
664 return ret;
666 krb5_free_principal(r->context, r->client_princ);
667 r->client_princ = anon_princ;
670 *authorized = TRUE;
671 *initiator_princ = principal;
673 return 0;
676 krb5_error_code
677 _kdc_gss_check_client(astgs_request_t r,
678 gss_client_params *gcp,
679 char **client_name)
681 krb5_error_code ret;
682 krb5_principal initiator_princ = NULL;
683 hdb_entry *initiator = NULL;
684 krb5_boolean authorized = FALSE;
685 HDB *clientdb = r->clientdb;
687 OM_uint32 minor;
688 gss_buffer_desc display_name = GSS_C_EMPTY_BUFFER;
689 gss_const_buffer_t display_name_p;
691 *client_name = NULL;
693 pa_gss_display_name(gcp->initiator_name, &display_name, &display_name_p);
696 * If no plugins handled the authorization request, then all clients
697 * are authorized as the directly corresponding Kerberos principal.
699 ret = pa_gss_authorize_plugin(r, gcp, display_name_p,
700 &authorized, &initiator_princ);
701 if (ret == KRB5_PLUGIN_NO_HANDLE)
702 ret = pa_gss_authorize_default(r, gcp, display_name_p,
703 &authorized, &initiator_princ);
704 if (ret == 0 && !authorized)
705 ret = KRB5_KDC_ERR_CLIENT_NAME_MISMATCH;
706 if (ret)
707 goto out;
709 ret = krb5_unparse_name(r->context, initiator_princ, client_name);
710 if (ret)
711 goto out;
713 kdc_log(r->context, r->config, 4,
714 "Mapped GSS %s initiator %.*s to principal %s",
715 gss_oid_to_name(gcp->mech_type),
716 (int)display_name_p->length, (char *)display_name_p->value,
717 *client_name);
719 ret = _kdc_db_fetch(r->context,
720 r->config,
721 initiator_princ,
722 HDB_F_FOR_AS_REQ | HDB_F_GET_CLIENT |
723 HDB_F_CANON | HDB_F_SYNTHETIC_OK,
724 NULL,
725 &r->clientdb,
726 &initiator);
727 if (ret) {
728 const char *msg = krb5_get_error_message(r->context, ret);
730 kdc_log(r->context, r->config, 4, "UNKNOWN -- %s: %s",
731 *client_name, msg);
732 krb5_free_error_message(r->context, msg);
734 goto out;
738 * If the AS-REQ client name was the well-known federated name, then
739 * replace the client name with the initiator name. Otherwise, the
740 * two principals must match, noting that GSS pre-authentication is
741 * for authentication, not general purpose impersonation.
743 if (krb5_principal_is_federated(r->context, r->client->principal)) {
744 initiator->flags.force_canonicalize = 1;
746 _kdc_free_ent(r->context, clientdb, r->client);
747 r->client = initiator;
748 initiator = NULL;
749 } else if (!krb5_principal_compare(r->context,
750 r->client->principal,
751 initiator->principal)) {
752 kdc_log(r->context, r->config, 2,
753 "GSS %s initiator %.*s does not match principal %s",
754 gss_oid_to_name(gcp->mech_type),
755 (int)display_name_p->length, (char *)display_name_p->value,
756 r->cname);
757 ret = KRB5_KDC_ERR_CLIENT_NAME_MISMATCH;
758 goto out;
761 out:
762 krb5_free_principal(r->context, initiator_princ);
763 if (initiator)
764 _kdc_free_ent(r->context, r->clientdb, initiator);
765 gss_release_buffer(&minor, &display_name);
767 return ret;
770 krb5_error_code
771 _kdc_gss_mk_pa_reply(astgs_request_t r,
772 gss_client_params *gcp)
774 krb5_error_code ret;
775 const KDC_REQ *req = &r->req;
777 if (gcp->major == GSS_S_COMPLETE) {
778 krb5_enctype enctype;
779 uint32_t kfe = 0;
780 krb5_keyblock *reply_key = NULL;
782 if (krb5_principal_is_krbtgt(r->context, r->server_princ))
783 kfe |= KFE_IS_TGS;
785 ret = _kdc_find_etype(r, kfe, req->req_body.etype.val,
786 req->req_body.etype.len, &enctype, NULL, NULL);
787 if (ret)
788 return ret;
790 ret = _krb5_gss_pa_derive_key(r->context, gcp->context_handle,
791 req->req_body.nonce,
792 enctype, &reply_key);
793 if (ret) {
794 kdc_log(r->context, r->config, 10,
795 "Failed to derive GSS reply key: %d", ret);
796 return ret;
799 krb5_free_keyblock_contents(r->context, &r->reply_key);
800 r->reply_key = *reply_key;
801 free(reply_key);
802 } else if (gcp->major == GSS_S_CONTINUE_NEEDED) {
803 ret = pa_gss_set_context_state(r, gcp);
804 if (ret)
805 return ret;
808 /* only return padata in error case if we have an error token */
809 if (!GSS_ERROR(gcp->major) || gcp->output_token.length) {
810 ret = krb5_padata_add(r->context, r->rep.padata, KRB5_PADATA_GSS,
811 gcp->output_token.value, gcp->output_token.length);
812 if (ret)
813 return ret;
815 /* token is now owned by r->rep.padata */
816 gcp->output_token.length = 0;
817 gcp->output_token.value = NULL;
820 if (gcp->major == GSS_S_CONTINUE_NEEDED)
821 ret = KRB5_KDC_ERR_MORE_PREAUTH_DATA_REQUIRED;
822 else
823 ret = _krb5_gss_map_error(gcp->major, gcp->minor);
825 return ret;
828 krb5_error_code
829 _kdc_gss_mk_composite_name_ad(astgs_request_t r,
830 gss_client_params *gcp)
832 krb5_error_code ret;
833 krb5_data data;
835 OM_uint32 major, minor;
836 gss_buffer_desc namebuf = GSS_C_EMPTY_BUFFER;
838 if (!r->config->enable_gss_auth_data || (gcp->flags & GSS_C_ANON_FLAG))
839 return 0;
841 major = gss_export_name_composite(&minor, gcp->initiator_name, &namebuf);
842 if (major == GSS_S_COMPLETE) {
843 _krb5_gss_buffer_to_data(&namebuf, &data);
845 ret = _kdc_tkt_add_if_relevant_ad(r->context, &r->et,
846 KRB5_AUTHDATA_GSS_COMPOSITE_NAME,
847 &data);
848 } else if (major != GSS_S_UNAVAILABLE)
849 ret = _krb5_gss_map_error(major, minor);
850 else
851 ret = 0;
853 gss_release_buffer(&minor, &namebuf);
855 return ret;
858 static void
859 pa_gss_dealloc_client_params(void *ptr)
861 gss_client_params *gcp = ptr;
862 OM_uint32 minor;
864 if (gcp == NULL)
865 return;
867 gss_delete_sec_context(&minor, &gcp->context_handle, GSS_C_NO_BUFFER);
868 gss_release_name(&minor, &gcp->initiator_name);
869 gss_release_buffer(&minor, &gcp->output_token);
870 free_Checksum(&gcp->req_body_checksum);
871 memset(gcp, 0, sizeof(*gcp));
874 krb5_error_code
875 _kdc_gss_get_mechanism_config(krb5_context context,
876 const char *section,
877 const char *key,
878 gss_OID_set *oidsp)
880 krb5_error_code ret;
881 char **mechs, **mechp;
883 gss_OID_set oids = GSS_C_NO_OID_SET;
884 OM_uint32 major, minor;
886 mechs = krb5_config_get_strings(context, NULL, section, key, NULL);
887 if (mechs == NULL)
888 return 0;
890 major = gss_create_empty_oid_set(&minor, &oids);
891 if (GSS_ERROR(major)) {
892 krb5_config_free_strings(mechs);
893 return _krb5_gss_map_error(major, minor);
896 for (mechp = mechs; *mechp; mechp++) {
897 gss_OID oid = gss_name_to_oid(*mechp);
898 if (oid == GSS_C_NO_OID)
899 continue;
901 major = gss_add_oid_set_member(&minor, oid, &oids);
902 if (GSS_ERROR(major))
903 break;
906 ret = _krb5_gss_map_error(major, minor);
907 if (ret == 0)
908 *oidsp = oids;
909 else
910 gss_release_oid_set(&minor, &oids);
912 krb5_config_free_strings(mechs);
914 return ret;
917 static void
918 pa_gss_display_status(astgs_request_t r,
919 OM_uint32 major,
920 OM_uint32 minor,
921 gss_client_params *gcp,
922 const char *msg)
924 krb5_error_code ret = _krb5_gss_map_error(major, minor);
925 gss_buffer_desc buf = GSS_C_EMPTY_BUFFER;
926 OM_uint32 dmaj, dmin;
927 OM_uint32 more = 0;
928 char *gmmsg = NULL;
929 char *gmsg = NULL;
930 char *s = NULL;
932 do {
933 gss_release_buffer(&dmin, &buf);
934 dmaj = gss_display_status(&dmin, major, GSS_C_GSS_CODE, GSS_C_NO_OID,
935 &more, &buf);
936 if (GSS_ERROR(dmaj) ||
937 buf.length >= INT_MAX ||
938 asprintf(&s, "%s%s%.*s", gmsg ? gmsg : "", gmsg ? ": " : "",
939 (int)buf.length, (char *)buf.value) == -1 ||
940 s == NULL) {
941 free(gmsg);
942 gmsg = NULL;
943 break;
945 gmsg = s;
946 s = NULL;
947 } while (!GSS_ERROR(dmaj) && more);
949 if (gcp->mech_type != GSS_C_NO_OID) {
950 do {
951 gss_release_buffer(&dmin, &buf);
952 dmaj = gss_display_status(&dmin, major, GSS_C_MECH_CODE,
953 gcp->mech_type, &more, &buf);
954 if (GSS_ERROR(dmaj) ||
955 asprintf(&s, "%s%s%.*s", gmmsg ? gmmsg : "", gmmsg ? ": " : "",
956 (int)buf.length, (char *)buf.value) == -1 ||
957 s == NULL) {
958 free(gmmsg);
959 gmmsg = NULL;
960 break;
962 gmmsg = s;
963 s = NULL;
964 } while (!GSS_ERROR(dmaj) && more);
967 if (gmsg == NULL)
968 krb5_set_error_message(r->context, ENOMEM,
969 "Error displaying GSS-API status");
970 else
971 krb5_set_error_message(r->context, ret, "%s%s%s%s", gmsg,
972 gmmsg ? " (" : "", gmmsg ? gmmsg : "",
973 gmmsg ? ")" : "");
974 krb5_prepend_error_message(r->context, ret, "%s", msg);
976 kdc_log(r->context, r->config, 1,
977 "%s: %s%s%s%s",
978 msg, gmsg, gmmsg ? " (" : "", gmmsg ? gmmsg : "",
979 gmmsg ? ")" : "");
981 free(gmmsg);
982 free(gmsg);
985 static const gss_buffer_desc
986 gss_pa_unknown_display_name = {
987 sizeof("<unknown name>") - 1,
988 "<unknown name>"
991 static void
992 pa_gss_display_name(gss_name_t name,
993 gss_buffer_t namebuf,
994 gss_const_buffer_t *namebuf_p)
996 OM_uint32 major, minor;
998 major = gss_display_name(&minor, name, namebuf, NULL);
999 if (GSS_ERROR(major))
1000 *namebuf_p = &gss_pa_unknown_display_name;
1001 else
1002 *namebuf_p = namebuf;
1005 static krb5_error_code KRB5_LIB_CALL
1006 pa_gss_finalize_pac_cb(krb5_context context,
1007 const void *plug,
1008 void *plugctx,
1009 void *userctx)
1011 const krb5plugin_gss_preauth_authorizer_ftable *authorizer = plug;
1013 return authorizer->finalize_pac(plugctx, userctx);
1017 krb5_error_code
1018 _kdc_gss_finalize_pac(astgs_request_t r,
1019 gss_client_params *gcp)
1021 krb5_error_code ret;
1023 krb5_clear_error_message(r->context);
1024 ret = _krb5_plugin_run_f(r->context, &gss_preauth_authorizer_data,
1025 0, r, pa_gss_finalize_pac_cb);
1027 if (ret == KRB5_PLUGIN_NO_HANDLE)
1028 ret = 0;
1030 return ret;