krb5: Red Hat gssproxy FILE ccache remove cred compatibility
[heimdal.git] / lib / krb5 / fast.c
blob575b96483015b04cbf8711d5cd4007df3b494946
1 /*
2 * Copyright (c) 2011 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
17 * 3. Neither the name of the Institute nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
34 #include "krb5_locl.h"
35 #ifndef WIN32
36 #include <heim-ipc.h>
37 #endif
39 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
40 _krb5_fast_cf2(krb5_context context,
41 krb5_keyblock *key1,
42 const char *pepper1,
43 krb5_keyblock *key2,
44 const char *pepper2,
45 krb5_keyblock *armorkey,
46 krb5_crypto *armor_crypto)
48 krb5_crypto crypto1, crypto2;
49 krb5_data pa1, pa2;
50 krb5_error_code ret;
52 ret = krb5_crypto_init(context, key1, 0, &crypto1);
53 if (ret)
54 return ret;
56 ret = krb5_crypto_init(context, key2, 0, &crypto2);
57 if (ret) {
58 krb5_crypto_destroy(context, crypto1);
59 return ret;
62 pa1.data = rk_UNCONST(pepper1);
63 pa1.length = strlen(pepper1);
64 pa2.data = rk_UNCONST(pepper2);
65 pa2.length = strlen(pepper2);
67 ret = krb5_crypto_fx_cf2(context, crypto1, crypto2, &pa1, &pa2,
68 key1->keytype, armorkey);
69 krb5_crypto_destroy(context, crypto1);
70 krb5_crypto_destroy(context, crypto2);
71 if (ret)
72 return ret;
74 if (armor_crypto) {
75 ret = krb5_crypto_init(context, armorkey, 0, armor_crypto);
76 if (ret)
77 krb5_free_keyblock_contents(context, armorkey);
80 return ret;
83 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
84 _krb5_fast_armor_key(krb5_context context,
85 krb5_keyblock *subkey,
86 krb5_keyblock *sessionkey,
87 krb5_keyblock *armorkey,
88 krb5_crypto *armor_crypto)
90 return _krb5_fast_cf2(context,
91 subkey,
92 "subkeyarmor",
93 sessionkey,
94 "ticketarmor",
95 armorkey,
96 armor_crypto);
99 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
100 _krb5_fast_explicit_armor_key(krb5_context context,
101 krb5_keyblock *armorkey,
102 krb5_keyblock *subkey,
103 krb5_keyblock *explicit_armorkey,
104 krb5_crypto *explicit_armor_crypto)
106 return _krb5_fast_cf2(context,
107 armorkey,
108 "explicitarmor",
109 subkey,
110 "tgsarmor",
111 explicit_armorkey,
112 explicit_armor_crypto);
115 static krb5_error_code
116 check_fast(krb5_context context, struct krb5_fast_state *state)
118 if (state && (state->flags & KRB5_FAST_EXPECTED)) {
119 krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED,
120 "Expected FAST, but no FAST "
121 "was in the response from the KDC");
122 return KRB5KRB_AP_ERR_MODIFIED;
124 return 0;
127 static krb5_error_code
128 make_local_fast_ap_fxarmor(krb5_context context,
129 krb5_ccache armor_ccache,
130 krb5_const_realm realm,
131 krb5_data *armor_value,
132 krb5_keyblock *armor_key,
133 krb5_crypto *armor_crypto)
135 krb5_auth_context auth_context = NULL;
136 krb5_creds cred, *credp = NULL;
137 krb5_error_code ret;
138 krb5_data empty;
139 krb5_const_realm tgs_realm;
141 if (armor_ccache == NULL) {
142 krb5_set_error_message(context, EINVAL,
143 "Armor credential cache required");
144 return EINVAL;
147 krb5_data_zero(&empty);
148 memset(&cred, 0, sizeof(cred));
150 ret = krb5_auth_con_init (context, &auth_context);
151 if (ret)
152 goto out;
154 ret = krb5_cc_get_principal(context, armor_ccache, &cred.client);
155 if (ret)
156 goto out;
159 * Make sure we don't ask for a krbtgt/WELLKNOWN:ANONYMOUS
161 if (krb5_principal_is_anonymous(context, cred.client,
162 KRB5_ANON_MATCH_UNAUTHENTICATED))
163 tgs_realm = realm;
164 else
165 tgs_realm = cred.client->realm;
167 ret = krb5_make_principal(context, &cred.server,
168 tgs_realm,
169 KRB5_TGS_NAME,
170 tgs_realm,
171 NULL);
172 if (ret)
173 goto out;
175 ret = krb5_get_credentials(context, 0, armor_ccache, &cred, &credp);
176 if (ret)
177 goto out;
179 ret = krb5_auth_con_add_AuthorizationData(context, auth_context,
180 KRB5_AUTHDATA_FX_FAST_ARMOR,
181 &empty);
182 if (ret)
183 goto out;
185 ret = krb5_mk_req_extended(context,
186 &auth_context,
187 AP_OPTS_USE_SUBKEY,
188 NULL,
189 credp,
190 armor_value);
191 if (ret)
192 goto out;
194 ret = _krb5_fast_armor_key(context,
195 auth_context->local_subkey,
196 auth_context->keyblock,
197 armor_key,
198 armor_crypto);
199 if (ret)
200 goto out;
202 out:
203 if (auth_context)
204 krb5_auth_con_free(context, auth_context);
205 if (credp)
206 krb5_free_creds(context, credp);
207 krb5_free_principal(context, cred.server);
208 krb5_free_principal(context, cred.client);
210 return ret;
213 #ifndef WIN32
214 static heim_base_once_t armor_service_once = HEIM_BASE_ONCE_INIT;
215 static heim_ipc armor_service = NULL;
217 static void
218 fast_armor_init_ipc(void *ctx)
220 heim_ipc *ipc = ctx;
221 heim_ipc_init_context("ANY:org.h5l.armor-service", ipc);
223 #endif
225 static krb5_error_code
226 make_fast_ap_fxarmor(krb5_context context,
227 struct krb5_fast_state *state,
228 krb5_const_realm realm,
229 KrbFastArmor **armor)
231 KrbFastArmor *fxarmor = NULL;
232 krb5_error_code ret;
234 *armor = NULL;
236 ALLOC(fxarmor, 1);
237 if (fxarmor == NULL) {
238 ret = ENOMEM;
239 goto out;
242 if (state->flags & KRB5_FAST_AP_ARMOR_SERVICE) {
243 #ifdef WIN32
244 krb5_set_error_message(context, ENOTSUP, "Fast armor IPC service not supportted yet on Windows");
245 ret = ENOTSUP;
246 goto out;
247 #else
248 KERB_ARMOR_SERVICE_REPLY msg;
249 krb5_data request, reply;
251 heim_base_once_f(&armor_service_once, &armor_service, fast_armor_init_ipc);
252 if (armor_service == NULL) {
253 krb5_set_error_message(context, ENOENT, "Failed to open fast armor service");
254 ret = ENOENT;
255 goto out;
258 krb5_data_zero(&reply);
260 request.data = rk_UNCONST(realm);
261 request.length = strlen(realm);
263 ret = heim_ipc_call(armor_service, &request, &reply, NULL);
264 if (ret) {
265 krb5_set_error_message(context, ret, "Failed to get armor service credential");
266 goto out;
269 ret = decode_KERB_ARMOR_SERVICE_REPLY(reply.data, reply.length, &msg, NULL);
270 krb5_data_free(&reply);
271 if (ret)
272 goto out;
274 ret = copy_KrbFastArmor(&msg.armor, fxarmor);
275 if (ret) {
276 free_KERB_ARMOR_SERVICE_REPLY(&msg);
277 goto out;
280 ret = krb5_copy_keyblock_contents(context, &msg.armor_key, &state->armor_key);
281 free_KERB_ARMOR_SERVICE_REPLY(&msg);
282 if (ret)
283 goto out;
285 ret = krb5_crypto_init(context, &state->armor_key, 0, &state->armor_crypto);
286 if (ret)
287 goto out;
288 #endif /* WIN32 */
289 } else {
290 fxarmor->armor_type = 1;
292 ret = make_local_fast_ap_fxarmor(context,
293 state->armor_ccache,
294 realm,
295 &fxarmor->armor_value,
296 &state->armor_key,
297 &state->armor_crypto);
298 if (ret)
299 goto out;
303 *armor = fxarmor;
304 fxarmor = NULL;
306 out:
307 if (fxarmor) {
308 free_KrbFastArmor(fxarmor);
309 free(fxarmor);
311 return ret;
314 static krb5_error_code
315 unwrap_fast_rep(krb5_context context,
316 struct krb5_fast_state *state,
317 PA_DATA *pa,
318 KrbFastResponse *fastrep)
320 PA_FX_FAST_REPLY fxfastrep;
321 krb5_error_code ret;
323 memset(&fxfastrep, 0, sizeof(fxfastrep));
325 ret = decode_PA_FX_FAST_REPLY(pa->padata_value.data,
326 pa->padata_value.length,
327 &fxfastrep, NULL);
328 if (ret)
329 return ret;
331 if (fxfastrep.element == choice_PA_FX_FAST_REPLY_armored_data) {
332 krb5_data data;
334 ret = krb5_decrypt_EncryptedData(context,
335 state->armor_crypto,
336 KRB5_KU_FAST_REP,
337 &fxfastrep.u.armored_data.enc_fast_rep,
338 &data);
339 if (ret)
340 goto out;
342 ret = decode_KrbFastResponse(data.data, data.length, fastrep, NULL);
343 krb5_data_free(&data);
344 if (ret)
345 goto out;
347 } else {
348 ret = KRB5KDC_ERR_PREAUTH_FAILED;
349 goto out;
352 out:
353 free_PA_FX_FAST_REPLY(&fxfastrep);
355 return ret;
358 static krb5_error_code
359 set_anon_principal(krb5_context context, PrincipalName **p)
362 ALLOC((*p), 1);
363 if (*p == NULL)
364 goto fail;
366 (*p)->name_type = KRB5_NT_PRINCIPAL;
368 ALLOC_SEQ(&(*p)->name_string, 2);
369 if ((*p)->name_string.val == NULL)
370 goto fail;
372 (*p)->name_string.val[0] = strdup(KRB5_WELLKNOWN_NAME);
373 if ((*p)->name_string.val[0] == NULL)
374 goto fail;
376 (*p)->name_string.val[1] = strdup(KRB5_ANON_NAME);
377 if ((*p)->name_string.val[1] == NULL)
378 goto fail;
380 return 0;
381 fail:
382 if (*p) {
383 if ((*p)->name_string.val) {
384 free((*p)->name_string.val[0]);
385 free((*p)->name_string.val[1]);
386 free((*p)->name_string.val);
388 free(*p);
391 return krb5_enomem(context);
394 krb5_error_code
395 _krb5_fast_create_armor(krb5_context context,
396 struct krb5_fast_state *state,
397 const char *realm)
399 krb5_error_code ret;
401 if (state->armor_crypto == NULL) {
402 if (state->armor_ccache || state->armor_ac || (state->flags & KRB5_FAST_AP_ARMOR_SERVICE)) {
404 * Instead of keeping state in FX_COOKIE in the KDC, we
405 * rebuild a new armor key for every request, because this
406 * is what the MIT KDC expect and RFC6113 is vage about
407 * what the behavior should be.
409 state->type = choice_PA_FX_FAST_REQUEST_armored_data;
410 } else {
411 return check_fast(context, state);
415 if (state->type == choice_PA_FX_FAST_REQUEST_armored_data) {
416 if (state->armor_crypto) {
417 krb5_crypto_destroy(context, state->armor_crypto);
418 state->armor_crypto = NULL;
420 if (state->strengthen_key) {
421 krb5_free_keyblock(context, state->strengthen_key);
422 state->strengthen_key = NULL;
424 krb5_free_keyblock_contents(context, &state->armor_key);
427 * If we have a armor auth context, its because the caller
428 * wants us to do an implicit FAST armor (TGS-REQ).
430 if (state->armor_ac) {
431 heim_assert((state->flags & KRB5_FAST_AS_REQ) == 0, "FAST AS with AC");
433 ret = _krb5_fast_armor_key(context,
434 state->armor_ac->local_subkey,
435 state->armor_ac->keyblock,
436 &state->armor_key,
437 &state->armor_crypto);
438 if (ret)
439 goto out;
440 } else {
441 heim_assert((state->flags & KRB5_FAST_AS_REQ) != 0, "FAST TGS without AC");
443 if (state->armor_data) {
444 free_KrbFastArmor(state->armor_data);
445 free(state->armor_data);
446 state->armor_data = NULL;
448 ret = make_fast_ap_fxarmor(context, state, realm,
449 &state->armor_data);
450 if (ret)
451 goto out;
453 } else {
454 heim_abort("unknown state type: %d", (int)state->type);
456 out:
457 return ret;
461 krb5_error_code
462 _krb5_fast_wrap_req(krb5_context context,
463 struct krb5_fast_state *state,
464 KDC_REQ *req)
466 PA_FX_FAST_REQUEST fxreq;
467 krb5_error_code ret;
468 KrbFastReq fastreq;
469 krb5_data data, aschecksum_data, tgschecksum_data;
470 const krb5_data *checksum_data = NULL;
471 size_t size = 0;
472 krb5_boolean readd_padata_to_outer = FALSE;
474 if (state->flags & KRB5_FAST_DISABLED) {
475 _krb5_debug(context, 10, "fast disabled, not doing any fast wrapping");
476 return 0;
479 memset(&fxreq, 0, sizeof(fxreq));
480 memset(&fastreq, 0, sizeof(fastreq));
481 krb5_data_zero(&data);
482 krb5_data_zero(&aschecksum_data);
483 krb5_data_zero(&tgschecksum_data);
485 if (state->armor_crypto == NULL)
486 return check_fast(context, state);
488 state->flags |= KRB5_FAST_EXPECTED;
490 fastreq.fast_options.hide_client_names = 1;
492 ret = copy_KDC_REQ_BODY(&req->req_body, &fastreq.req_body);
493 if (ret)
494 goto out;
497 * In the case of a AS-REQ, remove all account names. Want to this
498 * for TGS-REQ too, but due to layering this is tricky.
500 * 1. TGS-REQ need checksum of REQ-BODY
501 * 2. FAST needs checksum of TGS-REQ, so, FAST needs to happen after TGS-REQ
502 * 3. FAST privacy mangaling needs to happen before TGS-REQ does the checksum in 1.
504 * So lets not modify the bits for now for TGS-REQ
506 if (state->flags & KRB5_FAST_AS_REQ) {
507 free_KDC_REQ_BODY(&req->req_body);
509 req->req_body.realm = strdup(KRB5_ANON_REALM);
510 if (req->req_body.realm == NULL) {
511 ret = krb5_enomem(context);
512 goto out;
515 ret = set_anon_principal(context, &req->req_body.cname);
516 if (ret)
517 goto out;
519 ALLOC(req->req_body.till, 1);
520 *req->req_body.till = 0;
522 ASN1_MALLOC_ENCODE(KDC_REQ_BODY,
523 aschecksum_data.data,
524 aschecksum_data.length,
525 &req->req_body,
526 &size, ret);
527 if (ret)
528 goto out;
529 heim_assert(aschecksum_data.length == size, "ASN.1 internal error");
531 checksum_data = &aschecksum_data;
533 if (req->padata) {
534 ret = copy_METHOD_DATA(req->padata, &fastreq.padata);
535 free_METHOD_DATA(req->padata);
536 if (ret)
537 goto out;
539 } else {
540 const PA_DATA *tgs_req_ptr = NULL;
541 int tgs_req_idx = 0;
542 size_t i;
544 heim_assert(req->padata != NULL, "req->padata is NULL");
546 tgs_req_ptr = krb5_find_padata(req->padata->val,
547 req->padata->len,
548 KRB5_PADATA_TGS_REQ,
549 &tgs_req_idx);
550 heim_assert(tgs_req_ptr != NULL, "KRB5_PADATA_TGS_REQ not found");
551 heim_assert(tgs_req_idx == 0, "KRB5_PADATA_TGS_REQ not first");
553 tgschecksum_data.data = tgs_req_ptr->padata_value.data;
554 tgschecksum_data.length = tgs_req_ptr->padata_value.length;
555 checksum_data = &tgschecksum_data;
558 * Now copy all remaining once to
559 * the fastreq.padata and clear
560 * them in the outer req first,
561 * and remember to readd them later.
563 readd_padata_to_outer = TRUE;
565 for (i = 1; i < req->padata->len; i++) {
566 PA_DATA *val = &req->padata->val[i];
568 ret = krb5_padata_add(context,
569 &fastreq.padata,
570 val->padata_type,
571 val->padata_value.data,
572 val->padata_value.length);
573 if (ret) {
574 krb5_set_error_message(context, ret,
575 N_("malloc: out of memory", ""));
576 goto out;
578 val->padata_value.data = NULL;
579 val->padata_value.length = 0;
583 * Only TGS-REQ remaining
585 req->padata->len = 1;
588 if (req->padata == NULL) {
589 ALLOC(req->padata, 1);
590 if (req->padata == NULL) {
591 ret = krb5_enomem(context);
592 goto out;
596 ASN1_MALLOC_ENCODE(KrbFastReq, data.data, data.length, &fastreq, &size, ret);
597 if (ret)
598 goto out;
599 heim_assert(data.length == size, "ASN.1 internal error");
601 fxreq.element = state->type;
603 if (state->type == choice_PA_FX_FAST_REQUEST_armored_data) {
604 fxreq.u.armored_data.armor = state->armor_data;
605 state->armor_data = NULL;
607 heim_assert(state->armor_crypto != NULL,
608 "FAST armor key missing when FAST started");
610 ret = krb5_create_checksum(context, state->armor_crypto,
611 KRB5_KU_FAST_REQ_CHKSUM, 0,
612 checksum_data->data,
613 checksum_data->length,
614 &fxreq.u.armored_data.req_checksum);
615 if (ret)
616 goto out;
618 ret = krb5_encrypt_EncryptedData(context, state->armor_crypto,
619 KRB5_KU_FAST_ENC,
620 data.data,
621 data.length,
623 &fxreq.u.armored_data.enc_fast_req);
624 krb5_data_free(&data);
625 if (ret)
626 goto out;
628 } else {
629 krb5_data_free(&data);
630 heim_assert(false, "unknown FAST type, internal error");
633 ASN1_MALLOC_ENCODE(PA_FX_FAST_REQUEST, data.data, data.length, &fxreq, &size, ret);
634 if (ret)
635 goto out;
636 heim_assert(data.length == size, "ASN.1 internal error");
639 ret = krb5_padata_add(context, req->padata, KRB5_PADATA_FX_FAST, data.data, data.length);
640 if (ret)
641 goto out;
642 krb5_data_zero(&data);
644 if (readd_padata_to_outer) {
645 size_t i;
647 for (i = 0; i < fastreq.padata.len; i++) {
648 PA_DATA *val = &fastreq.padata.val[i];
650 ret = krb5_padata_add(context,
651 req->padata,
652 val->padata_type,
653 val->padata_value.data,
654 val->padata_value.length);
655 if (ret) {
656 krb5_set_error_message(context, ret,
657 N_("malloc: out of memory", ""));
658 goto out;
660 val->padata_value.data = NULL;
661 val->padata_value.length = 0;
665 out:
666 free_KrbFastReq(&fastreq);
667 free_PA_FX_FAST_REQUEST(&fxreq);
668 krb5_data_free(&data);
669 krb5_data_free(&aschecksum_data);
671 return ret;
674 krb5_error_code
675 _krb5_fast_unwrap_error(krb5_context context,
676 int32_t nonce,
677 struct krb5_fast_state *state,
678 METHOD_DATA *md,
679 KRB_ERROR *error)
681 KrbFastResponse fastrep;
682 krb5_error_code ret;
683 PA_DATA *pa;
684 int idx;
686 if (state->armor_crypto == NULL)
687 return check_fast(context, state);
689 memset(&fastrep, 0, sizeof(fastrep));
691 if (error->error_code != KRB5_KDC_ERR_MORE_PREAUTH_DATA_REQUIRED)
692 _krb5_debug(context, 10, "using FAST without FAST outer error code");
694 idx = 0;
695 pa = krb5_find_padata(md->val, md->len, KRB5_PADATA_FX_FAST, &idx);
696 if (pa == NULL) {
698 * Typically _krb5_fast_wrap_req() has set KRB5_FAST_EXPECTED, which
699 * means check_fast() will complain and return KRB5KRB_AP_ERR_MODIFIED.
701 * But for TGS-REP init_tgs_req() clears KRB5_FAST_EXPECTED and we'll
702 * ignore a missing KRB5_PADATA_FX_FAST.
704 return check_fast(context, state);
707 ret = unwrap_fast_rep(context, state, pa, &fastrep);
708 if (ret)
709 goto out;
711 if (fastrep.strengthen_key || nonce != (int32_t)fastrep.nonce) {
712 ret = KRB5KDC_ERR_PREAUTH_FAILED;
713 goto out;
716 idx = 0;
717 pa = krb5_find_padata(fastrep.padata.val, fastrep.padata.len, KRB5_PADATA_FX_ERROR, &idx);
718 if (pa == NULL) {
719 ret = KRB5_KDCREP_MODIFIED;
720 krb5_set_error_message(context, ret, N_("No wrapped error", ""));
721 goto out;
724 free_KRB_ERROR(error);
726 ret = krb5_rd_error(context, &pa->padata_value, error);
727 if (ret)
728 goto out;
730 if (error->e_data)
731 _krb5_debug(context, 10, "FAST wrapped KBB_ERROR contained e_data: %d",
732 (int)error->e_data->length);
734 free_METHOD_DATA(md);
735 md->val = fastrep.padata.val;
736 md->len = fastrep.padata.len;
738 fastrep.padata.val = NULL;
739 fastrep.padata.len = 0;
741 out:
742 free_KrbFastResponse(&fastrep);
743 return ret;
746 krb5_error_code
747 _krb5_fast_unwrap_kdc_rep(krb5_context context, int32_t nonce,
748 krb5_data *chksumdata,
749 struct krb5_fast_state *state, AS_REP *rep)
751 KrbFastResponse fastrep;
752 krb5_error_code ret;
753 PA_DATA *pa = NULL;
754 int idx = 0;
756 if (state == NULL || state->armor_crypto == NULL || rep->padata == NULL)
757 return check_fast(context, state);
759 /* find PA_FX_FAST_REPLY */
761 pa = krb5_find_padata(rep->padata->val, rep->padata->len,
762 KRB5_PADATA_FX_FAST, &idx);
763 if (pa == NULL)
764 return check_fast(context, state);
766 memset(&fastrep, 0, sizeof(fastrep));
768 ret = unwrap_fast_rep(context, state, pa, &fastrep);
769 if (ret)
770 goto out;
772 free_METHOD_DATA(rep->padata);
773 ret = copy_METHOD_DATA(&fastrep.padata, rep->padata);
774 if (ret)
775 goto out;
777 if (fastrep.strengthen_key) {
778 if (state->strengthen_key)
779 krb5_free_keyblock(context, state->strengthen_key);
781 ret = krb5_copy_keyblock(context, fastrep.strengthen_key, &state->strengthen_key);
782 if (ret)
783 goto out;
786 if (nonce != (int32_t)fastrep.nonce) {
787 ret = KRB5KDC_ERR_PREAUTH_FAILED;
788 goto out;
790 if (fastrep.finished) {
791 PrincipalName cname;
792 krb5_realm crealm = NULL;
794 if (chksumdata == NULL) {
795 ret = KRB5KDC_ERR_PREAUTH_FAILED;
796 goto out;
799 ret = krb5_verify_checksum(context, state->armor_crypto,
800 KRB5_KU_FAST_FINISHED,
801 chksumdata->data, chksumdata->length,
802 &fastrep.finished->ticket_checksum);
803 if (ret)
804 goto out;
806 /* update */
807 ret = copy_Realm(&fastrep.finished->crealm, &crealm);
808 if (ret)
809 goto out;
810 free_Realm(&rep->crealm);
811 rep->crealm = crealm;
813 ret = copy_PrincipalName(&fastrep.finished->cname, &cname);
814 if (ret)
815 goto out;
816 free_PrincipalName(&rep->cname);
817 rep->cname = cname;
818 } else if (chksumdata) {
819 /* expected fastrep.finish but didn't get it */
820 ret = KRB5KDC_ERR_PREAUTH_FAILED;
823 out:
824 free_KrbFastResponse(&fastrep);
825 return ret;
828 void
829 _krb5_fast_free(krb5_context context, struct krb5_fast_state *state)
831 if (state->armor_ccache) {
832 if (state->flags & KRB5_FAST_ANON_PKINIT_ARMOR)
833 krb5_cc_destroy(context, state->armor_ccache);
834 else
835 krb5_cc_close(context, state->armor_ccache);
837 if (state->armor_service)
838 krb5_free_principal(context, state->armor_service);
839 if (state->armor_crypto)
840 krb5_crypto_destroy(context, state->armor_crypto);
841 if (state->strengthen_key)
842 krb5_free_keyblock(context, state->strengthen_key);
843 krb5_free_keyblock_contents(context, &state->armor_key);
844 if (state->armor_data) {
845 free_KrbFastArmor(state->armor_data);
846 free(state->armor_data);
849 if (state->anon_pkinit_ctx)
850 krb5_init_creds_free(context, state->anon_pkinit_ctx);
851 if (state->anon_pkinit_opt)
852 krb5_get_init_creds_opt_free(context, state->anon_pkinit_opt);
854 memset(state, 0, sizeof(*state));
857 krb5_error_code
858 _krb5_fast_anon_pkinit_step(krb5_context context,
859 krb5_init_creds_context ctx,
860 struct krb5_fast_state *state,
861 const krb5_data *in,
862 krb5_data *out,
863 krb5_realm *out_realm,
864 unsigned int *flags)
866 krb5_error_code ret;
867 krb5_const_realm realm = _krb5_init_creds_get_cred_client(context, ctx)->realm;
868 krb5_init_creds_context anon_pk_ctx;
869 krb5_principal principal = NULL, anon_pk_client;
870 krb5_ccache ccache = NULL;
871 krb5_creds cred;
872 krb5_data data = { 3, rk_UNCONST("yes") };
874 krb5_data_zero(out);
875 *out_realm = NULL;
877 memset(&cred, 0, sizeof(cred));
879 if (state->anon_pkinit_opt == NULL) {
880 ret = krb5_get_init_creds_opt_alloc(context, &state->anon_pkinit_opt);
881 if (ret)
882 goto out;
884 krb5_get_init_creds_opt_set_tkt_life(state->anon_pkinit_opt, 60);
885 krb5_get_init_creds_opt_set_anonymous(state->anon_pkinit_opt, TRUE);
887 ret = krb5_make_principal(context, &principal, realm,
888 KRB5_WELLKNOWN_NAME, KRB5_ANON_NAME, NULL);
889 if (ret)
890 goto out;
892 ret = krb5_get_init_creds_opt_set_pkinit(context,
893 state->anon_pkinit_opt,
894 principal,
895 NULL, NULL, NULL, NULL,
896 KRB5_GIC_OPT_PKINIT_ANONYMOUS |
897 KRB5_GIC_OPT_PKINIT_NO_KDC_ANCHOR,
898 NULL, NULL, NULL);
899 if (ret)
900 goto out;
902 ret = krb5_init_creds_init(context, principal, NULL, NULL,
903 _krb5_init_creds_get_cred_starttime(context, ctx),
904 state->anon_pkinit_opt,
905 &state->anon_pkinit_ctx);
906 if (ret)
907 goto out;
910 anon_pk_ctx = state->anon_pkinit_ctx;
912 ret = krb5_init_creds_step(context, anon_pk_ctx, in, out, out_realm, flags);
913 if (ret ||
914 (*flags & KRB5_INIT_CREDS_STEP_FLAG_CONTINUE))
915 goto out;
917 ret = krb5_process_last_request(context, state->anon_pkinit_opt, anon_pk_ctx);
918 if (ret)
919 goto out;
921 ret = krb5_cc_new_unique(context, "MEMORY", NULL, &ccache);
922 if (ret)
923 goto out;
925 ret = krb5_init_creds_get_creds(context, anon_pk_ctx, &cred);
926 if (ret)
927 goto out;
929 if (!cred.flags.b.enc_pa_rep) {
930 ret = KRB5KDC_ERR_BADOPTION; /* KDC does not support FAST */
931 goto out;
934 anon_pk_client = _krb5_init_creds_get_cred_client(context, anon_pk_ctx);
936 ret = krb5_cc_initialize(context, ccache, anon_pk_client);
937 if (ret)
938 goto out;
940 ret = krb5_cc_store_cred(context, ccache, &cred);
941 if (ret)
942 goto out;
944 ret = krb5_cc_set_config(context, ccache, cred.server,
945 "fast_avail", &data);
946 if (ret && ret != KRB5_CC_NOSUPP)
947 return ret;
949 if (_krb5_pk_is_kdc_verified(context, state->anon_pkinit_opt))
950 state->flags |= KRB5_FAST_KDC_VERIFIED;
951 else
952 state->flags &= ~(KRB5_FAST_KDC_VERIFIED);
954 state->armor_ccache = ccache;
955 ccache = NULL;
957 krb5_init_creds_free(context, state->anon_pkinit_ctx);
958 state->anon_pkinit_ctx = NULL;
960 krb5_get_init_creds_opt_free(context, state->anon_pkinit_opt);
961 state->anon_pkinit_opt = NULL;
963 out:
964 krb5_free_principal(context, principal);
965 krb5_free_cred_contents(context, &cred);
966 if (ccache)
967 krb5_cc_destroy(context, ccache);
969 return ret;