1 /* 2 * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved. 3 * Copyright 2014 Nexenta Systems, 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 if ((ret = send_as_request2(context, &request, &err_reply, 1310 &local_as_reply, use_master, 1311 &hostname_used))) 1312 goto cleanup; 1313 1314 if (err_reply) { 1315 if (err_reply->error == KDC_ERR_PREAUTH_REQUIRED && 1316 err_reply->e_data.length > 0) { 1317 /* reset the list of preauth types to try */ 1318 if (preauth_to_use) { 1319 krb5_free_pa_data(context, preauth_to_use); 1320 preauth_to_use = NULL; 1321 } 1322 ret = decode_krb5_padata_sequence(&err_reply->e_data, 1323 &preauth_to_use); 1324 krb5_free_error(context, err_reply); 1325 err_reply = NULL; 1326 if (ret) 1327 goto cleanup; 1328 ret = sort_krb5_padata_sequence(context, 1329 &request.server->realm, 1330 preauth_to_use); 1331 if (ret) 1332 goto cleanup; 1333 /* continue to next iteration */ 1334 } else { 1335 if (err_reply->e_data.length > 0) { 1336 /* continue to next iteration */ 1337 } else { 1338 /* error + no hints = give up */ 1339 ret = (krb5_error_code) err_reply->error 1340 + ERROR_TABLE_BASE_krb5; 1341 goto cleanup; 1342 } 1343 } 1344 } else if (local_as_reply) { 1345 break; 1346 } else { 1347 ret = KRB5KRB_AP_ERR_MSG_TYPE; 1348 goto cleanup; 1349 } 1350 } 1351 1352 if (loopcount == MAX_IN_TKT_LOOPS) { 1353 ret = KRB5_GET_IN_TKT_LOOP; 1354 /* Solaris Kerberos */ 1355 { 1356 char *s_name = NULL; 1357 char *c_name = NULL; 1358 krb5_error_code serr, cerr; 1359 serr = krb5_unparse_name(context, creds->server, &s_name); 1360 cerr = krb5_unparse_name(context, creds->client, &c_name); 1361 krb5_set_error_message(context, ret, 1362 dgettext(TEXT_DOMAIN, 1363 "Looping detected getting initial creds: '%s' requesting ticket '%s'. Max loops is %d. Make sure a KDC is available"), 1364 cerr ? "unknown" : c_name, 1365 serr ? "unknown" : s_name, 1366 MAX_IN_TKT_LOOPS); 1367 if (s_name) 1368 krb5_free_unparsed_name(context, s_name); 1369 if (c_name) 1370 krb5_free_unparsed_name(context, c_name); 1371 } 1372 goto cleanup; 1373 } 1374 1375 /* process any preauth data in the as_reply */ 1376 krb5_clear_preauth_context_use_counts(context); 1377 if ((ret = sort_krb5_padata_sequence(context, &request.server->realm, 1378 local_as_reply->padata))) 1379 goto cleanup; 1380 get_data_rock.as_reply = local_as_reply; 1381 if ((ret = krb5_do_preauth(context, 1382 &request, 1383 encoded_request_body, encoded_previous_request, 1384 local_as_reply->padata, &kdc_padata, 1385 &salt, &s2kparams, &etype, &as_key, prompter, 1386 prompter_data, gak_fct, gak_data, 1387 &get_data_rock, options))) 1388 goto cleanup; 1389 1390 /* XXX For 1.1.1 and prior KDC's, when SAM is used w/ USE_SAD_AS_KEY, 1391 the AS_REP comes back encrypted in the user's longterm key 1392 instead of in the SAD. If there was a SAM preauth, there 1393 will be an as_key here which will be the SAD. If that fails, 1394 use the gak_fct to get the password, and try again. */ 1395 1396 /* XXX because etypes are handled poorly (particularly wrt SAM, 1397 where the etype is fixed by the kdc), we may want to try 1398 decrypt_as_reply twice. If there's an as_key available, try 1399 it. If decrypting the as_rep fails, or if there isn't an 1400 as_key at all yet, then use the gak_fct to get one, and try 1401 again. */ 1402 1403 if (as_key.length) 1404 ret = decrypt_as_reply(context, NULL, local_as_reply, NULL, 1405 NULL, &as_key, krb5_kdc_rep_decrypt_proc, 1406 NULL); 1407 else 1408 ret = -1; 1409 1410 if (ret) { 1411 /* if we haven't get gotten a key, get it now */ 1412 1413 if ((ret = ((*gak_fct)(context, request.client, 1414 local_as_reply->enc_part.enctype, 1415 prompter, prompter_data, &salt, &s2kparams, 1416 &as_key, gak_data)))) 1417 goto cleanup; 1418 1419 if ((ret = decrypt_as_reply(context, NULL, local_as_reply, NULL, 1420 NULL, &as_key, krb5_kdc_rep_decrypt_proc, 1421 NULL))) 1422 goto cleanup; 1423 } 1424 1425 if ((ret = verify_as_reply(context, time_now, &request, local_as_reply))) 1426 goto cleanup; 1427 1428 /* XXX this should be inside stash_as_reply, but as long as 1429 get_in_tkt is still around using that arg as an in/out, I can't 1430 do that */ 1431 /* Solaris Kerberos */ 1432 (void) memset(creds, 0, sizeof(*creds)); 1433 1434 /* Solaris Kerberos */ 1435 if ((ret = stash_as_reply(context, time_now, &request, local_as_reply, 1436 creds, (krb5_ccache)NULL))) 1437 goto cleanup; 1438 1439 /* success */ 1440 1441 ret = 0; 1442 1443 cleanup: 1444 if (ret != 0) { 1445 char *client_name = NULL; 1446 /* See if we can produce a more detailed error message. */ 1447 switch (ret) { 1448 case KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN: 1449 if (krb5_unparse_name(context, client, &client_name) == 0) { 1450 krb5_set_error_message(context, ret, 1451 dgettext(TEXT_DOMAIN, 1452 "Client '%s' not found in Kerberos database"), 1453 client_name); 1454 free(client_name); 1455 } 1456 break; 1457 /* Solaris Kerberos: spruce-up the err msg */ 1458 case KRB5_PREAUTH_FAILED: 1459 case KRB5KDC_ERR_PREAUTH_FAILED: 1460 if (krb5_unparse_name(context, client, &client_name) == 0) { 1461 krb5_set_error_message(context, ret, 1462 dgettext(TEXT_DOMAIN, 1463 "Client '%s' pre-authentication failed"), 1464 client_name); 1465 free(client_name); 1466 } 1467 break; 1468 /* Solaris Kerberos: spruce-up the err msg */ 1469 case KRB5KRB_AP_ERR_SKEW: /* KRB_AP_ERR_SKEW + ERROR_TABLE_BASE_krb5 */ 1470 { 1471 char *s_name = NULL; 1472 char *c_name = NULL; 1473 char stimestring[17]; 1474 char fill = ' '; 1475 krb5_error_code c_err, s_err, s_time; 1476 1477 s_err = krb5_unparse_name(context, 1478 err_reply->server, &s_name); 1479 s_time = krb5_timestamp_to_sfstring(err_reply->stime, 1480 stimestring, 1481 sizeof (stimestring), 1482 &fill); 1483 c_err = krb5_unparse_name(context, client, &c_name); 1484 krb5_set_error_message(context, ret, 1485 dgettext(TEXT_DOMAIN, 1486 "Clock skew too great: '%s' requesting ticket '%s' from KDC '%s' (%s). Skew is %dm"), 1487 c_err == 0 ? c_name : "unknown", 1488 s_err == 0 ? s_name : "unknown", 1489 hostname_used ? hostname_used : "unknown", 1490 s_time == 0 ? stimestring : "unknown", 1491 (s_time != 0) ? 0 : 1492 (abs(err_reply->stime - time_now) / 60)); 1493 if (s_name) 1494 krb5_free_unparsed_name(context, s_name); 1495 if (c_name) 1496 krb5_free_unparsed_name(context, c_name); 1497 } 1498 break; 1499 case KRB5_KDCREP_MODIFIED: 1500 if (krb5_unparse_name(context, client, &client_name) == 0) { 1501 /* 1502 * Solaris Kerberos 1503 * Extra err msg for common(?) case of 1504 * 'kinit user@lower-case-def-realm'. 1505 * DNS SRV recs will match (case insensitive) and trigger sendto 1506 * KDC and result in this error (at least w/MSFT AD KDC). 1507 */ 1508 char *realm = strpbrk(client_name, "@"); 1509 int set = 0; 1510 if (realm++) { 1511 if (realm && realm[0] && is_lower_case(realm)) { 1512 krb5_set_error_message(context, ret, 1513 dgettext(TEXT_DOMAIN, 1514 "KDC reply did not match expectations for client '%s': lower-case detected in realm '%s'"), 1515 client_name, realm); 1516 set = 1; 1517 } 1518 } 1519 if (!set) 1520 krb5_set_error_message(context, ret, 1521 dgettext(TEXT_DOMAIN, 1522 "KDC reply did not match expectations for client '%s'"), 1523 client_name); 1524 free(client_name); 1525 } 1526 break; 1527 default: 1528 break; 1529 } 1530 } 1531 if (err_reply) 1532 krb5_free_error(context, err_reply); 1533 krb5_preauth_request_context_fini(context); 1534 if (encoded_previous_request != NULL) { 1535 krb5_free_data(context, encoded_previous_request); 1536 encoded_previous_request = NULL; 1537 } 1538 if (encoded_request_body != NULL) { 1539 krb5_free_data(context, encoded_request_body); 1540 encoded_request_body = NULL; 1541 } 1542 if (request.server) 1543 krb5_free_principal(context, request.server); 1544 if (request.ktype && 1545 (!(options && (options->flags & KRB5_GET_INIT_CREDS_OPT_ETYPE_LIST)))) 1546 free(request.ktype); 1547 if (request.addresses && 1548 (!(options && 1549 (options->flags & KRB5_GET_INIT_CREDS_OPT_ADDRESS_LIST)))) 1550 krb5_free_addresses(context, request.addresses); 1551 if (preauth_to_use) 1552 krb5_free_pa_data(context, preauth_to_use); 1553 if (kdc_padata) 1554 krb5_free_pa_data(context, kdc_padata); 1555 if (request.padata) 1556 krb5_free_pa_data(context, request.padata); 1557 if (as_key.length) 1558 krb5_free_keyblock_contents(context, &as_key); 1559 if (salt.data && 1560 (!(options && (options->flags & KRB5_GET_INIT_CREDS_OPT_SALT)))) 1561 krb5_xfree(salt.data); 1562 krb5_free_data_contents(context, &s2kparams); 1563 if (as_reply) 1564 *as_reply = local_as_reply; 1565 else if (local_as_reply) 1566 krb5_free_kdc_rep(context, local_as_reply); 1567 if (hostname_used) 1568 free(hostname_used); 1569 return(ret); 1570 } 1571