Line data Source code
1 : /*
2 : * Copyright (c) 2011 Kungliga Tekniska Högskolan
3 : * (Royal Institute of Technology, Stockholm, Sweden).
4 : * All rights reserved.
5 : *
6 : * Redistribution and use in source and binary forms, with or without
7 : * modification, are permitted provided that the following conditions
8 : * are met:
9 : *
10 : * 1. Redistributions of source code must retain the above copyright
11 : * notice, this list of conditions and the following disclaimer.
12 : *
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.
16 : *
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.
20 : *
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.
32 : */
33 :
34 : #include "krb5_locl.h"
35 : #ifndef WIN32
36 : #include <heim-ipc.h>
37 : #endif
38 :
39 : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
40 180153 : _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)
47 : {
48 6632 : krb5_crypto crypto1, crypto2;
49 6632 : krb5_data pa1, pa2;
50 6632 : krb5_error_code ret;
51 :
52 180153 : ret = krb5_crypto_init(context, key1, 0, &crypto1);
53 180153 : if (ret)
54 0 : return ret;
55 :
56 180153 : ret = krb5_crypto_init(context, key2, 0, &crypto2);
57 180153 : if (ret) {
58 0 : krb5_crypto_destroy(context, crypto1);
59 0 : return ret;
60 : }
61 :
62 180153 : pa1.data = rk_UNCONST(pepper1);
63 180153 : pa1.length = strlen(pepper1);
64 180153 : pa2.data = rk_UNCONST(pepper2);
65 180153 : pa2.length = strlen(pepper2);
66 :
67 186785 : ret = krb5_crypto_fx_cf2(context, crypto1, crypto2, &pa1, &pa2,
68 180153 : key1->keytype, armorkey);
69 180153 : krb5_crypto_destroy(context, crypto1);
70 180153 : krb5_crypto_destroy(context, crypto2);
71 180153 : if (ret)
72 0 : return ret;
73 :
74 180153 : if (armor_crypto) {
75 92823 : ret = krb5_crypto_init(context, armorkey, 0, armor_crypto);
76 92823 : if (ret)
77 0 : krb5_free_keyblock_contents(context, armorkey);
78 : }
79 :
80 173521 : return ret;
81 : }
82 :
83 : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
84 92823 : _krb5_fast_armor_key(krb5_context context,
85 : krb5_keyblock *subkey,
86 : krb5_keyblock *sessionkey,
87 : krb5_keyblock *armorkey,
88 : krb5_crypto *armor_crypto)
89 : {
90 92823 : return _krb5_fast_cf2(context,
91 : subkey,
92 : "subkeyarmor",
93 : sessionkey,
94 : "ticketarmor",
95 : armorkey,
96 : armor_crypto);
97 : }
98 :
99 : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
100 324 : _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)
105 : {
106 324 : return _krb5_fast_cf2(context,
107 : armorkey,
108 : "explicitarmor",
109 : subkey,
110 : "tgsarmor",
111 : explicit_armorkey,
112 : explicit_armor_crypto);
113 : }
114 :
115 : static krb5_error_code
116 71858 : check_fast(krb5_context context, struct krb5_fast_state *state)
117 : {
118 70103 : if (state && (state->flags & KRB5_FAST_EXPECTED)) {
119 0 : krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED,
120 : "Expected FAST, but no FAST "
121 : "was in the response from the KDC");
122 0 : return KRB5KRB_AP_ERR_MODIFIED;
123 : }
124 69518 : return 0;
125 : }
126 :
127 : static krb5_error_code
128 17 : 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)
134 : {
135 17 : krb5_auth_context auth_context = NULL;
136 17 : krb5_creds cred, *credp = NULL;
137 0 : krb5_error_code ret;
138 0 : krb5_data empty;
139 0 : krb5_const_realm tgs_realm;
140 :
141 17 : if (armor_ccache == NULL) {
142 0 : krb5_set_error_message(context, EINVAL,
143 : "Armor credential cache required");
144 0 : return EINVAL;
145 : }
146 :
147 17 : krb5_data_zero(&empty);
148 17 : memset(&cred, 0, sizeof(cred));
149 :
150 17 : ret = krb5_auth_con_init (context, &auth_context);
151 17 : if (ret)
152 0 : goto out;
153 :
154 17 : ret = krb5_cc_get_principal(context, armor_ccache, &cred.client);
155 17 : if (ret)
156 0 : goto out;
157 :
158 : /*
159 : * Make sure we don't ask for a krbtgt/WELLKNOWN:ANONYMOUS
160 : */
161 17 : if (krb5_principal_is_anonymous(context, cred.client,
162 : KRB5_ANON_MATCH_UNAUTHENTICATED))
163 0 : tgs_realm = realm;
164 : else
165 17 : tgs_realm = cred.client->realm;
166 :
167 17 : ret = krb5_make_principal(context, &cred.server,
168 : tgs_realm,
169 : KRB5_TGS_NAME,
170 : tgs_realm,
171 : NULL);
172 17 : if (ret)
173 0 : goto out;
174 :
175 17 : ret = krb5_get_credentials(context, 0, armor_ccache, &cred, &credp);
176 17 : if (ret)
177 0 : goto out;
178 :
179 17 : ret = krb5_auth_con_add_AuthorizationData(context, auth_context,
180 : KRB5_AUTHDATA_FX_FAST_ARMOR,
181 : &empty);
182 17 : if (ret)
183 0 : goto out;
184 :
185 17 : ret = krb5_mk_req_extended(context,
186 : &auth_context,
187 : AP_OPTS_USE_SUBKEY,
188 : NULL,
189 : credp,
190 : armor_value);
191 17 : if (ret)
192 0 : goto out;
193 :
194 17 : ret = _krb5_fast_armor_key(context,
195 17 : auth_context->local_subkey,
196 17 : auth_context->keyblock,
197 : armor_key,
198 : armor_crypto);
199 17 : if (ret)
200 0 : goto out;
201 :
202 17 : out:
203 17 : if (auth_context)
204 17 : krb5_auth_con_free(context, auth_context);
205 17 : if (credp)
206 17 : krb5_free_creds(context, credp);
207 17 : krb5_free_principal(context, cred.server);
208 17 : krb5_free_principal(context, cred.client);
209 :
210 17 : return ret;
211 : }
212 :
213 : #ifndef WIN32
214 : static heim_base_once_t armor_service_once = HEIM_BASE_ONCE_INIT;
215 : static heim_ipc armor_service = NULL;
216 :
217 : static void
218 0 : fast_armor_init_ipc(void *ctx)
219 : {
220 0 : heim_ipc *ipc = ctx;
221 0 : heim_ipc_init_context("ANY:org.h5l.armor-service", ipc);
222 0 : }
223 : #endif
224 :
225 : static krb5_error_code
226 17 : make_fast_ap_fxarmor(krb5_context context,
227 : struct krb5_fast_state *state,
228 : krb5_const_realm realm,
229 : KrbFastArmor **armor)
230 : {
231 17 : KrbFastArmor *fxarmor = NULL;
232 0 : krb5_error_code ret;
233 :
234 17 : *armor = NULL;
235 :
236 17 : ALLOC(fxarmor, 1);
237 17 : if (fxarmor == NULL) {
238 0 : ret = ENOMEM;
239 0 : goto out;
240 : }
241 :
242 17 : 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 0 : KERB_ARMOR_SERVICE_REPLY msg;
249 0 : krb5_data request, reply;
250 :
251 0 : heim_base_once_f(&armor_service_once, &armor_service, fast_armor_init_ipc);
252 0 : if (armor_service == NULL) {
253 0 : krb5_set_error_message(context, ENOENT, "Failed to open fast armor service");
254 0 : ret = ENOENT;
255 0 : goto out;
256 : }
257 :
258 0 : krb5_data_zero(&reply);
259 :
260 0 : request.data = rk_UNCONST(realm);
261 0 : request.length = strlen(realm);
262 :
263 0 : ret = heim_ipc_call(armor_service, &request, &reply, NULL);
264 0 : if (ret) {
265 0 : krb5_set_error_message(context, ret, "Failed to get armor service credential");
266 0 : goto out;
267 : }
268 :
269 0 : ret = decode_KERB_ARMOR_SERVICE_REPLY(reply.data, reply.length, &msg, NULL);
270 0 : krb5_data_free(&reply);
271 0 : if (ret)
272 0 : goto out;
273 :
274 0 : ret = copy_KrbFastArmor(fxarmor, &msg.armor);
275 0 : if (ret) {
276 0 : free_KERB_ARMOR_SERVICE_REPLY(&msg);
277 0 : goto out;
278 : }
279 :
280 0 : ret = krb5_copy_keyblock_contents(context, &msg.armor_key, &state->armor_key);
281 0 : free_KERB_ARMOR_SERVICE_REPLY(&msg);
282 0 : if (ret)
283 0 : goto out;
284 :
285 0 : ret = krb5_crypto_init(context, &state->armor_key, 0, &state->armor_crypto);
286 0 : if (ret)
287 0 : goto out;
288 : #endif /* WIN32 */
289 : } else {
290 17 : fxarmor->armor_type = 1;
291 :
292 17 : ret = make_local_fast_ap_fxarmor(context,
293 : state->armor_ccache,
294 : realm,
295 17 : &fxarmor->armor_value,
296 : &state->armor_key,
297 : &state->armor_crypto);
298 17 : if (ret)
299 0 : goto out;
300 : }
301 :
302 :
303 17 : *armor = fxarmor;
304 17 : fxarmor = NULL;
305 :
306 17 : out:
307 17 : if (fxarmor) {
308 0 : free_KrbFastArmor(fxarmor);
309 0 : free(fxarmor);
310 : }
311 17 : return ret;
312 : }
313 :
314 : static krb5_error_code
315 43741 : unwrap_fast_rep(krb5_context context,
316 : struct krb5_fast_state *state,
317 : PA_DATA *pa,
318 : KrbFastResponse *fastrep)
319 : {
320 1658 : PA_FX_FAST_REPLY fxfastrep;
321 1658 : krb5_error_code ret;
322 :
323 43741 : memset(&fxfastrep, 0, sizeof(fxfastrep));
324 :
325 43741 : ret = decode_PA_FX_FAST_REPLY(pa->padata_value.data,
326 : pa->padata_value.length,
327 : &fxfastrep, NULL);
328 43741 : if (ret)
329 0 : return ret;
330 :
331 43741 : if (fxfastrep.element == choice_PA_FX_FAST_REPLY_armored_data) {
332 1658 : krb5_data data;
333 :
334 43741 : 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 43741 : if (ret)
340 0 : goto out;
341 :
342 43741 : ret = decode_KrbFastResponse(data.data, data.length, fastrep, NULL);
343 43741 : krb5_data_free(&data);
344 43741 : if (ret)
345 0 : goto out;
346 :
347 : } else {
348 0 : ret = KRB5KDC_ERR_PREAUTH_FAILED;
349 0 : goto out;
350 : }
351 :
352 43741 : out:
353 43741 : free_PA_FX_FAST_REPLY(&fxfastrep);
354 :
355 43741 : return ret;
356 : }
357 :
358 : static krb5_error_code
359 17 : set_anon_principal(krb5_context context, PrincipalName **p)
360 : {
361 :
362 17 : ALLOC((*p), 1);
363 17 : if (*p == NULL)
364 0 : goto fail;
365 :
366 17 : (*p)->name_type = KRB5_NT_PRINCIPAL;
367 :
368 17 : ALLOC_SEQ(&(*p)->name_string, 2);
369 17 : if ((*p)->name_string.val == NULL)
370 0 : goto fail;
371 :
372 17 : (*p)->name_string.val[0] = strdup(KRB5_WELLKNOWN_NAME);
373 17 : if ((*p)->name_string.val[0] == NULL)
374 0 : goto fail;
375 :
376 17 : (*p)->name_string.val[1] = strdup(KRB5_ANON_NAME);
377 17 : if ((*p)->name_string.val[1] == NULL)
378 0 : goto fail;
379 :
380 17 : return 0;
381 0 : fail:
382 0 : if (*p) {
383 0 : if ((*p)->name_string.val) {
384 0 : free((*p)->name_string.val[0]);
385 0 : free((*p)->name_string.val[1]);
386 0 : free((*p)->name_string.val);
387 : }
388 0 : free(*p);
389 : }
390 :
391 0 : return krb5_enomem(context);
392 : }
393 :
394 : krb5_error_code
395 86355 : _krb5_fast_create_armor(krb5_context context,
396 : struct krb5_fast_state *state,
397 : const char *realm)
398 : {
399 2828 : krb5_error_code ret;
400 :
401 86355 : if (state->armor_crypto == NULL) {
402 83161 : if (state->armor_ccache || state->armor_ac || (state->flags & KRB5_FAST_AP_ARMOR_SERVICE)) {
403 : /*
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.
408 : */
409 44825 : state->type = choice_PA_FX_FAST_REQUEST_armored_data;
410 : } else {
411 38336 : return check_fast(context, state);
412 : }
413 : }
414 :
415 48019 : if (state->type == choice_PA_FX_FAST_REQUEST_armored_data) {
416 48019 : if (state->armor_crypto) {
417 3194 : krb5_crypto_destroy(context, state->armor_crypto);
418 3194 : state->armor_crypto = NULL;
419 : }
420 48019 : if (state->strengthen_key) {
421 2480 : krb5_free_keyblock(context, state->strengthen_key);
422 2480 : state->strengthen_key = NULL;
423 : }
424 48019 : krb5_free_keyblock_contents(context, &state->armor_key);
425 :
426 : /*
427 : * If we have a armor auth context, its because the caller
428 : * wants us to do an implicit FAST armor (TGS-REQ).
429 : */
430 48019 : if (state->armor_ac) {
431 48002 : heim_assert((state->flags & KRB5_FAST_AS_REQ) == 0, "FAST AS with AC");
432 :
433 48002 : ret = _krb5_fast_armor_key(context,
434 46344 : state->armor_ac->local_subkey,
435 46344 : state->armor_ac->keyblock,
436 : &state->armor_key,
437 : &state->armor_crypto);
438 48002 : if (ret)
439 0 : goto out;
440 : } else {
441 17 : heim_assert((state->flags & KRB5_FAST_AS_REQ) != 0, "FAST TGS without AC");
442 :
443 17 : if (state->armor_data) {
444 0 : free_KrbFastArmor(state->armor_data);
445 0 : free(state->armor_data);
446 0 : state->armor_data = NULL;
447 : }
448 17 : ret = make_fast_ap_fxarmor(context, state, realm,
449 : &state->armor_data);
450 17 : if (ret)
451 0 : goto out;
452 : }
453 : } else {
454 0 : heim_abort("unknown state type: %d", (int)state->type);
455 : }
456 46361 : out:
457 46361 : return ret;
458 : }
459 :
460 :
461 : krb5_error_code
462 86355 : _krb5_fast_wrap_req(krb5_context context,
463 : struct krb5_fast_state *state,
464 : KDC_REQ *req)
465 : {
466 2828 : PA_FX_FAST_REQUEST fxreq;
467 2828 : krb5_error_code ret;
468 2828 : KrbFastReq fastreq;
469 2828 : krb5_data data, aschecksum_data, tgschecksum_data;
470 86355 : const krb5_data *checksum_data = NULL;
471 86355 : size_t size = 0;
472 86355 : krb5_boolean readd_padata_to_outer = FALSE;
473 :
474 86355 : if (state->flags & KRB5_FAST_DISABLED) {
475 38336 : _krb5_debug(context, 10, "fast disabled, not doing any fast wrapping");
476 38336 : return 0;
477 : }
478 :
479 48019 : memset(&fxreq, 0, sizeof(fxreq));
480 48019 : memset(&fastreq, 0, sizeof(fastreq));
481 48019 : krb5_data_zero(&data);
482 48019 : krb5_data_zero(&aschecksum_data);
483 48019 : krb5_data_zero(&tgschecksum_data);
484 :
485 48019 : if (state->armor_crypto == NULL)
486 0 : return check_fast(context, state);
487 :
488 48019 : state->flags |= KRB5_FAST_EXPECTED;
489 :
490 48019 : fastreq.fast_options.hide_client_names = 1;
491 :
492 48019 : ret = copy_KDC_REQ_BODY(&req->req_body, &fastreq.req_body);
493 48019 : if (ret)
494 0 : goto out;
495 :
496 : /*
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.
499 : *
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.
503 : *
504 : * So lets not modify the bits for now for TGS-REQ
505 : */
506 48019 : if (state->flags & KRB5_FAST_AS_REQ) {
507 17 : free_KDC_REQ_BODY(&req->req_body);
508 :
509 17 : req->req_body.realm = strdup(KRB5_ANON_REALM);
510 17 : if (req->req_body.realm == NULL) {
511 0 : ret = krb5_enomem(context);
512 0 : goto out;
513 : }
514 :
515 17 : ret = set_anon_principal(context, &req->req_body.cname);
516 17 : if (ret)
517 0 : goto out;
518 :
519 17 : ALLOC(req->req_body.till, 1);
520 17 : *req->req_body.till = 0;
521 :
522 17 : ASN1_MALLOC_ENCODE(KDC_REQ_BODY,
523 : aschecksum_data.data,
524 : aschecksum_data.length,
525 : &req->req_body,
526 : &size, ret);
527 17 : if (ret)
528 0 : goto out;
529 17 : heim_assert(aschecksum_data.length == size, "ASN.1 internal error");
530 :
531 17 : checksum_data = &aschecksum_data;
532 :
533 17 : if (req->padata) {
534 17 : ret = copy_METHOD_DATA(req->padata, &fastreq.padata);
535 17 : free_METHOD_DATA(req->padata);
536 17 : if (ret)
537 0 : goto out;
538 : }
539 : } else {
540 48002 : const PA_DATA *tgs_req_ptr = NULL;
541 48002 : int tgs_req_idx = 0;
542 1658 : size_t i;
543 :
544 48002 : heim_assert(req->padata != NULL, "req->padata is NULL");
545 :
546 48002 : tgs_req_ptr = krb5_find_padata(req->padata->val,
547 46344 : req->padata->len,
548 : KRB5_PADATA_TGS_REQ,
549 : &tgs_req_idx);
550 48002 : heim_assert(tgs_req_ptr != NULL, "KRB5_PADATA_TGS_REQ not found");
551 48002 : heim_assert(tgs_req_idx == 0, "KRB5_PADATA_TGS_REQ not first");
552 :
553 48002 : tgschecksum_data.data = tgs_req_ptr->padata_value.data;
554 48002 : tgschecksum_data.length = tgs_req_ptr->padata_value.length;
555 48002 : checksum_data = &tgschecksum_data;
556 :
557 : /*
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.
562 : */
563 48002 : readd_padata_to_outer = TRUE;
564 :
565 48904 : for (i = 1; i < req->padata->len; i++) {
566 902 : PA_DATA *val = &req->padata->val[i];
567 :
568 902 : ret = krb5_padata_add(context,
569 : &fastreq.padata,
570 902 : val->padata_type,
571 : val->padata_value.data,
572 : val->padata_value.length);
573 902 : if (ret) {
574 0 : krb5_set_error_message(context, ret,
575 0 : N_("malloc: out of memory", ""));
576 0 : goto out;
577 : }
578 902 : val->padata_value.data = NULL;
579 902 : val->padata_value.length = 0;
580 : }
581 :
582 : /*
583 : * Only TGS-REQ remaining
584 : */
585 48002 : req->padata->len = 1;
586 : }
587 :
588 48019 : if (req->padata == NULL) {
589 0 : ALLOC(req->padata, 1);
590 0 : if (req->padata == NULL) {
591 0 : ret = krb5_enomem(context);
592 0 : goto out;
593 : }
594 : }
595 :
596 48019 : ASN1_MALLOC_ENCODE(KrbFastReq, data.data, data.length, &fastreq, &size, ret);
597 48019 : if (ret)
598 0 : goto out;
599 48019 : heim_assert(data.length == size, "ASN.1 internal error");
600 :
601 48019 : fxreq.element = state->type;
602 :
603 48019 : if (state->type == choice_PA_FX_FAST_REQUEST_armored_data) {
604 48019 : fxreq.u.armored_data.armor = state->armor_data;
605 48019 : state->armor_data = NULL;
606 :
607 48019 : heim_assert(state->armor_crypto != NULL,
608 : "FAST armor key missing when FAST started");
609 :
610 49677 : ret = krb5_create_checksum(context, state->armor_crypto,
611 : KRB5_KU_FAST_REQ_CHKSUM, 0,
612 48019 : checksum_data->data,
613 48019 : checksum_data->length,
614 : &fxreq.u.armored_data.req_checksum);
615 48019 : if (ret)
616 0 : goto out;
617 :
618 48019 : ret = krb5_encrypt_EncryptedData(context, state->armor_crypto,
619 : KRB5_KU_FAST_ENC,
620 : data.data,
621 : data.length,
622 : 0,
623 : &fxreq.u.armored_data.enc_fast_req);
624 48019 : krb5_data_free(&data);
625 48019 : if (ret)
626 0 : goto out;
627 :
628 : } else {
629 0 : krb5_data_free(&data);
630 0 : heim_assert(false, "unknown FAST type, internal error");
631 : }
632 :
633 48019 : ASN1_MALLOC_ENCODE(PA_FX_FAST_REQUEST, data.data, data.length, &fxreq, &size, ret);
634 48019 : if (ret)
635 0 : goto out;
636 48019 : heim_assert(data.length == size, "ASN.1 internal error");
637 :
638 :
639 48019 : ret = krb5_padata_add(context, req->padata, KRB5_PADATA_FX_FAST, data.data, data.length);
640 48019 : if (ret)
641 0 : goto out;
642 48019 : krb5_data_zero(&data);
643 :
644 48019 : if (readd_padata_to_outer) {
645 : size_t i;
646 :
647 48904 : for (i = 0; i < fastreq.padata.len; i++) {
648 902 : PA_DATA *val = &fastreq.padata.val[i];
649 :
650 902 : ret = krb5_padata_add(context,
651 : req->padata,
652 902 : val->padata_type,
653 : val->padata_value.data,
654 : val->padata_value.length);
655 902 : if (ret) {
656 0 : krb5_set_error_message(context, ret,
657 0 : N_("malloc: out of memory", ""));
658 0 : goto out;
659 : }
660 902 : val->padata_value.data = NULL;
661 902 : val->padata_value.length = 0;
662 : }
663 : }
664 :
665 48019 : out:
666 48019 : free_KrbFastReq(&fastreq);
667 48019 : free_PA_FX_FAST_REQUEST(&fxreq);
668 48019 : krb5_data_free(&data);
669 48019 : krb5_data_free(&aschecksum_data);
670 :
671 48019 : return ret;
672 : }
673 :
674 : krb5_error_code
675 17026 : _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)
680 : {
681 585 : KrbFastResponse fastrep;
682 585 : krb5_error_code ret;
683 585 : PA_DATA *pa;
684 585 : int idx;
685 :
686 17026 : if (state->armor_crypto == NULL)
687 15764 : return check_fast(context, state);
688 :
689 1262 : memset(&fastrep, 0, sizeof(fastrep));
690 :
691 1262 : idx = 0;
692 1262 : pa = krb5_find_padata(md->val, md->len, KRB5_PADATA_FX_FAST, &idx);
693 1262 : if (pa == NULL) {
694 32 : ret = KRB5_KDCREP_MODIFIED;
695 32 : krb5_set_error_message(context, ret,
696 32 : N_("FAST fast response is missing FX-FAST", ""));
697 32 : goto out;
698 : }
699 :
700 1230 : ret = unwrap_fast_rep(context, state, pa, &fastrep);
701 1230 : if (ret)
702 0 : goto out;
703 :
704 1230 : if (fastrep.strengthen_key || nonce != (int32_t)fastrep.nonce) {
705 0 : ret = KRB5KDC_ERR_PREAUTH_FAILED;
706 0 : goto out;
707 : }
708 :
709 1230 : idx = 0;
710 1230 : pa = krb5_find_padata(fastrep.padata.val, fastrep.padata.len, KRB5_PADATA_FX_ERROR, &idx);
711 1230 : if (pa == NULL) {
712 0 : ret = KRB5_KDCREP_MODIFIED;
713 0 : krb5_set_error_message(context, ret, N_("No wrapped error", ""));
714 0 : goto out;
715 : }
716 :
717 1230 : free_KRB_ERROR(error);
718 :
719 1230 : ret = krb5_rd_error(context, &pa->padata_value, error);
720 1230 : if (ret)
721 0 : goto out;
722 :
723 1230 : if (error->e_data)
724 0 : _krb5_debug(context, 10, "FAST wrapped KBB_ERROR contained e_data: %d",
725 0 : (int)error->e_data->length);
726 :
727 1230 : free_METHOD_DATA(md);
728 1230 : md->val = fastrep.padata.val;
729 1230 : md->len = fastrep.padata.len;
730 :
731 1230 : fastrep.padata.val = NULL;
732 1230 : fastrep.padata.len = 0;
733 :
734 1262 : out:
735 1262 : free_KrbFastResponse(&fastrep);
736 1262 : return ret;
737 : }
738 :
739 : krb5_error_code
740 60269 : _krb5_fast_unwrap_kdc_rep(krb5_context context, int32_t nonce,
741 : krb5_data *chksumdata,
742 : struct krb5_fast_state *state, AS_REP *rep)
743 : {
744 2243 : KrbFastResponse fastrep;
745 2243 : krb5_error_code ret;
746 60269 : PA_DATA *pa = NULL;
747 60269 : int idx = 0;
748 :
749 60269 : if (state == NULL || state->armor_crypto == NULL || rep->padata == NULL)
750 18343 : return check_fast(context, state);
751 :
752 : /* find PA_FX_FAST_REPLY */
753 :
754 42511 : pa = krb5_find_padata(rep->padata->val, rep->padata->len,
755 : KRB5_PADATA_FX_FAST, &idx);
756 42511 : if (pa == NULL)
757 0 : return check_fast(context, state);
758 :
759 42511 : memset(&fastrep, 0, sizeof(fastrep));
760 :
761 42511 : ret = unwrap_fast_rep(context, state, pa, &fastrep);
762 42511 : if (ret)
763 0 : goto out;
764 :
765 42511 : free_METHOD_DATA(rep->padata);
766 42511 : ret = copy_METHOD_DATA(&fastrep.padata, rep->padata);
767 42511 : if (ret)
768 0 : goto out;
769 :
770 42511 : if (fastrep.strengthen_key) {
771 42511 : if (state->strengthen_key)
772 0 : krb5_free_keyblock(context, state->strengthen_key);
773 :
774 42511 : ret = krb5_copy_keyblock(context, fastrep.strengthen_key, &state->strengthen_key);
775 42511 : if (ret)
776 0 : goto out;
777 : }
778 :
779 42511 : if (nonce != (int32_t)fastrep.nonce) {
780 0 : ret = KRB5KDC_ERR_PREAUTH_FAILED;
781 0 : goto out;
782 : }
783 42511 : if (fastrep.finished) {
784 1658 : PrincipalName cname;
785 42511 : krb5_realm crealm = NULL;
786 :
787 42511 : if (chksumdata == NULL) {
788 0 : ret = KRB5KDC_ERR_PREAUTH_FAILED;
789 0 : goto out;
790 : }
791 :
792 42511 : ret = krb5_verify_checksum(context, state->armor_crypto,
793 : KRB5_KU_FAST_FINISHED,
794 : chksumdata->data, chksumdata->length,
795 40853 : &fastrep.finished->ticket_checksum);
796 42511 : if (ret)
797 0 : goto out;
798 :
799 : /* update */
800 42511 : ret = copy_Realm(&fastrep.finished->crealm, &crealm);
801 42511 : if (ret)
802 0 : goto out;
803 42511 : free_Realm(&rep->crealm);
804 42511 : rep->crealm = crealm;
805 :
806 42511 : ret = copy_PrincipalName(&fastrep.finished->cname, &cname);
807 42511 : if (ret)
808 0 : goto out;
809 42511 : free_PrincipalName(&rep->cname);
810 42511 : rep->cname = cname;
811 0 : } else if (chksumdata) {
812 : /* expected fastrep.finish but didn't get it */
813 0 : ret = KRB5KDC_ERR_PREAUTH_FAILED;
814 : }
815 :
816 0 : out:
817 42511 : free_KrbFastResponse(&fastrep);
818 42511 : return ret;
819 : }
820 :
821 : void
822 151701 : _krb5_fast_free(krb5_context context, struct krb5_fast_state *state)
823 : {
824 151701 : if (state->armor_ccache) {
825 10 : if (state->flags & KRB5_FAST_ANON_PKINIT_ARMOR)
826 0 : krb5_cc_destroy(context, state->armor_ccache);
827 : else
828 10 : krb5_cc_close(context, state->armor_ccache);
829 : }
830 151701 : if (state->armor_service)
831 0 : krb5_free_principal(context, state->armor_service);
832 151701 : if (state->armor_crypto)
833 44825 : krb5_crypto_destroy(context, state->armor_crypto);
834 151701 : if (state->strengthen_key)
835 40031 : krb5_free_keyblock(context, state->strengthen_key);
836 151701 : krb5_free_keyblock_contents(context, &state->armor_key);
837 151701 : if (state->armor_data) {
838 0 : free_KrbFastArmor(state->armor_data);
839 0 : free(state->armor_data);
840 : }
841 :
842 151701 : if (state->anon_pkinit_ctx)
843 104 : krb5_init_creds_free(context, state->anon_pkinit_ctx);
844 151701 : if (state->anon_pkinit_opt)
845 104 : krb5_get_init_creds_opt_free(context, state->anon_pkinit_opt);
846 :
847 151701 : memset(state, 0, sizeof(*state));
848 151701 : }
849 :
850 : krb5_error_code
851 208 : _krb5_fast_anon_pkinit_step(krb5_context context,
852 : krb5_init_creds_context ctx,
853 : struct krb5_fast_state *state,
854 : const krb5_data *in,
855 : krb5_data *out,
856 : krb5_realm *out_realm,
857 : unsigned int *flags)
858 : {
859 0 : krb5_error_code ret;
860 208 : krb5_const_realm realm = _krb5_init_creds_get_cred_client(context, ctx)->realm;
861 0 : krb5_init_creds_context anon_pk_ctx;
862 208 : krb5_principal principal = NULL, anon_pk_client;
863 208 : krb5_ccache ccache = NULL;
864 0 : krb5_creds cred;
865 208 : krb5_data data = { 3, rk_UNCONST("yes") };
866 :
867 208 : krb5_data_zero(out);
868 208 : *out_realm = NULL;
869 :
870 208 : memset(&cred, 0, sizeof(cred));
871 :
872 208 : if (state->anon_pkinit_opt == NULL) {
873 104 : ret = krb5_get_init_creds_opt_alloc(context, &state->anon_pkinit_opt);
874 104 : if (ret)
875 0 : goto out;
876 :
877 104 : krb5_get_init_creds_opt_set_tkt_life(state->anon_pkinit_opt, 60);
878 104 : krb5_get_init_creds_opt_set_anonymous(state->anon_pkinit_opt, TRUE);
879 :
880 104 : ret = krb5_make_principal(context, &principal, realm,
881 : KRB5_WELLKNOWN_NAME, KRB5_ANON_NAME, NULL);
882 104 : if (ret)
883 0 : goto out;
884 :
885 104 : ret = krb5_get_init_creds_opt_set_pkinit(context,
886 : state->anon_pkinit_opt,
887 : principal,
888 : NULL, NULL, NULL, NULL,
889 : KRB5_GIC_OPT_PKINIT_ANONYMOUS |
890 : KRB5_GIC_OPT_PKINIT_NO_KDC_ANCHOR,
891 : NULL, NULL, NULL);
892 104 : if (ret)
893 0 : goto out;
894 :
895 104 : ret = krb5_init_creds_init(context, principal, NULL, NULL,
896 : _krb5_init_creds_get_cred_starttime(context, ctx),
897 : state->anon_pkinit_opt,
898 : &state->anon_pkinit_ctx);
899 104 : if (ret)
900 0 : goto out;
901 : }
902 :
903 208 : anon_pk_ctx = state->anon_pkinit_ctx;
904 :
905 208 : ret = krb5_init_creds_step(context, anon_pk_ctx, in, out, out_realm, flags);
906 208 : if (ret ||
907 104 : (*flags & KRB5_INIT_CREDS_STEP_FLAG_CONTINUE))
908 208 : goto out;
909 :
910 0 : ret = krb5_process_last_request(context, state->anon_pkinit_opt, anon_pk_ctx);
911 0 : if (ret)
912 0 : goto out;
913 :
914 0 : ret = krb5_cc_new_unique(context, "MEMORY", NULL, &ccache);
915 0 : if (ret)
916 0 : goto out;
917 :
918 0 : ret = krb5_init_creds_get_creds(context, anon_pk_ctx, &cred);
919 0 : if (ret)
920 0 : goto out;
921 :
922 0 : if (!cred.flags.b.enc_pa_rep) {
923 0 : ret = KRB5KDC_ERR_BADOPTION; /* KDC does not support FAST */
924 0 : goto out;
925 : }
926 :
927 0 : anon_pk_client = _krb5_init_creds_get_cred_client(context, anon_pk_ctx);
928 :
929 0 : ret = krb5_cc_initialize(context, ccache, anon_pk_client);
930 0 : if (ret)
931 0 : goto out;
932 :
933 0 : ret = krb5_cc_store_cred(context, ccache, &cred);
934 0 : if (ret)
935 0 : goto out;
936 :
937 0 : ret = krb5_cc_set_config(context, ccache, cred.server,
938 : "fast_avail", &data);
939 0 : if (ret && ret != KRB5_CC_NOSUPP)
940 0 : return ret;
941 :
942 0 : if (_krb5_pk_is_kdc_verified(context, state->anon_pkinit_opt))
943 0 : state->flags |= KRB5_FAST_KDC_VERIFIED;
944 : else
945 0 : state->flags &= ~(KRB5_FAST_KDC_VERIFIED);
946 :
947 0 : state->armor_ccache = ccache;
948 0 : ccache = NULL;
949 :
950 0 : krb5_init_creds_free(context, state->anon_pkinit_ctx);
951 0 : state->anon_pkinit_ctx = NULL;
952 :
953 0 : krb5_get_init_creds_opt_free(context, state->anon_pkinit_opt);
954 0 : state->anon_pkinit_opt = NULL;
955 :
956 208 : out:
957 208 : krb5_free_principal(context, principal);
958 208 : krb5_free_cred_contents(context, &cred);
959 208 : if (ccache)
960 0 : krb5_cc_destroy(context, ccache);
961 :
962 208 : return ret;
963 : }
|