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