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