1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ 2 /* kdc/fast_util.c */ 3 /* 4 * Copyright (C) 2009, 2015 by the Massachusetts Institute of Technology. 5 * All rights reserved. 6 * 7 * Export of this software from the United States of America may 8 * require a specific license from the United States Government. 9 * It is the responsibility of any person or organization contemplating 10 * export to obtain such a license before exporting. 11 * 12 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and 13 * distribute this software and its documentation for any purpose and 14 * without fee is hereby granted, provided that the above copyright 15 * notice appear in all copies and that both that copyright notice and 16 * this permission notice appear in supporting documentation, and that 17 * the name of M.I.T. not be used in advertising or publicity pertaining 18 * to distribution of the software without specific, written prior 19 * permission. Furthermore if you modify this software you must label 20 * your software as modified software and not distribute it in such a 21 * fashion that it might be confused with the original M.I.T. software. 22 * M.I.T. makes no representations about the suitability of 23 * this software for any purpose. It is provided "as is" without express 24 * or implied warranty. 25 */ 26 27 #include <k5-int.h> 28 29 #include "kdc_util.h" 30 #include "extern.h" 31 32 /* Let cookies be valid for ten minutes. */ 33 #define COOKIE_LIFETIME 600 34 35 static krb5_error_code armor_ap_request 36 (struct kdc_request_state *state, krb5_fast_armor *armor) 37 { 38 krb5_error_code retval = 0; 39 krb5_auth_context authcontext = NULL; 40 krb5_ticket *ticket = NULL; 41 krb5_keyblock *subkey = NULL; 42 kdc_realm_t *realm = state->realm_data; 43 krb5_context context = realm->realm_context; 44 45 assert(armor->armor_type == KRB5_FAST_ARMOR_AP_REQUEST); 46 krb5_clear_error_message(context); 47 retval = krb5_auth_con_init(context, &authcontext); 48 /*disable replay cache*/ 49 if (retval == 0) 50 retval = krb5_auth_con_setflags(context, authcontext, 0); 51 if (retval == 0) 52 retval = krb5_rd_req(context, &authcontext, &armor->armor_value, 53 NULL /*server*/, realm->realm_keytab, 54 NULL, &ticket); 55 if (retval != 0) { 56 const char * errmsg = krb5_get_error_message(context, retval); 57 k5_setmsg(context, retval, _("%s while handling ap-request armor"), 58 errmsg); 59 krb5_free_error_message(context, errmsg); 60 } 61 if (retval == 0) { 62 if (!krb5_principal_compare_any_realm(context, realm->realm_tgsprinc, 63 ticket->server)) { 64 k5_setmsg(context, KRB5KDC_ERR_SERVER_NOMATCH, 65 _("ap-request armor for something other than the local " 66 "TGS")); 67 retval = KRB5KDC_ERR_SERVER_NOMATCH; 68 } 69 } 70 if (retval == 0) { 71 retval = krb5_auth_con_getrecvsubkey(context, authcontext, &subkey); 72 if (retval != 0 || subkey == NULL) { 73 k5_setmsg(context, KRB5KDC_ERR_POLICY, 74 _("ap-request armor without subkey")); 75 retval = KRB5KDC_ERR_POLICY; 76 } 77 } 78 if (retval == 0) 79 retval = krb5_c_fx_cf2_simple(context, 80 subkey, "subkeyarmor", 81 ticket->enc_part2->session, "ticketarmor", 82 &state->armor_key); 83 if (ticket) 84 krb5_free_ticket(context, ticket); 85 if (subkey) 86 krb5_free_keyblock(context, subkey); 87 if (authcontext) 88 krb5_auth_con_free(context, authcontext); 89 return retval; 90 } 91 92 static krb5_error_code 93 encrypt_fast_reply(struct kdc_request_state *state, 94 const krb5_fast_response *response, 95 krb5_data **fx_fast_reply) 96 { 97 krb5_context context = state->realm_data->realm_context; 98 krb5_error_code retval = 0; 99 krb5_enc_data encrypted_reply; 100 krb5_data *encoded_response = NULL; 101 102 assert(state->armor_key); 103 retval = encode_krb5_fast_response(response, &encoded_response); 104 if (retval== 0) 105 retval = krb5_encrypt_helper(context, state->armor_key, 106 KRB5_KEYUSAGE_FAST_REP, 107 encoded_response, &encrypted_reply); 108 if (encoded_response) 109 krb5_free_data(context, encoded_response); 110 encoded_response = NULL; 111 if (retval == 0) { 112 retval = encode_krb5_pa_fx_fast_reply(&encrypted_reply, 113 fx_fast_reply); 114 krb5_free_data_contents(context, &encrypted_reply.ciphertext); 115 } 116 return retval; 117 } 118 119 120 /* 121 * This function will find the FAST padata and, if FAST is successfully 122 * processed, will free the outer request and update the pointer to point to 123 * the inner request. checksummed_data points to the data that is in the 124 * armored_fast_request checksum; either the pa-tgs-req or the kdc-req-body. 125 */ 126 krb5_error_code 127 kdc_find_fast(krb5_kdc_req **requestptr, 128 krb5_data *checksummed_data, 129 krb5_keyblock *tgs_subkey, 130 krb5_keyblock *tgs_session, 131 struct kdc_request_state *state, 132 krb5_data **inner_body_out) 133 { 134 krb5_context context = state->realm_data->realm_context; 135 krb5_error_code retval = 0; 136 krb5_pa_data *fast_padata; 137 krb5_data scratch, plaintext, *inner_body = NULL; 138 krb5_fast_req * fast_req = NULL; 139 krb5_kdc_req *request = *requestptr; 140 krb5_fast_armored_req *fast_armored_req = NULL; 141 krb5_checksum *cksum; 142 krb5_boolean cksum_valid; 143 krb5_keyblock empty_keyblock; 144 145 if (inner_body_out != NULL) 146 *inner_body_out = NULL; 147 scratch.data = NULL; 148 krb5_clear_error_message(context); 149 memset(&empty_keyblock, 0, sizeof(krb5_keyblock)); 150 fast_padata = krb5int_find_pa_data(context, request->padata, 151 KRB5_PADATA_FX_FAST); 152 if (fast_padata != NULL){ 153 scratch.length = fast_padata->length; 154 scratch.data = (char *) fast_padata->contents; 155 retval = decode_krb5_pa_fx_fast_request(&scratch, &fast_armored_req); 156 if (retval == 0 &&fast_armored_req->armor) { 157 switch (fast_armored_req->armor->armor_type) { 158 case KRB5_FAST_ARMOR_AP_REQUEST: 159 if (tgs_subkey) { 160 retval = KRB5KDC_ERR_PREAUTH_FAILED; 161 k5_setmsg(context, retval, 162 _("Ap-request armor not permitted with TGS")); 163 break; 164 } 165 retval = armor_ap_request(state, fast_armored_req->armor); 166 break; 167 default: 168 k5_setmsg(context, KRB5KDC_ERR_PREAUTH_FAILED, 169 _("Unknown FAST armor type %d"), 170 fast_armored_req->armor->armor_type); 171 retval = KRB5KDC_ERR_PREAUTH_FAILED; 172 } 173 } 174 if (retval == 0 && !state->armor_key) { 175 if (tgs_subkey) 176 retval = krb5_c_fx_cf2_simple(context, 177 tgs_subkey, "subkeyarmor", 178 tgs_session, "ticketarmor", 179 &state->armor_key); 180 else { 181 retval = KRB5KDC_ERR_PREAUTH_FAILED; 182 k5_setmsg(context, retval, 183 _("No armor key but FAST armored request present")); 184 } 185 } 186 if (retval == 0) { 187 plaintext.length = fast_armored_req->enc_part.ciphertext.length; 188 plaintext.data = k5alloc(plaintext.length, &retval); 189 } 190 if (retval == 0) { 191 retval = krb5_c_decrypt(context, state->armor_key, 192 KRB5_KEYUSAGE_FAST_ENC, NULL, 193 &fast_armored_req->enc_part, 194 &plaintext); 195 if (retval == 0) 196 retval = decode_krb5_fast_req(&plaintext, &fast_req); 197 if (retval == 0 && inner_body_out != NULL) { 198 retval = fetch_asn1_field((unsigned char *)plaintext.data, 199 1, 2, &scratch); 200 if (retval == 0) { 201 retval = krb5_copy_data(context, &scratch, &inner_body); 202 } 203 } 204 if (plaintext.data) 205 free(plaintext.data); 206 } 207 cksum = &fast_armored_req->req_checksum; 208 if (retval == 0) 209 retval = krb5_c_verify_checksum(context, state->armor_key, 210 KRB5_KEYUSAGE_FAST_REQ_CHKSUM, 211 checksummed_data, cksum, 212 &cksum_valid); 213 if (retval == 0 && !cksum_valid) { 214 retval = KRB5KRB_AP_ERR_MODIFIED; 215 k5_setmsg(context, retval, 216 _("FAST req_checksum invalid; request modified")); 217 } 218 if (retval == 0) { 219 if (!krb5_c_is_keyed_cksum(cksum->checksum_type)) { 220 retval = KRB5KDC_ERR_POLICY; 221 k5_setmsg(context, retval, 222 _("Unkeyed checksum used in fast_req")); 223 } 224 } 225 if (retval == 0) { 226 if ((fast_req->fast_options & UNSUPPORTED_CRITICAL_FAST_OPTIONS) != 0) 227 retval = KRB5KDC_ERR_UNKNOWN_CRITICAL_FAST_OPTION; 228 } 229 if (retval == 0) { 230 state->fast_options = fast_req->fast_options; 231 fast_req->req_body->msg_type = request->msg_type; 232 krb5_free_kdc_req(context, request); 233 *requestptr = fast_req->req_body; 234 fast_req->req_body = NULL; 235 } 236 } 237 if (retval == 0 && inner_body_out != NULL) { 238 *inner_body_out = inner_body; 239 inner_body = NULL; 240 } 241 krb5_free_data(context, inner_body); 242 if (fast_req) 243 krb5_free_fast_req(context, fast_req); 244 if (fast_armored_req) 245 krb5_free_fast_armored_req(context, fast_armored_req); 246 return retval; 247 } 248 249 250 krb5_error_code 251 kdc_make_rstate(kdc_realm_t *active_realm, struct kdc_request_state **out) 252 { 253 struct kdc_request_state *state = malloc( sizeof(struct kdc_request_state)); 254 if (state == NULL) 255 return ENOMEM; 256 memset( state, 0, sizeof(struct kdc_request_state)); 257 state->realm_data = active_realm; 258 *out = state; 259 return 0; 260 } 261 262 void 263 kdc_free_rstate (struct kdc_request_state *s) 264 { 265 if (s == NULL) 266 return; 267 if (s->armor_key) 268 krb5_free_keyblock(s->realm_data->realm_context, s->armor_key); 269 if (s->strengthen_key) 270 krb5_free_keyblock(s->realm_data->realm_context, s->strengthen_key); 271 k5_zapfree_pa_data(s->in_cookie_padata); 272 k5_zapfree_pa_data(s->out_cookie_padata); 273 free(s); 274 } 275 276 krb5_error_code 277 kdc_fast_response_handle_padata(struct kdc_request_state *state, 278 krb5_kdc_req *request, 279 krb5_kdc_rep *rep, krb5_enctype enctype) 280 { 281 krb5_context context = state->realm_data->realm_context; 282 krb5_error_code retval = 0; 283 krb5_fast_finished finish; 284 krb5_fast_response fast_response; 285 krb5_data *encoded_ticket = NULL; 286 krb5_data *encrypted_reply = NULL; 287 krb5_pa_data *pa = NULL, **pa_array = NULL; 288 krb5_cksumtype cksumtype = CKSUMTYPE_RSA_MD5; 289 krb5_pa_data *empty_padata[] = {NULL}; 290 krb5_keyblock *strengthen_key = NULL; 291 292 if (!state->armor_key) 293 return 0; 294 memset(&finish, 0, sizeof(finish)); 295 retval = krb5_init_keyblock(context, enctype, 0, &strengthen_key); 296 if (retval == 0) 297 retval = krb5_c_make_random_key(context, enctype, strengthen_key); 298 if (retval == 0) { 299 state->strengthen_key = strengthen_key; 300 strengthen_key = NULL; 301 } 302 303 fast_response.padata = rep->padata; 304 if (fast_response.padata == NULL) 305 fast_response.padata = &empty_padata[0]; 306 fast_response.strengthen_key = state->strengthen_key; 307 fast_response.nonce = request->nonce; 308 fast_response.finished = &finish; 309 finish.client = rep->client; 310 pa_array = calloc(3, sizeof(*pa_array)); 311 if (pa_array == NULL) 312 retval = ENOMEM; 313 pa = calloc(1, sizeof(krb5_pa_data)); 314 if (retval == 0 && pa == NULL) 315 retval = ENOMEM; 316 if (retval == 0) 317 retval = krb5_us_timeofday(context, &finish.timestamp, &finish.usec); 318 if (retval == 0) 319 retval = encode_krb5_ticket(rep->ticket, &encoded_ticket); 320 if (retval == 0) 321 retval = krb5int_c_mandatory_cksumtype(context, 322 state->armor_key->enctype, 323 &cksumtype); 324 if (retval == 0) 325 retval = krb5_c_make_checksum(context, cksumtype, state->armor_key, 326 KRB5_KEYUSAGE_FAST_FINISHED, 327 encoded_ticket, &finish.ticket_checksum); 328 if (retval == 0) 329 retval = encrypt_fast_reply(state, &fast_response, &encrypted_reply); 330 if (retval == 0) { 331 pa[0].pa_type = KRB5_PADATA_FX_FAST; 332 pa[0].length = encrypted_reply->length; 333 pa[0].contents = (unsigned char *) encrypted_reply->data; 334 pa_array[0] = &pa[0]; 335 krb5_free_pa_data(context, rep->padata); 336 rep->padata = pa_array; 337 pa_array = NULL; 338 free(encrypted_reply); 339 encrypted_reply = NULL; 340 pa = NULL; 341 } 342 if (pa) 343 free(pa); 344 if (pa_array) 345 free(pa_array); 346 if (encrypted_reply) 347 krb5_free_data(context, encrypted_reply); 348 if (encoded_ticket) 349 krb5_free_data(context, encoded_ticket); 350 if (strengthen_key != NULL) 351 krb5_free_keyblock(context, strengthen_key); 352 if (finish.ticket_checksum.contents) 353 krb5_free_checksum_contents(context, &finish.ticket_checksum); 354 return retval; 355 } 356 357 358 /* 359 * We assume the caller is responsible for passing us an in_padata 360 * sufficient to include in a FAST error. In the FAST case we will 361 * set *fast_edata_out to the edata to be included in the error; in 362 * the non-FAST case we will set it to NULL. 363 */ 364 krb5_error_code 365 kdc_fast_handle_error(krb5_context context, 366 struct kdc_request_state *state, 367 krb5_kdc_req *request, 368 krb5_pa_data **in_padata, krb5_error *err, 369 krb5_data **fast_edata_out) 370 { 371 krb5_error_code retval = 0; 372 krb5_fast_response resp; 373 krb5_error fx_error; 374 krb5_data *encoded_fx_error = NULL, *encrypted_reply = NULL; 375 krb5_pa_data pa[1]; 376 krb5_pa_data *outer_pa[3]; 377 krb5_pa_data **inner_pa = NULL; 378 size_t size = 0; 379 380 *fast_edata_out = NULL; 381 memset(outer_pa, 0, sizeof(outer_pa)); 382 if (state->armor_key == NULL) 383 return 0; 384 fx_error = *err; 385 fx_error.e_data.data = NULL; 386 fx_error.e_data.length = 0; 387 for (size = 0; in_padata&&in_padata[size]; size++); 388 inner_pa = calloc(size + 2, sizeof(krb5_pa_data *)); 389 if (inner_pa == NULL) 390 retval = ENOMEM; 391 if (retval == 0) 392 for (size=0; in_padata&&in_padata[size]; size++) 393 inner_pa[size] = in_padata[size]; 394 if (retval == 0) 395 retval = encode_krb5_error(&fx_error, &encoded_fx_error); 396 if (retval == 0) { 397 pa[0].pa_type = KRB5_PADATA_FX_ERROR; 398 pa[0].length = encoded_fx_error->length; 399 pa[0].contents = (unsigned char *) encoded_fx_error->data; 400 inner_pa[size++] = &pa[0]; 401 } 402 if (retval == 0) { 403 resp.padata = inner_pa; 404 resp.nonce = request->nonce; 405 resp.strengthen_key = NULL; 406 resp.finished = NULL; 407 } 408 if (retval == 0) 409 retval = encrypt_fast_reply(state, &resp, &encrypted_reply); 410 if (inner_pa) 411 free(inner_pa); /*contained storage from caller and our stack*/ 412 if (retval == 0) { 413 pa[0].pa_type = KRB5_PADATA_FX_FAST; 414 pa[0].length = encrypted_reply->length; 415 pa[0].contents = (unsigned char *) encrypted_reply->data; 416 outer_pa[0] = &pa[0]; 417 } 418 retval = encode_krb5_padata_sequence(outer_pa, fast_edata_out); 419 if (encrypted_reply) 420 krb5_free_data(context, encrypted_reply); 421 if (encoded_fx_error) 422 krb5_free_data(context, encoded_fx_error); 423 return retval; 424 } 425 426 krb5_error_code 427 kdc_fast_handle_reply_key(struct kdc_request_state *state, 428 krb5_keyblock *existing_key, 429 krb5_keyblock **out_key) 430 { 431 krb5_context context = state->realm_data->realm_context; 432 krb5_error_code retval = 0; 433 434 if (state->armor_key) 435 retval = krb5_c_fx_cf2_simple(context, 436 state->strengthen_key, "strengthenkey", 437 existing_key, "replykey", out_key); 438 else 439 retval = krb5_copy_keyblock(context, existing_key, out_key); 440 return retval; 441 } 442 443 krb5_boolean 444 kdc_fast_hide_client(struct kdc_request_state *state) 445 { 446 return (state->fast_options & KRB5_FAST_OPTION_HIDE_CLIENT_NAMES) != 0; 447 } 448 449 /* Create a pa-data entry with the specified type and contents. */ 450 static krb5_error_code 451 make_padata(krb5_preauthtype pa_type, const void *contents, size_t len, 452 krb5_pa_data **out) 453 { 454 if (k5_alloc_pa_data(pa_type, len, out) != 0) 455 return ENOMEM; 456 memcpy((*out)->contents, contents, len); 457 return 0; 458 } 459 460 /* 461 * Derive the secure cookie encryption key from tgt_key and client_princ. The 462 * cookie key is derived with PRF+ using the concatenation of "COOKIE" and the 463 * unparsed client principal name as input. 464 */ 465 static krb5_error_code 466 derive_cookie_key(krb5_context context, krb5_keyblock *tgt_key, 467 krb5_const_principal client_princ, krb5_keyblock **key_out) 468 { 469 krb5_error_code ret; 470 krb5_data d; 471 char *princstr = NULL, *derive_input = NULL; 472 473 *key_out = NULL; 474 475 /* Construct the input string and derive the cookie key. */ 476 ret = krb5_unparse_name(context, client_princ, &princstr); 477 if (ret) 478 goto cleanup; 479 if (asprintf(&derive_input, "COOKIE%s", princstr) < 0) { 480 ret = ENOMEM; 481 goto cleanup; 482 } 483 d = string2data(derive_input); 484 ret = krb5_c_derive_prfplus(context, tgt_key, &d, ENCTYPE_NULL, key_out); 485 486 cleanup: 487 krb5_free_unparsed_name(context, princstr); 488 free(derive_input); 489 return ret; 490 } 491 492 /* Derive the cookie key for the specified kvno in tgt. tgt_key must be the 493 * decrypted first key data entry in tgt. */ 494 static krb5_error_code 495 get_cookie_key(krb5_context context, krb5_db_entry *tgt, 496 krb5_keyblock *tgt_key, krb5_kvno kvno, 497 krb5_const_principal client_princ, krb5_keyblock **key_out) 498 { 499 krb5_error_code ret; 500 krb5_keyblock storage, *key; 501 krb5_key_data *kd; 502 503 *key_out = NULL; 504 memset(&storage, 0, sizeof(storage)); 505 506 if (kvno == current_kvno(tgt)) { 507 /* Use the already-decrypted first key. */ 508 key = tgt_key; 509 } else { 510 /* The cookie used an older TGT key; find and decrypt it. */ 511 ret = krb5_dbe_find_enctype(context, tgt, -1, -1, kvno, &kd); 512 if (ret) 513 return ret; 514 ret = krb5_dbe_decrypt_key_data(context, NULL, kd, &storage, NULL); 515 if (ret) 516 return ret; 517 key = &storage; 518 } 519 520 ret = derive_cookie_key(context, key, client_princ, key_out); 521 krb5_free_keyblock_contents(context, &storage); 522 return ret; 523 } 524 525 /* Return true if there is any overlap between padata types in cpadata 526 * (from the cookie) and rpadata (from the request). */ 527 static krb5_boolean 528 is_relevant(krb5_pa_data *const *cpadata, krb5_pa_data *const *rpadata) 529 { 530 krb5_pa_data *const *p; 531 532 for (p = cpadata; p != NULL && *p != NULL; p++) { 533 if (krb5int_find_pa_data(NULL, rpadata, (*p)->pa_type) != NULL) 534 return TRUE; 535 } 536 return FALSE; 537 } 538 539 /* 540 * Locate and decode the FAST cookie in req, storing its contents in state for 541 * later access by preauth modules. If the cookie is expired, return 542 * KRB5KDC_ERR_PREAUTH_EXPIRED if its contents are relevant to req, and ignore 543 * it if they aren't. 544 */ 545 krb5_error_code 546 kdc_fast_read_cookie(krb5_context context, struct kdc_request_state *state, 547 krb5_kdc_req *req, krb5_db_entry *local_tgt, 548 krb5_keyblock *local_tgt_key) 549 { 550 krb5_error_code ret; 551 krb5_secure_cookie *cookie = NULL; 552 krb5_timestamp now; 553 krb5_keyblock *key = NULL; 554 krb5_enc_data enc; 555 krb5_pa_data *pa; 556 krb5_kvno kvno; 557 krb5_data plain = empty_data(); 558 559 pa = krb5int_find_pa_data(context, req->padata, KRB5_PADATA_FX_COOKIE); 560 if (pa == NULL) 561 return 0; 562 563 /* If it's not an MIT version 1 cookie, ignore it. It may be an empty 564 * "MIT" cookie or a cookie generated by a different KDC implementation. */ 565 if (pa->length <= 8 || memcmp(pa->contents, "MIT1", 4) != 0) 566 return 0; 567 568 /* Extract the kvno and generate the corresponding cookie key. */ 569 kvno = load_32_be(pa->contents + 4); 570 ret = get_cookie_key(context, local_tgt, local_tgt_key, kvno, req->client, 571 &key); 572 if (ret) 573 goto cleanup; 574 575 /* Decrypt and decode the cookie. */ 576 memset(&enc, 0, sizeof(enc)); 577 enc.enctype = key->enctype; 578 enc.ciphertext = make_data(pa->contents + 8, pa->length - 8); 579 ret = alloc_data(&plain, pa->length - 8); 580 if (ret) 581 goto cleanup; 582 ret = krb5_c_decrypt(context, key, KRB5_KEYUSAGE_PA_FX_COOKIE, NULL, &enc, 583 &plain); 584 if (ret) 585 goto cleanup; 586 ret = decode_krb5_secure_cookie(&plain, &cookie); 587 if (ret) 588 goto cleanup; 589 590 /* Check if the cookie is expired. */ 591 ret = krb5_timeofday(context, &now); 592 if (ret) 593 goto cleanup; 594 if (ts2tt(now) > cookie->time + COOKIE_LIFETIME) { 595 /* Don't accept the cookie contents. Only return an error if the 596 * cookie is relevant to the request. */ 597 if (is_relevant(cookie->data, req->padata)) 598 ret = KRB5KDC_ERR_PREAUTH_EXPIRED; 599 goto cleanup; 600 } 601 602 /* Steal the pa-data list pointer from the cookie and store it in state. */ 603 state->in_cookie_padata = cookie->data; 604 cookie->data = NULL; 605 606 cleanup: 607 zapfree(plain.data, plain.length); 608 krb5_free_keyblock(context, key); 609 k5_free_secure_cookie(context, cookie); 610 return 0; 611 } 612 613 /* If state contains a cookie value for pa_type, set *out to the corresponding 614 * data and return true. Otherwise set *out to empty and return false. */ 615 krb5_boolean 616 kdc_fast_search_cookie(struct kdc_request_state *state, 617 krb5_preauthtype pa_type, krb5_data *out) 618 { 619 krb5_pa_data *pa; 620 621 pa = krb5int_find_pa_data(NULL, state->in_cookie_padata, pa_type); 622 if (pa == NULL) { 623 *out = empty_data(); 624 return FALSE; 625 } else { 626 *out = make_data(pa->contents, pa->length); 627 return TRUE; 628 } 629 } 630 631 /* Set a cookie value in state for data, to be included in the outgoing 632 * cookie. Duplicate values are ignored. */ 633 krb5_error_code 634 kdc_fast_set_cookie(struct kdc_request_state *state, krb5_preauthtype pa_type, 635 const krb5_data *data) 636 { 637 krb5_pa_data **list = state->out_cookie_padata; 638 size_t count; 639 640 for (count = 0; list != NULL && list[count] != NULL; count++) { 641 if (list[count]->pa_type == pa_type) 642 return 0; 643 } 644 645 list = realloc(list, (count + 2) * sizeof(*list)); 646 if (list == NULL) 647 return ENOMEM; 648 state->out_cookie_padata = list; 649 list[count] = list[count + 1] = NULL; 650 return make_padata(pa_type, data->data, data->length, &list[count]); 651 } 652 653 /* Construct a cookie pa-data item using the cookie values from state, or a 654 * trivial "MIT" cookie if no values are set. */ 655 krb5_error_code 656 kdc_fast_make_cookie(krb5_context context, struct kdc_request_state *state, 657 krb5_db_entry *local_tgt, krb5_keyblock *local_tgt_key, 658 krb5_const_principal client_princ, 659 krb5_pa_data **cookie_out) 660 { 661 krb5_error_code ret; 662 krb5_secure_cookie cookie; 663 krb5_pa_data **contents = state->out_cookie_padata, *pa; 664 krb5_keyblock *key = NULL; 665 krb5_timestamp now; 666 krb5_enc_data enc; 667 krb5_data *der_cookie = NULL; 668 size_t ctlen; 669 670 *cookie_out = NULL; 671 memset(&enc, 0, sizeof(enc)); 672 673 /* Make a trivial cookie if there are no contents to marshal or we don't 674 * have a TGT entry to encrypt them. */ 675 if (contents == NULL || *contents == NULL || local_tgt_key == NULL) 676 return make_padata(KRB5_PADATA_FX_COOKIE, "MIT", 3, cookie_out); 677 678 ret = derive_cookie_key(context, local_tgt_key, client_princ, &key); 679 if (ret) 680 goto cleanup; 681 682 /* Encode the cookie. */ 683 ret = krb5_timeofday(context, &now); 684 if (ret) 685 goto cleanup; 686 cookie.time = ts2tt(now); 687 cookie.data = contents; 688 ret = encode_krb5_secure_cookie(&cookie, &der_cookie); 689 if (ret) 690 goto cleanup; 691 692 /* Encrypt the cookie in key. */ 693 ret = krb5_c_encrypt_length(context, key->enctype, der_cookie->length, 694 &ctlen); 695 if (ret) 696 goto cleanup; 697 ret = alloc_data(&enc.ciphertext, ctlen); 698 if (ret) 699 goto cleanup; 700 ret = krb5_c_encrypt(context, key, KRB5_KEYUSAGE_PA_FX_COOKIE, NULL, 701 der_cookie, &enc); 702 if (ret) 703 goto cleanup; 704 705 /* Construct the cookie pa-data entry. */ 706 ret = k5_alloc_pa_data(KRB5_PADATA_FX_COOKIE, 8 + enc.ciphertext.length, 707 &pa); 708 memcpy(pa->contents, "MIT1", 4); 709 store_32_be(current_kvno(local_tgt), pa->contents + 4); 710 memcpy(pa->contents + 8, enc.ciphertext.data, enc.ciphertext.length); 711 *cookie_out = pa; 712 713 cleanup: 714 krb5_free_keyblock(context, key); 715 if (der_cookie != NULL) { 716 zapfree(der_cookie->data, der_cookie->length); 717 free(der_cookie); 718 } 719 krb5_free_data_contents(context, &enc.ciphertext); 720 return ret; 721 } 722