1/* 2 * Copyright (c) 1997-2011 Kungliga Tekniska Högskolan 3 * (Royal Institute of Technology, Stockholm, Sweden). 4 * All rights reserved. 5 * 6 * Portions Copyright (c) 2010 - 2011 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 "kdc_locl.h" 37 38#define FAST_COOKIE_VERSION2 2 39 40static krb5_error_code 41get_fastuser_crypto(kdc_request_t r, const char *realm, krb5_enctype enctype, krb5_crypto *crypto) 42{ 43 krb5_principal fast_princ; 44 hdb_entry_ex *fast_user = NULL; 45 Key *cookie_key = NULL; 46 krb5_error_code ret; 47 48 *crypto = NULL; 49 50 ret = krb5_make_principal(r->context, &fast_princ, realm, 51 KRB5_WELLKNOWN_NAME, KRB5_FAST_COOKIE, NULL); 52 if (ret) 53 goto out; 54 55 ret = _kdc_db_fetch(r->context, r->config, fast_princ, 56 HDB_F_GET_CLIENT, NULL, NULL, &fast_user); 57 krb5_free_principal(r->context, fast_princ); 58 if (ret) { 59 /* 60 * Fall back to krbtgt 61 */ 62 ret = krb5_make_principal(r->context, &fast_princ, realm, 63 KRB5_TGS_NAME, KRB5_FAST_COOKIE, NULL); 64 if (ret) 65 goto out; 66 67 ret = _kdc_db_fetch(r->context, r->config, fast_princ, 68 HDB_F_GET_CLIENT, NULL, NULL, &fast_user); 69 krb5_free_principal(r->context, fast_princ); 70 if (ret) 71 goto out; 72 } 73 74 if (enctype == KRB5_ENCTYPE_NULL) 75 ret = _kdc_get_preferred_key(r->context, r->config, fast_user, 76 "fast-cookie", &enctype, &cookie_key); 77 else 78 ret = hdb_enctype2key(r->context, &fast_user->entry, 79 enctype, &cookie_key); 80 if (ret) 81 goto out; 82 83 ret = krb5_crypto_init(r->context, &cookie_key->key, 0, crypto); 84 if (ret) 85 goto out; 86 87 out: 88 if (fast_user) 89 _kdc_free_ent(r->context, fast_user); 90 91 return ret; 92} 93 94 95static krb5_error_code 96fast_parse_cookie(kdc_request_t r, const PA_DATA *pa) 97{ 98 krb5_crypto crypto = NULL; 99 krb5_error_code ret; 100 KDCFastCookie data; 101 krb5_data d1; 102 size_t len; 103 104 ret = decode_KDCFastCookie(pa->padata_value.data, 105 pa->padata_value.length, 106 &data, &len); 107 if (ret) 108 return ret; 109 110 if (len != pa->padata_value.length || data.version != FAST_COOKIE_VERSION2) { 111 free_KDCFastCookie(&data); 112 return KRB5KDC_ERR_POLICY; 113 } 114 115 ret = get_fastuser_crypto(r, data.realm, data.cookie.etype, &crypto); 116 if (ret) 117 goto out; 118 119 ret = krb5_decrypt_EncryptedData(r->context, crypto, 120 KRB5_KU_H5L_COOKIE, 121 &data.cookie, &d1); 122 krb5_crypto_destroy(r->context, crypto); 123 if (ret) 124 goto out; 125 126 ret = decode_KDCFastState(d1.data, d1.length, &r->fast, &len); 127 krb5_data_free(&d1); 128 if (ret) 129 goto out; 130 131 if (r->fast.expiration < kdc_time) { 132 kdc_log(r->context, r->config, 0, "fast cookie expired"); 133 ret = KRB5KDC_ERR_POLICY; 134 free_KDCFastState(&r->fast); 135 goto out; 136 } 137 138 out: 139 free_KDCFastCookie(&data); 140 141 return ret; 142} 143 144static krb5_error_code 145add_fast_cookie(kdc_request_t r, const char *realm, METHOD_DATA *method_data) 146{ 147 krb5_crypto crypto = NULL; 148 KDCFastCookie shell; 149 krb5_error_code ret; 150 krb5_data data; 151 size_t size = 0; 152 153 memset(&shell, 0, sizeof(shell)); 154 krb5_data_zero(&data); 155 156 r->fast.expiration = kdc_time + FAST_EXPIRATION_TIME; 157 158 ASN1_MALLOC_ENCODE(KDCFastState, data.data, data.length, 159 &r->fast, &size, ret); 160 if (ret) 161 return ret; 162 heim_assert(size == data.length, "internal asn1 encoder error"); 163 164 ret = get_fastuser_crypto(r, realm, KRB5_ENCTYPE_NULL, &crypto); 165 if (ret) { 166 kdc_log(r->context, r->config, 0, "Failed to find fastuser for cookie encryption: %d", ret); 167 goto out; 168 } 169 170 ret = krb5_encrypt_EncryptedData(r->context, crypto, 171 KRB5_KU_H5L_COOKIE, 172 data.data, data.length, 0, 173 &shell.cookie); 174 krb5_crypto_destroy(r->context, crypto); 175 if (ret) 176 goto out; 177 178 free(data.data); 179 data.data = NULL; 180 181 shell.version = FAST_COOKIE_VERSION2; 182 shell.realm = rk_UNCONST(realm); 183 184 ASN1_MALLOC_ENCODE(KDCFastCookie, data.data, data.length, 185 &shell, &size, ret); 186 free_EncryptedData(&shell.cookie); 187 if (ret) 188 goto out; 189 heim_assert(size == data.length, "internal asn1 encoder error"); 190 191 ret = krb5_padata_add(r->context, method_data, 192 KRB5_PADATA_FX_COOKIE, 193 data.data, data.length); 194 if (ret == 0) 195 data.data = NULL; 196 197 out: 198 if (data.data) 199 free(data.data); 200 return ret; 201} 202 203krb5_error_code 204_kdc_fast_mk_response(krb5_context context, 205 krb5_crypto armor_crypto, 206 METHOD_DATA *pa_data, 207 krb5_keyblock *strengthen_key, 208 KrbFastFinished *finished, 209 krb5uint32 nonce, 210 krb5_data *data) 211{ 212 PA_FX_FAST_REPLY fxfastrep; 213 KrbFastResponse fastrep; 214 krb5_error_code ret; 215 krb5_data buf; 216 size_t size = 0; 217 218 memset(&fxfastrep, 0, sizeof(fxfastrep)); 219 memset(&fastrep, 0, sizeof(fastrep)); 220 krb5_data_zero(data); 221 222 if (pa_data) { 223 fastrep.padata.val = pa_data->val; 224 fastrep.padata.len = pa_data->len; 225 } 226 fastrep.strengthen_key = strengthen_key; 227 fastrep.finished = finished; 228 fastrep.nonce = nonce; 229 230 ASN1_MALLOC_ENCODE(KrbFastResponse, buf.data, buf.length, 231 &fastrep, &size, ret); 232 if (ret) 233 return ret; 234 if (buf.length != size) 235 krb5_abortx(context, "internal asn.1 error"); 236 237 fxfastrep.element = choice_PA_FX_FAST_REPLY_armored_data; 238 239 ret = krb5_encrypt_EncryptedData(context, 240 armor_crypto, 241 KRB5_KU_FAST_REP, 242 buf.data, 243 buf.length, 244 0, 245 &fxfastrep.u.armored_data.enc_fast_rep); 246 krb5_data_free(&buf); 247 if (ret) 248 return ret; 249 250 ASN1_MALLOC_ENCODE(PA_FX_FAST_REPLY, data->data, data->length, 251 &fxfastrep, &size, ret); 252 free_PA_FX_FAST_REPLY(&fxfastrep); 253 if (ret) 254 return ret; 255 if (data->length != size) 256 krb5_abortx(context, "internal asn.1 error"); 257 258 return 0; 259} 260 261 262krb5_error_code 263_kdc_fast_mk_error(krb5_context context, 264 kdc_request_t r, 265 METHOD_DATA *error_method, 266 krb5_crypto armor_crypto, 267 const KDC_REQ_BODY *req_body, 268 krb5_error_code outer_error, 269 const char *e_text, 270 krb5_principal error_client, 271 krb5_principal error_server, 272 time_t *csec, int *cusec, 273 krb5_data *error_msg) 274{ 275 krb5_error_code ret; 276 krb5_data e_data; 277 size_t size = 0; 278 279 krb5_data_zero(&e_data); 280 281 if (armor_crypto) { 282 PA_FX_FAST_REPLY fxfastrep; 283 KrbFastResponse fastrep; 284 285 memset(&fxfastrep, 0, sizeof(fxfastrep)); 286 memset(&fastrep, 0, sizeof(fastrep)); 287 288 /* first add the KRB-ERROR to the fast errors */ 289 290 ret = krb5_mk_error(context, 291 outer_error, 292 e_text, 293 NULL, 294 error_client, 295 error_server, 296 NULL, 297 NULL, 298 &e_data); 299 if (ret) 300 return ret; 301 302 ret = krb5_padata_add(context, error_method, 303 KRB5_PADATA_FX_ERROR, 304 e_data.data, e_data.length); 305 if (ret) { 306 krb5_data_free(&e_data); 307 return ret; 308 } 309 310 ret = add_fast_cookie(r, r->server_princ->realm, error_method); 311 if (ret) { 312 kdc_log(r->context, r->config, 0, "failed to add fast cookie for FAST with: %d", ret); 313 free_METHOD_DATA(error_method); 314 return ret; 315 } 316 317 outer_error = KRB5_KDC_ERR_MORE_PREAUTH_DATA_REQUIRED; 318 e_text = NULL; 319 if (r->fast.flags.requested_hidden_names) { 320 error_client = NULL; 321 error_server = NULL; 322 } 323 csec = 0; 324 cusec = 0; 325 326 ret = _kdc_fast_mk_response(context, armor_crypto, 327 error_method, NULL, NULL, 328 req_body->nonce, &e_data); 329 free_METHOD_DATA(error_method); 330 if (ret) 331 return ret; 332 333 ret = krb5_padata_add(context, error_method, 334 KRB5_PADATA_FX_FAST, 335 e_data.data, e_data.length); 336 if (ret) 337 return ret; 338 339 } else if (r->use_fast_cookie) { 340 341 ret = add_fast_cookie(r, r->server_princ->realm, error_method); 342 if (ret) { 343 kdc_log(r->context, r->config, 0, "failed to add fast cookie forced cookie with: %d", ret); 344 free_METHOD_DATA(error_method); 345 return ret; 346 } 347 } 348 349 if (error_method && error_method->len) { 350 ASN1_MALLOC_ENCODE(METHOD_DATA, e_data.data, e_data.length, 351 error_method, &size, ret); 352 if (ret) 353 return ret; 354 if (e_data.length != size) 355 krb5_abortx(context, "internal asn.1 error"); 356 } 357 358 ret = krb5_mk_error(context, 359 outer_error, 360 e_text, 361 (e_data.length ? &e_data : NULL), 362 error_client, 363 error_server, 364 csec, 365 cusec, 366 error_msg); 367 krb5_data_free(&e_data); 368 369 return ret; 370} 371 372krb5_error_code 373_kdc_fast_unwrap_request(kdc_request_t r, 374 krb5_ticket *tgs_ticket, 375 krb5_auth_context tgs_ac) 376{ 377 krb5_principal armor_server = NULL; 378 hdb_entry_ex *armor_user = NULL; 379 PA_FX_FAST_REQUEST fxreq; 380 krb5_auth_context ac = NULL; 381 krb5_ticket *ticket = NULL; 382 krb5_flags ap_req_options; 383 Key *armor_key = NULL; 384 krb5_keyblock armorkey; 385 krb5_error_code ret; 386 krb5_ap_req ap_req; 387 unsigned char *buf; 388 KrbFastReq fastreq; 389 const PA_DATA *pa; 390 krb5_data data; 391 size_t len; 392 int i = 0; 393 394 i = 0; 395 pa = _kdc_find_padata(&r->req, &i, KRB5_PADATA_FX_FAST); 396 if (pa == NULL) { 397 kdc_log(r->context, r->config, 10, 398 "Not a fast request"); 399 400 /* 401 * Check for fx cookie in the outside (for SRP pa outside of FAST) 402 */ 403 i = 0; 404 pa = _kdc_find_padata(&r->req, &i, KRB5_PADATA_FX_COOKIE); 405 if (pa) { 406 ret = fast_parse_cookie(r, pa); 407 if (ret) 408 goto out; 409 } 410 411 return 0; 412 } 413 414 ret = decode_PA_FX_FAST_REQUEST(pa->padata_value.data, 415 pa->padata_value.length, 416 &fxreq, 417 &len); 418 if (ret) { 419 kdc_log(r->context, r->config, 0, 420 "Failed to decode FX_FAST_REQUEST: %d", ret); 421 goto out; 422 } 423 424 if (fxreq.element != choice_PA_FX_FAST_REQUEST_armored_data) { 425 kdc_log(r->context, r->config, 0, 426 "FAST contain unknown type: %d", (int)fxreq.element); 427 ret = KRB5KDC_ERR_PREAUTH_FAILED; 428 goto out; 429 } 430 431 /* 432 * If check for armor data or its not a TGS-REQ with implicit 433 * armor. 434 */ 435 if (fxreq.u.armored_data.armor == NULL && tgs_ac == NULL) { 436 kdc_log(r->context, r->config, 0, 437 "AS-REQ armor missing"); 438 ret = KRB5KDC_ERR_PREAUTH_FAILED; 439 goto out; 440 } 441 442 /* 443 * 444 */ 445 446 if (tgs_ac == NULL) { 447 448 if (fxreq.u.armored_data.armor->armor_type != 1) { 449 kdc_log(r->context, r->config, 0, 450 "AS-REQ armor type not ap-req"); 451 ret = KRB5KDC_ERR_PREAUTH_FAILED; 452 goto out; 453 } 454 455 ret = krb5_decode_ap_req(r->context, 456 &fxreq.u.armored_data.armor->armor_value, 457 &ap_req); 458 if(ret) { 459 kdc_log(r->context, r->config, 0, "AP-REQ decode failed"); 460 goto out; 461 } 462 463 /* Save that principal that was in the request */ 464 ret = _krb5_principalname2krb5_principal(r->context, 465 &armor_server, 466 ap_req.ticket.sname, 467 ap_req.ticket.realm); 468 if (ret) { 469 free_AP_REQ(&ap_req); 470 goto out; 471 } 472 473 ret = _kdc_db_fetch(r->context, r->config, armor_server, 474 HDB_F_GET_SERVER, NULL, NULL, &armor_user); 475 if(ret == HDB_ERR_NOT_FOUND_HERE) { 476 kdc_log(r->context, r->config, 5, 477 "Armor key does not have secrets at this KDC, " 478 "need to proxy"); 479 goto out; 480 } else if (ret) { 481 free_AP_REQ(&ap_req); 482 ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN; 483 goto out; 484 } 485 486 ret = hdb_enctype2key(r->context, &armor_user->entry, 487 ap_req.ticket.enc_part.etype, 488 &armor_key); 489 if (ret) { 490 free_AP_REQ(&ap_req); 491 goto out; 492 } 493 494 ret = krb5_verify_ap_req2(r->context, &ac, 495 &ap_req, 496 armor_server, 497 &armor_key->key, 498 0, 499 &ap_req_options, 500 &ticket, 501 KRB5_KU_AP_REQ_AUTH); 502 free_AP_REQ(&ap_req); 503 if (ret) 504 goto out; 505 } else { 506 heim_assert(tgs_ticket != NULL, "tgs_ac but not ticket ?"); 507 ac = tgs_ac; 508 ticket = tgs_ticket; 509 } 510 511 if (ac->remote_subkey == NULL) { 512 krb5_auth_con_free(r->context, ac); 513 kdc_log(r->context, r->config, 0, 514 "FAST AP-REQ remote subkey missing"); 515 ret = KRB5KDC_ERR_PREAUTH_FAILED; 516 goto out; 517 } 518 519 ret = _krb5_fast_armor_key(r->context, 520 ac->remote_subkey, 521 &ticket->ticket.key, 522 &armorkey, 523 &r->armor_crypto); 524 525 if (ret) 526 goto out; 527 528 krb5_free_keyblock_contents(r->context, &armorkey); 529 530 ret = krb5_decrypt_EncryptedData(r->context, r->armor_crypto, 531 KRB5_KU_FAST_ENC, 532 &fxreq.u.armored_data.enc_fast_req, 533 &data); 534 if (ret) { 535 kdc_log(r->context, r->config, 0, 536 "Failed to decrypt FAST request"); 537 goto out; 538 } 539 540 ret = decode_KrbFastReq(data.data, data.length, &fastreq, NULL); 541 krb5_data_free(&data); 542 if (ret) 543 goto out; 544 545 /* 546 * Now look for the FX-COOKIE in the inner padata. 547 */ 548 549 i = 0; 550 pa = krb5_find_padata(fastreq.padata.val, fastreq.padata.len, KRB5_PADATA_FX_COOKIE, &i); 551 if (pa) { 552 ret = fast_parse_cookie(r, pa); 553 if (ret) 554 goto out; 555 } 556 557 /* 558 * verify req-checksum of the outer body 559 */ 560 if (tgs_ac) { 561 562 /* 563 * -- For TGS, contains the checksum performed over the type 564 * -- AP-REQ in the PA-TGS-REQ padata. 565 */ 566 567 i = 0; 568 pa = _kdc_find_padata(&r->req, &i, KRB5_PADATA_TGS_REQ); 569 if (pa == NULL) { 570 kdc_log(r->context, r->config, 0, 571 "FAST TGS request missing TGS-REQ padata"); 572 ret = KRB5KRB_ERR_GENERIC; 573 goto out; 574 } 575 576 ret = krb5_verify_checksum(r->context, r->armor_crypto, 577 KRB5_KU_FAST_REQ_CHKSUM, 578 pa->padata_value.data, pa->padata_value.length, 579 &fxreq.u.armored_data.req_checksum); 580 if (ret) { 581 kdc_log(r->context, r->config, 0, 582 "FAST TGS request have a bad checksum"); 583 goto out; 584 } 585 586 } else { 587 size_t size = 0; 588 589 /* 590 * -- For AS, contains the checksum performed over the type 591 * -- KDC-REQ-BODY for the req-body field of the KDC-REQ 592 * -- structure; 593 */ 594 ASN1_MALLOC_ENCODE(KDC_REQ_BODY, buf, len, &r->req.req_body, &size, ret); 595 if (ret) 596 goto out; 597 heim_assert(size == len, "internal asn1 error"); 598 599 ret = krb5_verify_checksum(r->context, r->armor_crypto, 600 KRB5_KU_FAST_REQ_CHKSUM, 601 buf, len, 602 &fxreq.u.armored_data.req_checksum); 603 free(buf); 604 if (ret) { 605 kdc_log(r->context, r->config, 0, 606 "FAST AS request have a bad checksum"); 607 goto out; 608 } 609 } 610 611 /* 612 * check for unsupported mandatory options 613 */ 614 if (FastOptions2int(fastreq.fast_options) & 0xfffc) { 615 kdc_log(r->context, r->config, 0, 616 "FAST unsupported mandatory option set"); 617 ret = KRB5KDC_ERR_PREAUTH_FAILED; 618 goto out; 619 } 620 621 r->fast.flags.requested_hidden_names = fastreq.fast_options.hide_client_names; 622 623 /* KDC MUST ignore outer pa data preauth-14 - 6.5.5 */ 624 if (r->req.padata) 625 free_METHOD_DATA(r->req.padata); 626 else 627 ALLOC(r->req.padata); 628 629 ret = copy_METHOD_DATA(&fastreq.padata, r->req.padata); 630 if (ret) 631 goto out; 632 633 free_KDC_REQ_BODY(&r->req.req_body); 634 ret = copy_KDC_REQ_BODY(&fastreq.req_body, &r->req.req_body); 635 if (ret) 636 goto out; 637 638 free_KrbFastReq(&fastreq); 639 free_PA_FX_FAST_REQUEST(&fxreq); 640 641 kdc_log(r->context, r->config, 0, "Client uses FAST"); 642 out: 643 if (ac && ac != tgs_ac) 644 krb5_auth_con_free(r->context, ac); 645 if (ticket && ticket != tgs_ticket) 646 krb5_free_ticket(r->context, ticket); 647 648 if (armor_server) 649 krb5_free_principal(r->context, armor_server); 650 if(armor_user) 651 _kdc_free_ent(r->context, armor_user); 652 653 return ret; 654} 655 656krb5_error_code 657_kdc_fast_strengthen_reply_key(kdc_request_t r) 658{ 659 if (r->armor_crypto) { 660 krb5_keyblock new_reply_key; 661 krb5_error_code ret; 662 663 kdc_log(r->context, r->config, 0, 664 "FAST strengthen reply key with strengthen-key"); 665 666 heim_assert(r->reply_key.keytype != KRB5_ENCTYPE_NULL, "null enctype ?"); 667 668 ret = krb5_generate_random_keyblock(r->context, r->reply_key.keytype, &r->strengthen_key); 669 if (ret) 670 krb5_abortx(r->context, "random generator fail"); 671 672 _krb5_debug_keyblock(r->context, 10, "fast: strengthen_key", &r->strengthen_key); 673 _krb5_debug_keyblock(r->context, 10, "fast: old reply_key", &r->reply_key); 674 675 ret = _krb5_fast_cf2(r->context, 676 &r->strengthen_key, "strengthenkey", 677 &r->reply_key, "replykey", 678 &new_reply_key, NULL); 679 if (ret) 680 return ret; 681 682 krb5_free_keyblock_contents(r->context, &r->reply_key); 683 ret = krb5_copy_keyblock_contents(r->context, &new_reply_key, &r->reply_key); 684 krb5_free_keyblock_contents(r->context, &new_reply_key); 685 if (ret) 686 return ret; 687 } 688 689 _krb5_debug_keyblock(r->context, 10, "fast: reply_key", &r->reply_key); 690 691 return 0; 692} 693