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#include <heim-ipc.h> 36 37 38krb5_error_code 39_krb5_fast_cf2(krb5_context context, 40 krb5_keyblock *key1, 41 const char *pepper1, 42 krb5_keyblock *key2, 43 const char *pepper2, 44 krb5_keyblock *armorkey, 45 krb5_crypto *armor_crypto) 46{ 47 krb5_crypto crypto1, crypto2; 48 krb5_data pa1, pa2; 49 krb5_error_code ret; 50 51 ret = krb5_crypto_init(context, key1, 0, &crypto1); 52 if (ret) 53 return ret; 54 55 ret = krb5_crypto_init(context, key2, 0, &crypto2); 56 if (ret) { 57 krb5_crypto_destroy(context, crypto1); 58 return ret; 59 } 60 61 pa1.data = rk_UNCONST(pepper1); 62 pa1.length = strlen(pepper1); 63 pa2.data = rk_UNCONST(pepper2); 64 pa2.length = strlen(pepper2); 65 66 ret = krb5_crypto_fx_cf2(context, crypto1, crypto2, &pa1, &pa2, 67 key1->keytype, armorkey); 68 krb5_crypto_destroy(context, crypto1); 69 krb5_crypto_destroy(context, crypto2); 70 if (ret) 71 return ret; 72 73 if (armor_crypto) { 74 ret = krb5_crypto_init(context, armorkey, 0, armor_crypto); 75 if (ret) 76 krb5_free_keyblock_contents(context, armorkey); 77 } 78 79 return ret; 80} 81 82krb5_error_code 83_krb5_fast_armor_key(krb5_context context, 84 krb5_keyblock *subkey, 85 krb5_keyblock *sessionkey, 86 krb5_keyblock *armorkey, 87 krb5_crypto *armor_crypto) 88{ 89 return _krb5_fast_cf2(context, 90 subkey, 91 "subkeyarmor", 92 sessionkey, 93 "ticketarmor", 94 armorkey, 95 armor_crypto); 96} 97 98static krb5_error_code 99check_fast(krb5_context context, struct krb5_fast_state *state) 100{ 101 if (state && (state->flags & KRB5_FAST_EXPECTED)) { 102 krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED, 103 "Expected FAST, but no FAST " 104 "was in the response from the KDC"); 105 return KRB5KRB_AP_ERR_MODIFIED; 106 } 107 return 0; 108} 109 110krb5_error_code 111_krb5_make_fast_ap_fxarmor(krb5_context context, 112 krb5_ccache armor_ccache, 113 krb5_data *armor_value, 114 krb5_keyblock *armor_key, 115 krb5_crypto *armor_crypto) 116{ 117 krb5_auth_context auth_context = NULL; 118 krb5_creds cred, *credp = NULL; 119 krb5_error_code ret; 120 krb5_data empty; 121 122 krb5_data_zero(&empty); 123 memset(&cred, 0, sizeof(cred)); 124 125 ret = krb5_auth_con_init (context, &auth_context); 126 if (ret) 127 goto out; 128 129 ret = krb5_cc_get_principal(context, armor_ccache, &cred.client); 130 if (ret) 131 goto out; 132 133 ret = krb5_make_principal(context, &cred.server, 134 cred.client->realm, 135 KRB5_TGS_NAME, 136 cred.client->realm, 137 NULL); 138 if (ret) { 139 krb5_free_principal(context, cred.client); 140 goto out; 141 } 142 143 ret = krb5_get_credentials(context, 0, armor_ccache, &cred, &credp); 144 krb5_free_principal(context, cred.server); 145 krb5_free_principal(context, cred.client); 146 if (ret) 147 goto out; 148 149 ret = krb5_auth_con_add_AuthorizationData(context, auth_context, 150 KRB5_PADATA_FX_FAST_ARMOR, &empty); 151 if (ret) 152 goto out; 153 154 ret = krb5_mk_req_extended(context, 155 &auth_context, 156 AP_OPTS_USE_SUBKEY, 157 NULL, 158 credp, 159 armor_value); 160 if (ret) 161 goto out; 162 163 ret = _krb5_fast_armor_key(context, 164 auth_context->local_subkey, 165 auth_context->keyblock, 166 armor_key, 167 armor_crypto); 168 if (ret) 169 goto out; 170 171 out: 172 if (auth_context) 173 krb5_auth_con_free(context, auth_context); 174 if (credp) 175 krb5_free_creds(context, credp); 176 177 return ret; 178} 179 180static heim_base_once_t armor_service_once = HEIM_BASE_ONCE_INIT; 181static heim_ipc armor_service = NULL; 182 183static void 184fast_armor_init_ipc(void *ctx) 185{ 186 heim_ipc *ipc = ctx; 187 heim_ipc_init_context("ANY:org.h5l.armor-service", ipc); 188} 189 190 191static krb5_error_code 192make_fast_ap_fxarmor(krb5_context context, 193 struct krb5_fast_state *state, 194 const char *realm, 195 KrbFastArmor **armor) 196{ 197 KrbFastArmor *fxarmor = NULL; 198 krb5_error_code ret; 199 200 ALLOC(fxarmor, 1); 201 if (fxarmor == NULL) { 202 ret = ENOMEM; 203 goto out; 204 } 205 206 if (state->flags & KRB5_FAST_AP_ARMOR_SERVICE) { 207 KERB_ARMOR_SERVICE_REPLY msg; 208 krb5_data request, reply; 209 210 heim_base_once_f(&armor_service_once, &armor_service, fast_armor_init_ipc); 211 if (armor_service == NULL) { 212 free(fxarmor); 213 krb5_set_error_message(context, ENOENT, "Failed to open fast armor service"); 214 return ENOENT; 215 } 216 217 krb5_data_zero(&reply); 218 219 request.data = rk_UNCONST(realm); 220 request.length = strlen(realm); 221 222 ret = heim_ipc_call(armor_service, &request, &reply, NULL); 223 if (ret) { 224 krb5_set_error_message(context, ret, "Failed to get armor service credential"); 225 goto out; 226 } 227 228 ret = decode_KERB_ARMOR_SERVICE_REPLY(reply.data, reply.length, &msg, NULL); 229 krb5_data_free(&reply); 230 if (ret) 231 goto out; 232 233 ret = copy_KrbFastArmor(fxarmor, &msg.armor); 234 if (ret) { 235 free_KERB_ARMOR_SERVICE_REPLY(&msg); 236 goto out; 237 } 238 239 ret = krb5_copy_keyblock_contents(context, &msg.armor_key, &state->armor_key); 240 free_KERB_ARMOR_SERVICE_REPLY(&msg); 241 if (ret) 242 goto out; 243 244 ret = krb5_crypto_init(context, &state->armor_key, 0, &state->armor_crypto); 245 if (ret) 246 goto out; 247 248 } else { 249 250 fxarmor->armor_type = 1; 251 252 ret = _krb5_make_fast_ap_fxarmor(context, 253 state->armor_ccache, 254 &fxarmor->armor_value, 255 &state->armor_key, 256 &state->armor_crypto); 257 if (ret) 258 goto out; 259 } 260 261 262 *armor = fxarmor; 263 fxarmor = NULL; 264 out: 265 if (fxarmor) { 266 free_KrbFastArmor(fxarmor); 267 free(fxarmor); 268 } 269 return ret; 270} 271 272static krb5_error_code 273unwrap_fast_rep(krb5_context context, 274 struct krb5_fast_state *state, 275 PA_DATA *pa, 276 KrbFastResponse *fastrep) 277{ 278 PA_FX_FAST_REPLY fxfastrep; 279 krb5_error_code ret; 280 281 memset(&fxfastrep, 0, sizeof(fxfastrep)); 282 283 ret = decode_PA_FX_FAST_REPLY(pa->padata_value.data, pa->padata_value.length, &fxfastrep, NULL); 284 if (ret) 285 return ret; 286 287 if (fxfastrep.element == choice_PA_FX_FAST_REPLY_armored_data) { 288 krb5_data data; 289 290 ret = krb5_decrypt_EncryptedData(context, 291 state->armor_crypto, 292 KRB5_KU_FAST_REP, 293 &fxfastrep.u.armored_data.enc_fast_rep, 294 &data); 295 if (ret) 296 goto out; 297 298 ret = decode_KrbFastResponse(data.data, data.length, fastrep, NULL); 299 krb5_data_free(&data); 300 if (ret) 301 goto out; 302 303 } else { 304 ret = KRB5KDC_ERR_PREAUTH_FAILED; 305 goto out; 306 } 307 308 out: 309 free_PA_FX_FAST_REPLY(&fxfastrep); 310 311 return ret; 312} 313 314static krb5_error_code 315set_anon_principal(krb5_context context, PrincipalName **p) 316{ 317 318 ALLOC((*p), 1); 319 if (*p == NULL) 320 goto fail; 321 322 (*p)->name_type = KRB5_NT_PRINCIPAL; 323 324 ALLOC_SEQ(&(*p)->name_string, 2); 325 if ((*p)->name_string.val == NULL) 326 goto fail; 327 328 (*p)->name_string.val[0] = strdup(KRB5_WELLKNOWN_NAME); 329 if ((*p)->name_string.val[0] == NULL) 330 goto fail; 331 332 (*p)->name_string.val[1] = strdup(KRB5_ANON_NAME); 333 if ((*p)->name_string.val[1] == NULL) 334 goto fail; 335 336 return 0; 337 fail: 338 if (*p) { 339 if ((*p)->name_string.val) { 340 free((*p)->name_string.val[0]); 341 free((*p)->name_string.val[1]); 342 free((*p)->name_string.val); 343 } 344 free(*p); 345 } 346 347 return krb5_enomem(context); 348} 349 350krb5_error_code 351_krb5_fast_create_armor(krb5_context context, 352 struct krb5_fast_state *state, 353 const char *realm) 354{ 355 krb5_error_code ret; 356 357 if (state->armor_crypto == NULL) { 358 if (state->armor_ccache || state->armor_ac || (state->flags & KRB5_FAST_AP_ARMOR_SERVICE)) { 359 /* 360 * Instead of keeping state in FX_COOKIE in the KDC, we 361 * rebuild a new armor key for every request, because this 362 * is what the MIT KDC expect and RFC6113 is vage about 363 * what the behavior should be. 364 */ 365 state->type = choice_PA_FX_FAST_REQUEST_armored_data; 366 } else { 367 return check_fast(context, state); 368 } 369 } 370 371 if (state->type == choice_PA_FX_FAST_REQUEST_armored_data) { 372 373 if (state->armor_crypto) 374 krb5_crypto_destroy(context, state->armor_crypto); 375 krb5_free_keyblock_contents(context, &state->armor_key); 376 377 /* 378 * If we have a armor auth context, its because the caller 379 * wants us to do an implicit FAST armor (TGS-REQ). 380 */ 381 if (state->armor_ac) { 382 heim_assert((state->flags & KRB5_FAST_AS_REQ) == 0, "FAST AS with AC"); 383 384 ret = _krb5_fast_armor_key(context, 385 state->armor_ac->local_subkey, 386 state->armor_ac->keyblock, 387 &state->armor_key, 388 &state->armor_crypto); 389 if (ret) 390 goto out; 391 392 } else { 393 heim_assert((state->flags & KRB5_FAST_AS_REQ) != 0, "FAST TGS without AC"); 394 395 if (state->armor_data) { 396 free_KrbFastArmor(state->armor_data); 397 free(state->armor_data); 398 } 399 ret = make_fast_ap_fxarmor(context, state, realm, 400 &state->armor_data); 401 if (ret) 402 goto out; 403 } 404 } else { 405 heim_abort("unknown state type: %d", (int)state->type); 406 } 407 out: 408 return ret; 409} 410 411 412krb5_error_code 413_krb5_fast_wrap_req(krb5_context context, 414 struct krb5_fast_state *state, 415 krb5_data *checksum_data, 416 KDC_REQ *req) 417{ 418 PA_FX_FAST_REQUEST fxreq; 419 krb5_error_code ret; 420 KrbFastReq fastreq; 421 krb5_data data, aschecksum_data; 422 size_t size = 0; 423 424 if (state->flags & KRB5_FAST_DISABLED) { 425 _krb5_debugx(context, 10, "fast disabled, not doing any fast wrapping"); 426 return 0; 427 } 428 429 memset(&fxreq, 0, sizeof(fxreq)); 430 memset(&fastreq, 0, sizeof(fastreq)); 431 krb5_data_zero(&data); 432 krb5_data_zero(&aschecksum_data); 433 434 if (state->armor_crypto == NULL) 435 return check_fast(context, state); 436 437 state->flags |= KRB5_FAST_EXPECTED; 438 439 fastreq.fast_options.hide_client_names = 1; 440 441 ret = copy_KDC_REQ_BODY(&req->req_body, &fastreq.req_body); 442 if (ret) 443 goto out; 444 445 /* 446 * In the case of a AS-REQ, remove all account names. Want to this 447 * for TGS-REQ too, but due to layering this is tricky. 448 * 449 * 1. TGS-REQ need checksum of REQ-BODY 450 * 2. FAST needs checksum of TGS-REQ, so, FAST needs to happen after TGS-REQ 451 * 3. FAST privacy mangaling needs to happen before TGS-REQ does the checksum in 1. 452 * 453 * So lets not modify the bits for now for TGS-REQ 454 */ 455 if (state->flags & KRB5_FAST_AS_REQ) { 456 457 free_KDC_REQ_BODY(&req->req_body); 458 459 req->req_body.realm = strdup(KRB5_ANON_REALM); 460 if (req->req_body.realm == NULL) { 461 ret = krb5_enomem(context); 462 goto out; 463 } 464 465 ret = set_anon_principal(context, &req->req_body.cname); 466 if (ret) 467 goto out; 468 469 ALLOC(req->req_body.till, 1); 470 *req->req_body.till = 0; 471 472 heim_assert(checksum_data == NULL, "checksum data not NULL"); 473 474 ASN1_MALLOC_ENCODE(KDC_REQ_BODY, 475 aschecksum_data.data, 476 aschecksum_data.length, 477 &req->req_body, 478 &size, ret); 479 if (ret) 480 goto out; 481 heim_assert(aschecksum_data.length == size, "ASN.1 internal error"); 482 483 checksum_data = &aschecksum_data; 484 } 485 486 if (req->padata) { 487 ret = copy_METHOD_DATA(req->padata, &fastreq.padata); 488 free_METHOD_DATA(req->padata); 489 if (ret) 490 goto out; 491 } else { 492 ALLOC(req->padata, 1); 493 if (req->padata == NULL) { 494 ret = krb5_enomem(context); 495 goto out; 496 } 497 } 498 499 500 ASN1_MALLOC_ENCODE(KrbFastReq, data.data, data.length, &fastreq, &size, ret); 501 if (ret) 502 goto out; 503 heim_assert(data.length == size, "ASN.1 internal error"); 504 505 fxreq.element = state->type; 506 507 if (state->type == choice_PA_FX_FAST_REQUEST_armored_data) { 508 509 fxreq.u.armored_data.armor = state->armor_data; 510 state->armor_data = NULL; 511 if (ret) 512 goto out; 513 514 heim_assert(state->armor_crypto != NULL, 515 "FAST armor key missing when FAST started"); 516 517 ret = krb5_create_checksum(context, state->armor_crypto, 518 KRB5_KU_FAST_REQ_CHKSUM, 0, 519 checksum_data->data, 520 checksum_data->length, 521 &fxreq.u.armored_data.req_checksum); 522 if (ret) 523 goto out; 524 525 ret = krb5_encrypt_EncryptedData(context, state->armor_crypto, 526 KRB5_KU_FAST_ENC, 527 data.data, 528 data.length, 529 0, 530 &fxreq.u.armored_data.enc_fast_req); 531 krb5_data_free(&data); 532 if (ret) 533 goto out; 534 535 } else { 536 krb5_data_free(&data); 537 heim_assert(false, "unknown FAST type, internal error"); 538 } 539 540 ASN1_MALLOC_ENCODE(PA_FX_FAST_REQUEST, data.data, data.length, &fxreq, &size, ret); 541 if (ret) 542 goto out; 543 heim_assert(data.length == size, "ASN.1 internal error"); 544 545 546 ret = krb5_padata_add(context, req->padata, KRB5_PADATA_FX_FAST, data.data, data.length); 547 if (ret) 548 goto out; 549 krb5_data_zero(&data); 550 551 out: 552 free_PA_FX_FAST_REQUEST(&fxreq); 553 krb5_data_free(&data); 554 krb5_data_free(&aschecksum_data); 555 556 return ret; 557} 558 559krb5_error_code 560_krb5_fast_unwrap_error(krb5_context context, struct krb5_fast_state *state, 561 METHOD_DATA *md, KRB_ERROR *error) 562{ 563 KrbFastResponse fastrep; 564 krb5_error_code ret; 565 PA_DATA *pa; 566 int idx; 567 568 if (state->armor_crypto == NULL) 569 return check_fast(context, state); 570 571 memset(&fastrep, 0, sizeof(fastrep)); 572 573 if (error->error_code != KRB5_KDC_ERR_MORE_PREAUTH_DATA_REQUIRED) 574 _krb5_debugx(context, 10, "using fast but no FAST error code ?"); 575 576 idx = 0; 577 pa = krb5_find_padata(md->val, md->len, KRB5_PADATA_FX_FAST, &idx); 578 if (pa == NULL) { 579 ret = KRB5_KDCREP_MODIFIED; 580 krb5_set_error_message(context, ret, N_("FAST fast respons is missing fx-fast data", "")); 581 goto out; 582 } 583 584 ret = unwrap_fast_rep(context, state, pa, &fastrep); 585 if (ret) 586 goto out; 587 588 idx = 0; 589 pa = krb5_find_padata(fastrep.padata.val, fastrep.padata.len, KRB5_PADATA_FX_ERROR, &idx); 590 if (pa == NULL) { 591 ret = KRB5_KDCREP_MODIFIED; 592 krb5_set_error_message(context, ret, N_("No wrapped error", "")); 593 goto out; 594 } 595 596 free_KRB_ERROR(error); 597 598 ret = krb5_rd_error(context, &pa->padata_value, error); 599 if (ret) 600 goto out; 601 602 if (error->e_data) 603 _krb5_debugx(context, 10, "FAST wrapped KBB_ERROR contained e_data: %d", 604 (int)error->e_data->length); 605 606 free_METHOD_DATA(md); 607 md->val = fastrep.padata.val; 608 md->len = fastrep.padata.len; 609 610 fastrep.padata.val = NULL; 611 fastrep.padata.len = 0; 612 613 out: 614 free_KrbFastResponse(&fastrep); 615 return ret; 616} 617 618krb5_error_code 619_krb5_fast_unwrap_kdc_rep(krb5_context context, int32_t nonce, 620 krb5_data *chksumdata, 621 struct krb5_fast_state *state, AS_REP *rep) 622{ 623 KrbFastResponse fastrep; 624 krb5_error_code ret; 625 PA_DATA *pa = NULL; 626 int idx = 0; 627 628 if (state == NULL || state->armor_crypto == NULL || rep->padata == NULL) 629 return check_fast(context, state); 630 631 /* find PA_FX_FAST_REPLY */ 632 633 pa = krb5_find_padata(rep->padata->val, rep->padata->len, 634 KRB5_PADATA_FX_FAST, &idx); 635 if (pa == NULL) 636 return check_fast(context, state); 637 638 memset(&fastrep, 0, sizeof(fastrep)); 639 640 ret = unwrap_fast_rep(context, state, pa, &fastrep); 641 if (ret) 642 goto out; 643 644 free_METHOD_DATA(rep->padata); 645 ret = copy_METHOD_DATA(&fastrep.padata, rep->padata); 646 if (ret) 647 goto out; 648 649 if (fastrep.strengthen_key) { 650 if (state->strengthen_key) 651 krb5_free_keyblock(context, state->strengthen_key); 652 653 ret = krb5_copy_keyblock(context, fastrep.strengthen_key, &state->strengthen_key); 654 if (ret) 655 goto out; 656 } 657 658 if (nonce != (int32_t)fastrep.nonce) { 659 ret = KRB5KDC_ERR_PREAUTH_FAILED; 660 goto out; 661 } 662 if (fastrep.finished) { 663 PrincipalName cname; 664 krb5_realm crealm = NULL; 665 666#if 0 667 if (chksumdata == NULL) { 668 ret = KRB5KDC_ERR_PREAUTH_FAILED; 669 goto out; 670 } 671 672 ret = krb5_verify_checksum(context, state->armor_crypto, 673 KRB5_KU_FAST_FINISHED, 674 chksumdata->data, chksumdata->length, 675 &fastrep.finished->ticket_checksum); 676 if (ret) 677 goto out; 678#endif 679 680 /* update */ 681 ret = copy_Realm(&fastrep.finished->crealm, &crealm); 682 if (ret) 683 goto out; 684 free_Realm(&rep->crealm); 685 rep->crealm = crealm; 686 687 ret = copy_PrincipalName(&fastrep.finished->cname, &cname); 688 if (ret) 689 goto out; 690 free_PrincipalName(&rep->cname); 691 rep->cname = cname; 692 693#if 0 /* store authenticated checksum as kdc-offset */ 694 fastrep->finished.timestamp; 695 fastrep->finished.usec = 0; 696#endif 697 698 } else if (chksumdata) { 699 /* expected fastrep.finish but didn't get it */ 700 ret = KRB5KDC_ERR_PREAUTH_FAILED; 701 } 702 703 out: 704 return ret; 705} 706 707void 708_krb5_fast_free(krb5_context context, struct krb5_fast_state *state) 709{ 710 if (state->armor_ccache) 711 krb5_cc_close(context, state->armor_ccache); 712 if (state->armor_service) 713 krb5_free_principal(context, state->armor_service); 714 if (state->armor_crypto) 715 krb5_crypto_destroy(context, state->armor_crypto); 716 if (state->strengthen_key) 717 krb5_free_keyblock(context, state->strengthen_key); 718 krb5_free_keyblock_contents(context, &state->armor_key); 719 if (state->armor_data) { 720 free_KrbFastArmor(state->armor_data); 721 free(state->armor_data); 722 } 723 memset(state, 0, sizeof(*state)); 724} 725