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