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 H5L_FAST_COOKIE "org.h5l.fast-cookie" 39#define FAST_COOKIE_VERSION1 "H5L1" 40 41static krb5_error_code 42get_fastuser_crypto(kdc_request_t r, krb5_enctype enctype, krb5_crypto *crypto) 43{ 44 krb5_principal fast_princ; 45 hdb_entry_ex *fast_user = NULL; 46 Key *cookie_key = NULL; 47 krb5_error_code ret; 48 49 *crypto = NULL; 50 51 ret = krb5_make_principal(r->context, &fast_princ, 52 KRB5_WELLKNOWN_ORG_H5L_REALM, 53 KRB5_WELLKNOWN_NAME, H5L_FAST_COOKIE, NULL); 54 if (ret) 55 goto out; 56 57 ret = _kdc_db_fetch(r->context, r->config, fast_princ, 58 HDB_F_GET_CLIENT, NULL, NULL, &fast_user); 59 krb5_free_principal(r->context, fast_princ); 60 if (ret) 61 goto out; 62 63 if (enctype == KRB5_ENCTYPE_NULL) 64 ret = _kdc_get_preferred_key(r->context, r->config, fast_user, 65 "fast-cookie", &enctype, &cookie_key); 66 else 67 ret = hdb_enctype2key(r->context, &fast_user->entry, 68 enctype, &cookie_key); 69 if (ret) 70 goto out; 71 72 ret = krb5_crypto_init(r->context, &cookie_key->key, 0, crypto); 73 if (ret) 74 goto out; 75 76 out: 77 if (fast_user) 78 _kdc_free_ent(r->context, fast_user); 79 80 return ret; 81} 82 83 84static krb5_error_code 85fast_parse_cookie(kdc_request_t r, const PA_DATA *pa) 86{ 87 krb5_crypto crypto = NULL; 88 krb5_error_code ret; 89 KDCFastCookie data; 90 krb5_data d1; 91 size_t len; 92 93 ret = decode_KDCFastCookie(pa->padata_value.data, 94 pa->padata_value.length, 95 &data, &len); 96 if (ret) 97 return ret; 98 99 if (len != pa->padata_value.length || strcmp(FAST_COOKIE_VERSION1, data.version) != 0) { 100 free_KDCFastCookie(&data); 101 return KRB5KDC_ERR_POLICY; 102 } 103 104 ret = get_fastuser_crypto(r, data.cookie.etype, &crypto); 105 if (ret) 106 goto out; 107 108 ret = krb5_decrypt_EncryptedData(r->context, crypto, 109 KRB5_KU_H5L_COOKIE, 110 &data.cookie, &d1); 111 krb5_crypto_destroy(r->context, crypto); 112 if (ret) 113 goto out; 114 115 ret = decode_KDCFastState(d1.data, d1.length, &r->fast, &len); 116 krb5_data_free(&d1); 117 if (ret) 118 goto out; 119 120 if (r->fast.expiration < kdc_time) { 121 kdc_log(r->context, r->config, 0, "fast cookie expired"); 122 ret = KRB5KDC_ERR_POLICY; 123 goto out; 124 } 125 126 out: 127 free_KDCFastCookie(&data); 128 129 return ret; 130} 131 132static krb5_error_code 133fast_add_cookie(kdc_request_t r, METHOD_DATA *method_data) 134{ 135 krb5_crypto crypto = NULL; 136 KDCFastCookie shell; 137 krb5_error_code ret; 138 krb5_data data; 139 size_t size; 140 141 memset(&shell, 0, sizeof(shell)); 142 143 r->fast.expiration = kdc_time + FAST_EXPIRATION_TIME; 144 145 ASN1_MALLOC_ENCODE(KDCFastState, data.data, data.length, 146 &r->fast, &size, ret); 147 if (ret) 148 return ret; 149 heim_assert(size == data.length, "internal asn1 encoder error"); 150 151 ret = get_fastuser_crypto(r, KRB5_ENCTYPE_NULL, &crypto); 152 if (ret) 153 goto out; 154 155 ret = krb5_encrypt_EncryptedData(r->context, crypto, 156 KRB5_KU_H5L_COOKIE, 157 data.data, data.length, 0, 158 &shell.cookie); 159 krb5_crypto_destroy(r->context, crypto); 160 if (ret) 161 goto out; 162 163 free(data.data); 164 165 shell.version = FAST_COOKIE_VERSION1; 166 167 ASN1_MALLOC_ENCODE(KDCFastCookie, data.data, data.length, 168 &shell, &size, ret); 169 free_EncryptedData(&shell.cookie); 170 if (ret) 171 goto out; 172 heim_assert(size == data.length, "internal asn1 encoder error"); 173 174 ret = krb5_padata_add(r->context, method_data, 175 KRB5_PADATA_FX_COOKIE, 176 data.data, data.length); 177 out: 178 if (ret) 179 free(data.data); 180 return ret; 181} 182 183krb5_error_code 184_kdc_fast_mk_response(krb5_context context, 185 krb5_crypto armor_crypto, 186 METHOD_DATA *pa_data, 187 krb5_keyblock *strengthen_key, 188 KrbFastFinished *finished, 189 krb5uint32 nonce, 190 krb5_data *data) 191{ 192 PA_FX_FAST_REPLY fxfastrep; 193 KrbFastResponse fastrep; 194 krb5_error_code ret; 195 krb5_data buf; 196 size_t size; 197 198 memset(&fxfastrep, 0, sizeof(fxfastrep)); 199 memset(&fastrep, 0, sizeof(fastrep)); 200 krb5_data_zero(data); 201 202 if (pa_data) { 203 fastrep.padata.val = pa_data->val; 204 fastrep.padata.len = pa_data->len; 205 } 206 fastrep.strengthen_key = strengthen_key; 207 fastrep.finished = finished; 208 fastrep.nonce = nonce; 209 210 ASN1_MALLOC_ENCODE(KrbFastResponse, buf.data, buf.length, 211 &fastrep, &size, ret); 212 if (ret) 213 return ret; 214 if (buf.length != size) 215 krb5_abortx(context, "internal asn.1 error"); 216 217 fxfastrep.element = choice_PA_FX_FAST_REPLY_armored_data; 218 219 ret = krb5_encrypt_EncryptedData(context, 220 armor_crypto, 221 KRB5_KU_FAST_REP, 222 buf.data, 223 buf.length, 224 0, 225 &fxfastrep.u.armored_data.enc_fast_rep); 226 krb5_data_free(&buf); 227 if (ret) 228 return ret; 229 230 ASN1_MALLOC_ENCODE(PA_FX_FAST_REPLY, data->data, data->length, 231 &fxfastrep, &size, ret); 232 free_PA_FX_FAST_REPLY(&fxfastrep); 233 if (ret) 234 return ret; 235 if (data->length != size) 236 krb5_abortx(context, "internal asn.1 error"); 237 238 return 0; 239} 240 241 242krb5_error_code 243_kdc_fast_mk_error(krb5_context context, 244 kdc_request_t r, 245 METHOD_DATA *error_method, 246 krb5_crypto armor_crypto, 247 const KDC_REQ_BODY *req_body, 248 krb5_error_code outer_error, 249 const char *e_text, 250 krb5_principal error_client, 251 krb5_principal error_server, 252 time_t *csec, int *cusec, 253 krb5_data *error_msg) 254{ 255 krb5_error_code ret; 256 krb5_data e_data; 257 size_t size; 258 259 krb5_data_zero(&e_data); 260 261 if (armor_crypto) { 262 PA_FX_FAST_REPLY fxfastrep; 263 KrbFastResponse fastrep; 264 265 memset(&fxfastrep, 0, sizeof(fxfastrep)); 266 memset(&fastrep, 0, sizeof(fastrep)); 267 268 /* first add the KRB-ERROR to the fast errors */ 269 270 ret = krb5_mk_error(context, 271 outer_error, 272 e_text, 273 NULL, 274 error_client, 275 error_server, 276 NULL, 277 NULL, 278 &e_data); 279 if (ret) 280 return ret; 281 282 ret = krb5_padata_add(context, error_method, 283 KRB5_PADATA_FX_ERROR, 284 e_data.data, e_data.length); 285 if (ret) { 286 krb5_data_free(&e_data); 287 return ret; 288 } 289 290 if (/* hide_principal */ 0) { 291 error_client = NULL; 292 error_server = NULL; 293 e_text = NULL; 294 } 295 296 if (r) 297 ret = fast_add_cookie(r, error_method); 298 else 299 ret = krb5_padata_add(context, error_method, 300 KRB5_PADATA_FX_COOKIE, 301 NULL, 0); 302 if (ret) { 303 kdc_log(r->context, r->config, 0, "failed to add fast cookie with: %d", ret); 304 free_METHOD_DATA(error_method); 305 return ret; 306 } 307 308 ret = _kdc_fast_mk_response(context, armor_crypto, 309 error_method, NULL, NULL, 310 req_body->nonce, &e_data); 311 free_METHOD_DATA(error_method); 312 if (ret) 313 return ret; 314 315 ret = krb5_padata_add(context, error_method, 316 KRB5_PADATA_FX_FAST, 317 e_data.data, e_data.length); 318 if (ret) 319 return ret; 320 } 321 322 if (error_method && error_method->len) { 323 ASN1_MALLOC_ENCODE(METHOD_DATA, e_data.data, e_data.length, 324 error_method, &size, ret); 325 if (ret) 326 return ret; 327 if (e_data.length != size) 328 krb5_abortx(context, "internal asn.1 error"); 329 } 330 331 ret = krb5_mk_error(context, 332 outer_error, 333 e_text, 334 (e_data.length ? &e_data : NULL), 335 error_client, 336 error_server, 337 csec, 338 cusec, 339 error_msg); 340 krb5_data_free(&e_data); 341 342 return ret; 343} 344 345krb5_error_code 346_kdc_fast_unwrap_request(kdc_request_t r) 347{ 348 krb5_principal armor_server = NULL; 349 hdb_entry_ex *armor_user = NULL; 350 PA_FX_FAST_REQUEST fxreq; 351 krb5_auth_context ac = NULL; 352 krb5_ticket *ticket = NULL; 353 krb5_flags ap_req_options; 354 Key *armor_key = NULL; 355 krb5_keyblock armorkey; 356 krb5_error_code ret; 357 krb5_ap_req ap_req; 358 unsigned char *buf; 359 KrbFastReq fastreq; 360 size_t len, size; 361 krb5_data data; 362 const PA_DATA *pa; 363 int i = 0; 364 365 /* 366 * First look for FX_COOKIE and and process it 367 */ 368 pa = _kdc_find_padata(&r->req, &i, KRB5_PADATA_FX_COOKIE); 369 if (pa) { 370 ret = fast_parse_cookie(r, pa); 371 if (ret) 372 goto out; 373 } 374 375 i = 0; 376 pa = _kdc_find_padata(&r->req, &i, KRB5_PADATA_FX_FAST); 377 if (pa == NULL) 378 return 0; 379 380 ret = decode_PA_FX_FAST_REQUEST(pa->padata_value.data, 381 pa->padata_value.length, 382 &fxreq, 383 &len); 384 if (ret) 385 goto out; 386 if (len != pa->padata_value.length) { 387 ret = KRB5KDC_ERR_PREAUTH_FAILED; 388 goto out; 389 } 390 391 if (fxreq.element != choice_PA_FX_FAST_REQUEST_armored_data) { 392 kdc_log(r->context, r->config, 0, 393 "AS-REQ FAST contain unknown type: %d", (int)fxreq.element); 394 ret = KRB5KDC_ERR_PREAUTH_FAILED; 395 goto out; 396 } 397 398 /* pull out armor key */ 399 if (fxreq.u.armored_data.armor == NULL) { 400 kdc_log(r->context, r->config, 0, 401 "AS-REQ armor missing"); 402 ret = KRB5KDC_ERR_PREAUTH_FAILED; 403 goto out; 404 } 405 406 if (fxreq.u.armored_data.armor->armor_type != 1) { 407 kdc_log(r->context, r->config, 0, 408 "AS-REQ armor type not ap-req"); 409 ret = KRB5KDC_ERR_PREAUTH_FAILED; 410 goto out; 411 } 412 413 ret = krb5_decode_ap_req(r->context, 414 &fxreq.u.armored_data.armor->armor_value, 415 &ap_req); 416 if(ret) { 417 kdc_log(r->context, r->config, 0, "AP-REQ decode failed"); 418 goto out; 419 } 420 421 /* Save that principal that was in the request */ 422 ret = _krb5_principalname2krb5_principal(r->context, 423 &armor_server, 424 ap_req.ticket.sname, 425 ap_req.ticket.realm); 426 if (ret) { 427 free_AP_REQ(&ap_req); 428 goto out; 429 } 430 431 ret = _kdc_db_fetch(r->context, r->config, armor_server, 432 HDB_F_GET_SERVER, NULL, NULL, &armor_user); 433 if(ret == HDB_ERR_NOT_FOUND_HERE) { 434 kdc_log(r->context, r->config, 5, 435 "armor key does not have secrets at this KDC, " 436 "need to proxy"); 437 goto out; 438 } else if (ret) { 439 free_AP_REQ(&ap_req); 440 ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN; 441 goto out; 442 } 443 444 ret = hdb_enctype2key(r->context, &armor_user->entry, 445 ap_req.ticket.enc_part.etype, 446 &armor_key); 447 if (ret) { 448 free_AP_REQ(&ap_req); 449 goto out; 450 } 451 452 ret = krb5_verify_ap_req2(r->context, &ac, 453 &ap_req, 454 armor_server, 455 &armor_key->key, 456 0, 457 &ap_req_options, 458 &ticket, 459 KRB5_KU_AP_REQ_AUTH); 460 free_AP_REQ(&ap_req); 461 if (ret) 462 goto out; 463 464 if (ac->remote_subkey == NULL) { 465 krb5_auth_con_free(r->context, ac); 466 kdc_log(r->context, r->config, 0, 467 "FAST AP-REQ remote subkey missing"); 468 ret = KRB5KDC_ERR_PREAUTH_FAILED; 469 goto out; 470 } 471 472 ret = _krb5_fast_armor_key(r->context, 473 ac->remote_subkey, 474 &ticket->ticket.key, 475 &armorkey, 476 &r->armor_crypto); 477 krb5_auth_con_free(r->context, ac); 478 krb5_free_ticket(r->context, ticket); 479 if (ret) 480 goto out; 481 482 krb5_free_keyblock_contents(r->context, &armorkey); 483 484 /* verify req-checksum of the outer body */ 485 486 ASN1_MALLOC_ENCODE(KDC_REQ_BODY, buf, len, &r->req.req_body, &size, ret); 487 if (ret) 488 goto out; 489 if (size != len) { 490 ret = KRB5KDC_ERR_PREAUTH_FAILED; 491 goto out; 492 } 493 494 ret = krb5_verify_checksum(r->context, r->armor_crypto, 495 KRB5_KU_FAST_REQ_CHKSUM, 496 buf, len, 497 &fxreq.u.armored_data.req_checksum); 498 free(buf); 499 if (ret) { 500 kdc_log(r->context, r->config, 0, 501 "FAST request have a bad checksum"); 502 goto out; 503 } 504 505 ret = krb5_decrypt_EncryptedData(r->context, r->armor_crypto, 506 KRB5_KU_FAST_ENC, 507 &fxreq.u.armored_data.enc_fast_req, 508 &data); 509 if (ret) { 510 kdc_log(r->context, r->config, 0, 511 "Failed to decrypt FAST request"); 512 goto out; 513 } 514 515 ret = decode_KrbFastReq(data.data, data.length, &fastreq, &size); 516 if (ret) { 517 krb5_data_free(&data); 518 goto out; 519 } 520 if (data.length != size) { 521 krb5_data_free(&data); 522 ret = KRB5KDC_ERR_PREAUTH_FAILED; 523 goto out; 524 } 525 krb5_data_free(&data); 526 527 free_KDC_REQ_BODY(&r->req.req_body); 528 ret = copy_KDC_REQ_BODY(&fastreq.req_body, &r->req.req_body); 529 if (ret) 530 goto out; 531 532 /* check for unsupported mandatory options */ 533 if (FastOptions2int(fastreq.fast_options) & 0xfffc) { 534 kdc_log(r->context, r->config, 0, 535 "FAST unsupported mandatory option set"); 536 ret = KRB5KDC_ERR_PREAUTH_FAILED; 537 goto out; 538 } 539 540 /* KDC MUST ignore outer pa data preauth-14 - 6.5.5 */ 541 if (r->req.padata) 542 free_METHOD_DATA(r->req.padata); 543 else 544 ALLOC(r->req.padata); 545 546 ret = copy_METHOD_DATA(&fastreq.padata, r->req.padata); 547 if (ret) 548 goto out; 549 550 free_KrbFastReq(&fastreq); 551 free_PA_FX_FAST_REQUEST(&fxreq); 552 553 out: 554 if (armor_server) 555 krb5_free_principal(r->context, armor_server); 556 if(armor_user) 557 _kdc_free_ent(r->context, armor_user); 558 559 return ret; 560} 561