pfs.c
1 /* 2 * Copyright (c) 2014 Kungliga Tekniska Högskolan 3 * (Royal Institute of Technology, Stockholm, Sweden). 4 * All rights reserved. 5 * 6 * Portions Copyright (c) 2014 Apple Inc. All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * 3. Neither the name of the Institute nor the names of its contributors 20 * may be used to endorse or promote products derived from this software 21 * without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 */ 35 36 #include "krb5_locl.h" 37 38 #include <corecrypto/ccec.h> 39 #include <corecrypto/ccec25519.h> 40 41 #include <CommonCrypto/CommonRandomSPI.h> 42 43 #include <pkinit_asn1.h> 44 45 46 struct _krb5_pfs_data { 47 ccec25519secretkey x25519priv; 48 ccec25519pubkey x25519pub; 49 ccec_full_ctx_t p256; 50 krb5_keyblock keyblock; 51 AlgorithmIdentifier ai; 52 53 int config; 54 55 krb5_principal client; 56 KRB5_PFS_GROUP group; 57 }; 58 59 #define PFS_X25519 1 60 #define PFS_P256 2 61 62 /* 63 * 64 */ 65 66 static int 67 eval_string(krb5_context context, int value, const char *s) 68 { 69 int negative = 0; 70 int val = 0; 71 72 if (s[0] == '-') { 73 negative = 1; 74 s++; 75 } 76 77 if (strcasecmp(s, "ALL") == 0) { 78 val = PFS_X25519 | PFS_P256; 79 } else if (strcasecmp(s, "dh25519") == 0) { 80 val = PFS_X25519; 81 } else if (strcasecmp(s, "nist-p256") == 0) { 82 val = PFS_P256; 83 } else if (strcasecmp(s, "p256") == 0) { 84 val = PFS_P256; 85 } else if (strcasecmp(s, "all-nist") == 0) { 86 val = PFS_P256; 87 } else { 88 _krb5_debugx(context, 10, "unsupported dh curve(s): %s", s); 89 return value; 90 } 91 92 if (negative) 93 value &= ~val; 94 else 95 value |= val; 96 97 return value; 98 } 99 100 static int 101 _krb5_pfs_parse_pfs_config(krb5_context context, const char *name, const char *defval) 102 { 103 char **strs, **s; 104 int val = 0; 105 106 strs = krb5_config_get_strings(context, NULL, "libdefaults", name, NULL); 107 if (strs) { 108 for(s = strs; s && *s; s++) { 109 val = eval_string(context, val, *s); 110 } 111 } else { 112 val = eval_string(context, 0, defval); 113 } 114 krb5_config_free_strings(strs); 115 116 return val; 117 } 118 119 /* 120 * 121 */ 122 123 krb5_error_code 124 _krb5_auth_con_setup_pfs(krb5_context context, 125 krb5_auth_context auth_context, 126 krb5_enctype enctype) 127 { 128 ccec_const_cp_t cp = ccec_cp_256(); 129 struct ccrng_state *rng = ccDRBGGetRngState(); 130 struct _krb5_pfs_data *pfs = NULL; 131 132 _krb5_debugx(context, 20, "Setting up PFS for auth context"); 133 134 pfs = calloc(1, sizeof(*pfs)); 135 if (pfs == NULL) 136 return krb5_enomem(context); 137 138 pfs->config = _krb5_pfs_parse_pfs_config(context, "ap-req-dh-groups", "ALL"); 139 if (pfs->config == 0) { 140 _krb5_debugx(context, 10, "No PFS configuration"); 141 free(pfs); 142 return 0; 143 } 144 145 switch (enctype) { 146 case KRB5_ENCTYPE_AES128_CTS_HMAC_SHA1_96: 147 case KRB5_ENCTYPE_AES256_CTS_HMAC_SHA1_96: 148 case KRB5_ENCTYPE_DES3_CBC_SHA1: 149 case KRB5_ENCTYPE_ARCFOUR_HMAC_MD5: 150 pfs->ai.algorithm = asn1_oid_id_pkinit_kdf_ah_sha512; 151 pfs->ai.parameters = NULL; 152 break; 153 default: 154 free(pfs); 155 return 0; 156 } 157 158 pfs->p256 = (ccec_full_ctx_t)calloc(1, ccec_full_ctx_size(ccec_ccn_size(cp))); 159 if (pfs->p256 == NULL) { 160 free(pfs); 161 return krb5_enomem(context); 162 } 163 164 cccurve25519_make_key_pair(rng, pfs->x25519pub, pfs->x25519priv); 165 166 if (ccec_generate_key_fips(cp, rng, pfs->p256)) { 167 krb5_free_keyblock_contents(context, &pfs->keyblock); 168 free(pfs->p256); 169 free(pfs); 170 return krb5_enomem(context); 171 } 172 173 auth_context->pfs = pfs; 174 175 return 0; 176 } 177 178 /* 179 * 180 */ 181 182 void 183 _krb5_auth_con_free_pfs(krb5_context context, krb5_auth_context auth_context) 184 { 185 if (auth_context->pfs) { 186 free(auth_context->pfs->p256); 187 krb5_free_keyblock_contents(context, &auth_context->pfs->keyblock); 188 krb5_free_principal(context, auth_context->pfs->client); 189 190 memset(auth_context->pfs, 0, sizeof(*auth_context->pfs)); 191 free(auth_context->pfs); 192 auth_context->pfs = NULL; 193 } 194 } 195 196 /* 197 * 198 */ 199 200 static krb5_error_code 201 pfs_make_share_secret(krb5_context context, 202 krb5_auth_context auth_context, 203 krb5_enctype enctype, 204 KRB5_PFS_SELECTION *peer) 205 { 206 struct _krb5_pfs_data *pfs = auth_context->pfs; 207 krb5_data shared_secret; 208 krb5_error_code ret; 209 210 heim_assert(pfs, "no PFS requestd"); 211 212 krb5_data_zero(&shared_secret); 213 214 if (peer->group == KRB5_PFS_X25519 && (pfs->config & PFS_X25519)) { 215 if (peer->public_key.length != sizeof(ccec25519pubkey)) { 216 krb5_set_error_message(context, HEIM_PFS_GROUP_INVALID, 217 "public key of wrong length"); 218 return HEIM_PFS_GROUP_INVALID; 219 } 220 221 ret = krb5_data_alloc(&shared_secret, sizeof(ccec25519key)); 222 if (ret) 223 goto out; 224 225 cccurve25519(shared_secret.data, 226 pfs->x25519priv, 227 peer->public_key.data); 228 229 } else if (peer->group == KRB5_PFS_NIST_P256 && (pfs->config & PFS_P256)) { 230 ccec_const_cp_t cp = ccec_cp_256(); 231 #ifdef __clang__ 232 #pragma clang diagnostic push 233 #pragma clang diagnostic ignored "-Wvla" 234 #endif 235 ccec_pub_ctx_decl_cp(cp, pubkey); 236 #ifdef __clang__ 237 #pragma clang diagnostic pop 238 #endif 239 240 if (ccec_import_pub(cp, peer->public_key.length, peer->public_key.data, pubkey)) { 241 krb5_set_error_message(context, HEIM_PFS_GROUP_INVALID, 242 "failed to import public key"); 243 return HEIM_PFS_GROUP_INVALID; 244 } 245 246 ret = krb5_data_alloc(&shared_secret, ccec_ccn_size(cp)); 247 if (ret) 248 goto out; 249 250 if (ccec_compute_key(pfs->p256, pubkey, &shared_secret.length, shared_secret.data)) { 251 krb5_set_error_message(context, HEIM_PFS_GROUP_INVALID, 252 "Failed to complete share key"); 253 return HEIM_PFS_GROUP_INVALID; 254 } 255 256 } else { 257 krb5_set_error_message(context, HEIM_PFS_GROUP_INVALID, 258 "Group %d not accepted", 259 (int)peer->group); 260 return HEIM_PFS_GROUP_INVALID; 261 } 262 263 ret = _krb5_pk_kdf(context, 264 &pfs->ai, 265 shared_secret.data, 266 shared_secret.length, 267 pfs->client, 268 NULL, 269 enctype, 270 NULL, 271 NULL, 272 NULL, 273 &pfs->keyblock); 274 if (ret) 275 goto out; 276 277 _krb5_debug_keyblock(context, 20, "PFS shared keyblock", &pfs->keyblock); 278 279 pfs->group = peer->group; 280 281 out: 282 memset(shared_secret.data, 0, shared_secret.length); 283 krb5_data_free(&shared_secret); 284 285 return ret; 286 } 287 288 /* 289 * 290 */ 291 292 krb5_error_code 293 _krb5_pfs_update_key(krb5_context context, 294 krb5_auth_context auth_context, 295 const char *direction, 296 krb5_keyblock *subkey) 297 { 298 struct _krb5_pfs_data *pfs = auth_context->pfs; 299 krb5_keyblock newkey; 300 krb5_error_code ret; 301 302 memset(&newkey, 0, sizeof(newkey)); 303 304 heim_assert(pfs, "no PFS requestd"); 305 heim_assert(pfs->keyblock.keytype, "shared secret completed"); 306 heim_assert(pfs->group != KRB5_PFS_INVALID, "no pfs group selected"); 307 308 _krb5_debugx(context, 10, "krb5_pfs: updating to PFS key for direction %s", direction); 309 310 ret = _krb5_fast_cf2(context, subkey, 311 "AP PFS shared key", 312 &pfs->keyblock, 313 direction, 314 &newkey, 315 NULL); 316 /* 317 * If enctype doesn't support PRF, ignore the PFS variable, the 318 * peer should not have proposed it in the first place when the 319 * poor supported enctype was selected. 320 */ 321 if (ret == HEIM_PFS_ENCTYPE_PRF_NOT_SUPPORTED) 322 return 0; 323 else if (ret) 324 return ret; 325 326 krb5_free_keyblock_contents(context, subkey); 327 *subkey = newkey; 328 329 _krb5_debug_keyblock(context, 20, direction, subkey); 330 331 return 0; 332 } 333 334 static krb5_error_code 335 fill_x25519_proposal(KRB5_PFS_SELECTION *sel, 336 ccec25519pubkey pubkey) 337 { 338 sel->group = KRB5_PFS_X25519; 339 return krb5_data_copy(&sel->public_key, pubkey, sizeof(ccec25519pubkey)); 340 } 341 342 static krb5_error_code 343 add_x25519_proposal(krb5_context context, 344 KRB5_PFS_SELECTIONS *sels, 345 ccec25519pubkey pubkey) 346 { 347 KRB5_PFS_SELECTION sel; 348 krb5_error_code ret; 349 350 ret = fill_x25519_proposal(&sel, pubkey); 351 if (ret) 352 goto out; 353 354 ret = add_KRB5_PFS_SELECTIONS(sels, &sel); 355 out: 356 free_KRB5_PFS_SELECTION(&sel); 357 return ret; 358 } 359 360 static krb5_error_code 361 fill_nist_proposal(krb5_context context, 362 KRB5_PFS_SELECTION *sel, 363 KRB5_PFS_GROUP group, 364 ccec_full_ctx_t fullkey) 365 { 366 krb5_error_code ret; 367 368 sel->group = group; 369 sel->public_key.length = ccec_export_pub_size(ccec_ctx_pub(fullkey)); 370 sel->public_key.data = malloc(sel->public_key.length); 371 if (sel->public_key.data == NULL) { 372 ret = krb5_enomem(context); 373 goto out; 374 } 375 376 ccec_export_pub(ccec_ctx_pub(fullkey), sel->public_key.data); 377 ret = 0; 378 379 out: 380 return ret; 381 } 382 383 static krb5_error_code 384 add_nist_proposal(krb5_context context, 385 KRB5_PFS_SELECTIONS *sels, 386 KRB5_PFS_GROUP group, 387 ccec_full_ctx_t fullkey) 388 { 389 KRB5_PFS_SELECTION sel; 390 krb5_error_code ret; 391 392 ret = fill_nist_proposal(context, &sel, group, fullkey); 393 if (ret) 394 goto out; 395 396 ret = add_KRB5_PFS_SELECTIONS(sels, &sel); 397 out: 398 free_KRB5_PFS_SELECTION(&sel); 399 400 return ret; 401 } 402 403 static krb5_boolean 404 enctype_pfs_supported(krb5_context context, 405 krb5_auth_context auth_context) 406 { 407 krb5_error_code ret; 408 size_t size; 409 410 ret = krb5_crypto_prf_length(context, 411 auth_context->keyblock->keytype, 412 &size); 413 if (ret == HEIM_PFS_ENCTYPE_PRF_NOT_SUPPORTED) { 414 _krb5_debugx(context, 10, "Enctype %d doesn't support PFS", 415 auth_context->keyblock->keytype); 416 return FALSE; 417 } 418 if (ret || size == 0) 419 return FALSE; 420 421 return TRUE; 422 } 423 424 425 /* 426 * 427 * 428 */ 429 430 krb5_error_code 431 _krb5_pfs_ap_req(krb5_context context, 432 krb5_auth_context auth_context, 433 krb5_const_principal client, 434 AuthorizationData *output) 435 { 436 struct _krb5_pfs_data *pfs = auth_context->pfs; 437 AuthorizationDataElement ade; 438 krb5_crypto crypto = NULL; 439 KRB5_PFS_PROPOSE pp; 440 krb5_error_code ret; 441 size_t size = 0; 442 krb5_data data; 443 444 memset(&ade, 0, sizeof(ade)); 445 memset(&pp, 0, sizeof(pp)); 446 krb5_data_zero(&data); 447 448 if (!enctype_pfs_supported(context, auth_context)) 449 return 0; 450 451 if (pfs->config & PFS_X25519) { 452 ret = add_x25519_proposal(context, &pp.selections, pfs->x25519pub); 453 if (ret) 454 goto out; 455 } 456 457 if (pfs->config & PFS_P256) { 458 ret = add_nist_proposal(context, &pp.selections, KRB5_PFS_NIST_P256, pfs->p256); 459 if (ret) 460 goto out; 461 } 462 463 ret = krb5_copy_principal(context, client, &pfs->client); 464 if (ret) { 465 _krb5_auth_con_free_pfs(context, auth_context); 466 free_KRB5_PFS_PROPOSE(&pp); 467 return ret; 468 } 469 470 /* 471 * Create signture 472 */ 473 474 ret = krb5_crypto_init(context, auth_context->keyblock, 0, &crypto); 475 if (ret) 476 goto out; 477 478 ASN1_MALLOC_ENCODE(KRB5_PFS_PROPOSE, data.data, data.length, &pp, &size, ret); 479 if (ret) 480 goto out; 481 heim_assert(size == data.length, "internal asn1 error"); 482 483 pp.checksum = calloc(1, sizeof(*pp.checksum)); 484 if (pp.checksum == NULL) { 485 ret = krb5_enomem(context); 486 goto out; 487 } 488 489 ret = krb5_create_checksum(context, crypto, KRB5_KU_H5L_PFS_AP_CHECKSUM_CLIENT, 0, 490 data.data, data.length, pp.checksum); 491 if (ret) 492 goto out; 493 494 /* 495 * Encode and add 496 */ 497 498 ade.ad_type = KRB5_AUTHDATA_PFS; 499 500 ASN1_MALLOC_ENCODE(KRB5_PFS_PROPOSE, ade.ad_data.data, ade.ad_data.length, &pp, &size, ret); 501 if (ret) 502 goto out; 503 if (ade.ad_data.length != size) 504 krb5_abortx(context, "internal error in ASN.1 encoder"); 505 506 ret = add_AuthorizationData(output, &ade); 507 if (ret) 508 goto out; 509 510 out: 511 if (crypto) 512 krb5_crypto_destroy(context, crypto); 513 free_KRB5_PFS_PROPOSE(&pp); 514 free_AuthorizationDataElement(&ade); 515 free(data.data); 516 517 return ret; 518 } 519 520 /* 521 * 522 */ 523 524 static krb5_error_code 525 _validate_rd_req_checksum(krb5_context context, krb5_keyblock *keyblock, KRB5_PFS_PROPOSE *pp) 526 { 527 krb5_error_code ret; 528 krb5_crypto crypto; 529 Checksum *checksum; 530 krb5_data data; 531 size_t size = 0; 532 533 checksum = pp->checksum; 534 if (checksum == NULL) { 535 krb5_set_error_message(context, HEIM_PFS_CHECKSUM_WRONG, "peer sent no checksum"); 536 return HEIM_PFS_CHECKSUM_WRONG; 537 } 538 539 pp->checksum = NULL; 540 541 ASN1_MALLOC_ENCODE(KRB5_PFS_PROPOSE, data.data, data.length, pp, &size, ret); 542 pp->checksum = checksum; 543 if (ret) { 544 return ret; 545 } 546 heim_assert(data.length == size, "internal asn1 encode error"); 547 548 ret = krb5_crypto_init(context, keyblock, 0, &crypto); 549 if (ret) { 550 free(data.data); 551 return ret; 552 } 553 554 if (!krb5_checksum_is_keyed(context, checksum->cksumtype)) { 555 free(data.data); 556 ret = HEIM_PFS_CHECKSUM_WRONG; 557 krb5_set_error_message(context, ret, "checksum not keyed"); 558 return ret; 559 } 560 561 ret = krb5_verify_checksum(context, crypto, KRB5_KU_H5L_PFS_AP_CHECKSUM_CLIENT, 562 data.data, data.length, checksum); 563 krb5_crypto_destroy(context, crypto); 564 free(data.data); 565 566 return ret; 567 } 568 569 570 /* 571 * 572 */ 573 574 krb5_error_code 575 _krb5_pfs_rd_req(krb5_context context, 576 krb5_auth_context auth_context) 577 { 578 KRB5_PFS_SELECTION *selected = NULL; 579 AuthorizationData *ad = NULL; 580 KRB5_PFS_PROPOSE pp; 581 krb5_error_code ret; 582 krb5_data data; 583 size_t size = 0; 584 unsigned n; 585 586 memset(&pp, 0, sizeof(pp)); 587 krb5_data_zero(&data); 588 589 if (auth_context->authenticator == NULL) 590 return 0; 591 592 ad = auth_context->authenticator->authorization_data; 593 if (ad == NULL) 594 return 0; 595 596 heim_assert(auth_context->keyblock, "pfs: don't have keyblock"); 597 598 if (!enctype_pfs_supported(context, auth_context)) 599 return 0; 600 601 ret = _krb5_get_ad(context, ad, NULL, KRB5_AUTHDATA_PFS, &data); 602 if (ret) 603 return 0; 604 605 ret = decode_KRB5_PFS_PROPOSE(data.data, data.length, &pp, &size); 606 krb5_data_free(&data); 607 if (ret) 608 goto fail; 609 610 ret = _validate_rd_req_checksum(context, auth_context->keyblock, &pp); 611 if (ret) 612 goto fail; 613 614 ret = _krb5_auth_con_setup_pfs(context, auth_context, auth_context->keyblock->keytype); 615 if (ret) 616 goto fail; 617 618 for (n = 0; n < pp.selections.len && selected == NULL; n++) { 619 if (pp.selections.val[n].group == KRB5_PFS_X25519 && (auth_context->pfs->config & PFS_X25519)) 620 selected = &pp.selections.val[n]; 621 else if (pp.selections.val[n].group == KRB5_PFS_NIST_P256 && (auth_context->pfs->config & PFS_P256)) 622 selected = &pp.selections.val[n]; 623 } 624 625 if (selected == NULL) { 626 krb5_set_error_message(context, HEIM_PFS_GROUP_INVALID, "No acceptable PFS group sent"); 627 ret = HEIM_PFS_GROUP_INVALID; 628 goto fail; 629 } 630 631 ret = _krb5_principalname2krb5_principal(context, &auth_context->pfs->client, 632 auth_context->authenticator->cname, 633 auth_context->authenticator->crealm); 634 if (ret) 635 goto fail; 636 637 ret = pfs_make_share_secret(context, auth_context, 638 auth_context->keyblock->keytype, 639 selected); 640 if (ret) 641 goto fail; 642 643 free_KRB5_PFS_PROPOSE(&pp); 644 645 _krb5_debugx(context, 10, "PFS server made selected"); 646 647 return 0; 648 649 fail: 650 free_KRB5_PFS_PROPOSE(&pp); 651 _krb5_auth_con_free_pfs(context, auth_context); 652 return ret; 653 } 654 655 /* 656 * 657 */ 658 659 krb5_error_code 660 _krb5_pfs_mk_rep(krb5_context context, 661 krb5_auth_context auth_context, 662 AP_REP *rep) 663 { 664 struct _krb5_pfs_data *pfs = auth_context->pfs; 665 krb5_error_code ret; 666 krb5_crypto crypto = NULL; 667 krb5_data data; 668 size_t size = 0; 669 670 heim_assert(pfs, "no pfs"); 671 heim_assert(pfs->group != KRB5_PFS_INVALID, "no pfs group selected"); 672 673 krb5_data_zero(&data); 674 675 rep->pfs = calloc(1, sizeof(*rep->pfs)); 676 if (rep->pfs == NULL) 677 return krb5_enomem(context); 678 679 if (pfs->group == KRB5_PFS_X25519) { 680 ret = fill_x25519_proposal(&rep->pfs->selection, pfs->x25519pub); 681 if (ret) 682 goto out; 683 } else if (pfs->group == KRB5_PFS_NIST_P256) { 684 ret = fill_nist_proposal(context, &rep->pfs->selection, KRB5_PFS_NIST_P256, pfs->p256); 685 if (ret) 686 goto out; 687 } else { 688 heim_assert(0, "Invalid PFS group"); 689 } 690 691 /* 692 * 693 */ 694 695 ret = krb5_crypto_init(context, auth_context->keyblock, 0, &crypto); 696 if (ret) 697 goto out; 698 699 ASN1_MALLOC_ENCODE(KRB5_PFS_ACCEPT, data.data, data.length, rep->pfs, &size, ret); 700 if (ret) 701 goto out; 702 heim_assert(size == data.length, "internal asn1 error"); 703 704 rep->pfs->checksum = calloc(1, sizeof(*rep->pfs->checksum)); 705 if (rep->pfs->checksum == NULL) { 706 ret = krb5_enomem(context); 707 goto out; 708 } 709 710 ret = krb5_create_checksum(context, crypto, KRB5_KU_H5L_PFS_AP_CHECKSUM_SERVER, 0, 711 data.data, data.length, rep->pfs->checksum); 712 if (ret) 713 goto out; 714 715 /* 716 * 717 */ 718 719 _krb5_debugx(context, 20, "PFS deriving new keys on server"); 720 721 ret = _krb5_pfs_update_key(context, auth_context, "session key", auth_context->keyblock); 722 if (ret) 723 goto out; 724 725 if (auth_context->local_subkey) { 726 ret = _krb5_pfs_update_key(context, auth_context, "server key", auth_context->local_subkey); 727 if (ret) 728 goto out; 729 } 730 731 if (auth_context->remote_subkey) { 732 ret = _krb5_pfs_update_key(context, auth_context, "client key", auth_context->remote_subkey); 733 if (ret) 734 goto out; 735 } 736 737 auth_context->flags |= KRB5_AUTH_CONTEXT_USED_PFS; 738 739 out: 740 _krb5_auth_con_free_pfs(context, auth_context); 741 krb5_crypto_destroy(context, crypto); 742 if (data.data) 743 free(data.data); 744 if (ret) { 745 if (rep->pfs) { 746 free_KRB5_PFS_ACCEPT(rep->pfs); 747 free(rep->pfs); 748 rep->pfs = NULL; 749 } 750 } 751 752 return ret; 753 } 754 755 /* 756 * 757 */ 758 759 static krb5_error_code 760 _validate_rd_rep_checksum(krb5_context context, krb5_keyblock *keyblock, KRB5_PFS_ACCEPT *aa) 761 { 762 krb5_error_code ret; 763 krb5_crypto crypto; 764 Checksum *checksum; 765 krb5_data data; 766 size_t size = 0; 767 768 checksum = aa->checksum; 769 if (checksum == NULL) { 770 krb5_set_error_message(context, HEIM_PFS_CHECKSUM_WRONG, "peer sent no checksum"); 771 return HEIM_PFS_CHECKSUM_WRONG; 772 } 773 774 aa->checksum = NULL; 775 776 ASN1_MALLOC_ENCODE(KRB5_PFS_ACCEPT, data.data, data.length, aa, &size, ret); 777 aa->checksum = checksum; 778 if (ret) { 779 return ret; 780 } 781 heim_assert(data.length == size, "internal asn1 encode error"); 782 783 ret = krb5_crypto_init(context, keyblock, 0, &crypto); 784 if (ret) { 785 free(data.data); 786 return ret; 787 } 788 789 if (!krb5_checksum_is_keyed(context, checksum->cksumtype)) { 790 free(data.data); 791 ret = HEIM_PFS_CHECKSUM_WRONG; 792 krb5_set_error_message(context, ret, "checksum not keyed"); 793 return ret; 794 } 795 796 ret = krb5_verify_checksum(context, crypto, KRB5_KU_H5L_PFS_AP_CHECKSUM_SERVER, 797 data.data, data.length, checksum); 798 krb5_crypto_destroy(context, crypto); 799 free(data.data); 800 801 return ret; 802 } 803 804 /* 805 * 806 */ 807 808 krb5_error_code 809 _krb5_pfs_rd_rep(krb5_context context, krb5_auth_context auth_context, AP_REP *ap_rep) 810 { 811 krb5_error_code ret; 812 813 heim_assert(auth_context->pfs, "no pfs"); 814 heim_assert(ap_rep->pfs, "no pfs from server"); 815 816 ret = _validate_rd_rep_checksum(context, auth_context->keyblock, ap_rep->pfs); 817 if (ret) 818 goto out; 819 820 ret = pfs_make_share_secret(context, auth_context, 821 auth_context->keyblock->keytype, 822 &ap_rep->pfs->selection); 823 if (ret) 824 goto out; 825 826 _krb5_debugx(context, 10, "PFS client made secret"); 827 _krb5_debugx(context, 20, "PFS deriving new keys on client"); 828 829 ret = _krb5_pfs_update_key(context, auth_context, "session key", auth_context->keyblock); 830 if (ret) 831 goto out; 832 833 if (auth_context->local_subkey) { 834 ret = _krb5_pfs_update_key(context, auth_context, "client key", auth_context->local_subkey); 835 if (ret) 836 goto out; 837 } 838 839 heim_assert(auth_context->pfs->group != KRB5_PFS_INVALID, "no pfs group selected"); 840 auth_context->flags |= KRB5_AUTH_CONTEXT_USED_PFS; 841 out: 842 if (ret) { 843 _krb5_auth_con_free_pfs(context, auth_context); 844 } 845 846 return ret; 847 }