1 /* $NetBSD: get_cred.c,v 1.2 2017/01/28 21:31:49 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 41 static krb5_error_code 42 get_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 51 static krb5_error_code 52 make_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 88 static krb5_error_code 89 set_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 140 static krb5_error_code 141 init_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 286 fail: 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 296 KRB5_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 */ 339 static krb5_error_code KRB5_CALLCONV 340 decrypt_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 411 static krb5_error_code 412 get_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; 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 memset(&rep, 0, sizeof(rep)); 546 if(decode_TGS_REP(resp.data, resp.length, &rep.kdc_rep, &len) == 0) { 547 unsigned eflags = 0; 548 549 ret = krb5_copy_principal(context, 550 in_creds->client, 551 &out_creds->client); 552 if(ret) 553 goto out2; 554 ret = krb5_copy_principal(context, 555 in_creds->server, 556 &out_creds->server); 557 if(ret) 558 goto out2; 559 /* this should go someplace else */ 560 out_creds->times.endtime = in_creds->times.endtime; 561 562 /* XXX should do better testing */ 563 if (flags.b.constrained_delegation || impersonate_principal) 564 eflags |= EXTRACT_TICKET_ALLOW_CNAME_MISMATCH; 565 566 ret = _krb5_extract_ticket(context, 567 &rep, 568 out_creds, 569 &krbtgt->session, 570 NULL, 571 0, 572 &krbtgt->addresses, 573 nonce, 574 eflags, 575 NULL, 576 decrypt_tkt_with_subkey, 577 subkey); 578 out2: 579 krb5_free_kdc_rep(context, &rep); 580 } else if(krb5_rd_error(context, &resp, &error) == 0) { 581 ret = krb5_error_from_rd_error(context, &error, in_creds); 582 krb5_free_error_contents(context, &error); 583 } else if(resp.length > 0 && ((char*)resp.data)[0] == 4) { 584 ret = KRB5KRB_AP_ERR_V4_REPLY; 585 krb5_clear_error_message(context); 586 } else { 587 ret = KRB5KRB_AP_ERR_MSG_TYPE; 588 krb5_clear_error_message(context); 589 } 590 591 out: 592 if (second_ticket == &second_ticket_data) 593 free_Ticket(&second_ticket_data); 594 free_METHOD_DATA(&padata); 595 krb5_data_free(&resp); 596 krb5_data_free(&enc); 597 if(subkey) 598 krb5_free_keyblock(context, subkey); 599 return ret; 600 601 } 602 603 /* 604 * same as above, just get local addresses first if the krbtgt have 605 * them and the realm is not addressless 606 */ 607 608 static krb5_error_code 609 get_cred_kdc_address(krb5_context context, 610 krb5_ccache id, 611 krb5_kdc_flags flags, 612 krb5_addresses *addrs, 613 krb5_creds *in_creds, 614 krb5_creds *krbtgt, 615 krb5_principal impersonate_principal, 616 Ticket *second_ticket, 617 krb5_creds *out_creds) 618 { 619 krb5_error_code ret; 620 krb5_addresses addresses = { 0, NULL }; 621 622 /* 623 * Inherit the address-ness of the krbtgt if the address is not 624 * specified. 625 */ 626 627 if (addrs == NULL && krbtgt->addresses.len != 0) { 628 krb5_boolean noaddr; 629 630 krb5_appdefault_boolean(context, NULL, krbtgt->server->realm, 631 "no-addresses", FALSE, &noaddr); 632 633 if (!noaddr) { 634 krb5_get_all_client_addrs(context, &addresses); 635 /* XXX this sucks. */ 636 addrs = &addresses; 637 if(addresses.len == 0) 638 addrs = NULL; 639 } 640 } 641 ret = get_cred_kdc(context, id, flags, addrs, in_creds, 642 krbtgt, impersonate_principal, 643 second_ticket, out_creds); 644 krb5_free_addresses(context, &addresses); 645 return ret; 646 } 647 648 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 649 krb5_get_kdc_cred(krb5_context context, 650 krb5_ccache id, 651 krb5_kdc_flags flags, 652 krb5_addresses *addresses, 653 Ticket *second_ticket, 654 krb5_creds *in_creds, 655 krb5_creds **out_creds 656 ) 657 { 658 krb5_error_code ret; 659 krb5_creds *krbtgt; 660 661 *out_creds = calloc(1, sizeof(**out_creds)); 662 if(*out_creds == NULL) 663 return krb5_enomem(context); 664 ret = _krb5_get_krbtgt (context, 665 id, 666 in_creds->server->realm, 667 &krbtgt); 668 if(ret) { 669 free(*out_creds); 670 *out_creds = NULL; 671 return ret; 672 } 673 ret = get_cred_kdc(context, id, flags, addresses, 674 in_creds, krbtgt, NULL, NULL, *out_creds); 675 krb5_free_creds (context, krbtgt); 676 if(ret) { 677 free(*out_creds); 678 *out_creds = NULL; 679 } 680 return ret; 681 } 682 683 static int 684 not_found(krb5_context context, krb5_const_principal p, krb5_error_code code) 685 { 686 krb5_error_code ret; 687 char *str; 688 689 ret = krb5_unparse_name(context, p, &str); 690 if(ret) { 691 krb5_clear_error_message(context); 692 return code; 693 } 694 krb5_set_error_message(context, code, 695 N_("Matching credential (%s) not found", ""), str); 696 free(str); 697 return code; 698 } 699 700 static krb5_error_code 701 find_cred(krb5_context context, 702 krb5_ccache id, 703 krb5_principal server, 704 krb5_creds **tgts, 705 krb5_creds *out_creds) 706 { 707 krb5_error_code ret; 708 krb5_creds mcreds; 709 710 krb5_cc_clear_mcred(&mcreds); 711 mcreds.server = server; 712 krb5_timeofday(context, &mcreds.times.endtime); 713 ret = krb5_cc_retrieve_cred(context, id, 714 KRB5_TC_DONT_MATCH_REALM | 715 KRB5_TC_MATCH_TIMES, 716 &mcreds, out_creds); 717 if(ret == 0) 718 return 0; 719 while(tgts && *tgts){ 720 if(krb5_compare_creds(context, KRB5_TC_DONT_MATCH_REALM, 721 &mcreds, *tgts)){ 722 ret = krb5_copy_creds_contents(context, *tgts, out_creds); 723 return ret; 724 } 725 tgts++; 726 } 727 return not_found(context, server, KRB5_CC_NOTFOUND); 728 } 729 730 static krb5_error_code 731 add_cred(krb5_context context, krb5_creds const *tkt, krb5_creds ***tgts) 732 { 733 int i; 734 krb5_error_code ret; 735 krb5_creds **tmp = *tgts; 736 737 for(i = 0; tmp && tmp[i]; i++); /* XXX */ 738 tmp = realloc(tmp, (i+2)*sizeof(*tmp)); 739 if(tmp == NULL) 740 return krb5_enomem(context); 741 *tgts = tmp; 742 ret = krb5_copy_creds(context, tkt, &tmp[i]); 743 tmp[i+1] = NULL; 744 return ret; 745 } 746 747 static krb5_error_code 748 get_cred_kdc_capath_worker(krb5_context context, 749 krb5_kdc_flags flags, 750 krb5_ccache ccache, 751 krb5_creds *in_creds, 752 krb5_const_realm try_realm, 753 krb5_principal impersonate_principal, 754 Ticket *second_ticket, 755 krb5_creds **out_creds, 756 krb5_creds ***ret_tgts) 757 { 758 krb5_error_code ret; 759 krb5_creds *tgt = NULL; 760 krb5_creds tmp_creds; 761 krb5_const_realm client_realm, server_realm; 762 int ok_as_delegate = 1; 763 764 *out_creds = calloc(1, sizeof(**out_creds)); 765 if (*out_creds == NULL) 766 return krb5_enomem(context); 767 768 memset(&tmp_creds, 0, sizeof(tmp_creds)); 769 770 client_realm = krb5_principal_get_realm(context, in_creds->client); 771 server_realm = krb5_principal_get_realm(context, in_creds->server); 772 ret = krb5_copy_principal(context, in_creds->client, &tmp_creds.client); 773 if (ret) 774 goto out; 775 776 ret = krb5_make_principal(context, 777 &tmp_creds.server, 778 try_realm, 779 KRB5_TGS_NAME, 780 server_realm, 781 NULL); 782 if (ret) 783 goto out; 784 785 { 786 krb5_creds tgts; 787 788 /* 789 * If we have krbtgt/server_realm@try_realm cached, use it and we're 790 * done. 791 */ 792 ret = find_cred(context, ccache, tmp_creds.server, 793 *ret_tgts, &tgts); 794 if (ret == 0) { 795 /* only allow implicit ok_as_delegate if the realm is the clients realm */ 796 if (strcmp(try_realm, client_realm) != 0 797 || strcmp(try_realm, server_realm) != 0) { 798 ok_as_delegate = tgts.flags.b.ok_as_delegate; 799 } 800 801 ret = get_cred_kdc_address(context, ccache, flags, NULL, 802 in_creds, &tgts, 803 impersonate_principal, 804 second_ticket, 805 *out_creds); 806 krb5_free_cred_contents(context, &tgts); 807 if (ret == 0 && 808 !krb5_principal_compare(context, in_creds->server, 809 (*out_creds)->server)) { 810 ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN; 811 } 812 if (ret == 0 && ok_as_delegate == 0) 813 (*out_creds)->flags.b.ok_as_delegate = 0; 814 815 goto out; 816 } 817 } 818 819 if (krb5_realm_compare(context, in_creds->client, in_creds->server)) { 820 ret = not_found(context, in_creds->server, KRB5_CC_NOTFOUND); 821 goto out; 822 } 823 824 /* 825 * XXX This can loop forever, plus we recurse, so we can't just keep a 826 * count here. The count would have to get passed around by reference. 827 * 828 * The KDCs check for transit loops for us, and capath data is finite, so 829 * in fact we'll fall out of this loop at some point. We should do our own 830 * transit loop checking (like get_cred_kdc_referral()), and we should 831 * impose a max number of iterations altogether. But barring malicious or 832 * broken KDCs, this is good enough. 833 */ 834 while (1) { 835 heim_general_string tgt_inst; 836 837 ret = get_cred_kdc_capath(context, flags, ccache, &tmp_creds, 838 NULL, NULL, &tgt, ret_tgts); 839 if (ret) 840 goto out; 841 842 /* 843 * if either of the chain or the ok_as_delegate was stripped 844 * by the kdc, make sure we strip it too. 845 */ 846 if (ok_as_delegate == 0 || tgt->flags.b.ok_as_delegate == 0) { 847 ok_as_delegate = 0; 848 tgt->flags.b.ok_as_delegate = 0; 849 } 850 851 ret = add_cred(context, tgt, ret_tgts); 852 if (ret) 853 goto out; 854 tgt_inst = tgt->server->name.name_string.val[1]; 855 if (strcmp(tgt_inst, server_realm) == 0) 856 break; 857 krb5_free_principal(context, tmp_creds.server); 858 tmp_creds.server = NULL; 859 ret = krb5_make_principal(context, &tmp_creds.server, 860 tgt_inst, KRB5_TGS_NAME, server_realm, NULL); 861 if (ret) 862 goto out; 863 ret = krb5_free_creds(context, tgt); 864 tgt = NULL; 865 if (ret) 866 goto out; 867 } 868 869 ret = get_cred_kdc_address(context, ccache, flags, NULL, 870 in_creds, tgt, impersonate_principal, 871 second_ticket, *out_creds); 872 if (ret == 0 && 873 !krb5_principal_compare(context, in_creds->server, 874 (*out_creds)->server)) { 875 krb5_free_cred_contents(context, *out_creds); 876 ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN; 877 } 878 if (ret == 0 && ok_as_delegate == 0) 879 (*out_creds)->flags.b.ok_as_delegate = 0; 880 881 out: 882 if (ret) { 883 krb5_free_creds(context, *out_creds); 884 *out_creds = NULL; 885 } 886 if (tmp_creds.server) 887 krb5_free_principal(context, tmp_creds.server); 888 if (tmp_creds.client) 889 krb5_free_principal(context, tmp_creds.client); 890 if (tgt) 891 krb5_free_creds(context, tgt); 892 return ret; 893 } 894 895 /* 896 get_cred(server) 897 creds = cc_get_cred(server) 898 if(creds) return creds 899 tgt = cc_get_cred(krbtgt/server_realm@any_realm) 900 if(tgt) 901 return get_cred_tgt(server, tgt) 902 if(client_realm == server_realm) 903 return NULL 904 tgt = get_cred(krbtgt/server_realm@client_realm) 905 while(tgt_inst != server_realm) 906 tgt = get_cred(krbtgt/server_realm@tgt_inst) 907 return get_cred_tgt(server, tgt) 908 */ 909 910 static krb5_error_code 911 get_cred_kdc_capath(krb5_context context, 912 krb5_kdc_flags flags, 913 krb5_ccache ccache, 914 krb5_creds *in_creds, 915 krb5_principal impersonate_principal, 916 Ticket *second_ticket, 917 krb5_creds **out_creds, 918 krb5_creds ***ret_tgts) 919 { 920 krb5_error_code ret; 921 krb5_const_realm client_realm, server_realm, try_realm; 922 923 client_realm = krb5_principal_get_realm(context, in_creds->client); 924 server_realm = krb5_principal_get_realm(context, in_creds->server); 925 926 try_realm = client_realm; 927 ret = get_cred_kdc_capath_worker(context, flags, ccache, in_creds, try_realm, 928 impersonate_principal, second_ticket, out_creds, 929 ret_tgts); 930 931 if (ret == KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN) { 932 try_realm = krb5_config_get_string(context, NULL, "capaths", 933 client_realm, server_realm, NULL); 934 935 if (try_realm != NULL && strcmp(try_realm, client_realm)) { 936 ret = get_cred_kdc_capath_worker(context, flags, ccache, in_creds, 937 try_realm, impersonate_principal, 938 second_ticket, out_creds, ret_tgts); 939 } 940 } 941 942 return ret; 943 } 944 945 /* 946 * Get a service ticket from a KDC by chasing referrals from a start realm. 947 * 948 * All referral TGTs produced in the process are thrown away when we're done. 949 * We don't store them, and we don't allow other search mechanisms (capaths) to 950 * use referral TGTs produced here. 951 */ 952 static krb5_error_code 953 get_cred_kdc_referral(krb5_context context, 954 krb5_kdc_flags flags, 955 krb5_ccache ccache, 956 krb5_creds *in_creds, 957 krb5_principal impersonate_principal, 958 Ticket *second_ticket, 959 krb5_creds **out_creds) 960 { 961 krb5_realm start_realm = NULL; 962 krb5_data config_start_realm; 963 krb5_error_code ret; 964 krb5_creds tgt, referral, ticket; 965 krb5_creds **referral_tgts = NULL; /* used for loop detection */ 966 int loop = 0; 967 int ok_as_delegate = 1; 968 size_t i; 969 970 if (in_creds->server->name.name_string.len < 2 && !flags.b.canonicalize) { 971 krb5_set_error_message(context, KRB5KDC_ERR_PATH_NOT_ACCEPTED, 972 N_("Name too short to do referals, skipping", "")); 973 return KRB5KDC_ERR_PATH_NOT_ACCEPTED; 974 } 975 976 memset(&tgt, 0, sizeof(tgt)); 977 memset(&ticket, 0, sizeof(ticket)); 978 979 flags.b.canonicalize = 1; 980 981 *out_creds = NULL; 982 983 984 ret = krb5_cc_get_config(context, ccache, NULL, "start_realm", &config_start_realm); 985 if (ret == 0) { 986 start_realm = strndup(config_start_realm.data, config_start_realm.length); 987 krb5_data_free(&config_start_realm); 988 } else { 989 start_realm = strdup(krb5_principal_get_realm(context, in_creds->client)); 990 } 991 if (start_realm == NULL) 992 return krb5_enomem(context); 993 994 /* find tgt for the clients base realm */ 995 { 996 krb5_principal tgtname; 997 998 ret = krb5_make_principal(context, &tgtname, 999 start_realm, 1000 KRB5_TGS_NAME, 1001 start_realm, 1002 NULL); 1003 if (ret) { 1004 free(start_realm); 1005 return ret; 1006 } 1007 1008 ret = find_cred(context, ccache, tgtname, NULL, &tgt); 1009 krb5_free_principal(context, tgtname); 1010 if (ret) { 1011 free(start_realm); 1012 return ret; 1013 } 1014 } 1015 1016 referral = *in_creds; 1017 ret = krb5_copy_principal(context, in_creds->server, &referral.server); 1018 if (ret) { 1019 krb5_free_cred_contents(context, &tgt); 1020 free(start_realm); 1021 return ret; 1022 } 1023 ret = krb5_principal_set_realm(context, referral.server, start_realm); 1024 free(start_realm); 1025 start_realm = NULL; 1026 if (ret) { 1027 krb5_free_cred_contents(context, &tgt); 1028 krb5_free_principal(context, referral.server); 1029 return ret; 1030 } 1031 1032 while (loop++ < 17) { 1033 krb5_creds **tickets; 1034 krb5_creds mcreds; 1035 char *referral_realm; 1036 1037 /* Use cache if we are not doing impersonation or contrained deleg */ 1038 if (impersonate_principal == NULL || flags.b.constrained_delegation) { 1039 krb5_cc_clear_mcred(&mcreds); 1040 mcreds.server = referral.server; 1041 krb5_timeofday(context, &mcreds.times.endtime); 1042 ret = krb5_cc_retrieve_cred(context, ccache, KRB5_TC_MATCH_TIMES, 1043 &mcreds, &ticket); 1044 } else 1045 ret = EINVAL; 1046 1047 if (ret) { 1048 ret = get_cred_kdc_address(context, ccache, flags, NULL, 1049 &referral, &tgt, impersonate_principal, 1050 second_ticket, &ticket); 1051 if (ret) 1052 goto out; 1053 } 1054 1055 /* Did we get the right ticket ? */ 1056 if (krb5_principal_compare_any_realm(context, 1057 referral.server, 1058 ticket.server)) 1059 break; 1060 1061 if (!krb5_principal_is_krbtgt(context, ticket.server)) { 1062 krb5_set_error_message(context, KRB5KRB_AP_ERR_NOT_US, 1063 N_("Got back an non krbtgt " 1064 "ticket referrals", "")); 1065 ret = KRB5KRB_AP_ERR_NOT_US; 1066 goto out; 1067 } 1068 1069 referral_realm = ticket.server->name.name_string.val[1]; 1070 1071 /* check that there are no referrals loops */ 1072 tickets = referral_tgts; 1073 1074 krb5_cc_clear_mcred(&mcreds); 1075 mcreds.server = ticket.server; 1076 1077 while (tickets && *tickets){ 1078 if (krb5_compare_creds(context, 1079 KRB5_TC_DONT_MATCH_REALM, 1080 &mcreds, 1081 *tickets)) { 1082 krb5_set_error_message(context, KRB5_GET_IN_TKT_LOOP, 1083 N_("Referral from %s " 1084 "loops back to realm %s", ""), 1085 tgt.server->realm, 1086 referral_realm); 1087 ret = KRB5_GET_IN_TKT_LOOP; 1088 goto out; 1089 } 1090 tickets++; 1091 } 1092 1093 /* 1094 * if either of the chain or the ok_as_delegate was stripped 1095 * by the kdc, make sure we strip it too. 1096 */ 1097 1098 if (ok_as_delegate == 0 || ticket.flags.b.ok_as_delegate == 0) { 1099 ok_as_delegate = 0; 1100 ticket.flags.b.ok_as_delegate = 0; 1101 } 1102 1103 _krb5_debug(context, 6, "get_cred_kdc_referral: got referral " 1104 "to %s from %s", referral_realm, referral.server->realm); 1105 ret = add_cred(context, &ticket, &referral_tgts); 1106 if (ret) 1107 goto out; 1108 1109 /* try realm in the referral */ 1110 ret = krb5_principal_set_realm(context, 1111 referral.server, 1112 referral_realm); 1113 krb5_free_cred_contents(context, &tgt); 1114 tgt = ticket; 1115 memset(&ticket, 0, sizeof(ticket)); 1116 if (ret) 1117 goto out; 1118 } 1119 1120 ret = krb5_copy_creds(context, &ticket, out_creds); 1121 1122 out: 1123 for (i = 0; referral_tgts && referral_tgts[i]; i++) 1124 krb5_free_creds(context, referral_tgts[i]); 1125 free(referral_tgts); 1126 krb5_free_principal(context, referral.server); 1127 krb5_free_cred_contents(context, &tgt); 1128 krb5_free_cred_contents(context, &ticket); 1129 return ret; 1130 } 1131 1132 1133 /* 1134 * Glue function between referrals version and old client chasing 1135 * codebase. 1136 */ 1137 1138 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 1139 _krb5_get_cred_kdc_any(krb5_context context, 1140 krb5_kdc_flags flags, 1141 krb5_ccache ccache, 1142 krb5_creds *in_creds, 1143 krb5_principal impersonate_principal, 1144 Ticket *second_ticket, 1145 krb5_creds **out_creds, 1146 krb5_creds ***ret_tgts) 1147 { 1148 krb5_error_code ret; 1149 krb5_deltat offset; 1150 1151 ret = krb5_cc_get_kdc_offset(context, ccache, &offset); 1152 if (ret == 0) { 1153 context->kdc_sec_offset = offset; 1154 context->kdc_usec_offset = 0; 1155 } 1156 1157 if (strcmp(in_creds->server->realm, "") != 0) { 1158 /* 1159 * Non-empty realm? Try capaths first. We might have local 1160 * policy (capaths) to honor. 1161 */ 1162 ret = get_cred_kdc_capath(context, 1163 flags, 1164 ccache, 1165 in_creds, 1166 impersonate_principal, 1167 second_ticket, 1168 out_creds, 1169 ret_tgts); 1170 if (ret == 0) 1171 return ret; 1172 } 1173 1174 /* Otherwise try referrals */ 1175 return get_cred_kdc_referral(context, 1176 flags, 1177 ccache, 1178 in_creds, 1179 impersonate_principal, 1180 second_ticket, 1181 out_creds); 1182 } 1183 1184 static krb5_error_code 1185 check_cc(krb5_context context, krb5_flags options, krb5_ccache ccache, 1186 krb5_creds *in_creds, krb5_creds *out_creds) 1187 { 1188 krb5_error_code ret; 1189 krb5_timestamp now; 1190 krb5_times save_times = in_creds->times; 1191 NAME_TYPE save_type = in_creds->server->name.name_type; 1192 1193 krb5_timeofday(context, &now); 1194 1195 if (!(options & KRB5_GC_EXPIRED_OK) && 1196 in_creds->times.endtime < now) { 1197 in_creds->times.renew_till = 0; 1198 krb5_timeofday(context, &in_creds->times.endtime); 1199 options |= KRB5_TC_MATCH_TIMES; 1200 } 1201 1202 if (save_type == KRB5_NT_SRV_HST_NEEDS_CANON) { 1203 /* Avoid name canonicalization in krb5_cc_retrieve_cred() */ 1204 krb5_principal_set_type(context, in_creds->server, KRB5_NT_SRV_HST); 1205 } 1206 1207 ret = krb5_cc_retrieve_cred(context, ccache, 1208 (options & 1209 (KRB5_TC_DONT_MATCH_REALM | 1210 KRB5_TC_MATCH_KEYTYPE | 1211 KRB5_TC_MATCH_TIMES)), 1212 in_creds, out_creds); 1213 1214 in_creds->server->name.name_type = save_type; 1215 in_creds->times = save_times; 1216 return ret; 1217 } 1218 1219 static void 1220 store_cred(krb5_context context, krb5_ccache ccache, 1221 krb5_const_principal server_princ, krb5_creds *creds) 1222 { 1223 if (!krb5_principal_compare(context, creds->server, server_princ)) { 1224 krb5_principal tmp_princ = creds->server; 1225 /* 1226 * Store the cred with the pre-canon server princ first so it 1227 * can be found quickly in the future. 1228 */ 1229 creds->server = (krb5_principal)server_princ; 1230 krb5_cc_store_cred(context, ccache, creds); 1231 creds->server = tmp_princ; 1232 /* Then store again with the canonicalized server princ */ 1233 } 1234 krb5_cc_store_cred(context, ccache, creds); 1235 } 1236 1237 1238 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 1239 krb5_get_credentials_with_flags(krb5_context context, 1240 krb5_flags options, 1241 krb5_kdc_flags flags, 1242 krb5_ccache ccache, 1243 krb5_creds *in_creds, 1244 krb5_creds **out_creds) 1245 { 1246 krb5_error_code ret; 1247 krb5_name_canon_iterator name_canon_iter = NULL; 1248 krb5_name_canon_rule_options rule_opts; 1249 krb5_const_principal try_princ = NULL; 1250 krb5_principal save_princ = in_creds->server; 1251 krb5_creds **tgts; 1252 krb5_creds *res_creds; 1253 int i; 1254 1255 if (_krb5_have_debug(context, 5)) { 1256 char *unparsed; 1257 1258 ret = krb5_unparse_name(context, in_creds->server, &unparsed); 1259 if (ret) { 1260 _krb5_debug(context, 5, "krb5_get_creds: unable to display " 1261 "requested service principal"); 1262 } else { 1263 _krb5_debug(context, 5, "krb5_get_creds: requesting a ticket " 1264 "for %s", unparsed); 1265 free(unparsed); 1266 } 1267 } 1268 1269 if (in_creds->session.keytype) { 1270 ret = krb5_enctype_valid(context, in_creds->session.keytype); 1271 if (ret) 1272 return ret; 1273 options |= KRB5_TC_MATCH_KEYTYPE; 1274 } 1275 1276 *out_creds = NULL; 1277 res_creds = calloc(1, sizeof(*res_creds)); 1278 if (res_creds == NULL) 1279 return krb5_enomem(context); 1280 1281 ret = krb5_name_canon_iterator_start(context, in_creds->server, 1282 &name_canon_iter); 1283 if (ret) 1284 return ret; 1285 1286 next_rule: 1287 krb5_free_cred_contents(context, res_creds); 1288 memset(res_creds, 0, sizeof (*res_creds)); 1289 ret = krb5_name_canon_iterate(context, &name_canon_iter, &try_princ, 1290 &rule_opts); 1291 in_creds->server = rk_UNCONST(try_princ); 1292 if (ret) 1293 goto out; 1294 1295 if (name_canon_iter == NULL) { 1296 if (options & KRB5_GC_CACHED) 1297 ret = KRB5_CC_NOTFOUND; 1298 else 1299 ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN; 1300 goto out; 1301 } 1302 1303 ret = check_cc(context, options, ccache, in_creds, res_creds); 1304 if (ret == 0) { 1305 *out_creds = res_creds; 1306 res_creds = NULL; 1307 goto out; 1308 } else if(ret != KRB5_CC_END) { 1309 goto out; 1310 } 1311 if (options & KRB5_GC_CACHED) 1312 goto next_rule; 1313 1314 if(options & KRB5_GC_USER_USER) 1315 flags.b.enc_tkt_in_skey = 1; 1316 if (flags.b.enc_tkt_in_skey) 1317 options |= KRB5_GC_NO_STORE; 1318 1319 tgts = NULL; 1320 ret = _krb5_get_cred_kdc_any(context, flags, ccache, 1321 in_creds, NULL, NULL, out_creds, &tgts); 1322 for (i = 0; tgts && tgts[i]; i++) { 1323 if ((options & KRB5_GC_NO_STORE) == 0) 1324 krb5_cc_store_cred(context, ccache, tgts[i]); 1325 krb5_free_creds(context, tgts[i]); 1326 } 1327 free(tgts); 1328 1329 /* We don't yet have TGS w/ FAST, so we can't protect KBR-ERRORs */ 1330 if (ret == KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN && 1331 !(rule_opts & KRB5_NCRO_USE_FAST)) 1332 goto next_rule; 1333 1334 if(ret == 0 && (options & KRB5_GC_NO_STORE) == 0) 1335 store_cred(context, ccache, in_creds->server, *out_creds); 1336 1337 if (ret == 0 && _krb5_have_debug(context, 5)) { 1338 char *unparsed; 1339 1340 ret = krb5_unparse_name(context, (*out_creds)->server, &unparsed); 1341 if (ret) { 1342 _krb5_debug(context, 5, "krb5_get_creds: unable to display " 1343 "service principal"); 1344 } else { 1345 _krb5_debug(context, 5, "krb5_get_creds: got a ticket for %s", 1346 unparsed); 1347 free(unparsed); 1348 } 1349 } 1350 1351 out: 1352 in_creds->server = save_princ; 1353 krb5_free_creds(context, res_creds); 1354 krb5_free_name_canon_iterator(context, name_canon_iter); 1355 if (ret) 1356 return not_found(context, in_creds->server, ret); 1357 return 0; 1358 } 1359 1360 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 1361 krb5_get_credentials(krb5_context context, 1362 krb5_flags options, 1363 krb5_ccache ccache, 1364 krb5_creds *in_creds, 1365 krb5_creds **out_creds) 1366 { 1367 krb5_kdc_flags flags; 1368 flags.i = 0; 1369 return krb5_get_credentials_with_flags(context, options, flags, 1370 ccache, in_creds, out_creds); 1371 } 1372 1373 struct krb5_get_creds_opt_data { 1374 krb5_principal self; 1375 krb5_flags options; 1376 krb5_enctype enctype; 1377 Ticket *ticket; 1378 }; 1379 1380 1381 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 1382 krb5_get_creds_opt_alloc(krb5_context context, krb5_get_creds_opt *opt) 1383 { 1384 *opt = calloc(1, sizeof(**opt)); 1385 if (*opt == NULL) 1386 return krb5_enomem(context); 1387 return 0; 1388 } 1389 1390 KRB5_LIB_FUNCTION void KRB5_LIB_CALL 1391 krb5_get_creds_opt_free(krb5_context context, krb5_get_creds_opt opt) 1392 { 1393 if (opt->self) 1394 krb5_free_principal(context, opt->self); 1395 if (opt->ticket) { 1396 free_Ticket(opt->ticket); 1397 free(opt->ticket); 1398 } 1399 memset(opt, 0, sizeof(*opt)); 1400 free(opt); 1401 } 1402 1403 KRB5_LIB_FUNCTION void KRB5_LIB_CALL 1404 krb5_get_creds_opt_set_options(krb5_context context, 1405 krb5_get_creds_opt opt, 1406 krb5_flags options) 1407 { 1408 opt->options = options; 1409 } 1410 1411 KRB5_LIB_FUNCTION void KRB5_LIB_CALL 1412 krb5_get_creds_opt_add_options(krb5_context context, 1413 krb5_get_creds_opt opt, 1414 krb5_flags options) 1415 { 1416 opt->options |= options; 1417 } 1418 1419 KRB5_LIB_FUNCTION void KRB5_LIB_CALL 1420 krb5_get_creds_opt_set_enctype(krb5_context context, 1421 krb5_get_creds_opt opt, 1422 krb5_enctype enctype) 1423 { 1424 opt->enctype = enctype; 1425 } 1426 1427 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 1428 krb5_get_creds_opt_set_impersonate(krb5_context context, 1429 krb5_get_creds_opt opt, 1430 krb5_const_principal self) 1431 { 1432 if (opt->self) 1433 krb5_free_principal(context, opt->self); 1434 return krb5_copy_principal(context, self, &opt->self); 1435 } 1436 1437 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 1438 krb5_get_creds_opt_set_ticket(krb5_context context, 1439 krb5_get_creds_opt opt, 1440 const Ticket *ticket) 1441 { 1442 if (opt->ticket) { 1443 free_Ticket(opt->ticket); 1444 free(opt->ticket); 1445 opt->ticket = NULL; 1446 } 1447 if (ticket) { 1448 krb5_error_code ret; 1449 1450 opt->ticket = malloc(sizeof(*ticket)); 1451 if (opt->ticket == NULL) 1452 return krb5_enomem(context); 1453 ret = copy_Ticket(ticket, opt->ticket); 1454 if (ret) { 1455 free(opt->ticket); 1456 opt->ticket = NULL; 1457 krb5_set_error_message(context, ret, 1458 N_("malloc: out of memory", "")); 1459 return ret; 1460 } 1461 } 1462 return 0; 1463 } 1464 1465 1466 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 1467 krb5_get_creds(krb5_context context, 1468 krb5_get_creds_opt opt, 1469 krb5_ccache ccache, 1470 krb5_const_principal inprinc, 1471 krb5_creds **out_creds) 1472 { 1473 krb5_kdc_flags flags; 1474 krb5_flags options; 1475 krb5_creds in_creds; 1476 krb5_error_code ret; 1477 krb5_creds **tgts; 1478 krb5_creds *res_creds; 1479 krb5_const_principal try_princ = NULL; 1480 krb5_name_canon_iterator name_canon_iter = NULL; 1481 krb5_name_canon_rule_options rule_opts; 1482 int i; 1483 int type; 1484 const char *comp; 1485 1486 memset(&in_creds, 0, sizeof(in_creds)); 1487 in_creds.server = rk_UNCONST(inprinc); 1488 1489 if (_krb5_have_debug(context, 5)) { 1490 char *unparsed; 1491 1492 ret = krb5_unparse_name(context, in_creds.server, &unparsed); 1493 if (ret) { 1494 _krb5_debug(context, 5, "krb5_get_creds: unable to display " 1495 "requested service principal"); 1496 } else { 1497 _krb5_debug(context, 5, "krb5_get_creds: requesting a ticket " 1498 "for %s", unparsed); 1499 free(unparsed); 1500 } 1501 } 1502 1503 if (opt && opt->enctype) { 1504 ret = krb5_enctype_valid(context, opt->enctype); 1505 if (ret) 1506 return ret; 1507 } 1508 1509 ret = krb5_cc_get_principal(context, ccache, &in_creds.client); 1510 if (ret) 1511 return ret; 1512 1513 if (opt) 1514 options = opt->options; 1515 else 1516 options = 0; 1517 flags.i = 0; 1518 1519 *out_creds = NULL; 1520 res_creds = calloc(1, sizeof(*res_creds)); 1521 if (res_creds == NULL) { 1522 krb5_free_principal(context, in_creds.client); 1523 return krb5_enomem(context); 1524 } 1525 1526 if (opt && opt->enctype) { 1527 in_creds.session.keytype = opt->enctype; 1528 options |= KRB5_TC_MATCH_KEYTYPE; 1529 } 1530 1531 ret = krb5_name_canon_iterator_start(context, in_creds.server, 1532 &name_canon_iter); 1533 if (ret) 1534 goto out; 1535 1536 next_rule: 1537 ret = krb5_name_canon_iterate(context, &name_canon_iter, &try_princ, 1538 &rule_opts); 1539 in_creds.server = rk_UNCONST(try_princ); 1540 if (ret) 1541 goto out; 1542 1543 if (name_canon_iter == NULL) { 1544 if (options & KRB5_GC_CACHED) 1545 ret = KRB5_CC_NOTFOUND; 1546 else 1547 ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN; 1548 goto out; 1549 } 1550 1551 ret = check_cc(context, options, ccache, &in_creds, res_creds); 1552 if (ret == 0) { 1553 *out_creds = res_creds; 1554 res_creds = NULL; 1555 goto out; 1556 } else if (ret != KRB5_CC_END) { 1557 goto out; 1558 } 1559 if (options & KRB5_GC_CACHED) 1560 goto next_rule; 1561 1562 type = krb5_principal_get_type(context, try_princ); 1563 comp = krb5_principal_get_comp_string(context, try_princ, 0); 1564 if ((type == KRB5_NT_SRV_HST || type == KRB5_NT_UNKNOWN) && 1565 comp != NULL && strcmp(comp, "host") == 0) 1566 flags.b.canonicalize = 1; 1567 if (rule_opts & KRB5_NCRO_NO_REFERRALS) 1568 flags.b.canonicalize = 0; 1569 else 1570 flags.b.canonicalize = (options & KRB5_GC_CANONICALIZE) ? 1 : 0; 1571 if (options & KRB5_GC_USER_USER) { 1572 flags.b.enc_tkt_in_skey = 1; 1573 options |= KRB5_GC_NO_STORE; 1574 } 1575 if (options & KRB5_GC_FORWARDABLE) 1576 flags.b.forwardable = 1; 1577 if (options & KRB5_GC_NO_TRANSIT_CHECK) 1578 flags.b.disable_transited_check = 1; 1579 if (options & KRB5_GC_CONSTRAINED_DELEGATION) { 1580 flags.b.request_anonymous = 1; /* XXX ARGH confusion */ 1581 flags.b.constrained_delegation = 1; 1582 } 1583 1584 tgts = NULL; 1585 ret = _krb5_get_cred_kdc_any(context, flags, ccache, 1586 &in_creds, opt ? opt->self : 0, 1587 opt ? opt->ticket : 0, out_creds, 1588 &tgts); 1589 for (i = 0; tgts && tgts[i]; i++) { 1590 if ((options & KRB5_GC_NO_STORE) == 0) 1591 krb5_cc_store_cred(context, ccache, tgts[i]); 1592 krb5_free_creds(context, tgts[i]); 1593 } 1594 free(tgts); 1595 1596 /* We don't yet have TGS w/ FAST, so we can't protect KBR-ERRORs */ 1597 if (ret == KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN && 1598 !(rule_opts & KRB5_NCRO_USE_FAST)) 1599 goto next_rule; 1600 1601 if (ret == 0 && (options & KRB5_GC_NO_STORE) == 0) 1602 store_cred(context, ccache, inprinc, *out_creds); 1603 1604 if (ret == 0 && _krb5_have_debug(context, 5)) { 1605 char *unparsed; 1606 1607 ret = krb5_unparse_name(context, (*out_creds)->server, &unparsed); 1608 if (ret) { 1609 _krb5_debug(context, 5, "krb5_get_creds: unable to display " 1610 "service principal"); 1611 } else { 1612 _krb5_debug(context, 5, "krb5_get_creds: got a ticket for %s", 1613 unparsed); 1614 free(unparsed); 1615 } 1616 } 1617 1618 out: 1619 krb5_free_creds(context, res_creds); 1620 krb5_free_principal(context, in_creds.client); 1621 krb5_free_name_canon_iterator(context, name_canon_iter); 1622 if (ret) 1623 return not_found(context, inprinc, ret); 1624 return ret; 1625 } 1626 1627 /* 1628 * 1629 */ 1630 1631 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 1632 krb5_get_renewed_creds(krb5_context context, 1633 krb5_creds *creds, 1634 krb5_const_principal client, 1635 krb5_ccache ccache, 1636 const char *in_tkt_service) 1637 { 1638 krb5_error_code ret; 1639 krb5_kdc_flags flags; 1640 krb5_creds in, *template, *out = NULL; 1641 1642 memset(&in, 0, sizeof(in)); 1643 memset(creds, 0, sizeof(*creds)); 1644 1645 ret = krb5_copy_principal(context, client, &in.client); 1646 if (ret) 1647 return ret; 1648 1649 if (in_tkt_service) { 1650 ret = krb5_parse_name(context, in_tkt_service, &in.server); 1651 if (ret) { 1652 krb5_free_principal(context, in.client); 1653 return ret; 1654 } 1655 } else { 1656 const char *realm = krb5_principal_get_realm(context, client); 1657 1658 ret = krb5_make_principal(context, &in.server, realm, KRB5_TGS_NAME, 1659 realm, NULL); 1660 if (ret) { 1661 krb5_free_principal(context, in.client); 1662 return ret; 1663 } 1664 } 1665 1666 flags.i = 0; 1667 flags.b.renewable = flags.b.renew = 1; 1668 1669 /* 1670 * Get template from old credential cache for the same entry, if 1671 * this failes, no worries. 1672 */ 1673 ret = krb5_get_credentials(context, KRB5_GC_CACHED, ccache, &in, &template); 1674 if (ret == 0) { 1675 flags.b.forwardable = template->flags.b.forwardable; 1676 flags.b.proxiable = template->flags.b.proxiable; 1677 krb5_free_creds (context, template); 1678 } 1679 1680 ret = krb5_get_kdc_cred(context, ccache, flags, NULL, NULL, &in, &out); 1681 krb5_free_principal(context, in.client); 1682 krb5_free_principal(context, in.server); 1683 if (ret) 1684 return ret; 1685 1686 ret = krb5_copy_creds_contents(context, out, creds); 1687 krb5_free_creds(context, out); 1688 1689 return ret; 1690 } 1691