1 /* 2 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 3 * Use is subject to license terms. 4 */ 5 6 #pragma ident "%Z%%M% %I% %E% SMI" 7 8 /* 9 * lib/krb5/krb/get_in_tkt.c 10 * 11 * Copyright 1990,1991, 2003 by the Massachusetts Institute of Technology. 12 * All Rights Reserved. 13 * 14 * Export of this software from the United States of America may 15 * require a specific license from the United States Government. 16 * It is the responsibility of any person or organization contemplating 17 * export to obtain such a license before exporting. 18 * 19 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and 20 * distribute this software and its documentation for any purpose and 21 * without fee is hereby granted, provided that the above copyright 22 * notice appear in all copies and that both that copyright notice and 23 * this permission notice appear in supporting documentation, and that 24 * the name of M.I.T. not be used in advertising or publicity pertaining 25 * to distribution of the software without specific, written prior 26 * permission. Furthermore if you modify this software you must label 27 * your software as modified software and not distribute it in such a 28 * fashion that it might be confused with the original M.I.T. software. 29 * M.I.T. makes no representations about the suitability of 30 * this software for any purpose. It is provided "as is" without express 31 * or implied warranty. 32 * 33 * 34 * krb5_get_in_tkt() 35 */ 36 37 #include <string.h> 38 39 #include <k5-int.h> 40 #include <krb5.h> 41 #include <int-proto.h> 42 #include <os-proto.h> 43 44 /* 45 All-purpose initial ticket routine, usually called via 46 krb5_get_in_tkt_with_password or krb5_get_in_tkt_with_skey. 47 48 Attempts to get an initial ticket for creds->client to use server 49 creds->server, (realm is taken from creds->client), with options 50 options, and using creds->times.starttime, creds->times.endtime, 51 creds->times.renew_till as from, till, and rtime. 52 creds->times.renew_till is ignored unless the RENEWABLE option is requested. 53 54 key_proc is called to fill in the key to be used for decryption. 55 keyseed is passed on to key_proc. 56 57 decrypt_proc is called to perform the decryption of the response (the 58 encrypted part is in dec_rep->enc_part; the decrypted part should be 59 allocated and filled into dec_rep->enc_part2 60 arg is passed on to decrypt_proc. 61 62 If addrs is non-NULL, it is used for the addresses requested. If it is 63 null, the system standard addresses are used. 64 65 A succesful call will place the ticket in the credentials cache ccache 66 and fill in creds with the ticket information used/returned.. 67 68 returns system errors, encryption errors 69 70 */ 71 72 73 /* some typedef's for the function args to make things look a bit cleaner */ 74 75 typedef krb5_error_code (*git_key_proc) (krb5_context, 76 const krb5_enctype, 77 krb5_data *, 78 krb5_const_pointer, 79 krb5_keyblock **); 80 81 typedef krb5_error_code (*git_decrypt_proc) (krb5_context, 82 const krb5_keyblock *, 83 krb5_const_pointer, 84 krb5_kdc_rep *); 85 86 static krb5_error_code make_preauth_list (krb5_context, 87 krb5_preauthtype *, 88 int, krb5_pa_data ***); 89 90 /* 91 * This function performs 32 bit bounded addition so we can generate 92 * lifetimes without overflowing krb5_int32 93 */ 94 static krb5_int32 krb5int_addint32 (krb5_int32 x, krb5_int32 y) 95 { 96 if ((x > 0) && (y > (KRB5_INT32_MAX - x))) { 97 /* sum will be be greater than KRB5_INT32_MAX */ 98 return KRB5_INT32_MAX; 99 } else if ((x < 0) && (y < (KRB5_INT32_MIN - x))) { 100 /* sum will be less than KRB5_INT32_MIN */ 101 return KRB5_INT32_MIN; 102 } 103 104 return x + y; 105 } 106 107 /* 108 * This function sends a request to the KDC, and gets back a response; 109 * the response is parsed into ret_err_reply or ret_as_reply if the 110 * reponse is a KRB_ERROR or a KRB_AS_REP packet. If it is some other 111 * unexpected response, an error is returned. 112 */ 113 static krb5_error_code 114 send_as_request(krb5_context context, 115 krb5_kdc_req *request, 116 krb5_timestamp *time_now, 117 krb5_error ** ret_err_reply, 118 krb5_kdc_rep ** ret_as_reply, 119 int *use_master) 120 { 121 krb5_kdc_rep *as_reply = 0; 122 krb5_error_code retval; 123 krb5_data *packet = 0; 124 krb5_data reply; 125 char k4_version; /* same type as *(krb5_data::data) */ 126 int tcp_only = 0; 127 128 reply.data = 0; 129 130 if ((retval = krb5_timeofday(context, time_now))) 131 goto cleanup; 132 133 /* 134 * XXX we know they are the same size... and we should do 135 * something better than just the current time 136 */ 137 request->nonce = (krb5_int32) *time_now; 138 139 /* encode & send to KDC */ 140 if ((retval = encode_krb5_as_req(request, &packet)) != 0) 141 goto cleanup; 142 143 k4_version = packet->data[0]; 144 send_again: 145 retval = krb5_sendto_kdc(context, packet, 146 krb5_princ_realm(context, request->client), 147 &reply, use_master, tcp_only); 148 if (retval) 149 goto cleanup; 150 151 /* now decode the reply...could be error or as_rep */ 152 if (krb5_is_krb_error(&reply)) { 153 krb5_error *err_reply; 154 155 if ((retval = decode_krb5_error(&reply, &err_reply))) 156 /* some other error code--??? */ 157 goto cleanup; 158 159 if (ret_err_reply) { 160 if (err_reply->error == KRB_ERR_RESPONSE_TOO_BIG 161 && tcp_only == 0) { 162 tcp_only = 1; 163 krb5_free_error(context, err_reply); 164 free(reply.data); 165 reply.data = 0; 166 goto send_again; 167 } 168 *ret_err_reply = err_reply; 169 } else 170 krb5_free_error(context, err_reply); 171 goto cleanup; 172 } 173 174 /* 175 * Check to make sure it isn't a V4 reply. 176 */ 177 if (!krb5_is_as_rep(&reply)) { 178 /* these are in <kerberosIV/prot.h> as well but it isn't worth including. */ 179 #define V4_KRB_PROT_VERSION 4 180 #define V4_AUTH_MSG_ERR_REPLY (5<<1) 181 /* check here for V4 reply */ 182 unsigned int t_switch; 183 184 /* From v4 g_in_tkt.c: This used to be 185 switch (pkt_msg_type(rpkt) & ~1) { 186 but SCO 3.2v4 cc compiled that incorrectly. */ 187 t_switch = reply.data[1]; 188 t_switch &= ~1; 189 190 if (t_switch == V4_AUTH_MSG_ERR_REPLY 191 && (reply.data[0] == V4_KRB_PROT_VERSION 192 || reply.data[0] == k4_version)) { 193 retval = KRB5KRB_AP_ERR_V4_REPLY; 194 } else { 195 retval = KRB5KRB_AP_ERR_MSG_TYPE; 196 } 197 goto cleanup; 198 } 199 200 /* It must be a KRB_AS_REP message, or an bad returned packet */ 201 if ((retval = decode_krb5_as_rep(&reply, &as_reply))) 202 /* some other error code ??? */ 203 goto cleanup; 204 205 if (as_reply->msg_type != KRB5_AS_REP) { 206 retval = KRB5KRB_AP_ERR_MSG_TYPE; 207 krb5_free_kdc_rep(context, as_reply); 208 goto cleanup; 209 } 210 211 if (ret_as_reply) 212 *ret_as_reply = as_reply; 213 else 214 krb5_free_kdc_rep(context, as_reply); 215 216 cleanup: 217 if (packet) 218 krb5_free_data(context, packet); 219 if (reply.data) 220 free(reply.data); 221 return retval; 222 } 223 224 static krb5_error_code 225 decrypt_as_reply(krb5_context context, 226 krb5_kdc_req *request, 227 krb5_kdc_rep *as_reply, 228 git_key_proc key_proc, 229 krb5_const_pointer keyseed, 230 krb5_keyblock * key, 231 git_decrypt_proc decrypt_proc, 232 krb5_const_pointer decryptarg) 233 { 234 krb5_error_code retval; 235 krb5_keyblock * decrypt_key = 0; 236 krb5_data salt; 237 238 KRB5_LOG0(KRB5_INFO, "decrypt_as_reply() start"); 239 240 if (as_reply->enc_part2) 241 return 0; 242 243 if (key) 244 decrypt_key = key; 245 else if (request != NULL) { 246 if ((retval = krb5_principal2salt(context, request->client, &salt))) 247 return(retval); 248 249 retval = (*key_proc)(context, as_reply->enc_part.enctype, 250 &salt, keyseed, &decrypt_key); 251 krb5_xfree(salt.data); 252 if (retval) 253 goto cleanup; 254 } else { 255 KRB5_LOG0(KRB5_ERR, "decrypt_as_reply() end, " 256 "error key == NULL and request == NULL"); 257 return (EINVAL); 258 } 259 260 /* Solaris kerberos: Overwriting the decrypt_key->enctype because the 261 * decrypt key's enctype may not be an exact match with the enctype that the 262 * KDC used to encrypt this part of the AS reply. This assumes the 263 * as_reply->enc_part.enctype has been validated which is done by checking 264 * to see if the enctype that the KDC sent back in the as_reply is one of 265 * the enctypes originally requested. Note, if request is NULL then the 266 * as_reply->enc_part.enctype could not be validated. 267 */ 268 269 if (request != NULL) { 270 if (is_in_keytype(request->ktype, request->nktypes, 271 as_reply->enc_part.enctype)) { 272 273 decrypt_key->enctype = as_reply->enc_part.enctype; 274 275 } else { 276 KRB5_LOG0(KRB5_ERR, "decrypt_as_reply() end, " 277 "error is_in_keytype() returned false"); 278 retval = KRB5_BAD_ENCTYPE; 279 goto cleanup; 280 } 281 } 282 283 if ((retval = (*decrypt_proc)(context, decrypt_key, decryptarg, as_reply))){ 284 KRB5_LOG(KRB5_ERR, "decrypt_as_reply() error (*decrypt_proc)() retval " 285 "= %d", retval); 286 goto cleanup; 287 } 288 289 cleanup: 290 if (!key && decrypt_key) 291 krb5_free_keyblock(context, decrypt_key); 292 293 KRB5_LOG(KRB5_INFO, "decrypt_as_reply() end, retval = %d", retval); 294 295 return (retval); 296 } 297 298 static krb5_error_code 299 verify_as_reply(krb5_context context, 300 krb5_timestamp time_now, 301 krb5_kdc_req *request, 302 krb5_kdc_rep *as_reply) 303 { 304 krb5_error_code retval; 305 306 /* check the contents for sanity: */ 307 if (!as_reply->enc_part2->times.starttime) 308 as_reply->enc_part2->times.starttime = 309 as_reply->enc_part2->times.authtime; 310 311 if (!krb5_principal_compare(context, as_reply->client, request->client) 312 || !krb5_principal_compare(context, as_reply->enc_part2->server, request->server) 313 || !krb5_principal_compare(context, as_reply->ticket->server, request->server) 314 || (request->nonce != as_reply->enc_part2->nonce) 315 /* XXX check for extraneous flags */ 316 /* XXX || (!krb5_addresses_compare(context, addrs, as_reply->enc_part2->caddrs)) */ 317 || ((request->kdc_options & KDC_OPT_POSTDATED) && 318 (request->from != 0) && 319 (request->from != as_reply->enc_part2->times.starttime)) 320 || ((request->till != 0) && 321 (as_reply->enc_part2->times.endtime > request->till)) 322 || ((request->kdc_options & KDC_OPT_RENEWABLE) && 323 (request->rtime != 0) && 324 (as_reply->enc_part2->times.renew_till > request->rtime)) 325 || ((request->kdc_options & KDC_OPT_RENEWABLE_OK) && 326 !(request->kdc_options & KDC_OPT_RENEWABLE) && 327 (as_reply->enc_part2->flags & KDC_OPT_RENEWABLE) && 328 (request->till != 0) && 329 (as_reply->enc_part2->times.renew_till > request->till)) 330 ) 331 return KRB5_KDCREP_MODIFIED; 332 333 if (context->library_options & KRB5_LIBOPT_SYNC_KDCTIME) { 334 retval = krb5_set_real_time(context, 335 as_reply->enc_part2->times.authtime, 0); 336 if (retval) 337 return retval; 338 } else { 339 if ((request->from == 0) && 340 (labs(as_reply->enc_part2->times.starttime - time_now) 341 > context->clockskew)) 342 return (KRB5_KDCREP_SKEW); 343 } 344 return 0; 345 } 346 347 /*ARGSUSED*/ 348 static krb5_error_code 349 stash_as_reply(krb5_context context, 350 krb5_timestamp time_now, 351 krb5_kdc_req *request, 352 krb5_kdc_rep *as_reply, 353 krb5_creds * creds, 354 krb5_ccache ccache) 355 { 356 krb5_error_code retval; 357 krb5_data * packet; 358 krb5_principal client; 359 krb5_principal server; 360 361 client = NULL; 362 server = NULL; 363 364 if (!creds->client) 365 if ((retval = krb5_copy_principal(context, as_reply->client, &client))) 366 goto cleanup; 367 368 if (!creds->server) 369 if ((retval = krb5_copy_principal(context, as_reply->enc_part2->server, 370 &server))) 371 goto cleanup; 372 373 /* fill in the credentials */ 374 if ((retval = krb5_copy_keyblock_contents(context, 375 as_reply->enc_part2->session, 376 &creds->keyblock))) 377 goto cleanup; 378 379 creds->times = as_reply->enc_part2->times; 380 creds->is_skey = FALSE; /* this is an AS_REQ, so cannot 381 be encrypted in skey */ 382 creds->ticket_flags = as_reply->enc_part2->flags; 383 if ((retval = krb5_copy_addresses(context, as_reply->enc_part2->caddrs, 384 &creds->addresses))) 385 goto cleanup; 386 387 creds->second_ticket.length = 0; 388 creds->second_ticket.data = 0; 389 390 if ((retval = encode_krb5_ticket(as_reply->ticket, &packet))) 391 goto cleanup; 392 393 creds->ticket = *packet; 394 krb5_xfree(packet); 395 396 /* store it in the ccache! */ 397 if (ccache) 398 if ((retval = krb5_cc_store_cred(context, ccache, creds)) !=0) 399 goto cleanup; 400 401 if (!creds->client) 402 creds->client = client; 403 if (!creds->server) 404 creds->server = server; 405 406 cleanup: 407 if (retval) { 408 if (client) 409 krb5_free_principal(context, client); 410 if (server) 411 krb5_free_principal(context, server); 412 if (creds->keyblock.contents) { 413 memset((char *)creds->keyblock.contents, 0, 414 creds->keyblock.length); 415 krb5_xfree(creds->keyblock.contents); 416 creds->keyblock.contents = 0; 417 creds->keyblock.length = 0; 418 } 419 if (creds->ticket.data) { 420 krb5_xfree(creds->ticket.data); 421 creds->ticket.data = 0; 422 } 423 if (creds->addresses) { 424 krb5_free_addresses(context, creds->addresses); 425 creds->addresses = 0; 426 } 427 } 428 return (retval); 429 } 430 431 /*ARGSUSED*/ 432 static krb5_error_code 433 make_preauth_list(krb5_context context, 434 krb5_preauthtype * ptypes, 435 int nptypes, 436 krb5_pa_data *** ret_list) 437 { 438 krb5_preauthtype * ptypep; 439 krb5_pa_data ** preauthp; 440 int i; 441 442 if (nptypes < 0) { 443 for (nptypes=0, ptypep = ptypes; *ptypep; ptypep++, nptypes++) 444 ; 445 } 446 447 /* allocate space for a NULL to terminate the list */ 448 449 if ((preauthp = 450 (krb5_pa_data **) malloc((nptypes+1)*sizeof(krb5_pa_data *))) == NULL) 451 return(ENOMEM); 452 453 for (i=0; i<nptypes; i++) { 454 if ((preauthp[i] = 455 (krb5_pa_data *) malloc(sizeof(krb5_pa_data))) == NULL) { 456 for (; i>=0; i++) 457 free(preauthp[i]); 458 free(preauthp); 459 return (ENOMEM); 460 } 461 preauthp[i]->magic = KV5M_PA_DATA; 462 preauthp[i]->pa_type = ptypes[i]; 463 preauthp[i]->length = 0; 464 preauthp[i]->contents = 0; 465 } 466 467 /* fill in the terminating NULL */ 468 469 preauthp[nptypes] = NULL; 470 471 *ret_list = preauthp; 472 return 0; 473 } 474 475 #define MAX_IN_TKT_LOOPS 16 476 /* SUNW14resync - Solaris krb does not use this (appearently) */ 477 #if 0 478 static const krb5_enctype get_in_tkt_enctypes[] = { 479 ENCTYPE_DES3_CBC_SHA1, 480 ENCTYPE_ARCFOUR_HMAC, 481 ENCTYPE_DES_CBC_MD5, 482 ENCTYPE_DES_CBC_MD4, 483 ENCTYPE_DES_CBC_CRC, 484 0 485 }; 486 #endif 487 488 /* begin libdefaults parsing code. This should almost certainly move 489 somewhere else, but I don't know where the correct somewhere else 490 is yet. */ 491 492 /* XXX Duplicating this is annoying; try to work on a better way.*/ 493 static const char *const conf_yes[] = { 494 "y", "yes", "true", "t", "1", "on", 495 0, 496 }; 497 498 static const char *const conf_no[] = { 499 "n", "no", "false", "nil", "0", "off", 500 0, 501 }; 502 503 int 504 _krb5_conf_boolean(const char *s) 505 { 506 const char *const *p; 507 508 for(p=conf_yes; *p; p++) { 509 if (!strcasecmp(*p,s)) 510 return 1; 511 } 512 513 for(p=conf_no; *p; p++) { 514 if (!strcasecmp(*p,s)) 515 return 0; 516 } 517 518 /* Default to "no" */ 519 return 0; 520 } 521 522 static krb5_error_code 523 krb5_libdefault_string(krb5_context context, const krb5_data *realm, 524 const char *option, char **ret_value) 525 { 526 profile_t profile; 527 const char *names[5]; 528 char **nameval = NULL; 529 krb5_error_code retval; 530 char realmstr[1024]; 531 532 if (realm->length > sizeof(realmstr)-1) 533 return(EINVAL); 534 535 strncpy(realmstr, realm->data, realm->length); 536 realmstr[realm->length] = '\0'; 537 538 if (!context || (context->magic != KV5M_CONTEXT)) 539 return KV5M_CONTEXT; 540 541 profile = context->profile; 542 543 names[0] = "realms"; 544 545 /* 546 * Try number one: 547 * 548 * [realms] 549 * REALM = { 550 * option = <boolean> 551 * } 552 */ 553 554 names[1] = realmstr; 555 names[2] = option; 556 names[3] = 0; 557 retval = profile_get_values(profile, names, &nameval); 558 if (retval == 0 && nameval && nameval[0]) 559 goto goodbye; 560 561 /* 562 * Try number two: 563 * 564 * [libdefaults] 565 * option = <boolean> 566 */ 567 568 names[0] = "libdefaults"; 569 names[1] = option; 570 names[2] = 0; 571 retval = profile_get_values(profile, names, &nameval); 572 if (retval == 0 && nameval && nameval[0]) 573 goto goodbye; 574 575 goodbye: 576 if (!nameval) 577 return(ENOENT); 578 579 if (!nameval[0]) { 580 retval = ENOENT; 581 } else { 582 *ret_value = malloc(strlen(nameval[0]) + 1); 583 if (!*ret_value) 584 retval = ENOMEM; 585 else 586 strcpy(*ret_value, nameval[0]); 587 } 588 589 profile_free_list(nameval); 590 591 return retval; 592 } 593 594 /* not static so verify_init_creds() can call it */ 595 /* as well as the DNS code */ 596 597 krb5_error_code 598 krb5_libdefault_boolean(krb5_context context, const krb5_data *realm, 599 const char *option, int *ret_value) 600 { 601 char *string = NULL; 602 krb5_error_code retval; 603 604 retval = krb5_libdefault_string(context, realm, option, &string); 605 606 if (retval) 607 return(retval); 608 609 *ret_value = _krb5_conf_boolean(string); 610 free(string); 611 612 return(0); 613 } 614 615 krb5_error_code KRB5_CALLCONV 616 krb5_get_init_creds(krb5_context context, 617 krb5_creds *creds, 618 krb5_principal client, 619 krb5_prompter_fct prompter, 620 void *prompter_data, 621 krb5_deltat start_time, 622 char *in_tkt_service, 623 krb5_get_init_creds_opt *options, 624 krb5_gic_get_as_key_fct gak_fct, 625 void *gak_data, 626 int *use_master, 627 krb5_kdc_rep **as_reply) 628 { 629 krb5_error_code ret; 630 krb5_kdc_req request; 631 krb5_pa_data **padata; 632 int tempint; 633 char *tempstr = NULL; 634 krb5_deltat tkt_life; 635 krb5_deltat renew_life; 636 krb5_deltat max_life; 637 int loopcount; 638 krb5_data salt; 639 krb5_data s2kparams; 640 krb5_keyblock as_key; 641 krb5_error *err_reply; 642 krb5_kdc_rep *local_as_reply; 643 krb5_timestamp time_now; 644 krb5_enctype etype = 0; 645 646 /* initialize everything which will be freed at cleanup */ 647 648 s2kparams.data = NULL; 649 s2kparams.length = 0; 650 request.server = NULL; 651 request.ktype = NULL; 652 request.addresses = NULL; 653 request.padata = NULL; 654 padata = NULL; 655 salt.length = 0; 656 salt.data = NULL; 657 658 (void) memset(&as_key, 0, sizeof(as_key)); 659 660 local_as_reply = 0; 661 662 /* 663 * Set up the basic request structure 664 */ 665 request.magic = KV5M_KDC_REQ; 666 request.msg_type = KRB5_AS_REQ; 667 668 /* request.padata is filled in later */ 669 670 request.kdc_options = context->kdc_default_options; 671 672 /* forwardable */ 673 674 if (options && (options->flags & KRB5_GET_INIT_CREDS_OPT_FORWARDABLE)) 675 tempint = options->forwardable; 676 else if ((ret = krb5_libdefault_boolean(context, &client->realm, 677 "forwardable", &tempint)) == 0) 678 /*EMPTY*/ 679 ; 680 else 681 tempint = 0; 682 if (tempint) 683 request.kdc_options |= KDC_OPT_FORWARDABLE; 684 685 /* proxiable */ 686 687 if (options && (options->flags & KRB5_GET_INIT_CREDS_OPT_PROXIABLE)) 688 tempint = options->proxiable; 689 else if ((ret = krb5_libdefault_boolean(context, &client->realm, 690 "proxiable", &tempint)) == 0) 691 /*EMPTY*/ 692 ; 693 else 694 tempint = 0; 695 if (tempint) 696 request.kdc_options |= KDC_OPT_PROXIABLE; 697 698 /* allow_postdate */ 699 700 if (start_time > 0) 701 request.kdc_options |= (KDC_OPT_ALLOW_POSTDATE|KDC_OPT_POSTDATED); 702 703 /* ticket lifetime */ 704 705 if ((ret = krb5_timeofday(context, &request.from))) 706 goto cleanup; 707 request.from = krb5int_addint32(request.from, start_time); 708 709 if (options && (options->flags & KRB5_GET_INIT_CREDS_OPT_TKT_LIFE)) { 710 tkt_life = options->tkt_life; 711 } else if ((ret = krb5_libdefault_string(context, &client->realm, 712 "ticket_lifetime", &tempstr)) 713 == 0) { 714 if ((ret = krb5_string_to_deltat(tempstr, &tkt_life))) { 715 free(tempstr); 716 tempstr = NULL; 717 goto cleanup; 718 } 719 if (tempstr) { 720 free(tempstr); 721 tempstr = NULL; 722 } 723 } else { 724 /* this used to be hardcoded in kinit.c */ 725 tkt_life = 24*60*60; 726 } 727 request.till = krb5int_addint32(request.from, tkt_life); 728 729 /* renewable lifetime */ 730 731 if (options && (options->flags & KRB5_GET_INIT_CREDS_OPT_RENEW_LIFE)) { 732 renew_life = options->renew_life; 733 } else if ((ret = krb5_libdefault_string(context, &client->realm, 734 "renew_lifetime", &tempstr)) 735 == 0) { 736 if ((ret = krb5_string_to_deltat(tempstr, &renew_life))) { 737 free(tempstr); 738 goto cleanup; 739 } 740 if (tempstr) { 741 free(tempstr); 742 tempstr = NULL; 743 } 744 } else { 745 renew_life = 0; 746 } 747 if (renew_life > 0) 748 request.kdc_options |= KDC_OPT_RENEWABLE; 749 750 if (renew_life > 0) { 751 request.rtime = krb5int_addint32(request.from, renew_life); 752 if (request.rtime < request.till) { 753 /* don't ask for a smaller renewable time than the lifetime */ 754 request.rtime = request.till; 755 } 756 /* we are already asking for renewable tickets so strip this option */ 757 request.kdc_options &= ~(KDC_OPT_RENEWABLE_OK); 758 } else { 759 request.rtime = 0; 760 } 761 762 /* client */ 763 764 request.client = client; 765 766 /* service */ 767 768 if (in_tkt_service) { 769 /* this is ugly, because so are the data structures involved. I'm 770 in the library, so I'm going to manipulate the data structures 771 directly, otherwise, it will be worse. */ 772 773 if ((ret = krb5_parse_name(context, in_tkt_service, &request.server))) 774 goto cleanup; 775 776 /* stuff the client realm into the server principal. 777 realloc if necessary */ 778 if (request.server->realm.length < request.client->realm.length) 779 if ((request.server->realm.data = 780 (char *) realloc(request.server->realm.data, 781 request.client->realm.length)) == NULL) { 782 ret = ENOMEM; 783 goto cleanup; 784 } 785 786 request.server->realm.length = request.client->realm.length; 787 memcpy(request.server->realm.data, request.client->realm.data, 788 request.client->realm.length); 789 } else { 790 if ((ret = krb5_build_principal_ext(context, &request.server, 791 request.client->realm.length, 792 request.client->realm.data, 793 KRB5_TGS_NAME_SIZE, 794 KRB5_TGS_NAME, 795 request.client->realm.length, 796 request.client->realm.data, 797 0))) 798 goto cleanup; 799 } 800 801 /* nonce is filled in by send_as_request */ 802 803 if (options && (options->flags & KRB5_GET_INIT_CREDS_OPT_ETYPE_LIST)) { 804 request.ktype = options->etype_list; 805 request.nktypes = options->etype_list_length; 806 } else if ((ret = krb5_get_default_in_tkt_ktypes(context, 807 &request.ktype)) == 0) { 808 for (request.nktypes = 0; 809 request.ktype[request.nktypes]; 810 request.nktypes++) 811 ; 812 } else { 813 /* there isn't any useful default here. ret is set from above */ 814 goto cleanup; 815 } 816 817 if (options && (options->flags & KRB5_GET_INIT_CREDS_OPT_ADDRESS_LIST)) { 818 request.addresses = options->address_list; 819 } 820 /* it would be nice if this parsed out an address list, but 821 that would be work. */ 822 else if (((ret = krb5_libdefault_boolean(context, &client->realm, 823 "no_addresses", &tempint)) == 0) 824 || (tempint == 1)) { 825 /*EMPTY*/ 826 ; 827 } else if (((ret = krb5_libdefault_boolean(context, &client->realm, 828 "noaddresses", &tempint)) == 0) 829 || (tempint == 1)) { 830 /*EMPTY*/ 831 ; 832 } else { 833 if ((ret = krb5_os_localaddr(context, &request.addresses))) 834 goto cleanup; 835 } 836 837 request.authorization_data.ciphertext.length = 0; 838 request.authorization_data.ciphertext.data = 0; 839 request.unenc_authdata = 0; 840 request.second_ticket = 0; 841 842 /* set up the other state. */ 843 844 if (options && (options->flags & KRB5_GET_INIT_CREDS_OPT_PREAUTH_LIST)) { 845 if ((ret = make_preauth_list(context, options->preauth_list, 846 options->preauth_list_length, 847 &padata))) 848 goto cleanup; 849 } 850 851 /* the salt is allocated from somewhere, unless it is from the caller, 852 then it is a reference */ 853 854 if (options && (options->flags & KRB5_GET_INIT_CREDS_OPT_SALT)) { 855 salt = *options->salt; 856 } else { 857 salt.length = (unsigned int)-1; 858 salt.data = NULL; 859 } 860 861 /* now, loop processing preauth data and talking to the kdc */ 862 863 for (loopcount = 0; loopcount < MAX_IN_TKT_LOOPS; loopcount++) { 864 if (request.padata) { 865 krb5_free_pa_data(context, request.padata); 866 request.padata = NULL; 867 } 868 869 if ((ret = krb5_do_preauth(context, &request, 870 padata, &request.padata, 871 &salt, &s2kparams, &etype, &as_key, prompter, 872 prompter_data, gak_fct, gak_data))) 873 goto cleanup; 874 875 if (padata) { 876 krb5_free_pa_data(context, padata); 877 padata = 0; 878 } 879 880 err_reply = 0; 881 local_as_reply = 0; 882 if ((ret = send_as_request(context, &request, &time_now, &err_reply, 883 &local_as_reply, use_master))) 884 goto cleanup; 885 886 if (err_reply) { 887 if (err_reply->error == KDC_ERR_PREAUTH_REQUIRED && 888 err_reply->e_data.length > 0) { 889 ret = decode_krb5_padata_sequence(&err_reply->e_data, 890 &padata); 891 krb5_free_error(context, err_reply); 892 if (ret) 893 goto cleanup; 894 } else { 895 ret = (krb5_error_code) err_reply->error 896 + ERROR_TABLE_BASE_krb5; 897 krb5_free_error(context, err_reply); 898 goto cleanup; 899 } 900 } else if (local_as_reply) { 901 break; 902 } else { 903 ret = KRB5KRB_AP_ERR_MSG_TYPE; 904 goto cleanup; 905 } 906 } 907 908 if (loopcount == MAX_IN_TKT_LOOPS) { 909 ret = KRB5_GET_IN_TKT_LOOP; 910 goto cleanup; 911 } 912 913 /* process any preauth data in the as_reply */ 914 915 if ((ret = krb5_do_preauth(context, &request, 916 local_as_reply->padata, &padata, 917 &salt, &s2kparams, &etype, &as_key, prompter, 918 prompter_data, gak_fct, gak_data))) 919 goto cleanup; 920 921 /* XXX if there's padata on output, something is wrong, but it's 922 not obviously an error */ 923 924 /* XXX For 1.1.1 and prior KDC's, when SAM is used w/ USE_SAD_AS_KEY, 925 the AS_REP comes back encrypted in the user's longterm key 926 instead of in the SAD. If there was a SAM preauth, there 927 will be an as_key here which will be the SAD. If that fails, 928 use the gak_fct to get the password, and try again. */ 929 930 /* XXX because etypes are handled poorly (particularly wrt SAM, 931 where the etype is fixed by the kdc), we may want to try 932 decrypt_as_reply twice. If there's an as_key available, try 933 it. If decrypting the as_rep fails, or if there isn't an 934 as_key at all yet, then use the gak_fct to get one, and try 935 again. */ 936 937 if (as_key.length) 938 ret = decrypt_as_reply(context, (krb5_kdc_req *)NULL, local_as_reply, 939 (git_key_proc)NULL, (krb5_const_pointer)NULL, 940 &as_key, krb5_kdc_rep_decrypt_proc, 941 (krb5_const_pointer)NULL); 942 else 943 ret = -1; 944 945 if (ret) { 946 /* if we haven't get gotten a key, get it now */ 947 948 if ((ret = ((*gak_fct)(context, request.client, 949 local_as_reply->enc_part.enctype, 950 prompter, prompter_data, &salt, &s2kparams, 951 &as_key, gak_data)))) 952 goto cleanup; 953 954 if ((ret=decrypt_as_reply(context, (krb5_kdc_req *)NULL, 955 local_as_reply, (git_key_proc)NULL, 956 (krb5_const_pointer)NULL, &as_key, 957 krb5_kdc_rep_decrypt_proc, 958 (krb5_const_pointer)NULL))) 959 goto cleanup; 960 } 961 962 if ((ret = verify_as_reply(context, time_now, &request, local_as_reply))) 963 goto cleanup; 964 965 /* 966 * XXX this should be inside stash_as_reply, but as long as 967 * get_in_tkt is still around using that arg as an in/out, I can't 968 * do that 969 */ 970 (void) memset(creds, 0, sizeof(*creds)); 971 972 if ((ret = stash_as_reply(context, time_now, &request, local_as_reply, 973 creds, (krb5_ccache)NULL))) 974 goto cleanup; 975 976 /* success */ 977 978 ret = 0; 979 980 cleanup: 981 if (request.server) 982 krb5_free_principal(context, request.server); 983 if (request.ktype && 984 (!(options && (options->flags & KRB5_GET_INIT_CREDS_OPT_ETYPE_LIST)))) 985 free(request.ktype); 986 if (request.addresses && 987 (!(options && 988 (options->flags & KRB5_GET_INIT_CREDS_OPT_ADDRESS_LIST)))) 989 krb5_free_addresses(context, request.addresses); 990 if (padata) 991 krb5_free_pa_data(context, padata); 992 if (request.padata) 993 krb5_free_pa_data(context, request.padata); 994 if (as_key.length) 995 krb5_free_keyblock_contents(context, &as_key); 996 if (salt.data && 997 (!(options && (options->flags & KRB5_GET_INIT_CREDS_OPT_SALT)))) 998 krb5_xfree(salt.data); 999 krb5_free_data_contents(context, &s2kparams); 1000 if (as_reply) 1001 *as_reply = local_as_reply; 1002 else if (local_as_reply) 1003 krb5_free_kdc_rep(context, local_as_reply); 1004 1005 return(ret); 1006 } 1007