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