1 /* 2 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 3 * Use is subject to license terms. 4 */ 5 6 7 /* 8 * lib/krb5/krb/get_in_tkt.c 9 * 10 * Copyright 1990,1991, 2003 by the Massachusetts Institute of Technology. 11 * All Rights Reserved. 12 * 13 * Export of this software from the United States of America may 14 * require a specific license from the United States Government. 15 * It is the responsibility of any person or organization contemplating 16 * export to obtain such a license before exporting. 17 * 18 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and 19 * distribute this software and its documentation for any purpose and 20 * without fee is hereby granted, provided that the above copyright 21 * notice appear in all copies and that both that copyright notice and 22 * this permission notice appear in supporting documentation, and that 23 * the name of M.I.T. not be used in advertising or publicity pertaining 24 * to distribution of the software without specific, written prior 25 * permission. Furthermore if you modify this software you must label 26 * your software as modified software and not distribute it in such a 27 * fashion that it might be confused with the original M.I.T. software. 28 * M.I.T. makes no representations about the suitability of 29 * this software for any purpose. It is provided "as is" without express 30 * or implied warranty. 31 * 32 * 33 * krb5_get_in_tkt() 34 */ 35 36 #include <string.h> 37 38 #include "k5-int.h" 39 #include "int-proto.h" 40 #include "os-proto.h" 41 42 /* 43 All-purpose initial ticket routine, usually called via 44 krb5_get_in_tkt_with_password or krb5_get_in_tkt_with_skey. 45 46 Attempts to get an initial ticket for creds->client to use server 47 creds->server, (realm is taken from creds->client), with options 48 options, and using creds->times.starttime, creds->times.endtime, 49 creds->times.renew_till as from, till, and rtime. 50 creds->times.renew_till is ignored unless the RENEWABLE option is requested. 51 52 key_proc is called to fill in the key to be used for decryption. 53 keyseed is passed on to key_proc. 54 55 decrypt_proc is called to perform the decryption of the response (the 56 encrypted part is in dec_rep->enc_part; the decrypted part should be 57 allocated and filled into dec_rep->enc_part2 58 arg is passed on to decrypt_proc. 59 60 If addrs is non-NULL, it is used for the addresses requested. If it is 61 null, the system standard addresses are used. 62 63 A succesful call will place the ticket in the credentials cache ccache 64 and fill in creds with the ticket information used/returned.. 65 66 returns system errors, encryption errors 67 68 */ 69 70 /* Solaris Kerberos */ 71 #define max(a, b) ((a) > (b) ? (a) : (b)) 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 static krb5_error_code sort_krb5_padata_sequence(krb5_context context, 90 krb5_data *realm, 91 krb5_pa_data **padata); 92 93 /* 94 * This function performs 32 bit bounded addition so we can generate 95 * lifetimes without overflowing krb5_int32 96 */ 97 static krb5_int32 krb5int_addint32 (krb5_int32 x, krb5_int32 y) 98 { 99 if ((x > 0) && (y > (KRB5_INT32_MAX - x))) { 100 /* sum will be be greater than KRB5_INT32_MAX */ 101 return KRB5_INT32_MAX; 102 } else if ((x < 0) && (y < (KRB5_INT32_MIN - x))) { 103 /* sum will be less than KRB5_INT32_MIN */ 104 return KRB5_INT32_MIN; 105 } 106 107 return x + y; 108 } 109 110 /* 111 * This function sends a request to the KDC, and gets back a response; 112 * the response is parsed into ret_err_reply or ret_as_reply if the 113 * reponse is a KRB_ERROR or a KRB_AS_REP packet. If it is some other 114 * unexpected response, an error is returned. 115 */ 116 static krb5_error_code 117 send_as_request(krb5_context context, 118 krb5_kdc_req *request, 119 krb5_error ** ret_err_reply, 120 krb5_kdc_rep ** ret_as_reply, 121 int *use_master) 122 { 123 krb5_kdc_rep *as_reply = 0; 124 krb5_error_code retval; 125 krb5_data *packet = 0; 126 krb5_data reply; 127 char k4_version; /* same type as *(krb5_data::data) */ 128 int tcp_only = 0; 129 krb5_timestamp time_now; 130 131 reply.data = 0; 132 133 /* set the nonce if the caller expects us to do it */ 134 if (request->nonce == 0) { 135 if ((retval = krb5_timeofday(context, &time_now))) 136 goto cleanup; 137 request->nonce = (krb5_int32) time_now; 138 } 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 if (as_reply->enc_part2) 240 return 0; 241 242 if (key) 243 decrypt_key = key; 244 /* Solaris Kerberos */ 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 /* 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 return (retval); 294 } 295 296 static krb5_error_code 297 verify_as_reply(krb5_context context, 298 krb5_timestamp time_now, 299 krb5_kdc_req *request, 300 krb5_kdc_rep *as_reply) 301 { 302 krb5_error_code retval; 303 304 /* check the contents for sanity: */ 305 if (!as_reply->enc_part2->times.starttime) 306 as_reply->enc_part2->times.starttime = 307 as_reply->enc_part2->times.authtime; 308 309 if (!krb5_principal_compare(context, as_reply->client, request->client) 310 || !krb5_principal_compare(context, as_reply->enc_part2->server, request->server) 311 || !krb5_principal_compare(context, as_reply->ticket->server, request->server) 312 || (request->nonce != as_reply->enc_part2->nonce) 313 /* XXX check for extraneous flags */ 314 /* XXX || (!krb5_addresses_compare(context, addrs, as_reply->enc_part2->caddrs)) */ 315 || ((request->kdc_options & KDC_OPT_POSTDATED) && 316 (request->from != 0) && 317 (request->from != as_reply->enc_part2->times.starttime)) 318 || ((request->till != 0) && 319 (as_reply->enc_part2->times.endtime > request->till)) 320 || ((request->kdc_options & KDC_OPT_RENEWABLE) && 321 /* 322 * Solaris Kerberos: Here we error only if renewable_ok was not set. 323 */ 324 !(request->kdc_options & KDC_OPT_RENEWABLE_OK) && 325 (as_reply->enc_part2->flags & KDC_OPT_RENEWABLE) && 326 (request->rtime != 0) && 327 (as_reply->enc_part2->times.renew_till > request->rtime)) 328 || ((request->kdc_options & KDC_OPT_RENEWABLE_OK) && 329 !(request->kdc_options & KDC_OPT_RENEWABLE) && 330 (as_reply->enc_part2->flags & KDC_OPT_RENEWABLE) && 331 (request->till != 0) && 332 (as_reply->enc_part2->times.renew_till > request->till)) 333 /* 334 * Solaris Kerberos: renew_till should never be greater than till or 335 * rtime. 336 */ 337 || ((request->kdc_options & KDC_OPT_RENEWABLE_OK) && 338 (as_reply->enc_part2->flags & KDC_OPT_RENEWABLE) && 339 (request->till != 0) && 340 (request->rtime != 0) && 341 (as_reply->enc_part2->times.renew_till > max(request->till, 342 request->rtime))) 343 ) 344 return KRB5_KDCREP_MODIFIED; 345 346 if (context->library_options & KRB5_LIBOPT_SYNC_KDCTIME) { 347 retval = krb5_set_real_time(context, 348 as_reply->enc_part2->times.authtime, 0); 349 if (retval) 350 return retval; 351 } else { 352 if ((request->from == 0) && 353 (labs(as_reply->enc_part2->times.starttime - time_now) 354 > context->clockskew)) 355 return (KRB5_KDCREP_SKEW); 356 } 357 return 0; 358 } 359 360 /*ARGSUSED*/ 361 static krb5_error_code 362 stash_as_reply(krb5_context context, 363 krb5_timestamp time_now, 364 krb5_kdc_req *request, 365 krb5_kdc_rep *as_reply, 366 krb5_creds * creds, 367 krb5_ccache ccache) 368 { 369 krb5_error_code retval; 370 krb5_data * packet; 371 krb5_principal client; 372 krb5_principal server; 373 374 client = NULL; 375 server = NULL; 376 377 if (!creds->client) 378 if ((retval = krb5_copy_principal(context, as_reply->client, &client))) 379 goto cleanup; 380 381 if (!creds->server) 382 if ((retval = krb5_copy_principal(context, as_reply->enc_part2->server, 383 &server))) 384 goto cleanup; 385 386 /* fill in the credentials */ 387 if ((retval = krb5_copy_keyblock_contents(context, 388 as_reply->enc_part2->session, 389 &creds->keyblock))) 390 goto cleanup; 391 392 creds->times = as_reply->enc_part2->times; 393 creds->is_skey = FALSE; /* this is an AS_REQ, so cannot 394 be encrypted in skey */ 395 creds->ticket_flags = as_reply->enc_part2->flags; 396 if ((retval = krb5_copy_addresses(context, as_reply->enc_part2->caddrs, 397 &creds->addresses))) 398 goto cleanup; 399 400 creds->second_ticket.length = 0; 401 creds->second_ticket.data = 0; 402 403 if ((retval = encode_krb5_ticket(as_reply->ticket, &packet))) 404 goto cleanup; 405 406 creds->ticket = *packet; 407 krb5_xfree(packet); 408 409 /* store it in the ccache! */ 410 if (ccache) /* Solaris Kerberos */ 411 if ((retval = krb5_cc_store_cred(context, ccache, creds)) !=0) 412 goto cleanup; 413 414 if (!creds->client) 415 creds->client = client; 416 if (!creds->server) 417 creds->server = server; 418 419 cleanup: 420 if (retval) { 421 if (client) 422 krb5_free_principal(context, client); 423 if (server) 424 krb5_free_principal(context, server); 425 if (creds->keyblock.contents) { 426 memset((char *)creds->keyblock.contents, 0, 427 creds->keyblock.length); 428 krb5_xfree(creds->keyblock.contents); 429 creds->keyblock.contents = 0; 430 creds->keyblock.length = 0; 431 } 432 if (creds->ticket.data) { 433 krb5_xfree(creds->ticket.data); 434 creds->ticket.data = 0; 435 } 436 if (creds->addresses) { 437 krb5_free_addresses(context, creds->addresses); 438 creds->addresses = 0; 439 } 440 } 441 return (retval); 442 } 443 444 /*ARGSUSED*/ 445 static krb5_error_code 446 make_preauth_list(krb5_context context, 447 krb5_preauthtype * ptypes, 448 int nptypes, 449 krb5_pa_data *** ret_list) 450 { 451 krb5_preauthtype * ptypep; 452 krb5_pa_data ** preauthp; 453 int i; 454 455 if (nptypes < 0) { 456 for (nptypes=0, ptypep = ptypes; *ptypep; ptypep++, nptypes++) 457 ; 458 } 459 460 /* allocate space for a NULL to terminate the list */ 461 462 if ((preauthp = 463 (krb5_pa_data **) malloc((nptypes+1)*sizeof(krb5_pa_data *))) == NULL) 464 return(ENOMEM); 465 466 for (i=0; i<nptypes; i++) { 467 if ((preauthp[i] = 468 (krb5_pa_data *) malloc(sizeof(krb5_pa_data))) == NULL) { 469 for (; i>=0; i++) 470 free(preauthp[i]); 471 free(preauthp); 472 return (ENOMEM); 473 } 474 preauthp[i]->magic = KV5M_PA_DATA; 475 preauthp[i]->pa_type = ptypes[i]; 476 preauthp[i]->length = 0; 477 preauthp[i]->contents = 0; 478 } 479 480 /* fill in the terminating NULL */ 481 482 preauthp[nptypes] = NULL; 483 484 *ret_list = preauthp; 485 return 0; 486 } 487 488 #define MAX_IN_TKT_LOOPS 16 489 static const krb5_enctype get_in_tkt_enctypes[] = { 490 ENCTYPE_DES3_CBC_SHA1, 491 ENCTYPE_ARCFOUR_HMAC, 492 ENCTYPE_DES_CBC_MD5, 493 ENCTYPE_DES_CBC_MD4, 494 ENCTYPE_DES_CBC_CRC, 495 0 496 }; 497 498 krb5_error_code KRB5_CALLCONV 499 krb5_get_in_tkt(krb5_context context, 500 const krb5_flags options, 501 krb5_address * const * addrs, 502 krb5_enctype * ktypes, 503 krb5_preauthtype * ptypes, 504 git_key_proc key_proc, 505 krb5_const_pointer keyseed, 506 git_decrypt_proc decrypt_proc, 507 krb5_const_pointer decryptarg, 508 krb5_creds * creds, 509 krb5_ccache ccache, 510 krb5_kdc_rep ** ret_as_reply) 511 { 512 krb5_error_code retval; 513 krb5_timestamp time_now; 514 krb5_keyblock * decrypt_key = 0; 515 krb5_kdc_req request; 516 krb5_pa_data **padata = 0; 517 krb5_error * err_reply; 518 krb5_kdc_rep * as_reply = 0; 519 krb5_pa_data ** preauth_to_use = 0; 520 int loopcount = 0; 521 krb5_int32 do_more = 0; 522 int use_master = 0; 523 524 if (! krb5_realm_compare(context, creds->client, creds->server)) 525 return KRB5_IN_TKT_REALM_MISMATCH; 526 527 if (ret_as_reply) 528 *ret_as_reply = 0; 529 530 /* 531 * Set up the basic request structure 532 */ 533 request.magic = KV5M_KDC_REQ; 534 request.msg_type = KRB5_AS_REQ; 535 request.addresses = 0; 536 request.ktype = 0; 537 request.padata = 0; 538 if (addrs) 539 request.addresses = (krb5_address **) addrs; 540 else 541 if ((retval = krb5_os_localaddr(context, &request.addresses))) 542 goto cleanup; 543 request.kdc_options = options; 544 request.client = creds->client; 545 request.server = creds->server; 546 request.nonce = 0; 547 request.from = creds->times.starttime; 548 request.till = creds->times.endtime; 549 request.rtime = creds->times.renew_till; 550 551 request.ktype = malloc (sizeof(get_in_tkt_enctypes)); 552 if (request.ktype == NULL) { 553 retval = ENOMEM; 554 goto cleanup; 555 } 556 memcpy(request.ktype, get_in_tkt_enctypes, sizeof(get_in_tkt_enctypes)); 557 for (request.nktypes = 0;request.ktype[request.nktypes];request.nktypes++); 558 if (ktypes) { 559 int i, req, next = 0; 560 for (req = 0; ktypes[req]; req++) { 561 if (ktypes[req] == request.ktype[next]) { 562 next++; 563 continue; 564 } 565 for (i = next + 1; i < request.nktypes; i++) 566 if (ktypes[req] == request.ktype[i]) { 567 /* Found the enctype we want, but not in the 568 position we want. Move it, but keep the old 569 one from the desired slot around in case it's 570 later in our requested-ktypes list. */ 571 krb5_enctype t; 572 t = request.ktype[next]; 573 request.ktype[next] = request.ktype[i]; 574 request.ktype[i] = t; 575 next++; 576 break; 577 } 578 /* If we didn't find it, don't do anything special, just 579 drop it. */ 580 } 581 request.ktype[next] = 0; 582 request.nktypes = next; 583 } 584 request.authorization_data.ciphertext.length = 0; 585 request.authorization_data.ciphertext.data = 0; 586 request.unenc_authdata = 0; 587 request.second_ticket = 0; 588 589 /* 590 * If a list of preauth types are passed in, convert it to a 591 * preauth_to_use list. 592 */ 593 if (ptypes) { 594 retval = make_preauth_list(context, ptypes, -1, &preauth_to_use); 595 if (retval) 596 goto cleanup; 597 } 598 599 while (1) { 600 if (loopcount++ > MAX_IN_TKT_LOOPS) { 601 retval = KRB5_GET_IN_TKT_LOOP; 602 goto cleanup; 603 } 604 605 if ((retval = krb5_obtain_padata(context, preauth_to_use, key_proc, 606 keyseed, creds, &request)) != 0) 607 goto cleanup; 608 if (preauth_to_use) 609 krb5_free_pa_data(context, preauth_to_use); 610 preauth_to_use = 0; 611 612 err_reply = 0; 613 as_reply = 0; 614 615 if ((retval = krb5_timeofday(context, &time_now))) 616 goto cleanup; 617 618 /* 619 * XXX we know they are the same size... and we should do 620 * something better than just the current time 621 */ 622 request.nonce = (krb5_int32) time_now; 623 624 if ((retval = send_as_request(context, &request, &err_reply, 625 &as_reply, &use_master))) 626 goto cleanup; 627 628 if (err_reply) { 629 if (err_reply->error == KDC_ERR_PREAUTH_REQUIRED && 630 err_reply->e_data.length > 0) { 631 retval = decode_krb5_padata_sequence(&err_reply->e_data, 632 &preauth_to_use); 633 krb5_free_error(context, err_reply); 634 if (retval) 635 goto cleanup; 636 retval = sort_krb5_padata_sequence(context, 637 &request.server->realm, 638 padata); 639 if (retval) 640 goto cleanup; 641 continue; 642 } else { 643 retval = (krb5_error_code) err_reply->error 644 + ERROR_TABLE_BASE_krb5; 645 krb5_free_error(context, err_reply); 646 goto cleanup; 647 } 648 } else if (!as_reply) { 649 retval = KRB5KRB_AP_ERR_MSG_TYPE; 650 goto cleanup; 651 } 652 if ((retval = krb5_process_padata(context, &request, as_reply, 653 key_proc, keyseed, decrypt_proc, 654 &decrypt_key, creds, 655 &do_more)) != 0) 656 goto cleanup; 657 658 if (!do_more) 659 break; 660 } 661 662 if ((retval = decrypt_as_reply(context, &request, as_reply, key_proc, 663 keyseed, decrypt_key, decrypt_proc, 664 decryptarg))) 665 goto cleanup; 666 667 if ((retval = verify_as_reply(context, time_now, &request, as_reply))) 668 goto cleanup; 669 670 if ((retval = stash_as_reply(context, time_now, &request, as_reply, 671 creds, ccache))) 672 goto cleanup; 673 674 cleanup: 675 if (request.ktype) 676 free(request.ktype); 677 if (!addrs && request.addresses) 678 krb5_free_addresses(context, request.addresses); 679 if (request.padata) 680 krb5_free_pa_data(context, request.padata); 681 if (padata) 682 krb5_free_pa_data(context, padata); 683 if (preauth_to_use) 684 krb5_free_pa_data(context, preauth_to_use); 685 if (decrypt_key) 686 krb5_free_keyblock(context, decrypt_key); 687 if (as_reply) { 688 if (ret_as_reply) 689 *ret_as_reply = as_reply; 690 else 691 krb5_free_kdc_rep(context, as_reply); 692 } 693 return (retval); 694 } 695 696 /* begin libdefaults parsing code. This should almost certainly move 697 somewhere else, but I don't know where the correct somewhere else 698 is yet. */ 699 700 /* XXX Duplicating this is annoying; try to work on a better way.*/ 701 static const char *const conf_yes[] = { 702 "y", "yes", "true", "t", "1", "on", 703 0, 704 }; 705 706 static const char *const conf_no[] = { 707 "n", "no", "false", "nil", "0", "off", 708 0, 709 }; 710 711 int 712 _krb5_conf_boolean(const char *s) 713 { 714 const char *const *p; 715 716 for(p=conf_yes; *p; p++) { 717 if (!strcasecmp(*p,s)) 718 return 1; 719 } 720 721 for(p=conf_no; *p; p++) { 722 if (!strcasecmp(*p,s)) 723 return 0; 724 } 725 726 /* Default to "no" */ 727 return 0; 728 } 729 730 static krb5_error_code 731 krb5_libdefault_string(krb5_context context, const krb5_data *realm, 732 const char *option, char **ret_value) 733 { 734 profile_t profile; 735 const char *names[5]; 736 char **nameval = NULL; 737 krb5_error_code retval; 738 char realmstr[1024]; 739 740 if (realm->length > sizeof(realmstr)-1) 741 return(EINVAL); 742 743 strncpy(realmstr, realm->data, realm->length); 744 realmstr[realm->length] = '\0'; 745 746 if (!context || (context->magic != KV5M_CONTEXT)) 747 return KV5M_CONTEXT; 748 749 profile = context->profile; 750 751 /* Solaris Kerberos */ 752 names[0] = "realms"; 753 754 /* 755 * Try number one: 756 * 757 * [realms] 758 * REALM = { 759 * option = <boolean> 760 * } 761 */ 762 763 names[1] = realmstr; 764 names[2] = option; 765 names[3] = 0; 766 retval = profile_get_values(profile, names, &nameval); 767 if (retval == 0 && nameval && nameval[0]) 768 goto goodbye; 769 770 /* 771 * Try number two: 772 * 773 * [libdefaults] 774 * option = <boolean> 775 */ 776 777 names[0] = "libdefaults"; 778 names[1] = option; 779 names[2] = 0; 780 retval = profile_get_values(profile, names, &nameval); 781 if (retval == 0 && nameval && nameval[0]) 782 goto goodbye; 783 784 goodbye: 785 if (!nameval) 786 return(ENOENT); 787 788 if (!nameval[0]) { 789 retval = ENOENT; 790 } else { 791 *ret_value = malloc(strlen(nameval[0]) + 1); 792 if (!*ret_value) 793 retval = ENOMEM; 794 else 795 strcpy(*ret_value, nameval[0]); 796 } 797 798 profile_free_list(nameval); 799 800 return retval; 801 } 802 803 /* not static so verify_init_creds() can call it */ 804 /* as well as the DNS code */ 805 806 krb5_error_code 807 krb5_libdefault_boolean(krb5_context context, const krb5_data *realm, 808 const char *option, int *ret_value) 809 { 810 char *string = NULL; 811 krb5_error_code retval; 812 813 retval = krb5_libdefault_string(context, realm, option, &string); 814 815 if (retval) 816 return(retval); 817 818 *ret_value = _krb5_conf_boolean(string); 819 free(string); 820 821 return(0); 822 } 823 824 /* Sort a pa_data sequence so that types named in the "preferred_preauth_types" 825 * libdefaults entry are listed before any others. */ 826 static krb5_error_code 827 sort_krb5_padata_sequence(krb5_context context, krb5_data *realm, 828 krb5_pa_data **padata) 829 { 830 int i, j, base; 831 krb5_error_code ret; 832 const char *p; 833 long l; 834 char *q, *preauth_types = NULL; 835 krb5_pa_data *tmp; 836 int need_free_string = 1; 837 838 if ((padata == NULL) || (padata[0] == NULL)) { 839 return 0; 840 } 841 842 ret = krb5_libdefault_string(context, realm, "preferred_preauth_types", 843 &preauth_types); 844 if ((ret != 0) || (preauth_types == NULL)) { 845 /* Try to use PKINIT first. */ 846 preauth_types = "17, 16, 15, 14"; 847 need_free_string = 0; 848 } 849 850 #ifdef DEBUG 851 fprintf (stderr, "preauth data types before sorting:"); 852 for (i = 0; padata[i]; i++) { 853 fprintf (stderr, " %d", padata[i]->pa_type); 854 } 855 fprintf (stderr, "\n"); 856 #endif 857 858 base = 0; 859 for (p = preauth_types; *p != '\0';) { 860 /* skip whitespace to find an entry */ 861 p += strspn(p, ", "); 862 if (*p != '\0') { 863 /* see if we can extract a number */ 864 l = strtol(p, &q, 10); 865 if ((q != NULL) && (q > p)) { 866 /* got a valid number; search for a matchin entry */ 867 for (i = base; padata[i] != NULL; i++) { 868 /* bubble the matching entry to the front of the list */ 869 if (padata[i]->pa_type == l) { 870 tmp = padata[i]; 871 for (j = i; j > base; j--) 872 padata[j] = padata[j - 1]; 873 padata[base] = tmp; 874 base++; 875 break; 876 } 877 } 878 p = q; 879 } else { 880 break; 881 } 882 } 883 } 884 if (need_free_string) 885 free(preauth_types); 886 887 #ifdef DEBUG 888 fprintf (stderr, "preauth data types after sorting:"); 889 for (i = 0; padata[i]; i++) 890 fprintf (stderr, " %d", padata[i]->pa_type); 891 fprintf (stderr, "\n"); 892 #endif 893 894 return 0; 895 } 896 897 krb5_error_code KRB5_CALLCONV 898 krb5_get_init_creds(krb5_context context, 899 krb5_creds *creds, 900 krb5_principal client, 901 krb5_prompter_fct prompter, 902 void *prompter_data, 903 krb5_deltat start_time, 904 char *in_tkt_service, 905 krb5_gic_opt_ext *options, 906 krb5_gic_get_as_key_fct gak_fct, 907 void *gak_data, 908 int *use_master, 909 krb5_kdc_rep **as_reply) 910 { 911 krb5_error_code ret; 912 krb5_kdc_req request; 913 krb5_data *encoded_request_body, *encoded_previous_request; 914 krb5_pa_data **preauth_to_use, **kdc_padata; 915 int tempint; 916 char *tempstr = NULL; 917 krb5_deltat tkt_life; 918 krb5_deltat renew_life; 919 int loopcount; 920 krb5_data salt; 921 krb5_data s2kparams; 922 krb5_keyblock as_key; 923 krb5_error *err_reply; 924 krb5_kdc_rep *local_as_reply; 925 krb5_timestamp time_now; 926 krb5_enctype etype = 0; 927 krb5_preauth_client_rock get_data_rock; 928 929 /* initialize everything which will be freed at cleanup */ 930 931 s2kparams.data = NULL; 932 s2kparams.length = 0; 933 request.server = NULL; 934 request.ktype = NULL; 935 request.addresses = NULL; 936 request.padata = NULL; 937 encoded_request_body = NULL; 938 encoded_previous_request = NULL; 939 preauth_to_use = NULL; 940 kdc_padata = NULL; 941 as_key.length = 0; 942 salt.length = 0; 943 salt.data = NULL; 944 945 (void) memset(&as_key, 0, sizeof(as_key)); 946 947 local_as_reply = 0; 948 949 err_reply = NULL; 950 951 /* 952 * Set up the basic request structure 953 */ 954 request.magic = KV5M_KDC_REQ; 955 request.msg_type = KRB5_AS_REQ; 956 957 /* request.nonce is filled in when we send a request to the kdc */ 958 request.nonce = 0; 959 960 /* request.padata is filled in later */ 961 962 request.kdc_options = context->kdc_default_options; 963 964 /* forwardable */ 965 966 if (options && (options->flags & KRB5_GET_INIT_CREDS_OPT_FORWARDABLE)) 967 tempint = options->forwardable; 968 else if ((ret = krb5_libdefault_boolean(context, &client->realm, 969 "forwardable", &tempint)) == 0) 970 /*EMPTY*/ 971 ; 972 else 973 tempint = 0; 974 if (tempint) 975 request.kdc_options |= KDC_OPT_FORWARDABLE; 976 977 /* proxiable */ 978 979 if (options && (options->flags & KRB5_GET_INIT_CREDS_OPT_PROXIABLE)) 980 tempint = options->proxiable; 981 else if ((ret = krb5_libdefault_boolean(context, &client->realm, 982 "proxiable", &tempint)) == 0) 983 /*EMPTY*/ 984 ; 985 else 986 tempint = 0; 987 if (tempint) 988 request.kdc_options |= KDC_OPT_PROXIABLE; 989 990 /* allow_postdate */ 991 992 if (start_time > 0) 993 request.kdc_options |= (KDC_OPT_ALLOW_POSTDATE|KDC_OPT_POSTDATED); 994 995 /* ticket lifetime */ 996 997 if ((ret = krb5_timeofday(context, &request.from))) 998 goto cleanup; 999 request.from = krb5int_addint32(request.from, start_time); 1000 1001 if (options && (options->flags & KRB5_GET_INIT_CREDS_OPT_TKT_LIFE)) { 1002 tkt_life = options->tkt_life; 1003 } else if ((ret = krb5_libdefault_string(context, &client->realm, 1004 "ticket_lifetime", &tempstr)) 1005 == 0) { 1006 ret = krb5_string_to_deltat(tempstr, &tkt_life); 1007 free(tempstr); 1008 if (ret) { 1009 goto cleanup; 1010 } 1011 } else { 1012 /* this used to be hardcoded in kinit.c */ 1013 tkt_life = 24*60*60; 1014 } 1015 request.till = krb5int_addint32(request.from, tkt_life); 1016 1017 /* renewable lifetime */ 1018 1019 if (options && (options->flags & KRB5_GET_INIT_CREDS_OPT_RENEW_LIFE)) { 1020 renew_life = options->renew_life; 1021 } else if ((ret = krb5_libdefault_string(context, &client->realm, 1022 "renew_lifetime", &tempstr)) 1023 == 0) { 1024 ret = krb5_string_to_deltat(tempstr, &renew_life); 1025 free(tempstr); 1026 if (ret) { 1027 goto cleanup; 1028 } 1029 } else { 1030 renew_life = 0; 1031 } 1032 if (renew_life > 0) 1033 request.kdc_options |= KDC_OPT_RENEWABLE; 1034 1035 if (renew_life > 0) { 1036 request.rtime = krb5int_addint32(request.from, renew_life); 1037 if (request.rtime < request.till) { 1038 /* don't ask for a smaller renewable time than the lifetime */ 1039 request.rtime = request.till; 1040 } 1041 /* we are already asking for renewable tickets so strip this option */ 1042 request.kdc_options &= ~(KDC_OPT_RENEWABLE_OK); 1043 } else { 1044 request.rtime = 0; 1045 } 1046 1047 /* client */ 1048 1049 request.client = client; 1050 1051 /* service */ 1052 1053 if (in_tkt_service) { 1054 /* this is ugly, because so are the data structures involved. I'm 1055 in the library, so I'm going to manipulate the data structures 1056 directly, otherwise, it will be worse. */ 1057 1058 if ((ret = krb5_parse_name(context, in_tkt_service, &request.server))) 1059 goto cleanup; 1060 1061 /* stuff the client realm into the server principal. 1062 realloc if necessary */ 1063 if (request.server->realm.length < request.client->realm.length) 1064 if ((request.server->realm.data = 1065 (char *) realloc(request.server->realm.data, 1066 request.client->realm.length)) == NULL) { 1067 ret = ENOMEM; 1068 goto cleanup; 1069 } 1070 1071 request.server->realm.length = request.client->realm.length; 1072 memcpy(request.server->realm.data, request.client->realm.data, 1073 request.client->realm.length); 1074 } else { 1075 if ((ret = krb5_build_principal_ext(context, &request.server, 1076 request.client->realm.length, 1077 request.client->realm.data, 1078 KRB5_TGS_NAME_SIZE, 1079 KRB5_TGS_NAME, 1080 request.client->realm.length, 1081 request.client->realm.data, 1082 0))) 1083 goto cleanup; 1084 } 1085 1086 krb5_preauth_request_context_init(context); 1087 1088 /* nonce is filled in by send_as_request if we don't take care of it */ 1089 1090 if (options && (options->flags & KRB5_GET_INIT_CREDS_OPT_ETYPE_LIST)) { 1091 request.ktype = options->etype_list; 1092 request.nktypes = options->etype_list_length; 1093 } else if ((ret = krb5_get_default_in_tkt_ktypes(context, 1094 &request.ktype)) == 0) { 1095 for (request.nktypes = 0; 1096 request.ktype[request.nktypes]; 1097 request.nktypes++) 1098 ; 1099 } else { 1100 /* there isn't any useful default here. ret is set from above */ 1101 goto cleanup; 1102 } 1103 1104 if (options && (options->flags & KRB5_GET_INIT_CREDS_OPT_ADDRESS_LIST)) { 1105 request.addresses = options->address_list; 1106 } 1107 /* it would be nice if this parsed out an address list, but 1108 that would be work. */ 1109 else if (((ret = krb5_libdefault_boolean(context, &client->realm, 1110 "no_addresses", &tempint)) == 0) 1111 || (tempint == 1)) { 1112 /*EMPTY*/ 1113 ; 1114 } else if (((ret = krb5_libdefault_boolean(context, &client->realm, 1115 "noaddresses", &tempint)) == 0) 1116 || (tempint == 1)) { 1117 /*EMPTY*/ 1118 ; 1119 } else { 1120 if ((ret = krb5_os_localaddr(context, &request.addresses))) 1121 goto cleanup; 1122 } 1123 1124 request.authorization_data.ciphertext.length = 0; 1125 request.authorization_data.ciphertext.data = 0; 1126 request.unenc_authdata = 0; 1127 request.second_ticket = 0; 1128 1129 /* set up the other state. */ 1130 1131 if (options && (options->flags & KRB5_GET_INIT_CREDS_OPT_PREAUTH_LIST)) { 1132 if ((ret = make_preauth_list(context, options->preauth_list, 1133 options->preauth_list_length, 1134 &preauth_to_use))) 1135 goto cleanup; 1136 } 1137 1138 /* the salt is allocated from somewhere, unless it is from the caller, 1139 then it is a reference */ 1140 1141 if (options && (options->flags & KRB5_GET_INIT_CREDS_OPT_SALT)) { 1142 salt = *options->salt; 1143 } else { 1144 salt.length = SALT_TYPE_AFS_LENGTH; 1145 salt.data = NULL; 1146 } 1147 1148 1149 /* set the request nonce */ 1150 if ((ret = krb5_timeofday(context, &time_now))) 1151 goto cleanup; 1152 /* 1153 * XXX we know they are the same size... and we should do 1154 * something better than just the current time 1155 */ 1156 request.nonce = (krb5_int32) time_now; 1157 1158 /* give the preauth plugins a chance to prep the request body */ 1159 krb5_preauth_prepare_request(context, options, &request); 1160 ret = encode_krb5_kdc_req_body(&request, &encoded_request_body); 1161 if (ret) 1162 goto cleanup; 1163 1164 get_data_rock.magic = CLIENT_ROCK_MAGIC; 1165 get_data_rock.as_reply = NULL; 1166 1167 /* now, loop processing preauth data and talking to the kdc */ 1168 for (loopcount = 0; loopcount < MAX_IN_TKT_LOOPS; loopcount++) { 1169 if (request.padata) { 1170 krb5_free_pa_data(context, request.padata); 1171 request.padata = NULL; 1172 } 1173 if (!err_reply) { 1174 /* either our first attempt, or retrying after PREAUTH_NEEDED */ 1175 if ((ret = krb5_do_preauth(context, 1176 &request, 1177 encoded_request_body, 1178 encoded_previous_request, 1179 preauth_to_use, &request.padata, 1180 &salt, &s2kparams, &etype, &as_key, 1181 prompter, prompter_data, 1182 gak_fct, gak_data, 1183 &get_data_rock, options))) 1184 goto cleanup; 1185 } else { 1186 if (preauth_to_use != NULL) { 1187 /* 1188 * Retry after an error other than PREAUTH_NEEDED, 1189 * using e-data to figure out what to change. 1190 */ 1191 ret = krb5_do_preauth_tryagain(context, 1192 &request, 1193 encoded_request_body, 1194 encoded_previous_request, 1195 preauth_to_use, &request.padata, 1196 err_reply, 1197 &salt, &s2kparams, &etype, 1198 &as_key, 1199 prompter, prompter_data, 1200 gak_fct, gak_data, 1201 &get_data_rock, options); 1202 } else { 1203 /* No preauth supplied, so can't query the plug-ins. */ 1204 ret = KRB5KRB_ERR_GENERIC; 1205 } 1206 if (ret) { 1207 /* couldn't come up with anything better */ 1208 ret = err_reply->error + ERROR_TABLE_BASE_krb5; 1209 } 1210 krb5_free_error(context, err_reply); 1211 err_reply = NULL; 1212 if (ret) 1213 goto cleanup; 1214 } 1215 1216 if (encoded_previous_request != NULL) { 1217 krb5_free_data(context, encoded_previous_request); 1218 encoded_previous_request = NULL; 1219 } 1220 ret = encode_krb5_as_req(&request, &encoded_previous_request); 1221 if (ret) 1222 goto cleanup; 1223 1224 err_reply = 0; 1225 local_as_reply = 0; 1226 if ((ret = send_as_request(context, &request, &err_reply, 1227 &local_as_reply, use_master))) 1228 goto cleanup; 1229 1230 if (err_reply) { 1231 if (err_reply->error == KDC_ERR_PREAUTH_REQUIRED && 1232 err_reply->e_data.length > 0) { 1233 /* reset the list of preauth types to try */ 1234 if (preauth_to_use) { 1235 krb5_free_pa_data(context, preauth_to_use); 1236 preauth_to_use = NULL; 1237 } 1238 ret = decode_krb5_padata_sequence(&err_reply->e_data, 1239 &preauth_to_use); 1240 krb5_free_error(context, err_reply); 1241 err_reply = NULL; 1242 if (ret) 1243 goto cleanup; 1244 ret = sort_krb5_padata_sequence(context, 1245 &request.server->realm, 1246 preauth_to_use); 1247 if (ret) 1248 goto cleanup; 1249 /* continue to next iteration */ 1250 } else { 1251 if (err_reply->e_data.length > 0) { 1252 /* continue to next iteration */ 1253 } else { 1254 /* error + no hints = give up */ 1255 ret = (krb5_error_code) err_reply->error 1256 + ERROR_TABLE_BASE_krb5; 1257 krb5_free_error(context, err_reply); 1258 goto cleanup; 1259 } 1260 } 1261 } else if (local_as_reply) { 1262 break; 1263 } else { 1264 ret = KRB5KRB_AP_ERR_MSG_TYPE; 1265 goto cleanup; 1266 } 1267 } 1268 1269 if (loopcount == MAX_IN_TKT_LOOPS) { 1270 ret = KRB5_GET_IN_TKT_LOOP; 1271 goto cleanup; 1272 } 1273 1274 /* process any preauth data in the as_reply */ 1275 krb5_clear_preauth_context_use_counts(context); 1276 if ((ret = sort_krb5_padata_sequence(context, &request.server->realm, 1277 local_as_reply->padata))) 1278 goto cleanup; 1279 get_data_rock.as_reply = local_as_reply; 1280 if ((ret = krb5_do_preauth(context, 1281 &request, 1282 encoded_request_body, encoded_previous_request, 1283 local_as_reply->padata, &kdc_padata, 1284 &salt, &s2kparams, &etype, &as_key, prompter, 1285 prompter_data, gak_fct, gak_data, 1286 &get_data_rock, options))) 1287 goto cleanup; 1288 1289 /* XXX For 1.1.1 and prior KDC's, when SAM is used w/ USE_SAD_AS_KEY, 1290 the AS_REP comes back encrypted in the user's longterm key 1291 instead of in the SAD. If there was a SAM preauth, there 1292 will be an as_key here which will be the SAD. If that fails, 1293 use the gak_fct to get the password, and try again. */ 1294 1295 /* XXX because etypes are handled poorly (particularly wrt SAM, 1296 where the etype is fixed by the kdc), we may want to try 1297 decrypt_as_reply twice. If there's an as_key available, try 1298 it. If decrypting the as_rep fails, or if there isn't an 1299 as_key at all yet, then use the gak_fct to get one, and try 1300 again. */ 1301 1302 if (as_key.length) 1303 ret = decrypt_as_reply(context, NULL, local_as_reply, NULL, 1304 NULL, &as_key, krb5_kdc_rep_decrypt_proc, 1305 NULL); 1306 else 1307 ret = -1; 1308 1309 if (ret) { 1310 /* if we haven't get gotten a key, get it now */ 1311 1312 if ((ret = ((*gak_fct)(context, request.client, 1313 local_as_reply->enc_part.enctype, 1314 prompter, prompter_data, &salt, &s2kparams, 1315 &as_key, gak_data)))) 1316 goto cleanup; 1317 1318 if ((ret = decrypt_as_reply(context, NULL, local_as_reply, NULL, 1319 NULL, &as_key, krb5_kdc_rep_decrypt_proc, 1320 NULL))) 1321 goto cleanup; 1322 } 1323 1324 if ((ret = verify_as_reply(context, time_now, &request, local_as_reply))) 1325 goto cleanup; 1326 1327 /* XXX this should be inside stash_as_reply, but as long as 1328 get_in_tkt is still around using that arg as an in/out, I can't 1329 do that */ 1330 /* Solaris Kerberos */ 1331 (void) memset(creds, 0, sizeof(*creds)); 1332 1333 /* Solaris Kerberos */ 1334 if ((ret = stash_as_reply(context, time_now, &request, local_as_reply, 1335 creds, (krb5_ccache)NULL))) 1336 goto cleanup; 1337 1338 /* success */ 1339 1340 ret = 0; 1341 1342 cleanup: 1343 krb5_preauth_request_context_fini(context); 1344 if (encoded_previous_request != NULL) { 1345 krb5_free_data(context, encoded_previous_request); 1346 encoded_previous_request = NULL; 1347 } 1348 if (encoded_request_body != NULL) { 1349 krb5_free_data(context, encoded_request_body); 1350 encoded_request_body = NULL; 1351 } 1352 if (request.server) 1353 krb5_free_principal(context, request.server); 1354 if (request.ktype && 1355 (!(options && (options->flags & KRB5_GET_INIT_CREDS_OPT_ETYPE_LIST)))) 1356 free(request.ktype); 1357 if (request.addresses && 1358 (!(options && 1359 (options->flags & KRB5_GET_INIT_CREDS_OPT_ADDRESS_LIST)))) 1360 krb5_free_addresses(context, request.addresses); 1361 if (preauth_to_use) 1362 krb5_free_pa_data(context, preauth_to_use); 1363 if (kdc_padata) 1364 krb5_free_pa_data(context, kdc_padata); 1365 if (request.padata) 1366 krb5_free_pa_data(context, request.padata); 1367 if (as_key.length) 1368 krb5_free_keyblock_contents(context, &as_key); 1369 if (salt.data && 1370 (!(options && (options->flags & KRB5_GET_INIT_CREDS_OPT_SALT)))) 1371 krb5_xfree(salt.data); 1372 krb5_free_data_contents(context, &s2kparams); 1373 if (as_reply) 1374 *as_reply = local_as_reply; 1375 else if (local_as_reply) 1376 krb5_free_kdc_rep(context, local_as_reply); 1377 1378 return(ret); 1379 } 1380