1 /* $NetBSD: fast.c,v 1.2 2017/01/28 21:31:44 christos Exp $ */ 2 3 /* 4 * Copyright (c) 1997-2011 Kungliga Tekniska Högskolan 5 * (Royal Institute of Technology, Stockholm, Sweden). 6 * All rights reserved. 7 * 8 * Portions Copyright (c) 2010 - 2011 Apple Inc. All rights reserved. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 17 * 2. Redistributions in binary form must reproduce the above copyright 18 * notice, this list of conditions and the following disclaimer in the 19 * documentation and/or other materials provided with the distribution. 20 * 21 * 3. Neither the name of the Institute nor the names of its contributors 22 * may be used to endorse or promote products derived from this software 23 * without specific prior written permission. 24 * 25 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 28 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 29 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 30 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 31 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 33 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 34 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 35 * SUCH DAMAGE. 36 */ 37 38 #include "kdc_locl.h" 39 40 static krb5_error_code 41 get_fastuser_crypto(kdc_request_t r, 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, 51 KRB5_WELLKNOWN_ORG_H5L_REALM, 52 KRB5_WELLKNOWN_NAME, "org.h5l.fast-cookie", NULL); 53 if (ret) 54 goto out; 55 56 ret = _kdc_db_fetch(r->context, r->config, fast_princ, 57 HDB_F_GET_CLIENT, NULL, NULL, &fast_user); 58 krb5_free_principal(r->context, fast_princ); 59 if (ret) 60 goto out; 61 62 if (enctype == KRB5_ENCTYPE_NULL) 63 ret = _kdc_get_preferred_key(r->context, r->config, fast_user, 64 "fast-cookie", &enctype, &cookie_key); 65 else 66 ret = hdb_enctype2key(r->context, &fast_user->entry, NULL, 67 enctype, &cookie_key); 68 if (ret) 69 goto out; 70 71 ret = krb5_crypto_init(r->context, &cookie_key->key, 0, crypto); 72 if (ret) 73 goto out; 74 75 out: 76 if (fast_user) 77 _kdc_free_ent(r->context, fast_user); 78 79 return ret; 80 } 81 82 83 static krb5_error_code 84 fast_parse_cookie(kdc_request_t r, const PA_DATA *pa) 85 { 86 krb5_crypto crypto = NULL; 87 krb5_error_code ret; 88 KDCFastCookie data; 89 krb5_data d1; 90 size_t len; 91 92 ret = decode_KDCFastCookie(pa->padata_value.data, 93 pa->padata_value.length, 94 &data, &len); 95 if (ret) 96 return ret; 97 98 if (len != pa->padata_value.length || strcmp("H5L1", data.version) != 0) { 99 free_KDCFastCookie(&data); 100 return KRB5KDC_ERR_POLICY; 101 } 102 103 ret = get_fastuser_crypto(r, data.cookie.etype, &crypto); 104 if (ret) 105 goto out; 106 107 ret = krb5_decrypt_EncryptedData(r->context, crypto, 108 KRB5_KU_H5L_COOKIE, 109 &data.cookie, &d1); 110 krb5_crypto_destroy(r->context, crypto); 111 if (ret) 112 goto out; 113 114 ret = decode_KDCFastState(d1.data, d1.length, &r->fast, &len); 115 krb5_data_free(&d1); 116 if (ret) 117 goto out; 118 119 if (r->fast.expiration < kdc_time) { 120 kdc_log(r->context, r->config, 0, "fast cookie expired"); 121 ret = KRB5KDC_ERR_POLICY; 122 goto out; 123 } 124 125 out: 126 free_KDCFastCookie(&data); 127 128 return ret; 129 } 130 131 static krb5_error_code 132 fast_add_cookie(kdc_request_t r, METHOD_DATA *method_data) 133 { 134 krb5_crypto crypto = NULL; 135 KDCFastCookie shell; 136 krb5_error_code ret; 137 krb5_data data; 138 size_t size; 139 140 memset(&shell, 0, sizeof(shell)); 141 142 r->fast.expiration = kdc_time + FAST_EXPIRATION_TIME; 143 144 ASN1_MALLOC_ENCODE(KDCFastState, data.data, data.length, 145 &r->fast, &size, ret); 146 if (ret) 147 return ret; 148 heim_assert(size == data.length, "internal asn1 encoder error"); 149 150 ret = get_fastuser_crypto(r, KRB5_ENCTYPE_NULL, &crypto); 151 if (ret) 152 goto out; 153 154 ret = krb5_encrypt_EncryptedData(r->context, crypto, 155 KRB5_KU_H5L_COOKIE, 156 data.data, data.length, 0, 157 &shell.cookie); 158 krb5_crypto_destroy(r->context, crypto); 159 if (ret) 160 goto out; 161 162 free(data.data); 163 164 shell.version = "H5L1"; 165 166 ASN1_MALLOC_ENCODE(KDCFastCookie, data.data, data.length, 167 &shell, &size, ret); 168 free_EncryptedData(&shell.cookie); 169 if (ret) 170 goto out; 171 heim_assert(size == data.length, "internal asn1 encoder error"); 172 173 ret = krb5_padata_add(r->context, method_data, 174 KRB5_PADATA_FX_COOKIE, 175 data.data, data.length); 176 out: 177 if (ret) 178 free(data.data); 179 return ret; 180 } 181 182 krb5_error_code 183 _kdc_fast_mk_response(krb5_context context, 184 krb5_crypto armor_crypto, 185 METHOD_DATA *pa_data, 186 krb5_keyblock *strengthen_key, 187 KrbFastFinished *finished, 188 krb5uint32 nonce, 189 krb5_data *data) 190 { 191 PA_FX_FAST_REPLY fxfastrep; 192 KrbFastResponse fastrep; 193 krb5_error_code ret; 194 krb5_data buf; 195 size_t size; 196 197 memset(&fxfastrep, 0, sizeof(fxfastrep)); 198 memset(&fastrep, 0, sizeof(fastrep)); 199 krb5_data_zero(data); 200 201 if (pa_data) { 202 fastrep.padata.val = pa_data->val; 203 fastrep.padata.len = pa_data->len; 204 } 205 fastrep.strengthen_key = strengthen_key; 206 fastrep.finished = finished; 207 fastrep.nonce = nonce; 208 209 ASN1_MALLOC_ENCODE(KrbFastResponse, buf.data, buf.length, 210 &fastrep, &size, ret); 211 if (ret) 212 return ret; 213 if (buf.length != size) 214 krb5_abortx(context, "internal asn.1 error"); 215 216 fxfastrep.element = choice_PA_FX_FAST_REPLY_armored_data; 217 218 ret = krb5_encrypt_EncryptedData(context, 219 armor_crypto, 220 KRB5_KU_FAST_REP, 221 buf.data, 222 buf.length, 223 0, 224 &fxfastrep.u.armored_data.enc_fast_rep); 225 krb5_data_free(&buf); 226 if (ret) 227 return ret; 228 229 ASN1_MALLOC_ENCODE(PA_FX_FAST_REPLY, data->data, data->length, 230 &fxfastrep, &size, ret); 231 free_PA_FX_FAST_REPLY(&fxfastrep); 232 if (ret) 233 return ret; 234 if (data->length != size) 235 krb5_abortx(context, "internal asn.1 error"); 236 237 return 0; 238 } 239 240 241 krb5_error_code 242 _kdc_fast_mk_error(krb5_context context, 243 kdc_request_t r, 244 METHOD_DATA *error_method, 245 krb5_crypto armor_crypto, 246 const KDC_REQ_BODY *req_body, 247 krb5_error_code outer_error, 248 const char *e_text, 249 krb5_principal error_server, 250 const PrincipalName *error_client_name, 251 const Realm *error_client_realm, 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_ext(context, 271 outer_error, 272 e_text, 273 NULL, 274 error_server, 275 error_client_name, 276 error_client_realm, 277 NULL, 278 NULL, 279 &e_data); 280 if (ret) 281 return ret; 282 283 ret = krb5_padata_add(context, error_method, 284 KRB5_PADATA_FX_ERROR, 285 e_data.data, e_data.length); 286 if (ret) { 287 krb5_data_free(&e_data); 288 return ret; 289 } 290 291 if (/* hide_principal */ 0) { 292 error_client_name = NULL; 293 error_client_realm = NULL; 294 error_server = NULL; 295 e_text = NULL; 296 } 297 298 if (r) 299 ret = fast_add_cookie(r, error_method); 300 else 301 ret = krb5_padata_add(context, error_method, 302 KRB5_PADATA_FX_COOKIE, 303 NULL, 0); 304 if (ret) { 305 kdc_log(r->context, r->config, 0, "failed to add fast cookie with: %d", ret); 306 free_METHOD_DATA(error_method); 307 return ret; 308 } 309 310 ret = _kdc_fast_mk_response(context, armor_crypto, 311 error_method, NULL, NULL, 312 req_body->nonce, &e_data); 313 free_METHOD_DATA(error_method); 314 if (ret) 315 return ret; 316 317 ret = krb5_padata_add(context, error_method, 318 KRB5_PADATA_FX_FAST, 319 e_data.data, e_data.length); 320 if (ret) 321 return ret; 322 } 323 324 if (error_method && error_method->len) { 325 ASN1_MALLOC_ENCODE(METHOD_DATA, e_data.data, e_data.length, 326 error_method, &size, ret); 327 if (ret) 328 return ret; 329 if (e_data.length != size) 330 krb5_abortx(context, "internal asn.1 error"); 331 } 332 333 ret = krb5_mk_error_ext(context, 334 outer_error, 335 e_text, 336 (e_data.length ? &e_data : NULL), 337 error_server, 338 error_client_name, 339 error_client_realm, 340 csec, 341 cusec, 342 error_msg); 343 krb5_data_free(&e_data); 344 345 return ret; 346 } 347 348 krb5_error_code 349 _kdc_fast_unwrap_request(kdc_request_t r) 350 { 351 krb5_principal armor_server = NULL; 352 hdb_entry_ex *armor_user = NULL; 353 PA_FX_FAST_REQUEST fxreq; 354 krb5_auth_context ac = NULL; 355 krb5_ticket *ticket = NULL; 356 krb5_flags ap_req_options; 357 Key *armor_key = NULL; 358 krb5_keyblock armorkey; 359 krb5_error_code ret; 360 krb5_ap_req ap_req; 361 unsigned char *buf = NULL; 362 KrbFastReq fastreq; 363 size_t len, size; 364 krb5_data data; 365 const PA_DATA *pa; 366 int i = 0; 367 368 /* 369 * First look for FX_COOKIE and and process it 370 */ 371 pa = _kdc_find_padata(&r->req, &i, KRB5_PADATA_FX_COOKIE); 372 if (pa) { 373 ret = fast_parse_cookie(r, pa); 374 if (ret) 375 goto out; 376 } 377 378 i = 0; 379 pa = _kdc_find_padata(&r->req, &i, KRB5_PADATA_FX_FAST); 380 if (pa == NULL) 381 return 0; 382 383 ret = decode_PA_FX_FAST_REQUEST(pa->padata_value.data, 384 pa->padata_value.length, 385 &fxreq, 386 &len); 387 if (ret) 388 goto out; 389 if (len != pa->padata_value.length) { 390 ret = KRB5KDC_ERR_PREAUTH_FAILED; 391 goto out; 392 } 393 394 if (fxreq.element != choice_PA_FX_FAST_REQUEST_armored_data) { 395 kdc_log(r->context, r->config, 0, 396 "AS-REQ FAST contain unknown type: %d", (int)fxreq.element); 397 ret = KRB5KDC_ERR_PREAUTH_FAILED; 398 goto out; 399 } 400 401 /* pull out armor key */ 402 if (fxreq.u.armored_data.armor == NULL) { 403 kdc_log(r->context, r->config, 0, 404 "AS-REQ armor missing"); 405 ret = KRB5KDC_ERR_PREAUTH_FAILED; 406 goto out; 407 } 408 409 if (fxreq.u.armored_data.armor->armor_type != 1) { 410 kdc_log(r->context, r->config, 0, 411 "AS-REQ armor type not ap-req"); 412 ret = KRB5KDC_ERR_PREAUTH_FAILED; 413 goto out; 414 } 415 416 ret = krb5_decode_ap_req(r->context, 417 &fxreq.u.armored_data.armor->armor_value, 418 &ap_req); 419 if(ret) { 420 kdc_log(r->context, r->config, 0, "AP-REQ decode failed"); 421 goto out; 422 } 423 424 /* Save that principal that was in the request */ 425 ret = _krb5_principalname2krb5_principal(r->context, 426 &armor_server, 427 ap_req.ticket.sname, 428 ap_req.ticket.realm); 429 if (ret) { 430 free_AP_REQ(&ap_req); 431 goto out; 432 } 433 434 ret = _kdc_db_fetch(r->context, r->config, armor_server, 435 HDB_F_GET_SERVER, NULL, NULL, &armor_user); 436 if(ret == HDB_ERR_NOT_FOUND_HERE) { 437 kdc_log(r->context, r->config, 5, 438 "armor key does not have secrets at this KDC, " 439 "need to proxy"); 440 goto out; 441 } else if (ret) { 442 free_AP_REQ(&ap_req); 443 ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN; 444 goto out; 445 } 446 447 ret = hdb_enctype2key(r->context, &armor_user->entry, NULL, 448 ap_req.ticket.enc_part.etype, 449 &armor_key); 450 if (ret) { 451 free_AP_REQ(&ap_req); 452 goto out; 453 } 454 455 ret = krb5_verify_ap_req2(r->context, &ac, 456 &ap_req, 457 armor_server, 458 &armor_key->key, 459 0, 460 &ap_req_options, 461 &ticket, 462 KRB5_KU_AP_REQ_AUTH); 463 free_AP_REQ(&ap_req); 464 if (ret) 465 goto out; 466 467 if (ac->remote_subkey == NULL) { 468 krb5_auth_con_free(r->context, ac); 469 kdc_log(r->context, r->config, 0, 470 "FAST AP-REQ remote subkey missing"); 471 ret = KRB5KDC_ERR_PREAUTH_FAILED; 472 goto out; 473 } 474 475 ret = _krb5_fast_armor_key(r->context, 476 ac->remote_subkey, 477 &ticket->ticket.key, 478 &armorkey, 479 &r->armor_crypto); 480 krb5_auth_con_free(r->context, ac); 481 krb5_free_ticket(r->context, ticket); 482 if (ret) 483 goto out; 484 485 krb5_free_keyblock_contents(r->context, &armorkey); 486 487 /* verify req-checksum of the outer body */ 488 489 ASN1_MALLOC_ENCODE(KDC_REQ_BODY, buf, len, &r->req.req_body, &size, ret); 490 if (ret) 491 goto out; 492 if (size != len) { 493 ret = KRB5KDC_ERR_PREAUTH_FAILED; 494 goto out; 495 } 496 497 ret = krb5_verify_checksum(r->context, r->armor_crypto, 498 KRB5_KU_FAST_REQ_CHKSUM, 499 buf, len, 500 &fxreq.u.armored_data.req_checksum); 501 if (ret) { 502 kdc_log(r->context, r->config, 0, 503 "FAST request have a bad checksum"); 504 goto out; 505 } 506 507 ret = krb5_decrypt_EncryptedData(r->context, r->armor_crypto, 508 KRB5_KU_FAST_ENC, 509 &fxreq.u.armored_data.enc_fast_req, 510 &data); 511 if (ret) { 512 kdc_log(r->context, r->config, 0, 513 "Failed to decrypt FAST request"); 514 goto out; 515 } 516 517 ret = decode_KrbFastReq(data.data, data.length, &fastreq, &size); 518 if (ret) { 519 krb5_data_free(&data); 520 goto out; 521 } 522 if (data.length != size) { 523 krb5_data_free(&data); 524 ret = KRB5KDC_ERR_PREAUTH_FAILED; 525 goto out; 526 } 527 krb5_data_free(&data); 528 529 free_KDC_REQ_BODY(&r->req.req_body); 530 ret = copy_KDC_REQ_BODY(&fastreq.req_body, &r->req.req_body); 531 if (ret) 532 goto out; 533 534 /* check for unsupported mandatory options */ 535 if (FastOptions2int(fastreq.fast_options) & 0xfffc) { 536 kdc_log(r->context, r->config, 0, 537 "FAST unsupported mandatory option set"); 538 ret = KRB5KDC_ERR_PREAUTH_FAILED; 539 goto out; 540 } 541 542 /* KDC MUST ignore outer pa data preauth-14 - 6.5.5 */ 543 if (r->req.padata) 544 free_METHOD_DATA(r->req.padata); 545 else 546 ALLOC(r->req.padata); 547 548 ret = copy_METHOD_DATA(&fastreq.padata, r->req.padata); 549 if (ret) 550 goto out; 551 552 free_KrbFastReq(&fastreq); 553 free_PA_FX_FAST_REQUEST(&fxreq); 554 555 out: 556 if (armor_server) 557 krb5_free_principal(r->context, armor_server); 558 if(armor_user) 559 _kdc_free_ent(r->context, armor_user); 560 561 return ret; 562 } 563