1/* $NetBSD: get_cred.c,v 1.4 2023/06/19 21:41:44 christos Exp $ */ 2 3/* 4 * Copyright (c) 1997 - 2008 Kungliga Tekniska H��gskolan 5 * (Royal Institute of Technology, Stockholm, Sweden). 6 * All rights reserved. 7 * 8 * Portions Copyright (c) 2009 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 "krb5_locl.h" 39#include <assert.h> 40 41static krb5_error_code 42get_cred_kdc_capath(krb5_context, krb5_kdc_flags, 43 krb5_ccache, krb5_creds *, krb5_principal, 44 Ticket *, krb5_creds **, krb5_creds ***); 45 46/* 47 * Take the `body' and encode it into `padata' using the credentials 48 * in `creds'. 49 */ 50 51static krb5_error_code 52make_pa_tgs_req(krb5_context context, 53 krb5_auth_context ac, 54 KDC_REQ_BODY *body, 55 PA_DATA *padata, 56 krb5_creds *creds) 57{ 58 u_char *buf; 59 size_t buf_size; 60 size_t len = 0; 61 krb5_data in_data; 62 krb5_error_code ret; 63 64 ASN1_MALLOC_ENCODE(KDC_REQ_BODY, buf, buf_size, body, &len, ret); 65 if (ret) 66 goto out; 67 if(buf_size != len) 68 krb5_abortx(context, "internal error in ASN.1 encoder"); 69 70 in_data.length = len; 71 in_data.data = buf; 72 ret = _krb5_mk_req_internal(context, &ac, 0, &in_data, creds, 73 &padata->padata_value, 74 KRB5_KU_TGS_REQ_AUTH_CKSUM, 75 KRB5_KU_TGS_REQ_AUTH); 76 out: 77 free (buf); 78 if(ret) 79 return ret; 80 padata->padata_type = KRB5_PADATA_TGS_REQ; 81 return 0; 82} 83 84/* 85 * Set the `enc-authorization-data' in `req_body' based on `authdata' 86 */ 87 88static krb5_error_code 89set_auth_data (krb5_context context, 90 KDC_REQ_BODY *req_body, 91 krb5_authdata *authdata, 92 krb5_keyblock *subkey) 93{ 94 if(authdata->len) { 95 size_t len = 0, buf_size; 96 unsigned char *buf; 97 krb5_crypto crypto; 98 krb5_error_code ret; 99 100 ASN1_MALLOC_ENCODE(AuthorizationData, buf, buf_size, authdata, 101 &len, ret); 102 if (ret) 103 return ret; 104 if (buf_size != len) 105 krb5_abortx(context, "internal error in ASN.1 encoder"); 106 107 ALLOC(req_body->enc_authorization_data, 1); 108 if (req_body->enc_authorization_data == NULL) { 109 free (buf); 110 return krb5_enomem(context); 111 } 112 ret = krb5_crypto_init(context, subkey, 0, &crypto); 113 if (ret) { 114 free (buf); 115 free (req_body->enc_authorization_data); 116 req_body->enc_authorization_data = NULL; 117 return ret; 118 } 119 krb5_encrypt_EncryptedData(context, 120 crypto, 121 KRB5_KU_TGS_REQ_AUTH_DAT_SUBKEY, 122 buf, 123 len, 124 0, 125 req_body->enc_authorization_data); 126 free (buf); 127 krb5_crypto_destroy(context, crypto); 128 } else { 129 req_body->enc_authorization_data = NULL; 130 } 131 return 0; 132} 133 134/* 135 * Create a tgs-req in `t' with `addresses', `flags', `second_ticket' 136 * (if not-NULL), `in_creds', `krbtgt', and returning the generated 137 * subkey in `subkey'. 138 */ 139 140static krb5_error_code 141init_tgs_req (krb5_context context, 142 krb5_ccache ccache, 143 krb5_addresses *addresses, 144 krb5_kdc_flags flags, 145 Ticket *second_ticket, 146 krb5_creds *in_creds, 147 krb5_creds *krbtgt, 148 unsigned nonce, 149 const METHOD_DATA *padata, 150 krb5_keyblock **subkey, 151 TGS_REQ *t) 152{ 153 krb5_auth_context ac = NULL; 154 krb5_error_code ret = 0; 155 156 memset(t, 0, sizeof(*t)); 157 t->pvno = 5; 158 t->msg_type = krb_tgs_req; 159 if (in_creds->session.keytype) { 160 ALLOC_SEQ(&t->req_body.etype, 1); 161 if(t->req_body.etype.val == NULL) { 162 ret = krb5_enomem(context); 163 goto fail; 164 } 165 t->req_body.etype.val[0] = in_creds->session.keytype; 166 } else { 167 ret = _krb5_init_etype(context, 168 KRB5_PDU_TGS_REQUEST, 169 &t->req_body.etype.len, 170 &t->req_body.etype.val, 171 NULL); 172 } 173 if (ret) 174 goto fail; 175 t->req_body.addresses = addresses; 176 t->req_body.kdc_options = flags.b; 177 t->req_body.kdc_options.forwardable = krbtgt->flags.b.forwardable; 178 t->req_body.kdc_options.renewable = krbtgt->flags.b.renewable; 179 t->req_body.kdc_options.proxiable = krbtgt->flags.b.proxiable; 180 ret = copy_Realm(&in_creds->server->realm, &t->req_body.realm); 181 if (ret) 182 goto fail; 183 ALLOC(t->req_body.sname, 1); 184 if (t->req_body.sname == NULL) { 185 ret = krb5_enomem(context); 186 goto fail; 187 } 188 189 /* some versions of some code might require that the client be 190 present in TGS-REQs, but this is clearly against the spec */ 191 192 ret = copy_PrincipalName(&in_creds->server->name, t->req_body.sname); 193 if (ret) 194 goto fail; 195 196 if (krbtgt->times.starttime) { 197 ALLOC(t->req_body.from, 1); 198 if(t->req_body.from == NULL){ 199 ret = krb5_enomem(context); 200 goto fail; 201 } 202 *t->req_body.from = in_creds->times.starttime; 203 } 204 205 /* req_body.till should be NULL if there is no endtime specified, 206 but old MIT code (like DCE secd) doesn't like that */ 207 ALLOC(t->req_body.till, 1); 208 if(t->req_body.till == NULL){ 209 ret = krb5_enomem(context); 210 goto fail; 211 } 212 *t->req_body.till = in_creds->times.endtime; 213 214 if (t->req_body.kdc_options.renewable && krbtgt->times.renew_till) { 215 ALLOC(t->req_body.rtime, 1); 216 if(t->req_body.rtime == NULL){ 217 ret = krb5_enomem(context); 218 goto fail; 219 } 220 *t->req_body.rtime = in_creds->times.renew_till; 221 } 222 223 t->req_body.nonce = nonce; 224 if(second_ticket){ 225 ALLOC(t->req_body.additional_tickets, 1); 226 if (t->req_body.additional_tickets == NULL) { 227 ret = krb5_enomem(context); 228 goto fail; 229 } 230 ALLOC_SEQ(t->req_body.additional_tickets, 1); 231 if (t->req_body.additional_tickets->val == NULL) { 232 ret = krb5_enomem(context); 233 goto fail; 234 } 235 ret = copy_Ticket(second_ticket, t->req_body.additional_tickets->val); 236 if (ret) 237 goto fail; 238 } 239 ALLOC(t->padata, 1); 240 if (t->padata == NULL) { 241 ret = krb5_enomem(context); 242 goto fail; 243 } 244 ALLOC_SEQ(t->padata, 1 + padata->len); 245 if (t->padata->val == NULL) { 246 ret = krb5_enomem(context); 247 goto fail; 248 } 249 { 250 size_t i; 251 for (i = 0; i < padata->len; i++) { 252 ret = copy_PA_DATA(&padata->val[i], &t->padata->val[i + 1]); 253 if (ret) { 254 krb5_set_error_message(context, ret, 255 N_("malloc: out of memory", "")); 256 goto fail; 257 } 258 } 259 } 260 261 ret = krb5_auth_con_init(context, &ac); 262 if(ret) 263 goto fail; 264 265 ret = krb5_auth_con_generatelocalsubkey(context, ac, &krbtgt->session); 266 if (ret) 267 goto fail; 268 269 ret = set_auth_data (context, &t->req_body, &in_creds->authdata, 270 ac->local_subkey); 271 if (ret) 272 goto fail; 273 274 ret = make_pa_tgs_req(context, 275 ac, 276 &t->req_body, 277 &t->padata->val[0], 278 krbtgt); 279 if(ret) 280 goto fail; 281 282 ret = krb5_auth_con_getlocalsubkey(context, ac, subkey); 283 if (ret) 284 goto fail; 285 286fail: 287 if (ac) 288 krb5_auth_con_free(context, ac); 289 if (ret) { 290 t->req_body.addresses = NULL; 291 free_TGS_REQ (t); 292 } 293 return ret; 294} 295 296KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 297_krb5_get_krbtgt(krb5_context context, 298 krb5_ccache id, 299 krb5_realm realm, 300 krb5_creds **cred) 301{ 302 krb5_error_code ret; 303 krb5_creds tmp_cred; 304 305 memset(&tmp_cred, 0, sizeof(tmp_cred)); 306 307 ret = krb5_cc_get_principal(context, id, &tmp_cred.client); 308 if (ret) 309 return ret; 310 311 ret = krb5_make_principal(context, 312 &tmp_cred.server, 313 realm, 314 KRB5_TGS_NAME, 315 realm, 316 NULL); 317 if(ret) { 318 krb5_free_principal(context, tmp_cred.client); 319 return ret; 320 } 321 /* 322 * The forwardable TGT might not be the start TGT, in which case, it is 323 * generally, but not always already cached. Just in case, get it again if 324 * lost. 325 */ 326 ret = krb5_get_credentials(context, 327 0, 328 id, 329 &tmp_cred, 330 cred); 331 krb5_free_principal(context, tmp_cred.client); 332 krb5_free_principal(context, tmp_cred.server); 333 if(ret) 334 return ret; 335 return 0; 336} 337 338/* DCE compatible decrypt proc */ 339static krb5_error_code KRB5_CALLCONV 340decrypt_tkt_with_subkey (krb5_context context, 341 krb5_keyblock *key, 342 krb5_key_usage usage, 343 krb5_const_pointer skey, 344 krb5_kdc_rep *dec_rep) 345{ 346 const krb5_keyblock *subkey = skey; 347 krb5_error_code ret = 0; 348 krb5_data data; 349 size_t size; 350 krb5_crypto crypto; 351 352 assert(usage == 0); 353 354 krb5_data_zero(&data); 355 356 /* 357 * start out with trying with subkey if we have one 358 */ 359 if (subkey) { 360 ret = krb5_crypto_init(context, subkey, 0, &crypto); 361 if (ret) 362 return ret; 363 ret = krb5_decrypt_EncryptedData (context, 364 crypto, 365 KRB5_KU_TGS_REP_ENC_PART_SUB_KEY, 366 &dec_rep->kdc_rep.enc_part, 367 &data); 368 /* 369 * If the is Windows 2000 DC, we need to retry with key usage 370 * 8 when doing ARCFOUR. 371 */ 372 if (ret && subkey->keytype == ETYPE_ARCFOUR_HMAC_MD5) { 373 ret = krb5_decrypt_EncryptedData(context, 374 crypto, 375 8, 376 &dec_rep->kdc_rep.enc_part, 377 &data); 378 } 379 krb5_crypto_destroy(context, crypto); 380 } 381 if (subkey == NULL || ret) { 382 ret = krb5_crypto_init(context, key, 0, &crypto); 383 if (ret) 384 return ret; 385 ret = krb5_decrypt_EncryptedData (context, 386 crypto, 387 KRB5_KU_TGS_REP_ENC_PART_SESSION, 388 &dec_rep->kdc_rep.enc_part, 389 &data); 390 krb5_crypto_destroy(context, crypto); 391 } 392 if (ret) 393 return ret; 394 395 ret = decode_EncASRepPart(data.data, 396 data.length, 397 &dec_rep->enc_part, 398 &size); 399 if (ret) 400 ret = decode_EncTGSRepPart(data.data, 401 data.length, 402 &dec_rep->enc_part, 403 &size); 404 if (ret) 405 krb5_set_error_message(context, ret, 406 N_("Failed to decode encpart in ticket", "")); 407 krb5_data_free (&data); 408 return ret; 409} 410 411static krb5_error_code 412get_cred_kdc(krb5_context context, 413 krb5_ccache id, 414 krb5_kdc_flags flags, 415 krb5_addresses *addresses, 416 krb5_creds *in_creds, 417 krb5_creds *krbtgt, 418 krb5_principal impersonate_principal, 419 Ticket *second_ticket, 420 krb5_creds *out_creds) 421{ 422 TGS_REQ req; 423 krb5_data enc; 424 krb5_data resp; 425 krb5_kdc_rep rep = {0}; 426 KRB_ERROR error; 427 krb5_error_code ret; 428 unsigned nonce; 429 krb5_keyblock *subkey = NULL; 430 size_t len = 0; 431 Ticket second_ticket_data; 432 METHOD_DATA padata; 433 434 krb5_data_zero(&resp); 435 krb5_data_zero(&enc); 436 padata.val = NULL; 437 padata.len = 0; 438 439 krb5_generate_random_block(&nonce, sizeof(nonce)); 440 nonce &= 0xffffffff; 441 442 if(flags.b.enc_tkt_in_skey && second_ticket == NULL){ 443 ret = decode_Ticket(in_creds->second_ticket.data, 444 in_creds->second_ticket.length, 445 &second_ticket_data, &len); 446 if(ret) 447 return ret; 448 second_ticket = &second_ticket_data; 449 } 450 451 452 if (impersonate_principal) { 453 krb5_crypto crypto; 454 PA_S4U2Self self; 455 krb5_data data; 456 void *buf; 457 size_t size = 0; 458 459 self.name = impersonate_principal->name; 460 self.realm = impersonate_principal->realm; 461 self.auth = estrdup("Kerberos"); 462 463 ret = _krb5_s4u2self_to_checksumdata(context, &self, &data); 464 if (ret) { 465 free(self.auth); 466 goto out; 467 } 468 469 ret = krb5_crypto_init(context, &krbtgt->session, 0, &crypto); 470 if (ret) { 471 free(self.auth); 472 krb5_data_free(&data); 473 goto out; 474 } 475 476 ret = krb5_create_checksum(context, 477 crypto, 478 KRB5_KU_OTHER_CKSUM, 479 0, 480 data.data, 481 data.length, 482 &self.cksum); 483 krb5_crypto_destroy(context, crypto); 484 krb5_data_free(&data); 485 if (ret) { 486 free(self.auth); 487 goto out; 488 } 489 490 ASN1_MALLOC_ENCODE(PA_S4U2Self, buf, len, &self, &size, ret); 491 free(self.auth); 492 free_Checksum(&self.cksum); 493 if (ret) 494 goto out; 495 if (len != size) 496 krb5_abortx(context, "internal asn1 error"); 497 498 ret = krb5_padata_add(context, &padata, KRB5_PADATA_FOR_USER, buf, len); 499 if (ret) 500 goto out; 501 } 502 503 ret = init_tgs_req (context, 504 id, 505 addresses, 506 flags, 507 second_ticket, 508 in_creds, 509 krbtgt, 510 nonce, 511 &padata, 512 &subkey, 513 &req); 514 if (ret) 515 goto out; 516 517 ASN1_MALLOC_ENCODE(TGS_REQ, enc.data, enc.length, &req, &len, ret); 518 if (ret) 519 goto out; 520 if(enc.length != len) 521 krb5_abortx(context, "internal error in ASN.1 encoder"); 522 523 /* don't free addresses */ 524 req.req_body.addresses = NULL; 525 free_TGS_REQ(&req); 526 527 /* 528 * Send and receive 529 */ 530 { 531 krb5_sendto_ctx stctx; 532 ret = krb5_sendto_ctx_alloc(context, &stctx); 533 if (ret) 534 return ret; 535 krb5_sendto_ctx_set_func(stctx, _krb5_kdc_retry, NULL); 536 537 ret = krb5_sendto_context (context, stctx, &enc, 538 krbtgt->server->name.name_string.val[1], 539 &resp); 540 krb5_sendto_ctx_free(context, stctx); 541 } 542 if(ret) 543 goto out; 544 545 if(decode_TGS_REP(resp.data, resp.length, &rep.kdc_rep, &len) == 0) { 546 unsigned eflags = 0; 547 548 ret = krb5_copy_principal(context, 549 in_creds->client, 550 &out_creds->client); 551 if(ret) 552 goto out2; 553 ret = krb5_copy_principal(context, 554 in_creds->server, 555 &out_creds->server); 556 if(ret) 557 goto out2; 558 /* this should go someplace else */ 559 out_creds->times.endtime = in_creds->times.endtime; 560 561 /* XXX should do better testing */ 562 if (flags.b.cname_in_addl_tkt || impersonate_principal) 563 eflags |= EXTRACT_TICKET_ALLOW_CNAME_MISMATCH; 564 if (flags.b.request_anonymous) 565 eflags |= EXTRACT_TICKET_MATCH_ANON; 566 567 ret = _krb5_extract_ticket(context, 568 &rep, 569 out_creds, 570 &krbtgt->session, 571 NULL, 572 0, 573 &krbtgt->addresses, 574 nonce, 575 eflags, 576 NULL, 577 decrypt_tkt_with_subkey, 578 subkey); 579 out2: 580 krb5_free_kdc_rep(context, &rep); 581 } else if(krb5_rd_error(context, &resp, &error) == 0) { 582 ret = krb5_error_from_rd_error(context, &error, in_creds); 583 krb5_free_error_contents(context, &error); 584 } else if(resp.length > 0 && ((char*)resp.data)[0] == 4) { 585 ret = KRB5KRB_AP_ERR_V4_REPLY; 586 krb5_clear_error_message(context); 587 } else { 588 ret = KRB5KRB_AP_ERR_MSG_TYPE; 589 krb5_clear_error_message(context); 590 } 591 592out: 593 if (second_ticket == &second_ticket_data) 594 free_Ticket(&second_ticket_data); 595 free_METHOD_DATA(&padata); 596 krb5_data_free(&resp); 597 krb5_data_free(&enc); 598 if(subkey) 599 krb5_free_keyblock(context, subkey); 600 return ret; 601 602} 603 604/* 605 * same as above, just get local addresses first if the krbtgt have 606 * them and the realm is not addressless 607 */ 608 609static krb5_error_code 610get_cred_kdc_address(krb5_context context, 611 krb5_ccache id, 612 krb5_kdc_flags flags, 613 krb5_addresses *addrs, 614 krb5_creds *in_creds, 615 krb5_creds *krbtgt, 616 krb5_principal impersonate_principal, 617 Ticket *second_ticket, 618 krb5_creds *out_creds) 619{ 620 krb5_error_code ret; 621 krb5_addresses addresses = { 0, NULL }; 622 623 /* 624 * Inherit the address-ness of the krbtgt if the address is not 625 * specified. 626 */ 627 628 if (addrs == NULL && krbtgt->addresses.len != 0) { 629 krb5_boolean noaddr; 630 631 krb5_appdefault_boolean(context, NULL, krbtgt->server->realm, 632 "no-addresses", FALSE, &noaddr); 633 634 if (!noaddr) { 635 krb5_get_all_client_addrs(context, &addresses); 636 /* XXX this sucks. */ 637 addrs = &addresses; 638 if(addresses.len == 0) 639 addrs = NULL; 640 } 641 } 642 ret = get_cred_kdc(context, id, flags, addrs, in_creds, 643 krbtgt, impersonate_principal, 644 second_ticket, out_creds); 645 krb5_free_addresses(context, &addresses); 646 return ret; 647} 648 649KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 650krb5_get_kdc_cred(krb5_context context, 651 krb5_ccache id, 652 krb5_kdc_flags flags, 653 krb5_addresses *addresses, 654 Ticket *second_ticket, 655 krb5_creds *in_creds, 656 krb5_creds **out_creds 657 ) 658{ 659 krb5_error_code ret; 660 krb5_creds *krbtgt; 661 662 *out_creds = calloc(1, sizeof(**out_creds)); 663 if(*out_creds == NULL) 664 return krb5_enomem(context); 665 ret = _krb5_get_krbtgt (context, 666 id, 667 in_creds->server->realm, 668 &krbtgt); 669 if(ret) { 670 free(*out_creds); 671 *out_creds = NULL; 672 return ret; 673 } 674 ret = get_cred_kdc(context, id, flags, addresses, 675 in_creds, krbtgt, NULL, NULL, *out_creds); 676 krb5_free_creds (context, krbtgt); 677 if(ret) { 678 free(*out_creds); 679 *out_creds = NULL; 680 } 681 return ret; 682} 683 684static int 685not_found(krb5_context context, krb5_const_principal p, krb5_error_code code) 686{ 687 krb5_error_code ret; 688 const char *err; 689 char *str; 690 691 err = krb5_get_error_message(context, code); 692 ret = krb5_unparse_name(context, p, &str); 693 if(ret) { 694 krb5_clear_error_message(context); 695 return code; 696 } 697 krb5_set_error_message(context, code, N_("%s (%s)", ""), err, str); 698 free(str); 699 return code; 700} 701 702static krb5_error_code 703find_cred(krb5_context context, 704 krb5_ccache id, 705 krb5_principal server, 706 krb5_creds **tgts, 707 krb5_creds *out_creds) 708{ 709 krb5_error_code ret; 710 krb5_creds mcreds; 711 712 krb5_cc_clear_mcred(&mcreds); 713 mcreds.server = server; 714 krb5_timeofday(context, &mcreds.times.endtime); 715 ret = krb5_cc_retrieve_cred(context, id, 716 KRB5_TC_DONT_MATCH_REALM | 717 KRB5_TC_MATCH_TIMES, 718 &mcreds, out_creds); 719 if(ret == 0) 720 return 0; 721 while(tgts && *tgts){ 722 if(krb5_compare_creds(context, KRB5_TC_DONT_MATCH_REALM, 723 &mcreds, *tgts)){ 724 ret = krb5_copy_creds_contents(context, *tgts, out_creds); 725 return ret; 726 } 727 tgts++; 728 } 729 return not_found(context, server, KRB5_CC_NOTFOUND); 730} 731 732static krb5_error_code 733add_cred(krb5_context context, krb5_creds const *tkt, krb5_creds ***tgts) 734{ 735 int i; 736 krb5_error_code ret; 737 krb5_creds **tmp = *tgts; 738 739 for(i = 0; tmp && tmp[i]; i++); /* XXX */ 740 tmp = realloc(tmp, (i+2)*sizeof(*tmp)); 741 if(tmp == NULL) 742 return krb5_enomem(context); 743 *tgts = tmp; 744 ret = krb5_copy_creds(context, tkt, &tmp[i]); 745 tmp[i+1] = NULL; 746 return ret; 747} 748 749static krb5_error_code 750get_cred_kdc_capath_worker(krb5_context context, 751 krb5_kdc_flags flags, 752 krb5_ccache ccache, 753 krb5_creds *in_creds, 754 krb5_const_realm try_realm, 755 krb5_principal impersonate_principal, 756 Ticket *second_ticket, 757 krb5_creds **out_creds, 758 krb5_creds ***ret_tgts) 759{ 760 krb5_error_code ret; 761 krb5_creds *tgt = NULL; 762 krb5_creds tmp_creds; 763 krb5_const_realm client_realm, server_realm; 764 int ok_as_delegate = 1; 765 766 *out_creds = calloc(1, sizeof(**out_creds)); 767 if (*out_creds == NULL) 768 return krb5_enomem(context); 769 770 memset(&tmp_creds, 0, sizeof(tmp_creds)); 771 772 client_realm = krb5_principal_get_realm(context, in_creds->client); 773 server_realm = krb5_principal_get_realm(context, in_creds->server); 774 ret = krb5_copy_principal(context, in_creds->client, &tmp_creds.client); 775 if (ret) 776 goto out; 777 778 ret = krb5_make_principal(context, 779 &tmp_creds.server, 780 try_realm, 781 KRB5_TGS_NAME, 782 server_realm, 783 NULL); 784 if (ret) 785 goto out; 786 787 { 788 krb5_creds tgts; 789 790 /* 791 * If we have krbtgt/server_realm@try_realm cached, use it and we're 792 * done. 793 */ 794 ret = find_cred(context, ccache, tmp_creds.server, 795 *ret_tgts, &tgts); 796 if (ret == 0) { 797 /* only allow implicit ok_as_delegate if the realm is the clients realm */ 798 if (strcmp(try_realm, client_realm) != 0 799 || strcmp(try_realm, server_realm) != 0) { 800 ok_as_delegate = tgts.flags.b.ok_as_delegate; 801 } 802 803 ret = get_cred_kdc_address(context, ccache, flags, NULL, 804 in_creds, &tgts, 805 impersonate_principal, 806 second_ticket, 807 *out_creds); 808 krb5_free_cred_contents(context, &tgts); 809 if (ret == 0 && 810 !krb5_principal_compare(context, in_creds->server, 811 (*out_creds)->server)) { 812 ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN; 813 } 814 if (ret == 0 && ok_as_delegate == 0) 815 (*out_creds)->flags.b.ok_as_delegate = 0; 816 817 goto out; 818 } 819 } 820 821 if (krb5_realm_compare(context, in_creds->client, in_creds->server)) { 822 ret = not_found(context, in_creds->server, KRB5_CC_NOTFOUND); 823 goto out; 824 } 825 826 /* 827 * XXX This can loop forever, plus we recurse, so we can't just keep a 828 * count here. The count would have to get passed around by reference. 829 * 830 * The KDCs check for transit loops for us, and capath data is finite, so 831 * in fact we'll fall out of this loop at some point. We should do our own 832 * transit loop checking (like get_cred_kdc_referral()), and we should 833 * impose a max number of iterations altogether. But barring malicious or 834 * broken KDCs, this is good enough. 835 */ 836 while (1) { 837 heim_general_string tgt_inst; 838 839 ret = get_cred_kdc_capath(context, flags, ccache, &tmp_creds, 840 NULL, NULL, &tgt, ret_tgts); 841 if (ret) 842 goto out; 843 844 /* 845 * if either of the chain or the ok_as_delegate was stripped 846 * by the kdc, make sure we strip it too. 847 */ 848 if (ok_as_delegate == 0 || tgt->flags.b.ok_as_delegate == 0) { 849 ok_as_delegate = 0; 850 tgt->flags.b.ok_as_delegate = 0; 851 } 852 853 ret = add_cred(context, tgt, ret_tgts); 854 if (ret) 855 goto out; 856 tgt_inst = tgt->server->name.name_string.val[1]; 857 if (strcmp(tgt_inst, server_realm) == 0) 858 break; 859 krb5_free_principal(context, tmp_creds.server); 860 tmp_creds.server = NULL; 861 ret = krb5_make_principal(context, &tmp_creds.server, 862 tgt_inst, KRB5_TGS_NAME, server_realm, NULL); 863 if (ret) 864 goto out; 865 ret = krb5_free_creds(context, tgt); 866 tgt = NULL; 867 if (ret) 868 goto out; 869 } 870 871 ret = get_cred_kdc_address(context, ccache, flags, NULL, 872 in_creds, tgt, impersonate_principal, 873 second_ticket, *out_creds); 874 if (ret == 0 && 875 !krb5_principal_compare(context, in_creds->server, 876 (*out_creds)->server)) { 877 krb5_free_cred_contents(context, *out_creds); 878 ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN; 879 } 880 if (ret == 0 && ok_as_delegate == 0) 881 (*out_creds)->flags.b.ok_as_delegate = 0; 882 883out: 884 if (ret) { 885 krb5_free_creds(context, *out_creds); 886 *out_creds = NULL; 887 } 888 if (tmp_creds.server) 889 krb5_free_principal(context, tmp_creds.server); 890 if (tmp_creds.client) 891 krb5_free_principal(context, tmp_creds.client); 892 if (tgt) 893 krb5_free_creds(context, tgt); 894 return ret; 895} 896 897/* 898get_cred(server) 899 creds = cc_get_cred(server) 900 if(creds) return creds 901 tgt = cc_get_cred(krbtgt/server_realm@any_realm) 902 if(tgt) 903 return get_cred_tgt(server, tgt) 904 if(client_realm == server_realm) 905 return NULL 906 tgt = get_cred(krbtgt/server_realm@client_realm) 907 while(tgt_inst != server_realm) 908 tgt = get_cred(krbtgt/server_realm@tgt_inst) 909 return get_cred_tgt(server, tgt) 910 */ 911 912static krb5_error_code 913get_cred_kdc_capath(krb5_context context, 914 krb5_kdc_flags flags, 915 krb5_ccache ccache, 916 krb5_creds *in_creds, 917 krb5_principal impersonate_principal, 918 Ticket *second_ticket, 919 krb5_creds **out_creds, 920 krb5_creds ***ret_tgts) 921{ 922 krb5_error_code ret; 923 krb5_const_realm client_realm, server_realm, try_realm; 924 925 client_realm = krb5_principal_get_realm(context, in_creds->client); 926 server_realm = krb5_principal_get_realm(context, in_creds->server); 927 928 try_realm = client_realm; 929 ret = get_cred_kdc_capath_worker(context, flags, ccache, in_creds, try_realm, 930 impersonate_principal, second_ticket, out_creds, 931 ret_tgts); 932 933 if (ret == KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN) { 934 try_realm = krb5_config_get_string(context, NULL, "capaths", 935 client_realm, server_realm, NULL); 936 937 if (try_realm != NULL && strcmp(try_realm, client_realm)) { 938 ret = get_cred_kdc_capath_worker(context, flags, ccache, in_creds, 939 try_realm, impersonate_principal, 940 second_ticket, out_creds, ret_tgts); 941 } 942 } 943 944 return ret; 945} 946 947/* 948 * Get a service ticket from a KDC by chasing referrals from a start realm. 949 * 950 * All referral TGTs produced in the process are thrown away when we're done. 951 * We don't store them, and we don't allow other search mechanisms (capaths) to 952 * use referral TGTs produced here. 953 */ 954static krb5_error_code 955get_cred_kdc_referral(krb5_context context, 956 krb5_kdc_flags flags, 957 krb5_ccache ccache, 958 krb5_creds *in_creds, 959 krb5_principal impersonate_principal, 960 Ticket *second_ticket, 961 krb5_creds **out_creds) 962{ 963 krb5_realm start_realm = NULL; 964 krb5_data config_start_realm; 965 krb5_error_code ret; 966 krb5_creds tgt, referral, ticket; 967 krb5_creds **referral_tgts = NULL; /* used for loop detection */ 968 int loop = 0; 969 int ok_as_delegate = 1; 970 size_t i; 971 972 if (in_creds->server->name.name_string.len < 2 && !flags.b.canonicalize) { 973 krb5_set_error_message(context, KRB5KDC_ERR_PATH_NOT_ACCEPTED, 974 N_("Name too short to do referals, skipping", "")); 975 return KRB5KDC_ERR_PATH_NOT_ACCEPTED; 976 } 977 978 memset(&tgt, 0, sizeof(tgt)); 979 memset(&ticket, 0, sizeof(ticket)); 980 981 flags.b.canonicalize = 1; 982 983 *out_creds = NULL; 984 985 986 ret = krb5_cc_get_config(context, ccache, NULL, "start_realm", &config_start_realm); 987 if (ret == 0) { 988 start_realm = strndup(config_start_realm.data, config_start_realm.length); 989 krb5_data_free(&config_start_realm); 990 } else { 991 start_realm = strdup(krb5_principal_get_realm(context, in_creds->client)); 992 } 993 if (start_realm == NULL) 994 return krb5_enomem(context); 995 996 /* find tgt for the clients base realm */ 997 { 998 krb5_principal tgtname; 999 1000 ret = krb5_make_principal(context, &tgtname, 1001 start_realm, 1002 KRB5_TGS_NAME, 1003 start_realm, 1004 NULL); 1005 if (ret) { 1006 free(start_realm); 1007 return ret; 1008 } 1009 1010 ret = find_cred(context, ccache, tgtname, NULL, &tgt); 1011 krb5_free_principal(context, tgtname); 1012 if (ret) { 1013 free(start_realm); 1014 return ret; 1015 } 1016 } 1017 1018 referral = *in_creds; 1019 ret = krb5_copy_principal(context, in_creds->server, &referral.server); 1020 if (ret) { 1021 krb5_free_cred_contents(context, &tgt); 1022 free(start_realm); 1023 return ret; 1024 } 1025 ret = krb5_principal_set_realm(context, referral.server, start_realm); 1026 free(start_realm); 1027 start_realm = NULL; 1028 if (ret) { 1029 krb5_free_cred_contents(context, &tgt); 1030 krb5_free_principal(context, referral.server); 1031 return ret; 1032 } 1033 1034 while (loop++ < 17) { 1035 krb5_creds **tickets; 1036 krb5_creds mcreds; 1037 char *referral_realm; 1038 1039 /* Use cache if we are not doing impersonation or contrained deleg */ 1040 if (impersonate_principal == NULL || flags.b.cname_in_addl_tkt) { 1041 krb5_cc_clear_mcred(&mcreds); 1042 mcreds.server = referral.server; 1043 krb5_timeofday(context, &mcreds.times.endtime); 1044 ret = krb5_cc_retrieve_cred(context, ccache, KRB5_TC_MATCH_TIMES, 1045 &mcreds, &ticket); 1046 } else 1047 ret = EINVAL; 1048 1049 if (ret) { 1050 ret = get_cred_kdc_address(context, ccache, flags, NULL, 1051 &referral, &tgt, impersonate_principal, 1052 second_ticket, &ticket); 1053 if (ret) 1054 goto out; 1055 } 1056 1057 /* Did we get the right ticket ? */ 1058 if (krb5_principal_compare_any_realm(context, 1059 referral.server, 1060 ticket.server)) 1061 break; 1062 1063 if (!krb5_principal_is_krbtgt(context, ticket.server)) { 1064 krb5_set_error_message(context, KRB5KRB_AP_ERR_NOT_US, 1065 N_("Got back an non krbtgt " 1066 "ticket referrals", "")); 1067 ret = KRB5KRB_AP_ERR_NOT_US; 1068 goto out; 1069 } 1070 1071 referral_realm = ticket.server->name.name_string.val[1]; 1072 1073 /* check that there are no referrals loops */ 1074 tickets = referral_tgts; 1075 1076 krb5_cc_clear_mcred(&mcreds); 1077 mcreds.server = ticket.server; 1078 1079 while (tickets && *tickets){ 1080 if (krb5_compare_creds(context, 1081 KRB5_TC_DONT_MATCH_REALM, 1082 &mcreds, 1083 *tickets)) { 1084 krb5_set_error_message(context, KRB5_GET_IN_TKT_LOOP, 1085 N_("Referral from %s " 1086 "loops back to realm %s", ""), 1087 tgt.server->realm, 1088 referral_realm); 1089 ret = KRB5_GET_IN_TKT_LOOP; 1090 goto out; 1091 } 1092 tickets++; 1093 } 1094 1095 /* 1096 * if either of the chain or the ok_as_delegate was stripped 1097 * by the kdc, make sure we strip it too. 1098 */ 1099 1100 if (ok_as_delegate == 0 || ticket.flags.b.ok_as_delegate == 0) { 1101 ok_as_delegate = 0; 1102 ticket.flags.b.ok_as_delegate = 0; 1103 } 1104 1105 _krb5_debug(context, 6, "get_cred_kdc_referral: got referral " 1106 "to %s from %s", referral_realm, referral.server->realm); 1107 ret = add_cred(context, &ticket, &referral_tgts); 1108 if (ret) 1109 goto out; 1110 1111 /* try realm in the referral */ 1112 ret = krb5_principal_set_realm(context, 1113 referral.server, 1114 referral_realm); 1115 krb5_free_cred_contents(context, &tgt); 1116 tgt = ticket; 1117 memset(&ticket, 0, sizeof(ticket)); 1118 if (ret) 1119 goto out; 1120 } 1121 1122 ret = krb5_copy_creds(context, &ticket, out_creds); 1123 1124out: 1125 for (i = 0; referral_tgts && referral_tgts[i]; i++) 1126 krb5_free_creds(context, referral_tgts[i]); 1127 free(referral_tgts); 1128 krb5_free_principal(context, referral.server); 1129 krb5_free_cred_contents(context, &tgt); 1130 krb5_free_cred_contents(context, &ticket); 1131 return ret; 1132} 1133 1134 1135/* 1136 * Glue function between referrals version and old client chasing 1137 * codebase. 1138 */ 1139 1140KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 1141_krb5_get_cred_kdc_any(krb5_context context, 1142 krb5_kdc_flags flags, 1143 krb5_ccache ccache, 1144 krb5_creds *in_creds, 1145 krb5_principal impersonate_principal, 1146 Ticket *second_ticket, 1147 krb5_creds **out_creds, 1148 krb5_creds ***ret_tgts) 1149{ 1150 krb5_error_code ret; 1151 krb5_deltat offset; 1152 1153 ret = krb5_cc_get_kdc_offset(context, ccache, &offset); 1154 if (ret == 0) { 1155 context->kdc_sec_offset = offset; 1156 context->kdc_usec_offset = 0; 1157 } 1158 1159 if (strcmp(in_creds->server->realm, "") != 0) { 1160 /* 1161 * Non-empty realm? Try capaths first. We might have local 1162 * policy (capaths) to honor. 1163 */ 1164 ret = get_cred_kdc_capath(context, 1165 flags, 1166 ccache, 1167 in_creds, 1168 impersonate_principal, 1169 second_ticket, 1170 out_creds, 1171 ret_tgts); 1172 if (ret == 0) 1173 return ret; 1174 } 1175 1176 /* Otherwise try referrals */ 1177 return get_cred_kdc_referral(context, 1178 flags, 1179 ccache, 1180 in_creds, 1181 impersonate_principal, 1182 second_ticket, 1183 out_creds); 1184} 1185 1186static krb5_error_code 1187check_cc(krb5_context context, krb5_flags options, krb5_ccache ccache, 1188 krb5_creds *in_creds, krb5_creds *out_creds) 1189{ 1190 krb5_error_code ret; 1191 krb5_timestamp now; 1192 krb5_creds mcreds = *in_creds; 1193 1194 krb5_timeofday(context, &now); 1195 1196 if (!(options & KRB5_GC_EXPIRED_OK) && 1197 mcreds.times.endtime < now) { 1198 mcreds.times.renew_till = 0; 1199 krb5_timeofday(context, &mcreds.times.endtime); 1200 options |= KRB5_TC_MATCH_TIMES; 1201 } 1202 1203 if (mcreds.server->name.name_type == KRB5_NT_SRV_HST_NEEDS_CANON) { 1204 /* Avoid name canonicalization in krb5_cc_retrieve_cred() */ 1205 krb5_principal_set_type(context, mcreds.server, KRB5_NT_SRV_HST); 1206 } 1207 1208 if (options & KRB5_GC_ANONYMOUS) { 1209 ret = krb5_make_principal(context, 1210 &mcreds.client, 1211 krb5_principal_get_realm(context, mcreds.client), 1212 KRB5_WELLKNOWN_NAME, 1213 KRB5_ANON_NAME, 1214 NULL); 1215 if (ret) 1216 return ret; 1217 } 1218 1219 ret = krb5_cc_retrieve_cred(context, ccache, 1220 (options & 1221 (KRB5_TC_DONT_MATCH_REALM | 1222 KRB5_TC_MATCH_KEYTYPE | 1223 KRB5_TC_MATCH_TIMES)), 1224 &mcreds, out_creds); 1225 1226 if (options & KRB5_GC_ANONYMOUS) 1227 krb5_free_principal(context, mcreds.client); 1228 1229 return ret; 1230} 1231 1232static void 1233store_cred(krb5_context context, krb5_ccache ccache, 1234 krb5_const_principal server_princ, krb5_creds *creds) 1235{ 1236 if (!krb5_principal_compare(context, creds->server, server_princ)) { 1237 krb5_principal tmp_princ = creds->server; 1238 /* 1239 * Store the cred with the pre-canon server princ first so it 1240 * can be found quickly in the future. 1241 */ 1242 creds->server = (krb5_principal)server_princ; 1243 krb5_cc_store_cred(context, ccache, creds); 1244 creds->server = tmp_princ; 1245 /* Then store again with the canonicalized server princ */ 1246 } 1247 krb5_cc_store_cred(context, ccache, creds); 1248} 1249 1250 1251KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 1252krb5_get_credentials_with_flags(krb5_context context, 1253 krb5_flags options, 1254 krb5_kdc_flags flags, 1255 krb5_ccache ccache, 1256 krb5_creds *in_creds, 1257 krb5_creds **out_creds) 1258{ 1259 krb5_error_code ret; 1260 krb5_name_canon_iterator name_canon_iter = NULL; 1261 krb5_name_canon_rule_options rule_opts; 1262 krb5_const_principal try_princ = NULL; 1263 krb5_principal save_princ = in_creds->server; 1264 krb5_creds **tgts; 1265 krb5_creds *res_creds; 1266 int i; 1267 1268 if (_krb5_have_debug(context, 5)) { 1269 char *unparsed; 1270 1271 ret = krb5_unparse_name(context, in_creds->server, &unparsed); 1272 if (ret) { 1273 _krb5_debug(context, 5, "krb5_get_creds: unable to display " 1274 "requested service principal"); 1275 } else { 1276 _krb5_debug(context, 5, "krb5_get_creds: requesting a ticket " 1277 "for %s", unparsed); 1278 free(unparsed); 1279 } 1280 } 1281 1282 if (in_creds->session.keytype) { 1283 ret = krb5_enctype_valid(context, in_creds->session.keytype); 1284 if (ret) 1285 return ret; 1286 options |= KRB5_TC_MATCH_KEYTYPE; 1287 } 1288 1289 *out_creds = NULL; 1290 res_creds = calloc(1, sizeof(*res_creds)); 1291 if (res_creds == NULL) 1292 return krb5_enomem(context); 1293 1294 ret = krb5_name_canon_iterator_start(context, in_creds->server, 1295 &name_canon_iter); 1296 if (ret) 1297 return ret; 1298 1299next_rule: 1300 krb5_free_cred_contents(context, res_creds); 1301 memset(res_creds, 0, sizeof (*res_creds)); 1302 ret = krb5_name_canon_iterate(context, &name_canon_iter, &try_princ, 1303 &rule_opts); 1304 in_creds->server = rk_UNCONST(try_princ); 1305 if (ret) 1306 goto out; 1307 1308 if (name_canon_iter == NULL) { 1309 if (options & KRB5_GC_CACHED) 1310 ret = KRB5_CC_NOTFOUND; 1311 else 1312 ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN; 1313 goto out; 1314 } 1315 1316 ret = check_cc(context, options, ccache, in_creds, res_creds); 1317 if (ret == 0) { 1318 *out_creds = res_creds; 1319 res_creds = NULL; 1320 goto out; 1321 } else if(ret != KRB5_CC_END) { 1322 goto out; 1323 } 1324 if (options & KRB5_GC_CACHED) 1325 goto next_rule; 1326 1327 if(options & KRB5_GC_USER_USER) 1328 flags.b.enc_tkt_in_skey = 1; 1329 if (flags.b.enc_tkt_in_skey) 1330 options |= KRB5_GC_NO_STORE; 1331 1332 tgts = NULL; 1333 ret = _krb5_get_cred_kdc_any(context, flags, ccache, 1334 in_creds, NULL, NULL, out_creds, &tgts); 1335 for (i = 0; tgts && tgts[i]; i++) { 1336 if ((options & KRB5_GC_NO_STORE) == 0) 1337 krb5_cc_store_cred(context, ccache, tgts[i]); 1338 krb5_free_creds(context, tgts[i]); 1339 } 1340 free(tgts); 1341 1342 /* We don't yet have TGS w/ FAST, so we can't protect KBR-ERRORs */ 1343 if (ret == KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN && 1344 !(rule_opts & KRB5_NCRO_USE_FAST)) 1345 goto next_rule; 1346 1347 if(ret == 0 && (options & KRB5_GC_NO_STORE) == 0) 1348 store_cred(context, ccache, in_creds->server, *out_creds); 1349 1350 if (ret == 0 && _krb5_have_debug(context, 5)) { 1351 char *unparsed; 1352 1353 ret = krb5_unparse_name(context, (*out_creds)->server, &unparsed); 1354 if (ret) { 1355 _krb5_debug(context, 5, "krb5_get_creds: unable to display " 1356 "service principal"); 1357 } else { 1358 _krb5_debug(context, 5, "krb5_get_creds: got a ticket for %s", 1359 unparsed); 1360 free(unparsed); 1361 } 1362 } 1363 1364out: 1365 in_creds->server = save_princ; 1366 krb5_free_creds(context, res_creds); 1367 krb5_free_name_canon_iterator(context, name_canon_iter); 1368 if (ret) 1369 return not_found(context, in_creds->server, ret); 1370 return 0; 1371} 1372 1373KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 1374krb5_get_credentials(krb5_context context, 1375 krb5_flags options, 1376 krb5_ccache ccache, 1377 krb5_creds *in_creds, 1378 krb5_creds **out_creds) 1379{ 1380 krb5_kdc_flags flags; 1381 flags.i = 0; 1382 return krb5_get_credentials_with_flags(context, options, flags, 1383 ccache, in_creds, out_creds); 1384} 1385 1386struct krb5_get_creds_opt_data { 1387 krb5_principal self; 1388 krb5_flags options; 1389 krb5_enctype enctype; 1390 Ticket *ticket; 1391}; 1392 1393 1394KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 1395krb5_get_creds_opt_alloc(krb5_context context, krb5_get_creds_opt *opt) 1396{ 1397 *opt = calloc(1, sizeof(**opt)); 1398 if (*opt == NULL) 1399 return krb5_enomem(context); 1400 return 0; 1401} 1402 1403KRB5_LIB_FUNCTION void KRB5_LIB_CALL 1404krb5_get_creds_opt_free(krb5_context context, krb5_get_creds_opt opt) 1405{ 1406 if (opt->self) 1407 krb5_free_principal(context, opt->self); 1408 if (opt->ticket) { 1409 free_Ticket(opt->ticket); 1410 free(opt->ticket); 1411 } 1412 memset(opt, 0, sizeof(*opt)); 1413 free(opt); 1414} 1415 1416KRB5_LIB_FUNCTION void KRB5_LIB_CALL 1417krb5_get_creds_opt_set_options(krb5_context context, 1418 krb5_get_creds_opt opt, 1419 krb5_flags options) 1420{ 1421 opt->options = options; 1422} 1423 1424KRB5_LIB_FUNCTION void KRB5_LIB_CALL 1425krb5_get_creds_opt_add_options(krb5_context context, 1426 krb5_get_creds_opt opt, 1427 krb5_flags options) 1428{ 1429 opt->options |= options; 1430} 1431 1432KRB5_LIB_FUNCTION void KRB5_LIB_CALL 1433krb5_get_creds_opt_set_enctype(krb5_context context, 1434 krb5_get_creds_opt opt, 1435 krb5_enctype enctype) 1436{ 1437 opt->enctype = enctype; 1438} 1439 1440KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 1441krb5_get_creds_opt_set_impersonate(krb5_context context, 1442 krb5_get_creds_opt opt, 1443 krb5_const_principal self) 1444{ 1445 if (opt->self) 1446 krb5_free_principal(context, opt->self); 1447 return krb5_copy_principal(context, self, &opt->self); 1448} 1449 1450KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 1451krb5_get_creds_opt_set_ticket(krb5_context context, 1452 krb5_get_creds_opt opt, 1453 const Ticket *ticket) 1454{ 1455 if (opt->ticket) { 1456 free_Ticket(opt->ticket); 1457 free(opt->ticket); 1458 opt->ticket = NULL; 1459 } 1460 if (ticket) { 1461 krb5_error_code ret; 1462 1463 opt->ticket = malloc(sizeof(*ticket)); 1464 if (opt->ticket == NULL) 1465 return krb5_enomem(context); 1466 ret = copy_Ticket(ticket, opt->ticket); 1467 if (ret) { 1468 free(opt->ticket); 1469 opt->ticket = NULL; 1470 krb5_set_error_message(context, ret, 1471 N_("malloc: out of memory", "")); 1472 return ret; 1473 } 1474 } 1475 return 0; 1476} 1477 1478 1479KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 1480krb5_get_creds(krb5_context context, 1481 krb5_get_creds_opt opt, 1482 krb5_ccache ccache, 1483 krb5_const_principal inprinc, 1484 krb5_creds **out_creds) 1485{ 1486 krb5_kdc_flags flags; 1487 krb5_flags options; 1488 krb5_creds in_creds; 1489 krb5_error_code ret; 1490 krb5_creds **tgts; 1491 krb5_creds *res_creds; 1492 krb5_const_principal try_princ = NULL; 1493 krb5_name_canon_iterator name_canon_iter = NULL; 1494 krb5_name_canon_rule_options rule_opts; 1495 int i; 1496 int type; 1497 const char *comp; 1498 1499 memset(&in_creds, 0, sizeof(in_creds)); 1500 in_creds.server = rk_UNCONST(inprinc); 1501 1502 if (_krb5_have_debug(context, 5)) { 1503 char *unparsed; 1504 1505 ret = krb5_unparse_name(context, in_creds.server, &unparsed); 1506 if (ret) { 1507 _krb5_debug(context, 5, "krb5_get_creds: unable to display " 1508 "requested service principal"); 1509 } else { 1510 _krb5_debug(context, 5, "krb5_get_creds: requesting a ticket " 1511 "for %s", unparsed); 1512 free(unparsed); 1513 } 1514 } 1515 1516 if (opt && opt->enctype) { 1517 ret = krb5_enctype_valid(context, opt->enctype); 1518 if (ret) 1519 return ret; 1520 } 1521 1522 ret = krb5_cc_get_principal(context, ccache, &in_creds.client); 1523 if (ret) 1524 return ret; 1525 1526 if (opt) 1527 options = opt->options; 1528 else 1529 options = 0; 1530 flags.i = 0; 1531 1532 *out_creds = NULL; 1533 res_creds = calloc(1, sizeof(*res_creds)); 1534 if (res_creds == NULL) { 1535 krb5_free_principal(context, in_creds.client); 1536 return krb5_enomem(context); 1537 } 1538 1539 if (opt && opt->enctype) { 1540 in_creds.session.keytype = opt->enctype; 1541 options |= KRB5_TC_MATCH_KEYTYPE; 1542 } 1543 1544 ret = krb5_name_canon_iterator_start(context, in_creds.server, 1545 &name_canon_iter); 1546 if (ret) 1547 goto out; 1548 1549next_rule: 1550 ret = krb5_name_canon_iterate(context, &name_canon_iter, &try_princ, 1551 &rule_opts); 1552 in_creds.server = rk_UNCONST(try_princ); 1553 if (ret) 1554 goto out; 1555 1556 if (name_canon_iter == NULL) { 1557 if (options & KRB5_GC_CACHED) 1558 ret = KRB5_CC_NOTFOUND; 1559 else 1560 ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN; 1561 goto out; 1562 } 1563 1564 ret = check_cc(context, options, ccache, &in_creds, res_creds); 1565 if (ret == 0) { 1566 *out_creds = res_creds; 1567 res_creds = NULL; 1568 goto out; 1569 } else if (ret != KRB5_CC_END) { 1570 goto out; 1571 } 1572 if (options & KRB5_GC_CACHED) 1573 goto next_rule; 1574 1575 type = krb5_principal_get_type(context, try_princ); 1576 comp = krb5_principal_get_comp_string(context, try_princ, 0); 1577 if ((type == KRB5_NT_SRV_HST || type == KRB5_NT_UNKNOWN) && 1578 comp != NULL && strcmp(comp, "host") == 0) 1579 flags.b.canonicalize = 1; 1580 if (rule_opts & KRB5_NCRO_NO_REFERRALS) 1581 flags.b.canonicalize = 0; 1582 else 1583 flags.b.canonicalize = (options & KRB5_GC_CANONICALIZE) ? 1 : 0; 1584 if (options & KRB5_GC_USER_USER) { 1585 flags.b.enc_tkt_in_skey = 1; 1586 options |= KRB5_GC_NO_STORE; 1587 } 1588 if (options & KRB5_GC_FORWARDABLE) 1589 flags.b.forwardable = 1; 1590 if (options & KRB5_GC_NO_TRANSIT_CHECK) 1591 flags.b.disable_transited_check = 1; 1592 if (options & KRB5_GC_CONSTRAINED_DELEGATION) 1593 flags.b.cname_in_addl_tkt = 1; 1594 if (options & KRB5_GC_ANONYMOUS) 1595 flags.b.request_anonymous = 1; 1596 1597 tgts = NULL; 1598 ret = _krb5_get_cred_kdc_any(context, flags, ccache, 1599 &in_creds, opt ? opt->self : 0, 1600 opt ? opt->ticket : 0, out_creds, 1601 &tgts); 1602 for (i = 0; tgts && tgts[i]; i++) { 1603 if ((options & KRB5_GC_NO_STORE) == 0) 1604 krb5_cc_store_cred(context, ccache, tgts[i]); 1605 krb5_free_creds(context, tgts[i]); 1606 } 1607 free(tgts); 1608 1609 /* We don't yet have TGS w/ FAST, so we can't protect KBR-ERRORs */ 1610 if (ret == KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN && 1611 !(rule_opts & KRB5_NCRO_USE_FAST)) 1612 goto next_rule; 1613 1614 if (ret == 0 && (options & KRB5_GC_NO_STORE) == 0) 1615 store_cred(context, ccache, inprinc, *out_creds); 1616 1617 if (ret == 0 && _krb5_have_debug(context, 5)) { 1618 char *unparsed; 1619 1620 ret = krb5_unparse_name(context, (*out_creds)->server, &unparsed); 1621 if (ret) { 1622 _krb5_debug(context, 5, "krb5_get_creds: unable to display " 1623 "service principal"); 1624 } else { 1625 _krb5_debug(context, 5, "krb5_get_creds: got a ticket for %s", 1626 unparsed); 1627 free(unparsed); 1628 } 1629 } 1630 1631out: 1632 krb5_free_creds(context, res_creds); 1633 krb5_free_principal(context, in_creds.client); 1634 krb5_free_name_canon_iterator(context, name_canon_iter); 1635 if (ret) 1636 return not_found(context, inprinc, ret); 1637 return ret; 1638} 1639 1640/* 1641 * 1642 */ 1643 1644KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 1645krb5_get_renewed_creds(krb5_context context, 1646 krb5_creds *creds, 1647 krb5_const_principal client, 1648 krb5_ccache ccache, 1649 const char *in_tkt_service) 1650{ 1651 krb5_error_code ret; 1652 krb5_kdc_flags flags; 1653 krb5_creds in, *template, *out = NULL; 1654 1655 memset(&in, 0, sizeof(in)); 1656 memset(creds, 0, sizeof(*creds)); 1657 1658 ret = krb5_copy_principal(context, client, &in.client); 1659 if (ret) 1660 return ret; 1661 1662 if (in_tkt_service) { 1663 ret = krb5_parse_name(context, in_tkt_service, &in.server); 1664 if (ret) { 1665 krb5_free_principal(context, in.client); 1666 return ret; 1667 } 1668 } else { 1669 const char *realm = krb5_principal_get_realm(context, client); 1670 1671 ret = krb5_make_principal(context, &in.server, realm, KRB5_TGS_NAME, 1672 realm, NULL); 1673 if (ret) { 1674 krb5_free_principal(context, in.client); 1675 return ret; 1676 } 1677 } 1678 1679 flags.i = 0; 1680 flags.b.renewable = flags.b.renew = 1; 1681 1682 /* 1683 * Get template from old credential cache for the same entry, if 1684 * this failes, no worries. 1685 */ 1686 ret = krb5_get_credentials(context, KRB5_GC_CACHED, ccache, &in, &template); 1687 if (ret == 0) { 1688 flags.b.forwardable = template->flags.b.forwardable; 1689 flags.b.proxiable = template->flags.b.proxiable; 1690 krb5_free_creds (context, template); 1691 } 1692 1693 ret = krb5_get_kdc_cred(context, ccache, flags, NULL, NULL, &in, &out); 1694 krb5_free_principal(context, in.client); 1695 krb5_free_principal(context, in.server); 1696 if (ret) 1697 return ret; 1698 1699 ret = krb5_copy_creds_contents(context, out, creds); 1700 krb5_free_creds(context, out); 1701 1702 return ret; 1703} 1704