1 /* 2 * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved. 3 * Copyright 2022 Tintri by DDN, Inc. All rights reserved. 4 */ 5 /* 6 * lib/krb5/krb/get_in_tkt.c 7 * 8 * Copyright 1990,1991, 2003 by the Massachusetts Institute of Technology. 9 * All Rights Reserved. 10 * 11 * Export of this software from the United States of America may 12 * require a specific license from the United States Government. 13 * It is the responsibility of any person or organization contemplating 14 * export to obtain such a license before exporting. 15 * 16 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and 17 * distribute this software and its documentation for any purpose and 18 * without fee is hereby granted, provided that the above copyright 19 * notice appear in all copies and that both that copyright notice and 20 * this permission notice appear in supporting documentation, and that 21 * the name of M.I.T. not be used in advertising or publicity pertaining 22 * to distribution of the software without specific, written prior 23 * permission. Furthermore if you modify this software you must label 24 * your software as modified software and not distribute it in such a 25 * fashion that it might be confused with the original M.I.T. software. 26 * M.I.T. makes no representations about the suitability of 27 * this software for any purpose. It is provided "as is" without express 28 * or implied warranty. 29 * 30 * 31 * krb5_get_in_tkt() 32 */ 33 34 #include <string.h> 35 #include <ctype.h> 36 #include "k5-int.h" 37 #include "int-proto.h" 38 #include "os-proto.h" 39 #include <locale.h> 40 #include <syslog.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_request2(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 char **hostname_used) 123 124 { 125 krb5_kdc_rep *as_reply = 0; 126 krb5_error_code retval; 127 krb5_data *packet = 0; 128 krb5_data reply; 129 char k4_version; /* same type as *(krb5_data::data) */ 130 int tcp_only = 0; 131 krb5_timestamp time_now; 132 133 reply.data = 0; 134 135 /* Solaris Kerberos (illumos) */ 136 if (krb5_getenv("MS_INTEROP")) { 137 /* Don't bother with UDP. */ 138 tcp_only = 1; 139 } 140 141 /* set the nonce if the caller expects us to do it */ 142 if (request->nonce == 0) { 143 if ((retval = krb5_timeofday(context, &time_now))) 144 goto cleanup; 145 request->nonce = (krb5_int32) time_now; 146 } 147 148 /* encode & send to KDC */ 149 if ((retval = encode_krb5_as_req(request, &packet)) != 0) 150 goto cleanup; 151 152 k4_version = packet->data[0]; 153 send_again: 154 retval = krb5_sendto_kdc2(context, packet, 155 krb5_princ_realm(context, request->client), 156 &reply, use_master, tcp_only, hostname_used); 157 if (retval) 158 goto cleanup; 159 160 /* now decode the reply...could be error or as_rep */ 161 if (krb5_is_krb_error(&reply)) { 162 krb5_error *err_reply; 163 164 if ((retval = decode_krb5_error(&reply, &err_reply))) 165 /* some other error code--??? */ 166 goto cleanup; 167 168 if (ret_err_reply) { 169 if (err_reply->error == KRB_ERR_RESPONSE_TOO_BIG 170 && tcp_only == 0) { 171 tcp_only = 1; 172 krb5_free_error(context, err_reply); 173 free(reply.data); 174 reply.data = 0; 175 goto send_again; 176 } 177 *ret_err_reply = err_reply; 178 } else { 179 krb5_free_error(context, err_reply); 180 err_reply = NULL; 181 } 182 goto cleanup; 183 } 184 185 /* 186 * Check to make sure it isn't a V4 reply. 187 */ 188 if (!krb5_is_as_rep(&reply)) { 189 /* these are in <kerberosIV/prot.h> as well but it isn't worth including. */ 190 #define V4_KRB_PROT_VERSION 4 191 #define V4_AUTH_MSG_ERR_REPLY (5<<1) 192 /* check here for V4 reply */ 193 unsigned int t_switch; 194 195 /* From v4 g_in_tkt.c: This used to be 196 switch (pkt_msg_type(rpkt) & ~1) { 197 but SCO 3.2v4 cc compiled that incorrectly. */ 198 t_switch = reply.data[1]; 199 t_switch &= ~1; 200 201 if (t_switch == V4_AUTH_MSG_ERR_REPLY 202 && (reply.data[0] == V4_KRB_PROT_VERSION 203 || reply.data[0] == k4_version)) { 204 retval = KRB5KRB_AP_ERR_V4_REPLY; 205 } else { 206 retval = KRB5KRB_AP_ERR_MSG_TYPE; 207 } 208 goto cleanup; 209 } 210 211 /* It must be a KRB_AS_REP message, or an bad returned packet */ 212 if ((retval = decode_krb5_as_rep(&reply, &as_reply))) 213 /* some other error code ??? */ 214 goto cleanup; 215 216 if (as_reply->msg_type != KRB5_AS_REP) { 217 retval = KRB5KRB_AP_ERR_MSG_TYPE; 218 krb5_free_kdc_rep(context, as_reply); 219 goto cleanup; 220 } 221 222 if (ret_as_reply) 223 *ret_as_reply = as_reply; 224 else 225 krb5_free_kdc_rep(context, as_reply); 226 227 cleanup: 228 if (packet) 229 krb5_free_data(context, packet); 230 if (reply.data) 231 free(reply.data); 232 return retval; 233 } 234 235 static krb5_error_code 236 send_as_request(krb5_context context, 237 krb5_kdc_req *request, 238 krb5_error ** ret_err_reply, 239 krb5_kdc_rep ** ret_as_reply, 240 int *use_master) 241 { 242 return send_as_request2(context, 243 request, 244 ret_err_reply, 245 ret_as_reply, 246 use_master, 247 NULL); 248 } 249 250 static krb5_error_code 251 decrypt_as_reply(krb5_context context, 252 krb5_kdc_req *request, 253 krb5_kdc_rep *as_reply, 254 git_key_proc key_proc, 255 krb5_const_pointer keyseed, 256 krb5_keyblock * key, 257 git_decrypt_proc decrypt_proc, 258 krb5_const_pointer decryptarg) 259 { 260 krb5_error_code retval; 261 krb5_keyblock * decrypt_key = 0; 262 krb5_data salt; 263 264 if (as_reply->enc_part2) 265 return 0; 266 267 if (key) 268 decrypt_key = key; 269 /* Solaris Kerberos */ 270 else if (request != NULL) { 271 if ((retval = krb5_principal2salt(context, request->client, &salt))) 272 return(retval); 273 274 retval = (*key_proc)(context, as_reply->enc_part.enctype, 275 &salt, keyseed, &decrypt_key); 276 krb5_xfree(salt.data); 277 if (retval) 278 goto cleanup; 279 } else { 280 KRB5_LOG0(KRB5_ERR, "decrypt_as_reply() end, " 281 "error key == NULL and request == NULL"); 282 return (EINVAL); 283 } 284 285 /* 286 * Solaris kerberos: Overwriting the decrypt_key->enctype because the 287 * decrypt key's enctype may not be an exact match with the enctype that the 288 * KDC used to encrypt this part of the AS reply. This assumes the 289 * as_reply->enc_part.enctype has been validated which is done by checking 290 * to see if the enctype that the KDC sent back in the as_reply is one of 291 * the enctypes originally requested. Note, if request is NULL then the 292 * as_reply->enc_part.enctype could not be validated. 293 */ 294 295 if (request != NULL) { 296 if (is_in_keytype(request->ktype, request->nktypes, 297 as_reply->enc_part.enctype)) { 298 299 decrypt_key->enctype = as_reply->enc_part.enctype; 300 301 } else { 302 KRB5_LOG0(KRB5_ERR, "decrypt_as_reply() end, " 303 "error is_in_keytype() returned false"); 304 retval = KRB5_BAD_ENCTYPE; 305 goto cleanup; 306 } 307 } 308 309 if ((retval = (*decrypt_proc)(context, decrypt_key, decryptarg, as_reply))){ 310 KRB5_LOG(KRB5_ERR, "decrypt_as_reply() error (*decrypt_proc)() retval " 311 "= %d", retval); 312 goto cleanup; 313 } 314 315 cleanup: 316 if (!key && decrypt_key) 317 krb5_free_keyblock(context, decrypt_key); 318 return (retval); 319 } 320 321 static krb5_error_code 322 verify_as_reply(krb5_context context, 323 krb5_timestamp time_now, 324 krb5_kdc_req *request, 325 krb5_kdc_rep *as_reply) 326 { 327 krb5_error_code retval; 328 329 /* check the contents for sanity: */ 330 if (!as_reply->enc_part2->times.starttime) 331 as_reply->enc_part2->times.starttime = 332 as_reply->enc_part2->times.authtime; 333 334 if (!krb5_principal_compare(context, as_reply->client, request->client) 335 || !krb5_principal_compare(context, as_reply->enc_part2->server, request->server) 336 || !krb5_principal_compare(context, as_reply->ticket->server, request->server) 337 || (request->nonce != as_reply->enc_part2->nonce) 338 /* XXX check for extraneous flags */ 339 /* XXX || (!krb5_addresses_compare(context, addrs, as_reply->enc_part2->caddrs)) */ 340 || ((request->kdc_options & KDC_OPT_POSTDATED) && 341 (request->from != 0) && 342 (request->from != as_reply->enc_part2->times.starttime)) 343 || ((request->till != 0) && 344 (as_reply->enc_part2->times.endtime > request->till)) 345 || ((request->kdc_options & KDC_OPT_RENEWABLE) && 346 /* 347 * Solaris Kerberos: Here we error only if renewable_ok was not set. 348 */ 349 !(request->kdc_options & KDC_OPT_RENEWABLE_OK) && 350 (as_reply->enc_part2->flags & KDC_OPT_RENEWABLE) && 351 (request->rtime != 0) && 352 (as_reply->enc_part2->times.renew_till > request->rtime)) 353 || ((request->kdc_options & KDC_OPT_RENEWABLE_OK) && 354 !(request->kdc_options & KDC_OPT_RENEWABLE) && 355 (as_reply->enc_part2->flags & KDC_OPT_RENEWABLE) && 356 (request->till != 0) && 357 (as_reply->enc_part2->times.renew_till > request->till)) 358 /* 359 * Solaris Kerberos: renew_till should never be greater than till or 360 * rtime. 361 */ 362 || ((request->kdc_options & KDC_OPT_RENEWABLE_OK) && 363 (as_reply->enc_part2->flags & KDC_OPT_RENEWABLE) && 364 (request->till != 0) && 365 (request->rtime != 0) && 366 (as_reply->enc_part2->times.renew_till > max(request->till, 367 request->rtime))) 368 ) 369 return KRB5_KDCREP_MODIFIED; 370 371 if (context->library_options & KRB5_LIBOPT_SYNC_KDCTIME) { 372 retval = krb5_set_real_time(context, 373 as_reply->enc_part2->times.authtime, 0); 374 if (retval) 375 return retval; 376 } else { 377 if ((request->from == 0) && 378 (labs(as_reply->enc_part2->times.starttime - time_now) 379 > context->clockskew)) 380 return (KRB5_KDCREP_SKEW); 381 } 382 return 0; 383 } 384 385 /*ARGSUSED*/ 386 static krb5_error_code 387 stash_as_reply(krb5_context context, 388 krb5_timestamp time_now, 389 krb5_kdc_req *request, 390 krb5_kdc_rep *as_reply, 391 krb5_creds * creds, 392 krb5_ccache ccache) 393 { 394 krb5_error_code retval; 395 krb5_data * packet; 396 krb5_principal client; 397 krb5_principal server; 398 399 client = NULL; 400 server = NULL; 401 402 if (!creds->client) 403 if ((retval = krb5_copy_principal(context, as_reply->client, &client))) 404 goto cleanup; 405 406 if (!creds->server) 407 if ((retval = krb5_copy_principal(context, as_reply->enc_part2->server, 408 &server))) 409 goto cleanup; 410 411 /* fill in the credentials */ 412 if ((retval = krb5_copy_keyblock_contents(context, 413 as_reply->enc_part2->session, 414 &creds->keyblock))) 415 goto cleanup; 416 417 creds->times = as_reply->enc_part2->times; 418 creds->is_skey = FALSE; /* this is an AS_REQ, so cannot 419 be encrypted in skey */ 420 creds->ticket_flags = as_reply->enc_part2->flags; 421 if ((retval = krb5_copy_addresses(context, as_reply->enc_part2->caddrs, 422 &creds->addresses))) 423 goto cleanup; 424 425 creds->second_ticket.length = 0; 426 creds->second_ticket.data = 0; 427 428 if ((retval = encode_krb5_ticket(as_reply->ticket, &packet))) 429 goto cleanup; 430 431 creds->ticket = *packet; 432 krb5_xfree(packet); 433 434 /* store it in the ccache! */ 435 if (ccache) /* Solaris Kerberos */ 436 if ((retval = krb5_cc_store_cred(context, ccache, creds)) !=0) 437 goto cleanup; 438 439 if (!creds->client) 440 creds->client = client; 441 if (!creds->server) 442 creds->server = server; 443 444 cleanup: 445 if (retval) { 446 if (client) 447 krb5_free_principal(context, client); 448 if (server) 449 krb5_free_principal(context, server); 450 if (creds->keyblock.contents) { 451 memset((char *)creds->keyblock.contents, 0, 452 creds->keyblock.length); 453 krb5_xfree(creds->keyblock.contents); 454 creds->keyblock.contents = 0; 455 creds->keyblock.length = 0; 456 } 457 if (creds->ticket.data) { 458 krb5_xfree(creds->ticket.data); 459 creds->ticket.data = 0; 460 } 461 if (creds->addresses) { 462 krb5_free_addresses(context, creds->addresses); 463 creds->addresses = 0; 464 } 465 } 466 return (retval); 467 } 468 469 /*ARGSUSED*/ 470 static krb5_error_code 471 make_preauth_list(krb5_context context, 472 krb5_preauthtype * ptypes, 473 int nptypes, 474 krb5_pa_data *** ret_list) 475 { 476 krb5_preauthtype * ptypep; 477 krb5_pa_data ** preauthp; 478 int i; 479 480 if (nptypes < 0) { 481 for (nptypes=0, ptypep = ptypes; *ptypep; ptypep++, nptypes++) 482 ; 483 } 484 485 /* allocate space for a NULL to terminate the list */ 486 487 if ((preauthp = 488 (krb5_pa_data **) malloc((nptypes+1)*sizeof(krb5_pa_data *))) == NULL) 489 return(ENOMEM); 490 491 for (i=0; i<nptypes; i++) { 492 if ((preauthp[i] = 493 (krb5_pa_data *) malloc(sizeof(krb5_pa_data))) == NULL) { 494 for (; i>=0; i++) 495 free(preauthp[i]); 496 free(preauthp); 497 return (ENOMEM); 498 } 499 preauthp[i]->magic = KV5M_PA_DATA; 500 preauthp[i]->pa_type = ptypes[i]; 501 preauthp[i]->length = 0; 502 preauthp[i]->contents = 0; 503 } 504 505 /* fill in the terminating NULL */ 506 507 preauthp[nptypes] = NULL; 508 509 *ret_list = preauthp; 510 return 0; 511 } 512 513 #define MAX_IN_TKT_LOOPS 16 514 static const krb5_enctype get_in_tkt_enctypes[] = { 515 ENCTYPE_DES3_CBC_SHA1, 516 ENCTYPE_ARCFOUR_HMAC, 517 ENCTYPE_DES_CBC_MD5, 518 ENCTYPE_DES_CBC_MD4, 519 ENCTYPE_DES_CBC_CRC, 520 0 521 }; 522 523 krb5_error_code KRB5_CALLCONV 524 krb5_get_in_tkt(krb5_context context, 525 const krb5_flags options, 526 krb5_address * const * addrs, 527 krb5_enctype * ktypes, 528 krb5_preauthtype * ptypes, 529 git_key_proc key_proc, 530 krb5_const_pointer keyseed, 531 git_decrypt_proc decrypt_proc, 532 krb5_const_pointer decryptarg, 533 krb5_creds * creds, 534 krb5_ccache ccache, 535 krb5_kdc_rep ** ret_as_reply) 536 { 537 krb5_error_code retval; 538 krb5_timestamp time_now; 539 krb5_keyblock * decrypt_key = 0; 540 krb5_kdc_req request; 541 krb5_pa_data **padata = 0; 542 krb5_error * err_reply; 543 krb5_kdc_rep * as_reply = 0; 544 krb5_pa_data ** preauth_to_use = 0; 545 int loopcount = 0; 546 krb5_int32 do_more = 0; 547 int use_master = 0; 548 char *hostname_used = NULL; 549 550 if (! krb5_realm_compare(context, creds->client, creds->server)) { 551 /* Solaris Kerberos */ 552 char *s_name = NULL; 553 char *c_name = NULL; 554 krb5_error_code serr, cerr; 555 serr = krb5_unparse_name(context, creds->server, &s_name); 556 cerr = krb5_unparse_name(context, creds->client, &c_name); 557 krb5_set_error_message(context, KRB5_IN_TKT_REALM_MISMATCH, 558 dgettext(TEXT_DOMAIN, 559 "Client/server realm mismatch in initial ticket request: '%s' requesting ticket '%s'"), 560 cerr ? "unknown" : c_name, 561 serr ? "unknown" : s_name); 562 if (s_name) 563 krb5_free_unparsed_name(context, s_name); 564 if (c_name) 565 krb5_free_unparsed_name(context, c_name); 566 return KRB5_IN_TKT_REALM_MISMATCH; 567 } 568 569 if (ret_as_reply) 570 *ret_as_reply = 0; 571 572 /* 573 * Set up the basic request structure 574 */ 575 request.magic = KV5M_KDC_REQ; 576 request.msg_type = KRB5_AS_REQ; 577 request.addresses = 0; 578 request.ktype = 0; 579 request.padata = 0; 580 if (addrs) 581 request.addresses = (krb5_address **) addrs; 582 else 583 if ((retval = krb5_os_localaddr(context, &request.addresses))) 584 goto cleanup; 585 request.kdc_options = options; 586 request.client = creds->client; 587 request.server = creds->server; 588 request.nonce = 0; 589 request.from = creds->times.starttime; 590 request.till = creds->times.endtime; 591 request.rtime = creds->times.renew_till; 592 593 request.ktype = malloc (sizeof(get_in_tkt_enctypes)); 594 if (request.ktype == NULL) { 595 retval = ENOMEM; 596 goto cleanup; 597 } 598 memcpy(request.ktype, get_in_tkt_enctypes, sizeof(get_in_tkt_enctypes)); 599 for (request.nktypes = 0;request.ktype[request.nktypes];request.nktypes++); 600 if (ktypes) { 601 int i, req, next = 0; 602 for (req = 0; ktypes[req]; req++) { 603 if (ktypes[req] == request.ktype[next]) { 604 next++; 605 continue; 606 } 607 for (i = next + 1; i < request.nktypes; i++) 608 if (ktypes[req] == request.ktype[i]) { 609 /* Found the enctype we want, but not in the 610 position we want. Move it, but keep the old 611 one from the desired slot around in case it's 612 later in our requested-ktypes list. */ 613 krb5_enctype t; 614 t = request.ktype[next]; 615 request.ktype[next] = request.ktype[i]; 616 request.ktype[i] = t; 617 next++; 618 break; 619 } 620 /* If we didn't find it, don't do anything special, just 621 drop it. */ 622 } 623 request.ktype[next] = 0; 624 request.nktypes = next; 625 } 626 request.authorization_data.ciphertext.length = 0; 627 request.authorization_data.ciphertext.data = 0; 628 request.unenc_authdata = 0; 629 request.second_ticket = 0; 630 631 /* 632 * If a list of preauth types are passed in, convert it to a 633 * preauth_to_use list. 634 */ 635 if (ptypes) { 636 retval = make_preauth_list(context, ptypes, -1, &preauth_to_use); 637 if (retval) 638 goto cleanup; 639 } 640 641 while (1) { 642 if (loopcount++ > MAX_IN_TKT_LOOPS) { 643 retval = KRB5_GET_IN_TKT_LOOP; 644 /* Solaris Kerberos */ 645 { 646 char *s_name = NULL; 647 char *c_name = NULL; 648 krb5_error_code serr, cerr; 649 serr = krb5_unparse_name(context, creds->server, &s_name); 650 cerr = krb5_unparse_name(context, creds->client, &c_name); 651 krb5_set_error_message(context, retval, 652 dgettext(TEXT_DOMAIN, 653 "Looping detected getting ticket: '%s' requesting ticket '%s'. Max loops is %d. Make sure a KDC is available"), 654 cerr ? "unknown" : c_name, 655 serr ? "unknown" : s_name, 656 MAX_IN_TKT_LOOPS); 657 if (s_name) 658 krb5_free_unparsed_name(context, s_name); 659 if (c_name) 660 krb5_free_unparsed_name(context, c_name); 661 } 662 goto cleanup; 663 } 664 665 if ((retval = krb5_obtain_padata(context, preauth_to_use, key_proc, 666 keyseed, creds, &request)) != 0) 667 goto cleanup; 668 if (preauth_to_use) 669 krb5_free_pa_data(context, preauth_to_use); 670 preauth_to_use = 0; 671 672 err_reply = 0; 673 as_reply = 0; 674 675 if ((retval = krb5_timeofday(context, &time_now))) 676 goto cleanup; 677 678 /* 679 * XXX we know they are the same size... and we should do 680 * something better than just the current time 681 */ 682 request.nonce = (krb5_int32) time_now; 683 684 if ((retval = send_as_request2(context, &request, &err_reply, 685 &as_reply, &use_master, 686 &hostname_used))) 687 goto cleanup; 688 689 if (err_reply) { 690 if (err_reply->error == KDC_ERR_PREAUTH_REQUIRED && 691 err_reply->e_data.length > 0) { 692 retval = decode_krb5_padata_sequence(&err_reply->e_data, 693 &preauth_to_use); 694 krb5_free_error(context, err_reply); 695 err_reply = NULL; 696 if (retval) 697 goto cleanup; 698 retval = sort_krb5_padata_sequence(context, 699 &request.server->realm, 700 padata); 701 if (retval) 702 goto cleanup; 703 continue; 704 } else { 705 retval = (krb5_error_code) err_reply->error 706 + ERROR_TABLE_BASE_krb5; 707 krb5_free_error(context, err_reply); 708 err_reply = NULL; 709 goto cleanup; 710 } 711 } else if (!as_reply) { 712 retval = KRB5KRB_AP_ERR_MSG_TYPE; 713 goto cleanup; 714 } 715 if ((retval = krb5_process_padata(context, &request, as_reply, 716 key_proc, keyseed, decrypt_proc, 717 &decrypt_key, creds, 718 &do_more)) != 0) 719 goto cleanup; 720 721 if (!do_more) 722 break; 723 } 724 725 if ((retval = decrypt_as_reply(context, &request, as_reply, key_proc, 726 keyseed, decrypt_key, decrypt_proc, 727 decryptarg))) 728 goto cleanup; 729 730 if ((retval = verify_as_reply(context, time_now, &request, as_reply))) 731 goto cleanup; 732 733 if ((retval = stash_as_reply(context, time_now, &request, as_reply, 734 creds, ccache))) 735 goto cleanup; 736 737 cleanup: 738 if (request.ktype) 739 free(request.ktype); 740 if (!addrs && request.addresses) 741 krb5_free_addresses(context, request.addresses); 742 if (request.padata) 743 krb5_free_pa_data(context, request.padata); 744 if (padata) 745 krb5_free_pa_data(context, padata); 746 if (preauth_to_use) 747 krb5_free_pa_data(context, preauth_to_use); 748 if (decrypt_key) 749 krb5_free_keyblock(context, decrypt_key); 750 if (as_reply) { 751 if (ret_as_reply) 752 *ret_as_reply = as_reply; 753 else 754 krb5_free_kdc_rep(context, as_reply); 755 } 756 if (hostname_used) 757 free(hostname_used); 758 759 return (retval); 760 } 761 762 /* begin libdefaults parsing code. This should almost certainly move 763 somewhere else, but I don't know where the correct somewhere else 764 is yet. */ 765 766 /* XXX Duplicating this is annoying; try to work on a better way.*/ 767 static const char *const conf_yes[] = { 768 "y", "yes", "true", "t", "1", "on", 769 0, 770 }; 771 772 static const char *const conf_no[] = { 773 "n", "no", "false", "nil", "0", "off", 774 0, 775 }; 776 777 int 778 _krb5_conf_boolean(const char *s) 779 { 780 const char *const *p; 781 782 for(p=conf_yes; *p; p++) { 783 if (!strcasecmp(*p,s)) 784 return 1; 785 } 786 787 for(p=conf_no; *p; p++) { 788 if (!strcasecmp(*p,s)) 789 return 0; 790 } 791 792 /* Default to "no" */ 793 return 0; 794 } 795 796 static krb5_error_code 797 krb5_libdefault_string(krb5_context context, const krb5_data *realm, 798 const char *option, char **ret_value) 799 { 800 profile_t profile; 801 const char *names[5]; 802 char **nameval = NULL; 803 krb5_error_code retval; 804 char realmstr[1024]; 805 806 if (realm->length > sizeof(realmstr)-1) 807 return(EINVAL); 808 809 strncpy(realmstr, realm->data, realm->length); 810 realmstr[realm->length] = '\0'; 811 812 if (!context || (context->magic != KV5M_CONTEXT)) 813 return KV5M_CONTEXT; 814 815 profile = context->profile; 816 817 /* Solaris Kerberos */ 818 names[0] = "realms"; 819 820 /* 821 * Try number one: 822 * 823 * [realms] 824 * REALM = { 825 * option = <boolean> 826 * } 827 */ 828 829 names[1] = realmstr; 830 names[2] = option; 831 names[3] = 0; 832 retval = profile_get_values(profile, names, &nameval); 833 if (retval == 0 && nameval && nameval[0]) 834 goto goodbye; 835 836 /* 837 * Try number two: 838 * 839 * [libdefaults] 840 * option = <boolean> 841 */ 842 843 names[0] = "libdefaults"; 844 names[1] = option; 845 names[2] = 0; 846 retval = profile_get_values(profile, names, &nameval); 847 if (retval == 0 && nameval && nameval[0]) 848 goto goodbye; 849 850 goodbye: 851 if (!nameval) 852 return(ENOENT); 853 854 if (!nameval[0]) { 855 retval = ENOENT; 856 } else { 857 *ret_value = malloc(strlen(nameval[0]) + 1); 858 if (!*ret_value) 859 retval = ENOMEM; 860 else 861 strcpy(*ret_value, nameval[0]); 862 } 863 864 profile_free_list(nameval); 865 866 return retval; 867 } 868 869 /* not static so verify_init_creds() can call it */ 870 /* as well as the DNS code */ 871 872 krb5_error_code 873 krb5_libdefault_boolean(krb5_context context, const krb5_data *realm, 874 const char *option, int *ret_value) 875 { 876 char *string = NULL; 877 krb5_error_code retval; 878 879 retval = krb5_libdefault_string(context, realm, option, &string); 880 881 if (retval) 882 return(retval); 883 884 *ret_value = _krb5_conf_boolean(string); 885 free(string); 886 887 return(0); 888 } 889 890 /* Sort a pa_data sequence so that types named in the "preferred_preauth_types" 891 * libdefaults entry are listed before any others. */ 892 static krb5_error_code 893 sort_krb5_padata_sequence(krb5_context context, krb5_data *realm, 894 krb5_pa_data **padata) 895 { 896 int i, j, base; 897 krb5_error_code ret; 898 const char *p; 899 long l; 900 char *q, *preauth_types = NULL; 901 krb5_pa_data *tmp; 902 int need_free_string = 1; 903 904 if ((padata == NULL) || (padata[0] == NULL)) { 905 return 0; 906 } 907 908 ret = krb5_libdefault_string(context, realm, "preferred_preauth_types", 909 &preauth_types); 910 if ((ret != 0) || (preauth_types == NULL)) { 911 /* Try to use PKINIT first. */ 912 preauth_types = "17, 16, 15, 14"; 913 need_free_string = 0; 914 } 915 916 #ifdef DEBUG 917 fprintf (stderr, "preauth data types before sorting:"); 918 for (i = 0; padata[i]; i++) { 919 fprintf (stderr, " %d", padata[i]->pa_type); 920 } 921 fprintf (stderr, "\n"); 922 #endif 923 924 base = 0; 925 for (p = preauth_types; *p != '\0';) { 926 /* skip whitespace to find an entry */ 927 p += strspn(p, ", "); 928 if (*p != '\0') { 929 /* see if we can extract a number */ 930 l = strtol(p, &q, 10); 931 if ((q != NULL) && (q > p)) { 932 /* got a valid number; search for a matchin entry */ 933 for (i = base; padata[i] != NULL; i++) { 934 /* bubble the matching entry to the front of the list */ 935 if (padata[i]->pa_type == l) { 936 tmp = padata[i]; 937 for (j = i; j > base; j--) 938 padata[j] = padata[j - 1]; 939 padata[base] = tmp; 940 base++; 941 break; 942 } 943 } 944 p = q; 945 } else { 946 break; 947 } 948 } 949 } 950 if (need_free_string) 951 free(preauth_types); 952 953 #ifdef DEBUG 954 fprintf (stderr, "preauth data types after sorting:"); 955 for (i = 0; padata[i]; i++) 956 fprintf (stderr, " %d", padata[i]->pa_type); 957 fprintf (stderr, "\n"); 958 #endif 959 960 return 0; 961 } 962 963 /* 964 * Solaris Kerberos 965 * Return 1 if any char in string is lower-case. 966 */ 967 static int 968 is_lower_case(char *s) 969 { 970 if (!s) 971 return 0; 972 973 while (*s) { 974 if (islower((int)*s)) 975 return 1; 976 s++; 977 } 978 return 0; 979 } 980 981 krb5_error_code KRB5_CALLCONV 982 krb5_get_init_creds(krb5_context context, 983 krb5_creds *creds, 984 krb5_principal client, 985 krb5_prompter_fct prompter, 986 void *prompter_data, 987 krb5_deltat start_time, 988 char *in_tkt_service, 989 krb5_gic_opt_ext *options, 990 krb5_gic_get_as_key_fct gak_fct, 991 void *gak_data, 992 int *use_master, 993 krb5_kdc_rep **as_reply) 994 { 995 krb5_error_code ret; 996 krb5_kdc_req request; 997 krb5_data *encoded_request_body, *encoded_previous_request; 998 krb5_pa_data **preauth_to_use, **kdc_padata; 999 int tempint; 1000 char *tempstr = NULL; 1001 krb5_deltat tkt_life; 1002 krb5_deltat renew_life; 1003 int loopcount; 1004 krb5_data salt; 1005 krb5_data s2kparams; 1006 krb5_keyblock as_key; 1007 krb5_error *err_reply = NULL; 1008 krb5_kdc_rep *local_as_reply; 1009 krb5_timestamp time_now; 1010 krb5_enctype etype = 0; 1011 krb5_preauth_client_rock get_data_rock; 1012 char *hostname_used = NULL; 1013 1014 /* initialize everything which will be freed at cleanup */ 1015 1016 s2kparams.data = NULL; 1017 s2kparams.length = 0; 1018 request.server = NULL; 1019 request.ktype = NULL; 1020 request.addresses = NULL; 1021 request.padata = NULL; 1022 encoded_request_body = NULL; 1023 encoded_previous_request = NULL; 1024 preauth_to_use = NULL; 1025 kdc_padata = NULL; 1026 as_key.length = 0; 1027 salt.length = 0; 1028 salt.data = NULL; 1029 1030 (void) memset(&as_key, 0, sizeof(as_key)); 1031 1032 local_as_reply = 0; 1033 1034 /* 1035 * Set up the basic request structure 1036 */ 1037 request.magic = KV5M_KDC_REQ; 1038 request.msg_type = KRB5_AS_REQ; 1039 1040 /* request.nonce is filled in when we send a request to the kdc */ 1041 request.nonce = 0; 1042 1043 /* request.padata is filled in later */ 1044 1045 request.kdc_options = context->kdc_default_options; 1046 1047 /* forwardable */ 1048 1049 if (options && (options->flags & KRB5_GET_INIT_CREDS_OPT_FORWARDABLE)) 1050 tempint = options->forwardable; 1051 else if ((ret = krb5_libdefault_boolean(context, &client->realm, 1052 "forwardable", &tempint)) == 0) 1053 /*EMPTY*/ 1054 ; 1055 else 1056 tempint = 0; 1057 if (tempint) 1058 request.kdc_options |= KDC_OPT_FORWARDABLE; 1059 1060 /* proxiable */ 1061 1062 if (options && (options->flags & KRB5_GET_INIT_CREDS_OPT_PROXIABLE)) 1063 tempint = options->proxiable; 1064 else if ((ret = krb5_libdefault_boolean(context, &client->realm, 1065 "proxiable", &tempint)) == 0) 1066 /*EMPTY*/ 1067 ; 1068 else 1069 tempint = 0; 1070 if (tempint) 1071 request.kdc_options |= KDC_OPT_PROXIABLE; 1072 1073 /* allow_postdate */ 1074 1075 if (start_time > 0) 1076 request.kdc_options |= (KDC_OPT_ALLOW_POSTDATE|KDC_OPT_POSTDATED); 1077 1078 /* ticket lifetime */ 1079 1080 if ((ret = krb5_timeofday(context, &request.from))) 1081 goto cleanup; 1082 request.from = krb5int_addint32(request.from, start_time); 1083 1084 if (options && (options->flags & KRB5_GET_INIT_CREDS_OPT_TKT_LIFE)) { 1085 tkt_life = options->tkt_life; 1086 } else if ((ret = krb5_libdefault_string(context, &client->realm, 1087 "ticket_lifetime", &tempstr)) 1088 == 0) { 1089 ret = krb5_string_to_deltat(tempstr, &tkt_life); 1090 free(tempstr); 1091 if (ret) { 1092 goto cleanup; 1093 } 1094 } else { 1095 /* this used to be hardcoded in kinit.c */ 1096 tkt_life = 24*60*60; 1097 } 1098 request.till = krb5int_addint32(request.from, tkt_life); 1099 1100 /* renewable lifetime */ 1101 1102 if (options && (options->flags & KRB5_GET_INIT_CREDS_OPT_RENEW_LIFE)) { 1103 renew_life = options->renew_life; 1104 } else if ((ret = krb5_libdefault_string(context, &client->realm, 1105 "renew_lifetime", &tempstr)) 1106 == 0) { 1107 ret = krb5_string_to_deltat(tempstr, &renew_life); 1108 free(tempstr); 1109 if (ret) { 1110 goto cleanup; 1111 } 1112 } else { 1113 renew_life = 0; 1114 } 1115 if (renew_life > 0) 1116 request.kdc_options |= KDC_OPT_RENEWABLE; 1117 1118 if (renew_life > 0) { 1119 request.rtime = krb5int_addint32(request.from, renew_life); 1120 if (request.rtime < request.till) { 1121 /* don't ask for a smaller renewable time than the lifetime */ 1122 request.rtime = request.till; 1123 } 1124 /* we are already asking for renewable tickets so strip this option */ 1125 request.kdc_options &= ~(KDC_OPT_RENEWABLE_OK); 1126 } else { 1127 request.rtime = 0; 1128 } 1129 1130 /* client */ 1131 1132 request.client = client; 1133 1134 /* service */ 1135 1136 if (in_tkt_service) { 1137 /* this is ugly, because so are the data structures involved. I'm 1138 in the library, so I'm going to manipulate the data structures 1139 directly, otherwise, it will be worse. */ 1140 1141 if ((ret = krb5_parse_name(context, in_tkt_service, &request.server))) 1142 goto cleanup; 1143 1144 /* stuff the client realm into the server principal. 1145 realloc if necessary */ 1146 if (request.server->realm.length < request.client->realm.length) 1147 if ((request.server->realm.data = 1148 (char *) realloc(request.server->realm.data, 1149 request.client->realm.length)) == NULL) { 1150 ret = ENOMEM; 1151 goto cleanup; 1152 } 1153 1154 request.server->realm.length = request.client->realm.length; 1155 memcpy(request.server->realm.data, request.client->realm.data, 1156 request.client->realm.length); 1157 } else { 1158 if ((ret = krb5_build_principal_ext(context, &request.server, 1159 request.client->realm.length, 1160 request.client->realm.data, 1161 KRB5_TGS_NAME_SIZE, 1162 KRB5_TGS_NAME, 1163 request.client->realm.length, 1164 request.client->realm.data, 1165 0))) 1166 goto cleanup; 1167 } 1168 1169 krb5_preauth_request_context_init(context); 1170 1171 /* nonce is filled in by send_as_request if we don't take care of it */ 1172 1173 if (options && (options->flags & KRB5_GET_INIT_CREDS_OPT_ETYPE_LIST)) { 1174 request.ktype = options->etype_list; 1175 request.nktypes = options->etype_list_length; 1176 } else if ((ret = krb5_get_default_in_tkt_ktypes(context, 1177 &request.ktype)) == 0) { 1178 for (request.nktypes = 0; 1179 request.ktype[request.nktypes]; 1180 request.nktypes++) 1181 ; 1182 } else { 1183 /* there isn't any useful default here. ret is set from above */ 1184 goto cleanup; 1185 } 1186 1187 if (options && (options->flags & KRB5_GET_INIT_CREDS_OPT_ADDRESS_LIST)) { 1188 request.addresses = options->address_list; 1189 } 1190 /* it would be nice if this parsed out an address list, but 1191 that would be work. */ 1192 else if (((ret = krb5_libdefault_boolean(context, &client->realm, 1193 "no_addresses", &tempint)) == 0) 1194 || (tempint == 1)) { 1195 /*EMPTY*/ 1196 ; 1197 } else if (((ret = krb5_libdefault_boolean(context, &client->realm, 1198 "noaddresses", &tempint)) == 0) 1199 || (tempint == 1)) { 1200 /*EMPTY*/ 1201 ; 1202 } else { 1203 if ((ret = krb5_os_localaddr(context, &request.addresses))) 1204 goto cleanup; 1205 } 1206 1207 request.authorization_data.ciphertext.length = 0; 1208 request.authorization_data.ciphertext.data = 0; 1209 request.unenc_authdata = 0; 1210 request.second_ticket = 0; 1211 1212 /* set up the other state. */ 1213 1214 if (options && (options->flags & KRB5_GET_INIT_CREDS_OPT_PREAUTH_LIST)) { 1215 if ((ret = make_preauth_list(context, options->preauth_list, 1216 options->preauth_list_length, 1217 &preauth_to_use))) 1218 goto cleanup; 1219 } 1220 1221 /* the salt is allocated from somewhere, unless it is from the caller, 1222 then it is a reference */ 1223 1224 if (options && (options->flags & KRB5_GET_INIT_CREDS_OPT_SALT)) { 1225 salt = *options->salt; 1226 } else { 1227 salt.length = SALT_TYPE_AFS_LENGTH; 1228 salt.data = NULL; 1229 } 1230 1231 1232 /* set the request nonce */ 1233 if ((ret = krb5_timeofday(context, &time_now))) 1234 goto cleanup; 1235 /* 1236 * XXX we know they are the same size... and we should do 1237 * something better than just the current time 1238 */ 1239 request.nonce = (krb5_int32) time_now; 1240 1241 /* give the preauth plugins a chance to prep the request body */ 1242 krb5_preauth_prepare_request(context, options, &request); 1243 ret = encode_krb5_kdc_req_body(&request, &encoded_request_body); 1244 if (ret) 1245 goto cleanup; 1246 1247 get_data_rock.magic = CLIENT_ROCK_MAGIC; 1248 get_data_rock.as_reply = NULL; 1249 1250 /* now, loop processing preauth data and talking to the kdc */ 1251 for (loopcount = 0; loopcount < MAX_IN_TKT_LOOPS; loopcount++) { 1252 if (request.padata) { 1253 krb5_free_pa_data(context, request.padata); 1254 request.padata = NULL; 1255 } 1256 if (!err_reply) { 1257 /* either our first attempt, or retrying after PREAUTH_NEEDED */ 1258 if ((ret = krb5_do_preauth(context, 1259 &request, 1260 encoded_request_body, 1261 encoded_previous_request, 1262 preauth_to_use, &request.padata, 1263 &salt, &s2kparams, &etype, &as_key, 1264 prompter, prompter_data, 1265 gak_fct, gak_data, 1266 &get_data_rock, options))) 1267 goto cleanup; 1268 } else { 1269 if (preauth_to_use != NULL) { 1270 /* 1271 * Retry after an error other than PREAUTH_NEEDED, 1272 * using e-data to figure out what to change. 1273 */ 1274 ret = krb5_do_preauth_tryagain(context, 1275 &request, 1276 encoded_request_body, 1277 encoded_previous_request, 1278 preauth_to_use, &request.padata, 1279 err_reply, 1280 &salt, &s2kparams, &etype, 1281 &as_key, 1282 prompter, prompter_data, 1283 gak_fct, gak_data, 1284 &get_data_rock, options); 1285 } else { 1286 /* No preauth supplied, so can't query the plug-ins. */ 1287 ret = KRB5KRB_ERR_GENERIC; 1288 } 1289 if (ret) { 1290 /* couldn't come up with anything better */ 1291 ret = err_reply->error + ERROR_TABLE_BASE_krb5; 1292 } 1293 krb5_free_error(context, err_reply); 1294 err_reply = NULL; 1295 if (ret) 1296 goto cleanup; 1297 } 1298 1299 if (encoded_previous_request != NULL) { 1300 krb5_free_data(context, encoded_previous_request); 1301 encoded_previous_request = NULL; 1302 } 1303 ret = encode_krb5_as_req(&request, &encoded_previous_request); 1304 if (ret) 1305 goto cleanup; 1306 1307 err_reply = NULL; 1308 local_as_reply = 0; 1309 1310 free(hostname_used); 1311 hostname_used = NULL; 1312 1313 if ((ret = send_as_request2(context, &request, &err_reply, 1314 &local_as_reply, use_master, 1315 &hostname_used))) 1316 goto cleanup; 1317 1318 if (err_reply) { 1319 if (err_reply->error == KDC_ERR_PREAUTH_REQUIRED && 1320 err_reply->e_data.length > 0) { 1321 /* reset the list of preauth types to try */ 1322 if (preauth_to_use) { 1323 krb5_free_pa_data(context, preauth_to_use); 1324 preauth_to_use = NULL; 1325 } 1326 ret = decode_krb5_padata_sequence(&err_reply->e_data, 1327 &preauth_to_use); 1328 krb5_free_error(context, err_reply); 1329 err_reply = NULL; 1330 if (ret) 1331 goto cleanup; 1332 ret = sort_krb5_padata_sequence(context, 1333 &request.server->realm, 1334 preauth_to_use); 1335 if (ret) 1336 goto cleanup; 1337 /* continue to next iteration */ 1338 } else { 1339 if (err_reply->e_data.length > 0) { 1340 /* continue to next iteration */ 1341 } else { 1342 /* error + no hints = give up */ 1343 ret = (krb5_error_code) err_reply->error 1344 + ERROR_TABLE_BASE_krb5; 1345 goto cleanup; 1346 } 1347 } 1348 } else if (local_as_reply) { 1349 break; 1350 } else { 1351 ret = KRB5KRB_AP_ERR_MSG_TYPE; 1352 goto cleanup; 1353 } 1354 } 1355 1356 if (loopcount == MAX_IN_TKT_LOOPS) { 1357 ret = KRB5_GET_IN_TKT_LOOP; 1358 /* Solaris Kerberos */ 1359 { 1360 char *s_name = NULL; 1361 char *c_name = NULL; 1362 krb5_error_code serr, cerr; 1363 serr = krb5_unparse_name(context, creds->server, &s_name); 1364 cerr = krb5_unparse_name(context, creds->client, &c_name); 1365 krb5_set_error_message(context, ret, 1366 dgettext(TEXT_DOMAIN, 1367 "Looping detected getting initial creds: '%s' requesting ticket '%s'. Max loops is %d. Make sure a KDC is available"), 1368 cerr ? "unknown" : c_name, 1369 serr ? "unknown" : s_name, 1370 MAX_IN_TKT_LOOPS); 1371 if (s_name) 1372 krb5_free_unparsed_name(context, s_name); 1373 if (c_name) 1374 krb5_free_unparsed_name(context, c_name); 1375 } 1376 goto cleanup; 1377 } 1378 1379 /* process any preauth data in the as_reply */ 1380 krb5_clear_preauth_context_use_counts(context); 1381 if ((ret = sort_krb5_padata_sequence(context, &request.server->realm, 1382 local_as_reply->padata))) 1383 goto cleanup; 1384 get_data_rock.as_reply = local_as_reply; 1385 if ((ret = krb5_do_preauth(context, 1386 &request, 1387 encoded_request_body, encoded_previous_request, 1388 local_as_reply->padata, &kdc_padata, 1389 &salt, &s2kparams, &etype, &as_key, prompter, 1390 prompter_data, gak_fct, gak_data, 1391 &get_data_rock, options))) 1392 goto cleanup; 1393 1394 /* XXX For 1.1.1 and prior KDC's, when SAM is used w/ USE_SAD_AS_KEY, 1395 the AS_REP comes back encrypted in the user's longterm key 1396 instead of in the SAD. If there was a SAM preauth, there 1397 will be an as_key here which will be the SAD. If that fails, 1398 use the gak_fct to get the password, and try again. */ 1399 1400 /* XXX because etypes are handled poorly (particularly wrt SAM, 1401 where the etype is fixed by the kdc), we may want to try 1402 decrypt_as_reply twice. If there's an as_key available, try 1403 it. If decrypting the as_rep fails, or if there isn't an 1404 as_key at all yet, then use the gak_fct to get one, and try 1405 again. */ 1406 1407 if (as_key.length) 1408 ret = decrypt_as_reply(context, NULL, local_as_reply, NULL, 1409 NULL, &as_key, krb5_kdc_rep_decrypt_proc, 1410 NULL); 1411 else 1412 ret = -1; 1413 1414 if (ret) { 1415 /* if we haven't get gotten a key, get it now */ 1416 1417 if ((ret = ((*gak_fct)(context, request.client, 1418 local_as_reply->enc_part.enctype, 1419 prompter, prompter_data, &salt, &s2kparams, 1420 &as_key, gak_data)))) 1421 goto cleanup; 1422 1423 if ((ret = decrypt_as_reply(context, NULL, local_as_reply, NULL, 1424 NULL, &as_key, krb5_kdc_rep_decrypt_proc, 1425 NULL))) 1426 goto cleanup; 1427 } 1428 1429 if ((ret = verify_as_reply(context, time_now, &request, local_as_reply))) 1430 goto cleanup; 1431 1432 /* XXX this should be inside stash_as_reply, but as long as 1433 get_in_tkt is still around using that arg as an in/out, I can't 1434 do that */ 1435 /* Solaris Kerberos */ 1436 (void) memset(creds, 0, sizeof(*creds)); 1437 1438 /* Solaris Kerberos */ 1439 if ((ret = stash_as_reply(context, time_now, &request, local_as_reply, 1440 creds, (krb5_ccache)NULL))) 1441 goto cleanup; 1442 1443 /* success */ 1444 1445 ret = 0; 1446 1447 cleanup: 1448 if (ret != 0) { 1449 char *client_name = NULL; 1450 /* See if we can produce a more detailed error message. */ 1451 switch (ret) { 1452 case KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN: 1453 if (krb5_unparse_name(context, client, &client_name) == 0) { 1454 krb5_set_error_message(context, ret, 1455 dgettext(TEXT_DOMAIN, 1456 "Client '%s' not found in Kerberos database"), 1457 client_name); 1458 free(client_name); 1459 } 1460 break; 1461 /* Solaris Kerberos: spruce-up the err msg */ 1462 case KRB5_PREAUTH_FAILED: 1463 case KRB5KDC_ERR_PREAUTH_FAILED: 1464 if (krb5_unparse_name(context, client, &client_name) == 0) { 1465 krb5_set_error_message(context, ret, 1466 dgettext(TEXT_DOMAIN, 1467 "Client '%s' pre-authentication failed"), 1468 client_name); 1469 free(client_name); 1470 } 1471 break; 1472 /* Solaris Kerberos: spruce-up the err msg */ 1473 case KRB5KRB_AP_ERR_SKEW: /* KRB_AP_ERR_SKEW + ERROR_TABLE_BASE_krb5 */ 1474 { 1475 char *s_name = NULL; 1476 char *c_name = NULL; 1477 char stimestring[17]; 1478 char fill = ' '; 1479 krb5_error_code c_err, s_err, s_time; 1480 1481 s_err = krb5_unparse_name(context, 1482 err_reply->server, &s_name); 1483 s_time = krb5_timestamp_to_sfstring(err_reply->stime, 1484 stimestring, 1485 sizeof (stimestring), 1486 &fill); 1487 c_err = krb5_unparse_name(context, client, &c_name); 1488 krb5_set_error_message(context, ret, 1489 dgettext(TEXT_DOMAIN, 1490 "Clock skew too great: '%s' requesting ticket '%s' from KDC '%s' (%s). Skew is %dm"), 1491 c_err == 0 ? c_name : "unknown", 1492 s_err == 0 ? s_name : "unknown", 1493 hostname_used ? hostname_used : "unknown", 1494 s_time == 0 ? stimestring : "unknown", 1495 (s_time != 0) ? 0 : 1496 (abs(err_reply->stime - time_now) / 60)); 1497 if (s_name) 1498 krb5_free_unparsed_name(context, s_name); 1499 if (c_name) 1500 krb5_free_unparsed_name(context, c_name); 1501 } 1502 break; 1503 case KRB5_KDCREP_MODIFIED: 1504 if (krb5_unparse_name(context, client, &client_name) == 0) { 1505 /* 1506 * Solaris Kerberos 1507 * Extra err msg for common(?) case of 1508 * 'kinit user@lower-case-def-realm'. 1509 * DNS SRV recs will match (case insensitive) and trigger sendto 1510 * KDC and result in this error (at least w/MSFT AD KDC). 1511 */ 1512 char *realm = strpbrk(client_name, "@"); 1513 int set = 0; 1514 if (realm++) { 1515 if (realm && realm[0] && is_lower_case(realm)) { 1516 krb5_set_error_message(context, ret, 1517 dgettext(TEXT_DOMAIN, 1518 "KDC reply did not match expectations for client '%s': lower-case detected in realm '%s'"), 1519 client_name, realm); 1520 set = 1; 1521 } 1522 } 1523 if (!set) 1524 krb5_set_error_message(context, ret, 1525 dgettext(TEXT_DOMAIN, 1526 "KDC reply did not match expectations for client '%s'"), 1527 client_name); 1528 free(client_name); 1529 } 1530 break; 1531 default: 1532 break; 1533 } 1534 } 1535 if (err_reply) 1536 krb5_free_error(context, err_reply); 1537 krb5_preauth_request_context_fini(context); 1538 if (encoded_previous_request != NULL) { 1539 krb5_free_data(context, encoded_previous_request); 1540 encoded_previous_request = NULL; 1541 } 1542 if (encoded_request_body != NULL) { 1543 krb5_free_data(context, encoded_request_body); 1544 encoded_request_body = NULL; 1545 } 1546 if (request.server) 1547 krb5_free_principal(context, request.server); 1548 if (request.ktype && 1549 (!(options && (options->flags & KRB5_GET_INIT_CREDS_OPT_ETYPE_LIST)))) 1550 free(request.ktype); 1551 if (request.addresses && 1552 (!(options && 1553 (options->flags & KRB5_GET_INIT_CREDS_OPT_ADDRESS_LIST)))) 1554 krb5_free_addresses(context, request.addresses); 1555 if (preauth_to_use) 1556 krb5_free_pa_data(context, preauth_to_use); 1557 if (kdc_padata) 1558 krb5_free_pa_data(context, kdc_padata); 1559 if (request.padata) 1560 krb5_free_pa_data(context, request.padata); 1561 if (as_key.length) 1562 krb5_free_keyblock_contents(context, &as_key); 1563 if (salt.data && 1564 (!(options && (options->flags & KRB5_GET_INIT_CREDS_OPT_SALT)))) 1565 krb5_xfree(salt.data); 1566 krb5_free_data_contents(context, &s2kparams); 1567 if (as_reply) 1568 *as_reply = local_as_reply; 1569 else if (local_as_reply) 1570 krb5_free_kdc_rep(context, local_as_reply); 1571 if (hostname_used) 1572 free(hostname_used); 1573 return(ret); 1574 } 1575