1/* 2 * Copyright (c) 1997 - 2007 Kungliga Tekniska Högskolan 3 * (Royal Institute of Technology, Stockholm, Sweden). 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * 3. Neither the name of the Institute nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 */ 33 34#include "krb5_locl.h" 35 36static krb5_error_code 37decrypt_tkt_enc_part(krb5_context context, 38 krb5_keyblock *key, 39 EncryptedData *enc_part, 40 EncTicketPart *decr_part) 41{ 42 krb5_error_code ret; 43 krb5_data plain; 44 size_t len; 45 krb5_crypto crypto; 46 47 ret = krb5_crypto_init(context, key, 0, &crypto); 48 if (ret) 49 return ret; 50 ret = krb5_decrypt_EncryptedData(context, 51 crypto, 52 KRB5_KU_TICKET, 53 enc_part, 54 &plain); 55 krb5_crypto_destroy(context, crypto); 56 if (ret) 57 return ret; 58 59 ret = decode_EncTicketPart(plain.data, plain.length, decr_part, &len); 60 if (ret) 61 krb5_set_error_message(context, ret, 62 N_("Failed to decode encrypted " 63 "ticket part", "")); 64 krb5_data_free(&plain); 65 return ret; 66} 67 68static krb5_error_code 69decrypt_authenticator (krb5_context context, 70 EncryptionKey *key, 71 EncryptedData *enc_part, 72 Authenticator *authenticator, 73 krb5_key_usage usage) 74{ 75 krb5_error_code ret; 76 krb5_data plain; 77 size_t len; 78 krb5_crypto crypto; 79 80 ret = krb5_crypto_init(context, key, 0, &crypto); 81 if (ret) 82 return ret; 83 ret = krb5_decrypt_EncryptedData (context, 84 crypto, 85 usage /* KRB5_KU_AP_REQ_AUTH */, 86 enc_part, 87 &plain); 88 /* for backwards compatibility, also try the old usage */ 89 if (ret && usage == KRB5_KU_TGS_REQ_AUTH) 90 ret = krb5_decrypt_EncryptedData (context, 91 crypto, 92 KRB5_KU_AP_REQ_AUTH, 93 enc_part, 94 &plain); 95 krb5_crypto_destroy(context, crypto); 96 if (ret) 97 return ret; 98 99 ret = decode_Authenticator(plain.data, plain.length, 100 authenticator, &len); 101 krb5_data_free (&plain); 102 return ret; 103} 104 105KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 106krb5_decode_ap_req(krb5_context context, 107 const krb5_data *inbuf, 108 krb5_ap_req *ap_req) 109{ 110 krb5_error_code ret; 111 size_t len; 112 ret = decode_AP_REQ(inbuf->data, inbuf->length, ap_req, &len); 113 if (ret) 114 return ret; 115 if (ap_req->pvno != 5){ 116 free_AP_REQ(ap_req); 117 krb5_clear_error_message (context); 118 return KRB5KRB_AP_ERR_BADVERSION; 119 } 120 if (ap_req->msg_type != krb_ap_req){ 121 free_AP_REQ(ap_req); 122 krb5_clear_error_message (context); 123 return KRB5KRB_AP_ERR_MSG_TYPE; 124 } 125 if (ap_req->ticket.tkt_vno != 5){ 126 free_AP_REQ(ap_req); 127 krb5_clear_error_message (context); 128 return KRB5KRB_AP_ERR_BADVERSION; 129 } 130 return 0; 131} 132 133static krb5_error_code 134check_transited(krb5_context context, Ticket *ticket, EncTicketPart *enc) 135{ 136 char **realms; 137 unsigned int num_realms, n; 138 krb5_error_code ret; 139 140 /* 141 * Windows 2000 and 2003 uses this inside their TGT so it's normaly 142 * not seen by others, however, samba4 joined with a Windows AD as 143 * a Domain Controller gets exposed to this. 144 */ 145 if(enc->transited.tr_type == 0 && enc->transited.contents.length == 0) 146 return 0; 147 148 if(enc->transited.tr_type != DOMAIN_X500_COMPRESS) 149 return KRB5KDC_ERR_TRTYPE_NOSUPP; 150 151 if(enc->transited.contents.length == 0) 152 return 0; 153 154 ret = krb5_domain_x500_decode(context, enc->transited.contents, 155 &realms, &num_realms, 156 enc->crealm, 157 ticket->realm); 158 if(ret) 159 return ret; 160 ret = krb5_check_transited(context, enc->crealm, 161 ticket->realm, 162 realms, num_realms, NULL); 163 for (n = 0; n < num_realms; n++) 164 free(realms[n]); 165 free(realms); 166 return ret; 167} 168 169static krb5_error_code 170find_etypelist(krb5_context context, 171 krb5_auth_context auth_context, 172 EtypeList *etypes) 173{ 174 krb5_error_code ret; 175 krb5_data data; 176 177 ret = _krb5_get_ad(context, auth_context->authenticator->authorization_data, NULL, KRB5_AUTHDATA_GSS_API_ETYPE_NEGOTIATION, &data); 178 if (ret) 179 return 0; 180 181 ret = decode_EtypeList(data.data, data.length, etypes, NULL); 182 krb5_data_free(&data); 183 if (ret) 184 krb5_clear_error_message(context); 185 186 return ret; 187} 188 189KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 190krb5_decrypt_ticket(krb5_context context, 191 Ticket *ticket, 192 krb5_keyblock *key, 193 EncTicketPart *out, 194 krb5_flags flags) 195{ 196 EncTicketPart t; 197 krb5_error_code ret; 198 ret = decrypt_tkt_enc_part(context, key, &ticket->enc_part, &t); 199 if (ret) 200 return ret; 201 202 { 203 krb5_timestamp now; 204 time_t start = t.authtime; 205 206 krb5_timeofday(context, &now); 207 if(t.starttime) 208 start = *t.starttime; 209 if(start - now > context->max_skew 210 || (t.flags.invalid 211 && !(flags & KRB5_VERIFY_AP_REQ_IGNORE_INVALID))) { 212 free_EncTicketPart(&t); 213 krb5_clear_error_message(context); 214 return KRB5KRB_AP_ERR_TKT_NYV; 215 } 216 if(now - t.endtime > context->max_skew) { 217 free_EncTicketPart(&t); 218 krb5_clear_error_message(context); 219 return KRB5KRB_AP_ERR_TKT_EXPIRED; 220 } 221 222 if(!t.flags.transited_policy_checked) { 223 ret = check_transited(context, ticket, &t); 224 if(ret) { 225 free_EncTicketPart(&t); 226 return ret; 227 } 228 } 229 } 230 231 if(out) 232 *out = t; 233 else 234 free_EncTicketPart(&t); 235 return 0; 236} 237 238KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 239krb5_verify_authenticator_checksum(krb5_context context, 240 krb5_auth_context ac, 241 void *data, 242 size_t len) 243{ 244 krb5_error_code ret; 245 krb5_keyblock *key; 246 krb5_authenticator authenticator; 247 krb5_crypto crypto; 248 249 ret = krb5_auth_con_getauthenticator(context, 250 ac, 251 &authenticator); 252 if(ret) 253 return ret; 254 if(authenticator->cksum == NULL) { 255 krb5_free_authenticator(context, &authenticator); 256 return -17; 257 } 258 ret = krb5_auth_con_getkey(context, ac, &key); 259 if(ret) { 260 krb5_free_authenticator(context, &authenticator); 261 return ret; 262 } 263 ret = krb5_crypto_init(context, key, 0, &crypto); 264 if(ret) 265 goto out; 266 ret = krb5_verify_checksum(context, 267 crypto, 268 KRB5_KU_AP_REQ_AUTH_CKSUM, 269 data, 270 len, 271 authenticator->cksum); 272 krb5_crypto_destroy(context, crypto); 273 out: 274 krb5_free_authenticator(context, &authenticator); 275 krb5_free_keyblock(context, key); 276 return ret; 277} 278 279 280KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 281krb5_verify_ap_req(krb5_context context, 282 krb5_auth_context *auth_context, 283 krb5_ap_req *ap_req, 284 krb5_const_principal server, 285 krb5_keyblock *keyblock, 286 krb5_flags flags, 287 krb5_flags *ap_req_options, 288 krb5_ticket **ticket) 289{ 290 return krb5_verify_ap_req2(context, 291 auth_context, 292 ap_req, 293 server, 294 keyblock, 295 flags, 296 ap_req_options, 297 ticket, 298 KRB5_KU_AP_REQ_AUTH); 299} 300 301KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 302krb5_verify_ap_req2(krb5_context context, 303 krb5_auth_context *auth_context, 304 krb5_ap_req *ap_req, 305 krb5_const_principal server, 306 krb5_keyblock *keyblock, 307 krb5_flags flags, 308 krb5_flags *ap_req_options, 309 krb5_ticket **ticket, 310 krb5_key_usage usage) 311{ 312 krb5_ticket *t; 313 krb5_auth_context ac; 314 krb5_error_code ret; 315 EtypeList etypes; 316 317 memset(&etypes, 0, sizeof(etypes)); 318 319 if(ticket) 320 *ticket = NULL; 321 322 if (auth_context && *auth_context) { 323 ac = *auth_context; 324 } else { 325 ret = krb5_auth_con_init(context, &ac); 326 if (ret) 327 return ret; 328 } 329 330 t = calloc(1, sizeof(*t)); 331 if (t == NULL) { 332 ret = ENOMEM; 333 krb5_clear_error_message(context); 334 goto out; 335 } 336 337 if (ap_req->ap_options.use_session_key && ac->keyblock){ 338 ret = krb5_decrypt_ticket(context, &ap_req->ticket, 339 ac->keyblock, 340 &t->ticket, 341 flags); 342 krb5_free_keyblock(context, ac->keyblock); 343 ac->keyblock = NULL; 344 }else 345 ret = krb5_decrypt_ticket(context, &ap_req->ticket, 346 keyblock, 347 &t->ticket, 348 flags); 349 350 if(ret) 351 goto out; 352 353 ret = _krb5_principalname2krb5_principal(context, 354 &t->server, 355 ap_req->ticket.sname, 356 ap_req->ticket.realm); 357 if (ret) goto out; 358 ret = _krb5_principalname2krb5_principal(context, 359 &t->client, 360 t->ticket.cname, 361 t->ticket.crealm); 362 if (ret) goto out; 363 364 ret = decrypt_authenticator(context, 365 &t->ticket.key, 366 &ap_req->authenticator, 367 ac->authenticator, 368 usage); 369 if (ret) 370 goto out; 371 372 { 373 krb5_principal p1, p2; 374 krb5_boolean res; 375 376 _krb5_principalname2krb5_principal(context, 377 &p1, 378 ac->authenticator->cname, 379 ac->authenticator->crealm); 380 _krb5_principalname2krb5_principal(context, 381 &p2, 382 t->ticket.cname, 383 t->ticket.crealm); 384 res = krb5_principal_compare(context, p1, p2); 385 krb5_free_principal(context, p1); 386 krb5_free_principal(context, p2); 387 if (!res) { 388 ret = KRB5KRB_AP_ERR_BADMATCH; 389 krb5_clear_error_message(context); 390 goto out; 391 } 392 } 393 394 /* check addresses */ 395 396 if (t->ticket.caddr 397 && ac->remote_address 398 && !krb5_address_search(context, 399 ac->remote_address, 400 t->ticket.caddr)) { 401 ret = KRB5KRB_AP_ERR_BADADDR; 402 krb5_clear_error_message(context); 403 goto out; 404 } 405 406 /* check timestamp in authenticator */ 407 { 408 krb5_timestamp now; 409 410 krb5_timeofday(context, &now); 411 412 if (krb5_time_abs(ac->authenticator->ctime, now) > context->max_skew) { 413 ret = KRB5KRB_AP_ERR_SKEW; 414 krb5_clear_error_message(context); 415 goto out; 416 } 417 } 418 419 if (ac->authenticator->seq_number) 420 krb5_auth_con_setremoteseqnumber(context, ac, 421 *ac->authenticator->seq_number); 422 423 /* XXX - Xor sequence numbers */ 424 425 if (ac->authenticator->subkey) { 426 ret = krb5_auth_con_setremotesubkey(context, ac, 427 ac->authenticator->subkey); 428 if (ret) 429 goto out; 430 } 431 432 ret = find_etypelist(context, ac, &etypes); 433 if (ret) 434 goto out; 435 436 ac->keytype = (krb5_keytype)ETYPE_NULL; 437 438 if (etypes.val) { 439 size_t i; 440 441 for (i = 0; i < etypes.len; i++) { 442 if (krb5_enctype_valid(context, etypes.val[i]) == 0) { 443 ac->keytype = etypes.val[i]; 444 break; 445 } 446 } 447 } 448 449 /* save key */ 450 ret = krb5_copy_keyblock(context, &t->ticket.key, &ac->keyblock); 451 if (ret) goto out; 452 453 if (ap_req_options) { 454 *ap_req_options = 0; 455 if (ac->keytype != ETYPE_NULL) 456 *ap_req_options |= AP_OPTS_USE_SUBKEY; 457 if (ap_req->ap_options.use_session_key) 458 *ap_req_options |= AP_OPTS_USE_SESSION_KEY; 459 if (ap_req->ap_options.mutual_required) 460 *ap_req_options |= AP_OPTS_MUTUAL_REQUIRED; 461 } 462 463 if(ticket) 464 *ticket = t; 465 else 466 krb5_free_ticket(context, t); 467 if (auth_context) { 468 if (*auth_context == NULL) 469 *auth_context = ac; 470 } else 471 krb5_auth_con_free(context, ac); 472 free_EtypeList(&etypes); 473 return 0; 474 out: 475 free_EtypeList(&etypes); 476 if (t) 477 krb5_free_ticket(context, t); 478 if (auth_context == NULL || *auth_context == NULL) 479 krb5_auth_con_free(context, ac); 480 return ret; 481} 482 483/* 484 * 485 */ 486 487struct krb5_rd_req_in_ctx_data { 488 krb5_keytab keytab; 489 krb5_keyblock *keyblock; 490 krb5_boolean check_pac; 491}; 492 493struct krb5_rd_req_out_ctx_data { 494 krb5_keyblock *keyblock; 495 int flags; 496 krb5_flags ap_req_options; 497 krb5_ticket *ticket; 498 krb5_principal server; 499}; 500 501/** 502 * Allocate a krb5_rd_req_in_ctx as an input parameter to 503 * krb5_rd_req_ctx(). The caller should free the context with 504 * krb5_rd_req_in_ctx_free() when done with the context. 505 * 506 * @param context Keberos 5 context. 507 * @param ctx in ctx to krb5_rd_req_ctx(). 508 * 509 * @return Kerberos 5 error code, see krb5_get_error_message(). 510 * 511 * @ingroup krb5_auth 512 */ 513 514KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 515krb5_rd_req_in_ctx_alloc(krb5_context context, krb5_rd_req_in_ctx *ctx) 516{ 517 *ctx = calloc(1, sizeof(**ctx)); 518 if (*ctx == NULL) { 519 krb5_set_error_message(context, ENOMEM, 520 N_("malloc: out of memory", "")); 521 return ENOMEM; 522 } 523 (*ctx)->check_pac = (context->flags & KRB5_CTX_F_CHECK_PAC) ? 1 : 0; 524 return 0; 525} 526 527/** 528 * Set the keytab that krb5_rd_req_ctx() will use. 529 * 530 * @param context Keberos 5 context. 531 * @param in in ctx to krb5_rd_req_ctx(). 532 * @param keytab keytab that krb5_rd_req_ctx() will use, only copy the 533 * pointer, so the caller must free they keytab after 534 * krb5_rd_req_in_ctx_free() is called. 535 * 536 * @return Kerberos 5 error code, see krb5_get_error_message(). 537 * 538 * @ingroup krb5_auth 539 */ 540 541KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 542krb5_rd_req_in_set_keytab(krb5_context context, 543 krb5_rd_req_in_ctx in, 544 krb5_keytab keytab) 545{ 546 in->keytab = keytab; 547 return 0; 548} 549 550/** 551 * Set if krb5_rq_red() is going to check the Windows PAC or not 552 * 553 * @param context Keberos 5 context. 554 * @param in krb5_rd_req_in_ctx to check the option on. 555 * @param flag flag to select if to check the pac (TRUE) or not (FALSE). 556 * 557 * @return Kerberos 5 error code, see krb5_get_error_message(). 558 * 559 * @ingroup krb5_auth 560 */ 561 562KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 563krb5_rd_req_in_set_pac_check(krb5_context context, 564 krb5_rd_req_in_ctx in, 565 krb5_boolean flag) 566{ 567 in->check_pac = flag; 568 return 0; 569} 570 571 572KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 573krb5_rd_req_in_set_keyblock(krb5_context context, 574 krb5_rd_req_in_ctx in, 575 krb5_keyblock *keyblock) 576{ 577 in->keyblock = keyblock; /* XXX should make copy */ 578 return 0; 579} 580 581KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 582krb5_rd_req_out_get_ap_req_options(krb5_context context, 583 krb5_rd_req_out_ctx out, 584 krb5_flags *ap_req_options) 585{ 586 *ap_req_options = out->ap_req_options; 587 return 0; 588} 589 590KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 591krb5_rd_req_out_get_ticket(krb5_context context, 592 krb5_rd_req_out_ctx out, 593 krb5_ticket **ticket) 594{ 595 return krb5_copy_ticket(context, out->ticket, ticket); 596} 597 598KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 599krb5_rd_req_out_get_keyblock(krb5_context context, 600 krb5_rd_req_out_ctx out, 601 krb5_keyblock **keyblock) 602{ 603 return krb5_copy_keyblock(context, out->keyblock, keyblock); 604} 605 606int KRB5_LIB_FUNCTION 607krb5_rd_req_out_get_flags(krb5_context context, krb5_rd_req_out_ctx ctx) 608{ 609 return ctx->flags; 610} 611 612 613/** 614 * Get the principal that was used in the request from the 615 * client. Might not match whats in the ticket if krb5_rd_req_ctx() 616 * searched in the keytab for a matching key. 617 * 618 * @param context a Kerberos 5 context. 619 * @param out a krb5_rd_req_out_ctx from krb5_rd_req_ctx(). 620 * @param principal return principal, free with krb5_free_principal(). 621 * 622 * @ingroup krb5_auth 623 */ 624 625KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 626krb5_rd_req_out_get_server(krb5_context context, 627 krb5_rd_req_out_ctx out, 628 krb5_principal *principal) 629{ 630 return krb5_copy_principal(context, out->server, principal); 631} 632 633KRB5_LIB_FUNCTION void KRB5_LIB_CALL 634krb5_rd_req_in_ctx_free(krb5_context context, krb5_rd_req_in_ctx ctx) 635{ 636 free(ctx); 637} 638 639/** 640 * Free the krb5_rd_req_out_ctx. 641 * 642 * @param context Keberos 5 context. 643 * @param ctx krb5_rd_req_out_ctx context to free. 644 * 645 * @ingroup krb5_auth 646 */ 647 648KRB5_LIB_FUNCTION void KRB5_LIB_CALL 649krb5_rd_req_out_ctx_free(krb5_context context, krb5_rd_req_out_ctx ctx) 650{ 651 if (ctx->ticket) 652 krb5_free_ticket(context, ctx->ticket); 653 if (ctx->keyblock) 654 krb5_free_keyblock(context, ctx->keyblock); 655 if (ctx->server) 656 krb5_free_principal(context, ctx->server); 657 free(ctx); 658} 659 660/* 661 * 662 */ 663 664KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 665krb5_rd_req(krb5_context context, 666 krb5_auth_context *auth_context, 667 const krb5_data *inbuf, 668 krb5_const_principal server, 669 krb5_keytab keytab, 670 krb5_flags *ap_req_options, 671 krb5_ticket **ticket) 672{ 673 krb5_error_code ret; 674 krb5_rd_req_in_ctx in; 675 krb5_rd_req_out_ctx out; 676 677 ret = krb5_rd_req_in_ctx_alloc(context, &in); 678 if (ret) 679 return ret; 680 681 ret = krb5_rd_req_in_set_keytab(context, in, keytab); 682 if (ret) { 683 krb5_rd_req_in_ctx_free(context, in); 684 return ret; 685 } 686 687 ret = krb5_rd_req_ctx(context, auth_context, inbuf, server, in, &out); 688 krb5_rd_req_in_ctx_free(context, in); 689 if (ret) 690 return ret; 691 692 if (ap_req_options) 693 *ap_req_options = out->ap_req_options; 694 if (ticket) { 695 ret = krb5_copy_ticket(context, out->ticket, ticket); 696 if (ret) 697 goto out; 698 } 699 700 out: 701 krb5_rd_req_out_ctx_free(context, out); 702 return ret; 703} 704 705/* 706 * 707 */ 708 709KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 710krb5_rd_req_with_keyblock(krb5_context context, 711 krb5_auth_context *auth_context, 712 const krb5_data *inbuf, 713 krb5_const_principal server, 714 krb5_keyblock *keyblock, 715 krb5_flags *ap_req_options, 716 krb5_ticket **ticket) 717{ 718 krb5_error_code ret; 719 krb5_rd_req_in_ctx in; 720 krb5_rd_req_out_ctx out; 721 722 ret = krb5_rd_req_in_ctx_alloc(context, &in); 723 if (ret) 724 return ret; 725 726 ret = krb5_rd_req_in_set_keyblock(context, in, keyblock); 727 if (ret) { 728 krb5_rd_req_in_ctx_free(context, in); 729 return ret; 730 } 731 732 ret = krb5_rd_req_ctx(context, auth_context, inbuf, server, in, &out); 733 krb5_rd_req_in_ctx_free(context, in); 734 if (ret) 735 return ret; 736 737 if (ap_req_options) 738 *ap_req_options = out->ap_req_options; 739 if (ticket) { 740 ret = krb5_copy_ticket(context, out->ticket, ticket); 741 if (ret) 742 goto out; 743 } 744 745 out: 746 krb5_rd_req_out_ctx_free(context, out); 747 return ret; 748} 749 750/* 751 * 752 */ 753 754static krb5_error_code 755get_key_from_keytab(krb5_context context, 756 krb5_ap_req *ap_req, 757 krb5_const_principal server, 758 krb5_keytab keytab, 759 krb5_keyblock **out_key) 760{ 761 krb5_keytab_entry entry; 762 krb5_error_code ret; 763 int kvno; 764 krb5_keytab real_keytab; 765 766 if(keytab == NULL) 767 krb5_kt_default(context, &real_keytab); 768 else 769 real_keytab = keytab; 770 771 if (ap_req->ticket.enc_part.kvno) 772 kvno = *ap_req->ticket.enc_part.kvno; 773 else 774 kvno = 0; 775 776 ret = krb5_kt_get_entry(context, 777 real_keytab, 778 server, 779 kvno, 780 ap_req->ticket.enc_part.etype, 781 &entry); 782 if(ret) 783 goto out; 784 ret = krb5_copy_keyblock(context, &entry.keyblock, out_key); 785 krb5_kt_free_entry(context, &entry); 786 out: 787 if(keytab == NULL) 788 krb5_kt_close(context, real_keytab); 789 790 return ret; 791} 792 793/** 794 * The core server function that verify application authentication 795 * requests from clients. 796 * 797 * @param context Keberos 5 context. 798 * @param auth_context the authentication context, can be NULL, then 799 * default values for the authentication context will used. 800 * @param inbuf the (AP-REQ) authentication buffer 801 * 802 * @param server the server with authenticate as, if NULL the function 803 * will try to find any available credential in the keytab 804 * that will verify the reply. The function will prefer the 805 * server the server client specified in the AP-REQ, but if 806 * there is no mach, it will try all keytab entries for a 807 * match. This have serious performance issues for larger keytabs. 808 * 809 * @param inctx control the behavior of the function, if NULL, the 810 * default behavior is used. 811 * @param outctx the return outctx, free with krb5_rd_req_out_ctx_free(). 812 * @return Kerberos 5 error code, see krb5_get_error_message(). 813 * 814 * @ingroup krb5_auth 815 */ 816 817KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 818krb5_rd_req_ctx(krb5_context context, 819 krb5_auth_context *auth_context, 820 const krb5_data *inbuf, 821 krb5_const_principal server, 822 krb5_rd_req_in_ctx inctx, 823 krb5_rd_req_out_ctx *outctx) 824{ 825 krb5_error_code ret; 826 krb5_ap_req ap_req; 827 krb5_rd_req_out_ctx o = NULL; 828 krb5_keytab id = NULL, keytab = NULL; 829 krb5_principal service = NULL; 830 831 *outctx = NULL; 832 833 o = calloc(1, sizeof(*o)); 834 if (o == NULL) { 835 krb5_set_error_message(context, ENOMEM, 836 N_("malloc: out of memory", "")); 837 return ENOMEM; 838 } 839 840 if (*auth_context == NULL) { 841 ret = krb5_auth_con_init(context, auth_context); 842 if (ret) 843 goto out; 844 } 845 846 ret = krb5_decode_ap_req(context, inbuf, &ap_req); 847 if(ret) 848 goto out; 849 850 /* Save that principal that was in the request */ 851 ret = _krb5_principalname2krb5_principal(context, 852 &o->server, 853 ap_req.ticket.sname, 854 ap_req.ticket.realm); 855 if (ret) 856 goto out; 857 858 if (ap_req.ap_options.use_session_key && 859 (*auth_context)->keyblock == NULL) { 860 ret = KRB5KRB_AP_ERR_NOKEY; 861 krb5_set_error_message(context, ret, 862 N_("krb5_rd_req: user to user auth " 863 "without session key given", "")); 864 goto out; 865 } 866 867 if (inctx && inctx->keytab) 868 id = inctx->keytab; 869 870 if((*auth_context)->keyblock){ 871 ret = krb5_copy_keyblock(context, 872 (*auth_context)->keyblock, 873 &o->keyblock); 874 if (ret) 875 goto out; 876 } else if(inctx && inctx->keyblock){ 877 ret = krb5_copy_keyblock(context, 878 inctx->keyblock, 879 &o->keyblock); 880 if (ret) 881 goto out; 882 } else { 883 884 if(id == NULL) { 885 krb5_kt_default(context, &keytab); 886 id = keytab; 887 } 888 if (id == NULL) 889 goto out; 890 891 if (server == NULL) { 892 ret = _krb5_principalname2krb5_principal(context, 893 &service, 894 ap_req.ticket.sname, 895 ap_req.ticket.realm); 896 if (ret) 897 goto out; 898 server = service; 899 } 900 901 ret = get_key_from_keytab(context, 902 &ap_req, 903 server, 904 id, 905 &o->keyblock); 906 if (ret) { 907 /* If caller specified a server, fail. */ 908 if (service == NULL && (context->flags & KRB5_CTX_F_RD_REQ_IGNORE) == 0) 909 goto out; 910 /* Otherwise, fall back to iterating over the keytab. This 911 * have serious performace issues for larger keytab. 912 */ 913 o->keyblock = NULL; 914 } 915 } 916 917 if (o->keyblock) { 918 /* 919 * We got an exact keymatch, use that. 920 */ 921 922 ret = krb5_verify_ap_req2(context, 923 auth_context, 924 &ap_req, 925 server, 926 o->keyblock, 927 0, 928 &o->ap_req_options, 929 &o->ticket, 930 KRB5_KU_AP_REQ_AUTH); 931 932 if (ret) 933 goto out; 934 935 } else { 936 /* 937 * Interate over keytab to find a key that can decrypt the request. 938 */ 939 940 krb5_keytab_entry entry; 941 krb5_kt_cursor cursor; 942 int done = 0, kvno = 0; 943 944 memset(&cursor, 0, sizeof(cursor)); 945 946 if (ap_req.ticket.enc_part.kvno) 947 kvno = *ap_req.ticket.enc_part.kvno; 948 949 ret = krb5_kt_start_seq_get(context, id, &cursor); 950 if (ret) 951 goto out; 952 953 done = 0; 954 while (!done) { 955 krb5_principal p; 956 957 ret = krb5_kt_next_entry(context, id, &entry, &cursor); 958 if (ret) { 959 _krb5_kt_principal_not_found(context, ret, id, o->server, 960 ap_req.ticket.enc_part.etype, 961 kvno); 962 krb5_kt_end_seq_get(context, id, &cursor); 963 goto out; 964 } 965 966 if (entry.keyblock.keytype != ap_req.ticket.enc_part.etype) { 967 krb5_kt_free_entry (context, &entry); 968 continue; 969 } 970 971 ret = krb5_verify_ap_req2(context, 972 auth_context, 973 &ap_req, 974 server, 975 &entry.keyblock, 976 0, 977 &o->ap_req_options, 978 &o->ticket, 979 KRB5_KU_AP_REQ_AUTH); 980 if (ret) { 981 krb5_kt_free_entry(context, &entry); 982 continue; 983 } 984 985 /* 986 * Found a match, save the keyblock for PAC processing, 987 * and update the service principal in the ticket to match 988 * whatever is in the keytab. 989 */ 990 991 ret = krb5_copy_keyblock(context, 992 &entry.keyblock, 993 &o->keyblock); 994 if (ret) { 995 krb5_kt_free_entry(context, &entry); 996 krb5_kt_end_seq_get(context, id, &cursor); 997 goto out; 998 } 999 1000 ret = krb5_copy_principal(context, entry.principal, &p); 1001 if (ret) { 1002 krb5_kt_free_entry(context, &entry); 1003 krb5_kt_end_seq_get(context, id, &cursor); 1004 goto out; 1005 } 1006 krb5_free_principal(context, o->ticket->server); 1007 o->ticket->server = p; 1008 1009 krb5_kt_free_entry(context, &entry); 1010 1011 done = 1; 1012 } 1013 krb5_kt_end_seq_get(context, id, &cursor); 1014 } 1015 1016 /* If there is a PAC, verify its server signature */ 1017 if (inctx == NULL || inctx->check_pac) { 1018 krb5_pac pac; 1019 krb5_data data; 1020 1021 ret = krb5_ticket_get_authorization_data_type(context, 1022 o->ticket, 1023 KRB5_AUTHDATA_WIN2K_PAC, 1024 &data); 1025 if (ret == 0) { 1026 ret = krb5_pac_parse(context, data.data, data.length, &pac); 1027 krb5_data_free(&data); 1028 if (ret) 1029 goto out; 1030 1031 ret = krb5_pac_verify(context, 1032 pac, 1033 o->ticket->ticket.authtime, 1034 o->ticket->client, 1035 o->keyblock, 1036 NULL); 1037 krb5_pac_free(context, pac); 1038 if (ret == 0) 1039 o->flags |= KRB5_RD_REQ_OUT_PAC_VALID; 1040 ret = 0; 1041 } else 1042 ret = 0; 1043 } 1044 out: 1045 1046 if (ret || outctx == NULL) { 1047 krb5_rd_req_out_ctx_free(context, o); 1048 } else 1049 *outctx = o; 1050 1051 free_AP_REQ(&ap_req); 1052 1053 if (service) 1054 krb5_free_principal(context, service); 1055 1056 if (keytab) 1057 krb5_kt_close(context, keytab); 1058 1059 return ret; 1060} 1061