1 /* 2 * Copyright (c) 1997 - 2007 Kungliga Tekniska H�gskolan 3 * (Royal Institute of Technology, Stockholm, Sweden). 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * 3. Neither the name of the Institute nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 */ 33 34 #include <krb5_locl.h> 35 36 RCSID("$Id: get_cred.c 21668 2007-07-22 11:28:05Z lha $"); 37 38 /* 39 * Take the `body' and encode it into `padata' using the credentials 40 * in `creds'. 41 */ 42 43 static krb5_error_code 44 make_pa_tgs_req(krb5_context context, 45 krb5_auth_context ac, 46 KDC_REQ_BODY *body, 47 PA_DATA *padata, 48 krb5_creds *creds, 49 krb5_key_usage usage) 50 { 51 u_char *buf; 52 size_t buf_size; 53 size_t len; 54 krb5_data in_data; 55 krb5_error_code ret; 56 57 ASN1_MALLOC_ENCODE(KDC_REQ_BODY, buf, buf_size, body, &len, ret); 58 if (ret) 59 goto out; 60 if(buf_size != len) 61 krb5_abortx(context, "internal error in ASN.1 encoder"); 62 63 in_data.length = len; 64 in_data.data = buf; 65 ret = _krb5_mk_req_internal(context, &ac, 0, &in_data, creds, 66 &padata->padata_value, 67 KRB5_KU_TGS_REQ_AUTH_CKSUM, 68 usage 69 /* KRB5_KU_TGS_REQ_AUTH */); 70 out: 71 free (buf); 72 if(ret) 73 return ret; 74 padata->padata_type = KRB5_PADATA_TGS_REQ; 75 return 0; 76 } 77 78 /* 79 * Set the `enc-authorization-data' in `req_body' based on `authdata' 80 */ 81 82 static krb5_error_code 83 set_auth_data (krb5_context context, 84 KDC_REQ_BODY *req_body, 85 krb5_authdata *authdata, 86 krb5_keyblock *key) 87 { 88 if(authdata->len) { 89 size_t len, buf_size; 90 unsigned char *buf; 91 krb5_crypto crypto; 92 krb5_error_code ret; 93 94 ASN1_MALLOC_ENCODE(AuthorizationData, buf, buf_size, authdata, 95 &len, ret); 96 if (ret) 97 return ret; 98 if (buf_size != len) 99 krb5_abortx(context, "internal error in ASN.1 encoder"); 100 101 ALLOC(req_body->enc_authorization_data, 1); 102 if (req_body->enc_authorization_data == NULL) { 103 free (buf); 104 krb5_set_error_string(context, "malloc: out of memory"); 105 return ENOMEM; 106 } 107 ret = krb5_crypto_init(context, key, 0, &crypto); 108 if (ret) { 109 free (buf); 110 free (req_body->enc_authorization_data); 111 req_body->enc_authorization_data = NULL; 112 return ret; 113 } 114 krb5_encrypt_EncryptedData(context, 115 crypto, 116 KRB5_KU_TGS_REQ_AUTH_DAT_SUBKEY, 117 /* KRB5_KU_TGS_REQ_AUTH_DAT_SESSION? */ 118 buf, 119 len, 120 0, 121 req_body->enc_authorization_data); 122 free (buf); 123 krb5_crypto_destroy(context, crypto); 124 } else { 125 req_body->enc_authorization_data = NULL; 126 } 127 return 0; 128 } 129 130 /* 131 * Create a tgs-req in `t' with `addresses', `flags', `second_ticket' 132 * (if not-NULL), `in_creds', `krbtgt', and returning the generated 133 * subkey in `subkey'. 134 */ 135 136 static krb5_error_code 137 init_tgs_req (krb5_context context, 138 krb5_ccache ccache, 139 krb5_addresses *addresses, 140 krb5_kdc_flags flags, 141 Ticket *second_ticket, 142 krb5_creds *in_creds, 143 krb5_creds *krbtgt, 144 unsigned nonce, 145 const METHOD_DATA *padata, 146 krb5_keyblock **subkey, 147 TGS_REQ *t, 148 krb5_key_usage usage) 149 { 150 krb5_error_code ret = 0; 151 152 memset(t, 0, sizeof(*t)); 153 t->pvno = 5; 154 t->msg_type = krb_tgs_req; 155 if (in_creds->session.keytype) { 156 ALLOC_SEQ(&t->req_body.etype, 1); 157 if(t->req_body.etype.val == NULL) { 158 ret = ENOMEM; 159 krb5_set_error_string(context, "malloc: out of memory"); 160 goto fail; 161 } 162 t->req_body.etype.val[0] = in_creds->session.keytype; 163 } else { 164 ret = krb5_init_etype(context, 165 &t->req_body.etype.len, 166 &t->req_body.etype.val, 167 NULL); 168 } 169 if (ret) 170 goto fail; 171 t->req_body.addresses = addresses; 172 t->req_body.kdc_options = flags.b; 173 ret = copy_Realm(&in_creds->server->realm, &t->req_body.realm); 174 if (ret) 175 goto fail; 176 ALLOC(t->req_body.sname, 1); 177 if (t->req_body.sname == NULL) { 178 ret = ENOMEM; 179 krb5_set_error_string(context, "malloc: out of memory"); 180 goto fail; 181 } 182 183 /* some versions of some code might require that the client be 184 present in TGS-REQs, but this is clearly against the spec */ 185 186 ret = copy_PrincipalName(&in_creds->server->name, t->req_body.sname); 187 if (ret) 188 goto fail; 189 190 /* req_body.till should be NULL if there is no endtime specified, 191 but old MIT code (like DCE secd) doesn't like that */ 192 ALLOC(t->req_body.till, 1); 193 if(t->req_body.till == NULL){ 194 ret = ENOMEM; 195 krb5_set_error_string(context, "malloc: out of memory"); 196 goto fail; 197 } 198 *t->req_body.till = in_creds->times.endtime; 199 200 t->req_body.nonce = nonce; 201 if(second_ticket){ 202 ALLOC(t->req_body.additional_tickets, 1); 203 if (t->req_body.additional_tickets == NULL) { 204 ret = ENOMEM; 205 krb5_set_error_string(context, "malloc: out of memory"); 206 goto fail; 207 } 208 ALLOC_SEQ(t->req_body.additional_tickets, 1); 209 if (t->req_body.additional_tickets->val == NULL) { 210 ret = ENOMEM; 211 krb5_set_error_string(context, "malloc: out of memory"); 212 goto fail; 213 } 214 ret = copy_Ticket(second_ticket, t->req_body.additional_tickets->val); 215 if (ret) 216 goto fail; 217 } 218 ALLOC(t->padata, 1); 219 if (t->padata == NULL) { 220 ret = ENOMEM; 221 krb5_set_error_string(context, "malloc: out of memory"); 222 goto fail; 223 } 224 ALLOC_SEQ(t->padata, 1 + padata->len); 225 if (t->padata->val == NULL) { 226 ret = ENOMEM; 227 krb5_set_error_string(context, "malloc: out of memory"); 228 goto fail; 229 } 230 { 231 int i; 232 for (i = 0; i < padata->len; i++) { 233 ret = copy_PA_DATA(&padata->val[i], &t->padata->val[i + 1]); 234 if (ret) { 235 krb5_set_error_string(context, "malloc: out of memory"); 236 goto fail; 237 } 238 } 239 } 240 241 { 242 krb5_auth_context ac; 243 krb5_keyblock *key = NULL; 244 245 ret = krb5_auth_con_init(context, &ac); 246 if(ret) 247 goto fail; 248 249 if (krb5_config_get_bool_default(context, NULL, FALSE, 250 "realms", 251 krbtgt->server->realm, 252 "tgs_require_subkey", 253 NULL)) 254 { 255 ret = krb5_generate_subkey (context, &krbtgt->session, &key); 256 if (ret) { 257 krb5_auth_con_free (context, ac); 258 goto fail; 259 } 260 261 ret = krb5_auth_con_setlocalsubkey(context, ac, key); 262 if (ret) { 263 if (key) 264 krb5_free_keyblock (context, key); 265 krb5_auth_con_free (context, ac); 266 goto fail; 267 } 268 } 269 270 ret = set_auth_data (context, &t->req_body, &in_creds->authdata, 271 key ? key : &krbtgt->session); 272 if (ret) { 273 if (key) 274 krb5_free_keyblock (context, key); 275 krb5_auth_con_free (context, ac); 276 goto fail; 277 } 278 279 ret = make_pa_tgs_req(context, 280 ac, 281 &t->req_body, 282 &t->padata->val[0], 283 krbtgt, 284 usage); 285 if(ret) { 286 if (key) 287 krb5_free_keyblock (context, key); 288 krb5_auth_con_free(context, ac); 289 goto fail; 290 } 291 *subkey = key; 292 293 krb5_auth_con_free(context, ac); 294 } 295 fail: 296 if (ret) { 297 t->req_body.addresses = NULL; 298 free_TGS_REQ (t); 299 } 300 return ret; 301 } 302 303 krb5_error_code 304 _krb5_get_krbtgt(krb5_context context, 305 krb5_ccache id, 306 krb5_realm realm, 307 krb5_creds **cred) 308 { 309 krb5_error_code ret; 310 krb5_creds tmp_cred; 311 312 memset(&tmp_cred, 0, sizeof(tmp_cred)); 313 314 ret = krb5_cc_get_principal(context, id, &tmp_cred.client); 315 if (ret) 316 return ret; 317 318 ret = krb5_make_principal(context, 319 &tmp_cred.server, 320 realm, 321 KRB5_TGS_NAME, 322 realm, 323 NULL); 324 if(ret) { 325 krb5_free_principal(context, tmp_cred.client); 326 return ret; 327 } 328 ret = krb5_get_credentials(context, 329 KRB5_GC_CACHED, 330 id, 331 &tmp_cred, 332 cred); 333 krb5_free_principal(context, tmp_cred.client); 334 krb5_free_principal(context, tmp_cred.server); 335 if(ret) 336 return ret; 337 return 0; 338 } 339 340 /* DCE compatible decrypt proc */ 341 static krb5_error_code 342 decrypt_tkt_with_subkey (krb5_context context, 343 krb5_keyblock *key, 344 krb5_key_usage usage, 345 krb5_const_pointer subkey, 346 krb5_kdc_rep *dec_rep) 347 { 348 krb5_error_code ret; 349 krb5_data data; 350 size_t size; 351 krb5_crypto crypto; 352 353 ret = krb5_crypto_init(context, key, 0, &crypto); 354 if (ret) 355 return ret; 356 ret = krb5_decrypt_EncryptedData (context, 357 crypto, 358 usage, 359 &dec_rep->kdc_rep.enc_part, 360 &data); 361 krb5_crypto_destroy(context, crypto); 362 if(ret && subkey){ 363 /* DCE compat -- try to decrypt with subkey */ 364 ret = krb5_crypto_init(context, subkey, 0, &crypto); 365 if (ret) 366 return ret; 367 ret = krb5_decrypt_EncryptedData (context, 368 crypto, 369 KRB5_KU_TGS_REP_ENC_PART_SUB_KEY, 370 &dec_rep->kdc_rep.enc_part, 371 &data); 372 krb5_crypto_destroy(context, crypto); 373 } 374 if (ret) 375 return ret; 376 377 ret = krb5_decode_EncASRepPart(context, 378 data.data, 379 data.length, 380 &dec_rep->enc_part, 381 &size); 382 if (ret) 383 ret = krb5_decode_EncTGSRepPart(context, 384 data.data, 385 data.length, 386 &dec_rep->enc_part, 387 &size); 388 krb5_data_free (&data); 389 return ret; 390 } 391 392 static krb5_error_code 393 get_cred_kdc_usage(krb5_context context, 394 krb5_ccache id, 395 krb5_kdc_flags flags, 396 krb5_addresses *addresses, 397 krb5_creds *in_creds, 398 krb5_creds *krbtgt, 399 krb5_principal impersonate_principal, 400 Ticket *second_ticket, 401 krb5_creds *out_creds, 402 krb5_key_usage usage) 403 { 404 TGS_REQ req; 405 krb5_data enc; 406 krb5_data resp; 407 krb5_kdc_rep rep; 408 KRB_ERROR error; 409 krb5_error_code ret; 410 unsigned nonce; 411 krb5_keyblock *subkey = NULL; 412 size_t len; 413 Ticket second_ticket_data; 414 METHOD_DATA padata; 415 416 krb5_data_zero(&resp); 417 krb5_data_zero(&enc); 418 padata.val = NULL; 419 padata.len = 0; 420 421 krb5_generate_random_block(&nonce, sizeof(nonce)); 422 nonce &= 0xffffffff; 423 424 if(flags.b.enc_tkt_in_skey && second_ticket == NULL){ 425 ret = decode_Ticket(in_creds->second_ticket.data, 426 in_creds->second_ticket.length, 427 &second_ticket_data, &len); 428 if(ret) 429 return ret; 430 second_ticket = &second_ticket_data; 431 } 432 433 434 if (impersonate_principal) { 435 krb5_crypto crypto; 436 PA_S4U2Self self; 437 krb5_data data; 438 void *buf; 439 size_t size; 440 441 self.name = impersonate_principal->name; 442 self.realm = impersonate_principal->realm; 443 self.auth = estrdup("Kerberos"); 444 445 ret = _krb5_s4u2self_to_checksumdata(context, &self, &data); 446 if (ret) { 447 free(self.auth); 448 goto out; 449 } 450 451 ret = krb5_crypto_init(context, &krbtgt->session, 0, &crypto); 452 if (ret) { 453 free(self.auth); 454 krb5_data_free(&data); 455 goto out; 456 } 457 458 ret = krb5_create_checksum(context, 459 crypto, 460 KRB5_KU_OTHER_CKSUM, 461 0, 462 data.data, 463 data.length, 464 &self.cksum); 465 krb5_crypto_destroy(context, crypto); 466 krb5_data_free(&data); 467 if (ret) { 468 free(self.auth); 469 goto out; 470 } 471 472 ASN1_MALLOC_ENCODE(PA_S4U2Self, buf, len, &self, &size, ret); 473 free(self.auth); 474 free_Checksum(&self.cksum); 475 if (ret) 476 goto out; 477 if (len != size) 478 krb5_abortx(context, "internal asn1 error"); 479 480 ret = krb5_padata_add(context, &padata, KRB5_PADATA_S4U2SELF, buf, len); 481 if (ret) 482 goto out; 483 } 484 485 ret = init_tgs_req (context, 486 id, 487 addresses, 488 flags, 489 second_ticket, 490 in_creds, 491 krbtgt, 492 nonce, 493 &padata, 494 &subkey, 495 &req, 496 usage); 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 ret = krb5_copy_principal(context, 531 in_creds->client, 532 &out_creds->client); 533 if(ret) 534 goto out; 535 ret = krb5_copy_principal(context, 536 in_creds->server, 537 &out_creds->server); 538 if(ret) 539 goto out; 540 /* this should go someplace else */ 541 out_creds->times.endtime = in_creds->times.endtime; 542 543 ret = _krb5_extract_ticket(context, 544 &rep, 545 out_creds, 546 &krbtgt->session, 547 NULL, 548 KRB5_KU_TGS_REP_ENC_PART_SESSION, 549 &krbtgt->addresses, 550 nonce, 551 EXTRACT_TICKET_ALLOW_CNAME_MISMATCH| 552 EXTRACT_TICKET_ALLOW_SERVER_MISMATCH, 553 decrypt_tkt_with_subkey, 554 subkey); 555 krb5_free_kdc_rep(context, &rep); 556 } else if(krb5_rd_error(context, &resp, &error) == 0) { 557 ret = krb5_error_from_rd_error(context, &error, in_creds); 558 krb5_free_error_contents(context, &error); 559 } else if(resp.data && ((char*)resp.data)[0] == 4) { 560 ret = KRB5KRB_AP_ERR_V4_REPLY; 561 krb5_clear_error_string(context); 562 } else { 563 ret = KRB5KRB_AP_ERR_MSG_TYPE; 564 krb5_clear_error_string(context); 565 } 566 567 out: 568 if (second_ticket == &second_ticket_data) 569 free_Ticket(&second_ticket_data); 570 free_METHOD_DATA(&padata); 571 krb5_data_free(&resp); 572 krb5_data_free(&enc); 573 if(subkey){ 574 krb5_free_keyblock_contents(context, subkey); 575 free(subkey); 576 } 577 return ret; 578 579 } 580 581 static krb5_error_code 582 get_cred_kdc(krb5_context context, 583 krb5_ccache id, 584 krb5_kdc_flags flags, 585 krb5_addresses *addresses, 586 krb5_creds *in_creds, 587 krb5_creds *krbtgt, 588 krb5_principal impersonate_principal, 589 Ticket *second_ticket, 590 krb5_creds *out_creds) 591 { 592 krb5_error_code ret; 593 594 ret = get_cred_kdc_usage(context, id, flags, addresses, in_creds, 595 krbtgt, impersonate_principal, second_ticket, 596 out_creds, KRB5_KU_TGS_REQ_AUTH); 597 if (ret == KRB5KRB_AP_ERR_BAD_INTEGRITY) { 598 krb5_clear_error_string (context); 599 ret = get_cred_kdc_usage(context, id, flags, addresses, in_creds, 600 krbtgt, impersonate_principal, second_ticket, 601 out_creds, KRB5_KU_AP_REQ_AUTH); 602 } 603 return ret; 604 } 605 606 /* same as above, just get local addresses first */ 607 608 static krb5_error_code 609 get_cred_kdc_la(krb5_context context, krb5_ccache id, krb5_kdc_flags flags, 610 krb5_creds *in_creds, krb5_creds *krbtgt, 611 krb5_principal impersonate_principal, Ticket *second_ticket, 612 krb5_creds *out_creds) 613 { 614 krb5_error_code ret; 615 krb5_addresses addresses, *addrs = &addresses; 616 617 krb5_get_all_client_addrs(context, &addresses); 618 /* XXX this sucks. */ 619 if(addresses.len == 0) 620 addrs = NULL; 621 ret = get_cred_kdc(context, id, flags, addrs, 622 in_creds, krbtgt, impersonate_principal, second_ticket, 623 out_creds); 624 krb5_free_addresses(context, &addresses); 625 return ret; 626 } 627 628 krb5_error_code KRB5_LIB_FUNCTION 629 krb5_get_kdc_cred(krb5_context context, 630 krb5_ccache id, 631 krb5_kdc_flags flags, 632 krb5_addresses *addresses, 633 Ticket *second_ticket, 634 krb5_creds *in_creds, 635 krb5_creds **out_creds 636 ) 637 { 638 krb5_error_code ret; 639 krb5_creds *krbtgt; 640 641 *out_creds = calloc(1, sizeof(**out_creds)); 642 if(*out_creds == NULL) { 643 krb5_set_error_string(context, "malloc: out of memory"); 644 return ENOMEM; 645 } 646 ret = _krb5_get_krbtgt (context, 647 id, 648 in_creds->server->realm, 649 &krbtgt); 650 if(ret) { 651 free(*out_creds); 652 return ret; 653 } 654 ret = get_cred_kdc(context, id, flags, addresses, 655 in_creds, krbtgt, NULL, NULL, *out_creds); 656 krb5_free_creds (context, krbtgt); 657 if(ret) 658 free(*out_creds); 659 return ret; 660 } 661 662 static void 663 not_found(krb5_context context, krb5_const_principal p) 664 { 665 krb5_error_code ret; 666 char *str; 667 668 ret = krb5_unparse_name(context, p, &str); 669 if(ret) { 670 krb5_clear_error_string(context); 671 return; 672 } 673 krb5_set_error_string(context, "Matching credential (%s) not found", str); 674 free(str); 675 } 676 677 static krb5_error_code 678 find_cred(krb5_context context, 679 krb5_ccache id, 680 krb5_principal server, 681 krb5_creds **tgts, 682 krb5_creds *out_creds) 683 { 684 krb5_error_code ret; 685 krb5_creds mcreds; 686 687 krb5_cc_clear_mcred(&mcreds); 688 mcreds.server = server; 689 ret = krb5_cc_retrieve_cred(context, id, KRB5_TC_DONT_MATCH_REALM, 690 &mcreds, out_creds); 691 if(ret == 0) 692 return 0; 693 while(tgts && *tgts){ 694 if(krb5_compare_creds(context, KRB5_TC_DONT_MATCH_REALM, 695 &mcreds, *tgts)){ 696 ret = krb5_copy_creds_contents(context, *tgts, out_creds); 697 return ret; 698 } 699 tgts++; 700 } 701 not_found(context, server); 702 return KRB5_CC_NOTFOUND; 703 } 704 705 static krb5_error_code 706 add_cred(krb5_context context, krb5_creds ***tgts, krb5_creds *tkt) 707 { 708 int i; 709 krb5_error_code ret; 710 krb5_creds **tmp = *tgts; 711 712 for(i = 0; tmp && tmp[i]; i++); /* XXX */ 713 tmp = realloc(tmp, (i+2)*sizeof(*tmp)); 714 if(tmp == NULL) { 715 krb5_set_error_string(context, "malloc: out of memory"); 716 return ENOMEM; 717 } 718 *tgts = tmp; 719 ret = krb5_copy_creds(context, tkt, &tmp[i]); 720 tmp[i+1] = NULL; 721 return ret; 722 } 723 724 /* 725 get_cred(server) 726 creds = cc_get_cred(server) 727 if(creds) return creds 728 tgt = cc_get_cred(krbtgt/server_realm@any_realm) 729 if(tgt) 730 return get_cred_tgt(server, tgt) 731 if(client_realm == server_realm) 732 return NULL 733 tgt = get_cred(krbtgt/server_realm@client_realm) 734 while(tgt_inst != server_realm) 735 tgt = get_cred(krbtgt/server_realm@tgt_inst) 736 return get_cred_tgt(server, tgt) 737 */ 738 739 static krb5_error_code 740 get_cred_from_kdc_flags(krb5_context context, 741 krb5_kdc_flags flags, 742 krb5_ccache ccache, 743 krb5_creds *in_creds, 744 krb5_principal impersonate_principal, 745 Ticket *second_ticket, 746 krb5_creds **out_creds, 747 krb5_creds ***ret_tgts) 748 { 749 krb5_error_code ret; 750 krb5_creds *tgt, tmp_creds; 751 krb5_const_realm client_realm, server_realm, try_realm; 752 753 *out_creds = NULL; 754 755 client_realm = krb5_principal_get_realm(context, in_creds->client); 756 server_realm = krb5_principal_get_realm(context, in_creds->server); 757 memset(&tmp_creds, 0, sizeof(tmp_creds)); 758 ret = krb5_copy_principal(context, in_creds->client, &tmp_creds.client); 759 if(ret) 760 return ret; 761 762 try_realm = krb5_config_get_string(context, NULL, "capaths", 763 client_realm, server_realm, NULL); 764 765 #if 1 766 /* XXX remove in future release */ 767 if(try_realm == NULL) 768 try_realm = krb5_config_get_string(context, NULL, "libdefaults", 769 "capath", server_realm, NULL); 770 #endif 771 772 if (try_realm == NULL) 773 try_realm = client_realm; 774 775 ret = krb5_make_principal(context, 776 &tmp_creds.server, 777 try_realm, 778 KRB5_TGS_NAME, 779 server_realm, 780 NULL); 781 if(ret){ 782 krb5_free_principal(context, tmp_creds.client); 783 return ret; 784 } 785 { 786 krb5_creds tgts; 787 /* XXX try krb5_cc_retrieve_cred first? */ 788 ret = find_cred(context, ccache, tmp_creds.server, 789 *ret_tgts, &tgts); 790 if(ret == 0){ 791 *out_creds = calloc(1, sizeof(**out_creds)); 792 if(*out_creds == NULL) { 793 krb5_set_error_string(context, "malloc: out of memory"); 794 ret = ENOMEM; 795 } else { 796 krb5_boolean noaddr; 797 798 krb5_appdefault_boolean(context, NULL, tgts.server->realm, 799 "no-addresses", FALSE, &noaddr); 800 801 if (noaddr) 802 ret = get_cred_kdc(context, ccache, flags, NULL, 803 in_creds, &tgts, 804 impersonate_principal, 805 second_ticket, 806 *out_creds); 807 else 808 ret = get_cred_kdc_la(context, ccache, flags, 809 in_creds, &tgts, 810 impersonate_principal, 811 second_ticket, 812 *out_creds); 813 if (ret) { 814 free (*out_creds); 815 *out_creds = NULL; 816 } 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 not_found(context, in_creds->server); 826 return KRB5_CC_NOTFOUND; 827 } 828 /* XXX this can loop forever */ 829 while(1){ 830 heim_general_string tgt_inst; 831 832 ret = get_cred_from_kdc_flags(context, flags, ccache, &tmp_creds, 833 NULL, NULL, &tgt, ret_tgts); 834 if(ret) { 835 krb5_free_principal(context, tmp_creds.server); 836 krb5_free_principal(context, tmp_creds.client); 837 return ret; 838 } 839 ret = add_cred(context, ret_tgts, tgt); 840 if(ret) { 841 krb5_free_principal(context, tmp_creds.server); 842 krb5_free_principal(context, tmp_creds.client); 843 return ret; 844 } 845 tgt_inst = tgt->server->name.name_string.val[1]; 846 if(strcmp(tgt_inst, server_realm) == 0) 847 break; 848 krb5_free_principal(context, tmp_creds.server); 849 ret = krb5_make_principal(context, &tmp_creds.server, 850 tgt_inst, KRB5_TGS_NAME, server_realm, NULL); 851 if(ret) { 852 krb5_free_principal(context, tmp_creds.server); 853 krb5_free_principal(context, tmp_creds.client); 854 return ret; 855 } 856 ret = krb5_free_creds(context, tgt); 857 if(ret) { 858 krb5_free_principal(context, tmp_creds.server); 859 krb5_free_principal(context, tmp_creds.client); 860 return ret; 861 } 862 } 863 864 krb5_free_principal(context, tmp_creds.server); 865 krb5_free_principal(context, tmp_creds.client); 866 *out_creds = calloc(1, sizeof(**out_creds)); 867 if(*out_creds == NULL) { 868 krb5_set_error_string(context, "malloc: out of memory"); 869 ret = ENOMEM; 870 } else { 871 krb5_boolean noaddr; 872 873 krb5_appdefault_boolean(context, NULL, tgt->server->realm, 874 "no-addresses", KRB5_ADDRESSLESS_DEFAULT, 875 &noaddr); 876 if (noaddr) 877 ret = get_cred_kdc (context, ccache, flags, NULL, 878 in_creds, tgt, NULL, NULL, 879 *out_creds); 880 else 881 ret = get_cred_kdc_la(context, ccache, flags, 882 in_creds, tgt, NULL, NULL, 883 *out_creds); 884 if (ret) { 885 free (*out_creds); 886 *out_creds = NULL; 887 } 888 } 889 krb5_free_creds(context, tgt); 890 return ret; 891 } 892 893 krb5_error_code KRB5_LIB_FUNCTION 894 krb5_get_cred_from_kdc_opt(krb5_context context, 895 krb5_ccache ccache, 896 krb5_creds *in_creds, 897 krb5_creds **out_creds, 898 krb5_creds ***ret_tgts, 899 krb5_flags flags) 900 { 901 krb5_kdc_flags f; 902 f.i = flags; 903 return get_cred_from_kdc_flags(context, f, ccache, 904 in_creds, NULL, NULL, 905 out_creds, ret_tgts); 906 } 907 908 krb5_error_code KRB5_LIB_FUNCTION 909 krb5_get_cred_from_kdc(krb5_context context, 910 krb5_ccache ccache, 911 krb5_creds *in_creds, 912 krb5_creds **out_creds, 913 krb5_creds ***ret_tgts) 914 { 915 return krb5_get_cred_from_kdc_opt(context, ccache, 916 in_creds, out_creds, ret_tgts, 0); 917 } 918 919 920 krb5_error_code KRB5_LIB_FUNCTION 921 krb5_get_credentials_with_flags(krb5_context context, 922 krb5_flags options, 923 krb5_kdc_flags flags, 924 krb5_ccache ccache, 925 krb5_creds *in_creds, 926 krb5_creds **out_creds) 927 { 928 krb5_error_code ret; 929 krb5_creds **tgts; 930 krb5_creds *res_creds; 931 int i; 932 933 *out_creds = NULL; 934 res_creds = calloc(1, sizeof(*res_creds)); 935 if (res_creds == NULL) { 936 krb5_set_error_string(context, "malloc: out of memory"); 937 return ENOMEM; 938 } 939 940 if (in_creds->session.keytype) 941 options |= KRB5_TC_MATCH_KEYTYPE; 942 943 /* 944 * If we got a credential, check if credential is expired before 945 * returning it. 946 */ 947 ret = krb5_cc_retrieve_cred(context, 948 ccache, 949 in_creds->session.keytype ? 950 KRB5_TC_MATCH_KEYTYPE : 0, 951 in_creds, res_creds); 952 /* 953 * If we got a credential, check if credential is expired before 954 * returning it, but only if KRB5_GC_EXPIRED_OK is not set. 955 */ 956 if (ret == 0) { 957 krb5_timestamp timeret; 958 959 /* If expired ok, don't bother checking */ 960 if(options & KRB5_GC_EXPIRED_OK) { 961 *out_creds = res_creds; 962 return 0; 963 } 964 965 krb5_timeofday(context, &timeret); 966 if(res_creds->times.endtime > timeret) { 967 *out_creds = res_creds; 968 return 0; 969 } 970 if(options & KRB5_GC_CACHED) 971 krb5_cc_remove_cred(context, ccache, 0, res_creds); 972 973 } else if(ret != KRB5_CC_END) { 974 free(res_creds); 975 return ret; 976 } 977 free(res_creds); 978 if(options & KRB5_GC_CACHED) { 979 not_found(context, in_creds->server); 980 return KRB5_CC_NOTFOUND; 981 } 982 if(options & KRB5_GC_USER_USER) 983 flags.b.enc_tkt_in_skey = 1; 984 if (flags.b.enc_tkt_in_skey) 985 options |= KRB5_GC_NO_STORE; 986 987 tgts = NULL; 988 ret = get_cred_from_kdc_flags(context, flags, ccache, 989 in_creds, NULL, NULL, out_creds, &tgts); 990 for(i = 0; tgts && tgts[i]; i++) { 991 krb5_cc_store_cred(context, ccache, tgts[i]); 992 krb5_free_creds(context, tgts[i]); 993 } 994 free(tgts); 995 if(ret == 0 && (options & KRB5_GC_NO_STORE) == 0) 996 krb5_cc_store_cred(context, ccache, *out_creds); 997 return ret; 998 } 999 1000 krb5_error_code KRB5_LIB_FUNCTION 1001 krb5_get_credentials(krb5_context context, 1002 krb5_flags options, 1003 krb5_ccache ccache, 1004 krb5_creds *in_creds, 1005 krb5_creds **out_creds) 1006 { 1007 krb5_kdc_flags flags; 1008 flags.i = 0; 1009 return krb5_get_credentials_with_flags(context, options, flags, 1010 ccache, in_creds, out_creds); 1011 } 1012 1013 struct krb5_get_creds_opt_data { 1014 krb5_principal self; 1015 krb5_flags options; 1016 krb5_enctype enctype; 1017 Ticket *ticket; 1018 }; 1019 1020 1021 krb5_error_code KRB5_LIB_FUNCTION 1022 krb5_get_creds_opt_alloc(krb5_context context, krb5_get_creds_opt *opt) 1023 { 1024 *opt = calloc(1, sizeof(**opt)); 1025 if (*opt == NULL) { 1026 krb5_set_error_string(context, "malloc: out of memory"); 1027 return ENOMEM; 1028 } 1029 return 0; 1030 } 1031 1032 void KRB5_LIB_FUNCTION 1033 krb5_get_creds_opt_free(krb5_context context, krb5_get_creds_opt opt) 1034 { 1035 if (opt->self) 1036 krb5_free_principal(context, opt->self); 1037 memset(opt, 0, sizeof(*opt)); 1038 free(opt); 1039 } 1040 1041 void KRB5_LIB_FUNCTION 1042 krb5_get_creds_opt_set_options(krb5_context context, 1043 krb5_get_creds_opt opt, 1044 krb5_flags options) 1045 { 1046 opt->options = options; 1047 } 1048 1049 void KRB5_LIB_FUNCTION 1050 krb5_get_creds_opt_add_options(krb5_context context, 1051 krb5_get_creds_opt opt, 1052 krb5_flags options) 1053 { 1054 opt->options |= options; 1055 } 1056 1057 void KRB5_LIB_FUNCTION 1058 krb5_get_creds_opt_set_enctype(krb5_context context, 1059 krb5_get_creds_opt opt, 1060 krb5_enctype enctype) 1061 { 1062 opt->enctype = enctype; 1063 } 1064 1065 krb5_error_code KRB5_LIB_FUNCTION 1066 krb5_get_creds_opt_set_impersonate(krb5_context context, 1067 krb5_get_creds_opt opt, 1068 krb5_const_principal self) 1069 { 1070 if (opt->self) 1071 krb5_free_principal(context, opt->self); 1072 return krb5_copy_principal(context, self, &opt->self); 1073 } 1074 1075 krb5_error_code KRB5_LIB_FUNCTION 1076 krb5_get_creds_opt_set_ticket(krb5_context context, 1077 krb5_get_creds_opt opt, 1078 const Ticket *ticket) 1079 { 1080 if (opt->ticket) { 1081 free_Ticket(opt->ticket); 1082 free(opt->ticket); 1083 opt->ticket = NULL; 1084 } 1085 if (ticket) { 1086 krb5_error_code ret; 1087 1088 opt->ticket = malloc(sizeof(*ticket)); 1089 if (opt->ticket == NULL) { 1090 krb5_set_error_string(context, "malloc: out of memory"); 1091 return ENOMEM; 1092 } 1093 ret = copy_Ticket(ticket, opt->ticket); 1094 if (ret) { 1095 free(opt->ticket); 1096 opt->ticket = NULL; 1097 krb5_set_error_string(context, "malloc: out of memory"); 1098 return ret; 1099 } 1100 } 1101 return 0; 1102 } 1103 1104 1105 1106 krb5_error_code KRB5_LIB_FUNCTION 1107 krb5_get_creds(krb5_context context, 1108 krb5_get_creds_opt opt, 1109 krb5_ccache ccache, 1110 krb5_const_principal inprinc, 1111 krb5_creds **out_creds) 1112 { 1113 krb5_kdc_flags flags; 1114 krb5_flags options; 1115 krb5_creds in_creds; 1116 krb5_error_code ret; 1117 krb5_creds **tgts; 1118 krb5_creds *res_creds; 1119 int i; 1120 1121 memset(&in_creds, 0, sizeof(in_creds)); 1122 in_creds.server = rk_UNCONST(inprinc); 1123 1124 ret = krb5_cc_get_principal(context, ccache, &in_creds.client); 1125 if (ret) 1126 return ret; 1127 1128 options = opt->options; 1129 flags.i = 0; 1130 1131 *out_creds = NULL; 1132 res_creds = calloc(1, sizeof(*res_creds)); 1133 if (res_creds == NULL) { 1134 krb5_free_principal(context, in_creds.client); 1135 krb5_set_error_string(context, "malloc: out of memory"); 1136 return ENOMEM; 1137 } 1138 1139 if (opt->enctype) { 1140 in_creds.session.keytype = opt->enctype; 1141 options |= KRB5_TC_MATCH_KEYTYPE; 1142 } 1143 1144 /* 1145 * If we got a credential, check if credential is expired before 1146 * returning it. 1147 */ 1148 ret = krb5_cc_retrieve_cred(context, 1149 ccache, 1150 opt->enctype ? KRB5_TC_MATCH_KEYTYPE : 0, 1151 &in_creds, res_creds); 1152 /* 1153 * If we got a credential, check if credential is expired before 1154 * returning it, but only if KRB5_GC_EXPIRED_OK is not set. 1155 */ 1156 if (ret == 0) { 1157 krb5_timestamp timeret; 1158 1159 /* If expired ok, don't bother checking */ 1160 if(options & KRB5_GC_EXPIRED_OK) { 1161 *out_creds = res_creds; 1162 krb5_free_principal(context, in_creds.client); 1163 return 0; 1164 } 1165 1166 krb5_timeofday(context, &timeret); 1167 if(res_creds->times.endtime > timeret) { 1168 *out_creds = res_creds; 1169 krb5_free_principal(context, in_creds.client); 1170 return 0; 1171 } 1172 if(options & KRB5_GC_CACHED) 1173 krb5_cc_remove_cred(context, ccache, 0, res_creds); 1174 1175 } else if(ret != KRB5_CC_END) { 1176 free(res_creds); 1177 krb5_free_principal(context, in_creds.client); 1178 return ret; 1179 } 1180 free(res_creds); 1181 if(options & KRB5_GC_CACHED) { 1182 not_found(context, in_creds.server); 1183 krb5_free_principal(context, in_creds.client); 1184 return KRB5_CC_NOTFOUND; 1185 } 1186 if(options & KRB5_GC_USER_USER) { 1187 flags.b.enc_tkt_in_skey = 1; 1188 options |= KRB5_GC_NO_STORE; 1189 } 1190 if (options & KRB5_GC_FORWARDABLE) 1191 flags.b.forwardable = 1; 1192 if (options & KRB5_GC_NO_TRANSIT_CHECK) 1193 flags.b.disable_transited_check = 1; 1194 if (options & KRB5_GC_CONSTRAINED_DELEGATION) { 1195 flags.b.request_anonymous = 1; /* XXX ARGH confusion */ 1196 flags.b.constrained_delegation = 1; 1197 } 1198 1199 tgts = NULL; 1200 ret = get_cred_from_kdc_flags(context, flags, ccache, 1201 &in_creds, opt->self, opt->ticket, 1202 out_creds, &tgts); 1203 krb5_free_principal(context, in_creds.client); 1204 for(i = 0; tgts && tgts[i]; i++) { 1205 krb5_cc_store_cred(context, ccache, tgts[i]); 1206 krb5_free_creds(context, tgts[i]); 1207 } 1208 free(tgts); 1209 if(ret == 0 && (options & KRB5_GC_NO_STORE) == 0) 1210 krb5_cc_store_cred(context, ccache, *out_creds); 1211 return ret; 1212 } 1213 1214 /* 1215 * 1216 */ 1217 1218 krb5_error_code KRB5_LIB_FUNCTION 1219 krb5_get_renewed_creds(krb5_context context, 1220 krb5_creds *creds, 1221 krb5_const_principal client, 1222 krb5_ccache ccache, 1223 const char *in_tkt_service) 1224 { 1225 krb5_error_code ret; 1226 krb5_kdc_flags flags; 1227 krb5_creds in, *template, *out = NULL; 1228 1229 memset(&in, 0, sizeof(in)); 1230 memset(creds, 0, sizeof(*creds)); 1231 1232 ret = krb5_copy_principal(context, client, &in.client); 1233 if (ret) 1234 return ret; 1235 1236 if (in_tkt_service) { 1237 ret = krb5_parse_name(context, in_tkt_service, &in.server); 1238 if (ret) { 1239 krb5_free_principal(context, in.client); 1240 return ret; 1241 } 1242 } else { 1243 const char *realm = krb5_principal_get_realm(context, client); 1244 1245 ret = krb5_make_principal(context, &in.server, realm, KRB5_TGS_NAME, 1246 realm, NULL); 1247 if (ret) { 1248 krb5_free_principal(context, in.client); 1249 return ret; 1250 } 1251 } 1252 1253 flags.i = 0; 1254 flags.b.renewable = flags.b.renew = 1; 1255 1256 /* 1257 * Get template from old credential cache for the same entry, if 1258 * this failes, no worries. 1259 */ 1260 ret = krb5_get_credentials(context, KRB5_GC_CACHED, ccache, &in, &template); 1261 if (ret == 0) { 1262 flags.b.forwardable = template->flags.b.forwardable; 1263 flags.b.proxiable = template->flags.b.proxiable; 1264 krb5_free_creds (context, template); 1265 } 1266 1267 ret = krb5_get_kdc_cred(context, ccache, flags, NULL, NULL, &in, &out); 1268 krb5_free_principal(context, in.client); 1269 krb5_free_principal(context, in.server); 1270 if (ret) 1271 return ret; 1272 1273 ret = krb5_copy_creds_contents(context, out, creds); 1274 krb5_free_creds(context, out); 1275 1276 return ret; 1277 } 1278