1 /* 2 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 3 * Use is subject to license terms. 4 */ 5 6 #pragma ident "%Z%%M% %I% %E% SMI" 7 8 /* 9 * Copyright 1995, 2003 by the Massachusetts Institute of Technology. All 10 * Rights Reserved. 11 * 12 * Export of this software from the United States of America may 13 * require a specific license from the United States Government. 14 * It is the responsibility of any person or organization contemplating 15 * export to obtain such a license before exporting. 16 * 17 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and 18 * distribute this software and its documentation for any purpose and 19 * without fee is hereby granted, provided that the above copyright 20 * notice appear in all copies and that both that copyright notice and 21 * this permission notice appear in supporting documentation, and that 22 * the name of M.I.T. not be used in advertising or publicity pertaining 23 * to distribution of the software without specific, written prior 24 * permission. Furthermore if you modify this software you must label 25 * your software as modified software and not distribute it in such a 26 * fashion that it might be confused with the original M.I.T. software. 27 * M.I.T. makes no representations about the suitability of 28 * this software for any purpose. It is provided "as is" without express 29 * or implied warranty. 30 * 31 */ 32 33 /* 34 * This file contains routines for establishing, verifying, and any other 35 * necessary functions, for utilizing the pre-authentication field of the 36 * kerberos kdc request, with various hardware/software verification devices. 37 */ 38 39 #include <k5-int.h> 40 41 typedef krb5_error_code (*pa_function)(krb5_context, 42 krb5_kdc_req *request, 43 krb5_pa_data *in_padata, 44 krb5_pa_data **out_padata, 45 krb5_data *salt, 46 krb5_data *s2kparams, 47 krb5_enctype *etype, 48 krb5_keyblock *as_key, 49 krb5_prompter_fct prompter_fct, 50 void *prompter_data, 51 krb5_gic_get_as_key_fct gak_fct, 52 void *gak_data); 53 54 typedef struct _pa_types_t { 55 krb5_preauthtype type; 56 pa_function fct; 57 int flags; 58 } pa_types_t; 59 60 #define PA_REAL 0x0001 61 #define PA_INFO 0x0002 62 63 /*ARGSUSED*/ 64 static 65 krb5_error_code pa_salt(krb5_context context, 66 krb5_kdc_req *request, 67 krb5_pa_data *in_padata, 68 krb5_pa_data **out_padata, 69 krb5_data *salt, 70 krb5_data *s2kparams, 71 krb5_enctype *etype, 72 krb5_keyblock *as_key, 73 krb5_prompter_fct prompter, void *prompter_data, 74 krb5_gic_get_as_key_fct gak_fct, void *gak_data) 75 { 76 krb5_data tmp; 77 78 tmp.data = (char *)in_padata->contents; 79 tmp.length = in_padata->length; 80 krb5_free_data_contents(context, salt); 81 krb5int_copy_data_contents(context, &tmp, salt); 82 83 if (in_padata->pa_type == KRB5_PADATA_AFS3_SALT) 84 salt->length = -1; 85 86 return(0); 87 } 88 89 /*ARGSUSED*/ 90 static 91 krb5_error_code pa_enc_timestamp(krb5_context context, 92 krb5_kdc_req *request, 93 krb5_pa_data *in_padata, 94 krb5_pa_data **out_padata, 95 krb5_data *salt, 96 krb5_data *s2kparams, 97 krb5_enctype *etype, 98 krb5_keyblock *as_key, 99 krb5_prompter_fct prompter, 100 void *prompter_data, 101 krb5_gic_get_as_key_fct gak_fct, 102 void *gak_data) 103 { 104 krb5_error_code ret; 105 krb5_pa_enc_ts pa_enc; 106 krb5_data *tmp; 107 krb5_enc_data enc_data; 108 krb5_pa_data *pa; 109 110 if (as_key->length == 0) { 111 #ifdef DEBUG 112 if (salt != NULL && salt->data != NULL) { 113 fprintf (stderr, "%s:%d: salt len=%d", __FILE__, __LINE__, 114 salt->length); 115 if (salt->length > 0) 116 fprintf (stderr, " '%*s'", salt->length, salt->data); 117 fprintf (stderr, "; *etype=%d request->ktype[0]=%d\n", 118 *etype, request->ktype[0]); 119 } 120 #endif 121 if ((ret = ((*gak_fct)(context, request->client, 122 *etype ? *etype : request->ktype[0], 123 prompter, prompter_data, 124 salt, s2kparams, as_key, gak_data)))) 125 return(ret); 126 } 127 128 /* now get the time of day, and encrypt it accordingly */ 129 130 if ((ret = krb5_us_timeofday(context, &pa_enc.patimestamp, &pa_enc.pausec))) 131 return(ret); 132 133 if ((ret = encode_krb5_pa_enc_ts(&pa_enc, &tmp))) 134 return(ret); 135 136 #ifdef DEBUG 137 fprintf (stderr, "key type %d bytes %02x %02x ...\n", 138 as_key->enctype, 139 as_key->contents[0], as_key->contents[1]); 140 #endif 141 ret = krb5_encrypt_helper(context, as_key, 142 KRB5_KEYUSAGE_AS_REQ_PA_ENC_TS, 143 tmp, &enc_data); 144 #ifdef DEBUG 145 fprintf (stderr, "enc data { type=%d kvno=%d data=%02x %02x ... }\n", 146 enc_data.enctype, enc_data.kvno, 147 0xff & enc_data.ciphertext.data[0], 148 0xff & enc_data.ciphertext.data[1]); 149 #endif 150 151 krb5_free_data(context, tmp); 152 153 if (ret) { 154 krb5_xfree(enc_data.ciphertext.data); 155 return(ret); 156 } 157 158 ret = encode_krb5_enc_data(&enc_data, &tmp); 159 160 krb5_xfree(enc_data.ciphertext.data); 161 162 if (ret) 163 return(ret); 164 165 if ((pa = (krb5_pa_data *) malloc(sizeof(krb5_pa_data))) == NULL) { 166 krb5_free_data(context, tmp); 167 return(ENOMEM); 168 } 169 170 pa->magic = KV5M_PA_DATA; 171 pa->pa_type = KRB5_PADATA_ENC_TIMESTAMP; 172 pa->length = tmp->length; 173 pa->contents = (krb5_octet *) tmp->data; 174 175 *out_padata = pa; 176 177 krb5_xfree(tmp); 178 179 return(0); 180 } 181 182 static 183 char *sam_challenge_banner(krb5_int32 sam_type) 184 { 185 char *label; 186 187 switch (sam_type) { 188 case PA_SAM_TYPE_ENIGMA: /* Enigma Logic */ 189 label = "Challenge for Enigma Logic mechanism"; 190 break; 191 case PA_SAM_TYPE_DIGI_PATH: /* Digital Pathways */ 192 case PA_SAM_TYPE_DIGI_PATH_HEX: /* Digital Pathways */ 193 label = "Challenge for Digital Pathways mechanism"; 194 break; 195 case PA_SAM_TYPE_ACTIVCARD_DEC: /* Digital Pathways */ 196 case PA_SAM_TYPE_ACTIVCARD_HEX: /* Digital Pathways */ 197 label = "Challenge for Activcard mechanism"; 198 break; 199 case PA_SAM_TYPE_SKEY_K0: /* S/key where KDC has key 0 */ 200 label = "Challenge for Enhanced S/Key mechanism"; 201 break; 202 case PA_SAM_TYPE_SKEY: /* Traditional S/Key */ 203 label = "Challenge for Traditional S/Key mechanism"; 204 break; 205 case PA_SAM_TYPE_SECURID: /* Security Dynamics */ 206 label = "Challenge for Security Dynamics mechanism"; 207 break; 208 case PA_SAM_TYPE_SECURID_PREDICT: /* predictive Security Dynamics */ 209 label = "Challenge for Security Dynamics mechanism"; 210 break; 211 default: 212 label = "Challenge from authentication server"; 213 break; 214 } 215 216 return(label); 217 } 218 219 /* this macro expands to the int,ptr necessary for "%.*s" in an sprintf */ 220 221 #define SAMDATA(kdata, str, maxsize) \ 222 (int)((kdata.length)? \ 223 ((((kdata.length)<=(maxsize))?(kdata.length):strlen(str))): \ 224 strlen(str)), \ 225 (kdata.length)? \ 226 ((((kdata.length)<=(maxsize))?(kdata.data):(str))):(str) 227 228 /* XXX Danger! This code is not in sync with the kerberos-password-02 229 draft. This draft cannot be implemented as written. This code is 230 compatible with earlier versions of mit krb5 and cygnus kerbnet. */ 231 232 /*ARGSUSED*/ 233 static 234 krb5_error_code pa_sam(krb5_context context, 235 krb5_kdc_req *request, 236 krb5_pa_data *in_padata, 237 krb5_pa_data **out_padata, 238 krb5_data *salt, 239 krb5_data *s2kparams, 240 krb5_enctype *etype, 241 krb5_keyblock *as_key, 242 krb5_prompter_fct prompter, 243 void *prompter_data, 244 krb5_gic_get_as_key_fct gak_fct, 245 void *gak_data) 246 { 247 krb5_error_code ret; 248 krb5_data tmpsam; 249 char name[100], banner[100]; 250 char prompt[100], response[100]; 251 krb5_data response_data; 252 krb5_prompt kprompt; 253 krb5_prompt_type prompt_type; 254 krb5_data defsalt; 255 krb5_sam_challenge *sam_challenge = 0; 256 krb5_sam_response sam_response; 257 /* these two get encrypted and stuffed in to sam_response */ 258 krb5_enc_sam_response_enc enc_sam_response_enc; 259 krb5_data * scratch; 260 krb5_pa_data * pa; 261 krb5_enc_data * enc_data; 262 size_t enclen; 263 264 if (prompter == NULL) 265 return (EIO); 266 267 tmpsam.length = in_padata->length; 268 tmpsam.data = (char *) in_padata->contents; 269 if ((ret = decode_krb5_sam_challenge(&tmpsam, &sam_challenge))) 270 return(ret); 271 272 if (sam_challenge->sam_flags & KRB5_SAM_MUST_PK_ENCRYPT_SAD) { 273 krb5_xfree(sam_challenge); 274 return(KRB5_SAM_UNSUPPORTED); 275 } 276 /* If we need the password from the user (USE_SAD_AS_KEY not set), */ 277 /* then get it here. Exception for "old" KDCs with CryptoCard */ 278 /* support which uses the USE_SAD_AS_KEY flag, but still needs pwd */ 279 280 if (!(sam_challenge->sam_flags & KRB5_SAM_USE_SAD_AS_KEY) || 281 (sam_challenge->sam_type == PA_SAM_TYPE_CRYPTOCARD)) { 282 283 /* etype has either been set by caller or by KRB5_PADATA_ETYPE_INFO */ 284 /* message from the KDC. If it is not set, pick an enctype that we */ 285 /* think the KDC will have for us. */ 286 287 if (etype && *etype == 0) 288 *etype = ENCTYPE_DES_CBC_CRC; 289 290 if ((ret = (gak_fct)(context, request->client, *etype, prompter, 291 prompter_data, salt, s2kparams, as_key, gak_data))) 292 return(ret); 293 } 294 295 sprintf(name, "%.*s", 296 SAMDATA(sam_challenge->sam_type_name, "SAM Authentication", 297 sizeof(name) - 1)); 298 299 sprintf(banner, "%.*s", 300 SAMDATA(sam_challenge->sam_challenge_label, 301 sam_challenge_banner(sam_challenge->sam_type), 302 sizeof(banner)-1)); 303 304 /* sprintf(prompt, "Challenge is [%s], %s: ", challenge, prompt); */ 305 sprintf(prompt, "%s%.*s%s%.*s", 306 sam_challenge->sam_challenge.length?"Challenge is [":"", 307 SAMDATA(sam_challenge->sam_challenge, "", 20), 308 sam_challenge->sam_challenge.length?"], ":"", 309 SAMDATA(sam_challenge->sam_response_prompt, "passcode", 55)); 310 311 response_data.data = response; 312 response_data.length = sizeof(response); 313 314 kprompt.prompt = prompt; 315 kprompt.hidden = 1; 316 kprompt.reply = &response_data; 317 prompt_type = KRB5_PROMPT_TYPE_PREAUTH; 318 319 /* PROMPTER_INVOCATION */ 320 krb5int_set_prompt_types(context, &prompt_type); 321 if ((ret = ((*prompter)(context, prompter_data, name, 322 banner, 1, &kprompt)))) { 323 krb5_xfree(sam_challenge); 324 krb5int_set_prompt_types(context, 0); 325 return(ret); 326 } 327 krb5int_set_prompt_types(context, 0); 328 329 enc_sam_response_enc.sam_nonce = sam_challenge->sam_nonce; 330 if (sam_challenge->sam_nonce == 0) { 331 if ((ret = krb5_us_timeofday(context, 332 &enc_sam_response_enc.sam_timestamp, 333 &enc_sam_response_enc.sam_usec))) { 334 krb5_xfree(sam_challenge); 335 return(ret); 336 } 337 338 sam_response.sam_patimestamp = enc_sam_response_enc.sam_timestamp; 339 } 340 341 /* XXX What if more than one flag is set? */ 342 if (sam_challenge->sam_flags & KRB5_SAM_SEND_ENCRYPTED_SAD) { 343 344 /* Most of this should be taken care of before we get here. We */ 345 /* will need the user's password and as_key to encrypt the SAD */ 346 /* and we want to preserve ordering of user prompts (first */ 347 /* password, then SAM data) so that user's won't be confused. */ 348 349 if (as_key->length) { 350 krb5_free_keyblock_contents(context, as_key); 351 as_key->length = 0; 352 } 353 354 /* generate a salt using the requested principal */ 355 356 if ((salt->length == -1) && (salt->data == NULL)) { 357 if ((ret = krb5_principal2salt(context, request->client, 358 &defsalt))) { 359 krb5_xfree(sam_challenge); 360 return(ret); 361 } 362 363 salt = &defsalt; 364 } else { 365 defsalt.length = 0; 366 } 367 368 /* generate a key using the supplied password */ 369 370 ret = krb5_c_string_to_key(context, ENCTYPE_DES_CBC_MD5, 371 (krb5_data *)gak_data, salt, as_key); 372 373 if (defsalt.length) 374 krb5_xfree(defsalt.data); 375 376 if (ret) { 377 krb5_xfree(sam_challenge); 378 return(ret); 379 } 380 381 /* encrypt the passcode with the key from above */ 382 383 enc_sam_response_enc.sam_sad = response_data; 384 } else if (sam_challenge->sam_flags & KRB5_SAM_USE_SAD_AS_KEY) { 385 386 /* process the key as password */ 387 388 if (as_key->length) { 389 krb5_free_keyblock_contents(context, as_key); 390 as_key->length = 0; 391 } 392 393 #if 0 394 if ((salt->length == -1) && (salt->data == NULL)) { 395 if (ret = krb5_principal2salt(context, request->client, 396 &defsalt)) { 397 krb5_xfree(sam_challenge); 398 return(ret); 399 } 400 401 salt = &defsalt; 402 } else { 403 defsalt.length = 0; 404 } 405 #else 406 defsalt.length = 0; 407 salt = NULL; 408 #endif 409 410 /* XXX As of the passwords-04 draft, no enctype is specified, 411 the server uses ENCTYPE_DES_CBC_MD5. In the future the 412 server should send a PA-SAM-ETYPE-INFO containing the enctype. */ 413 414 ret = krb5_c_string_to_key(context, ENCTYPE_DES_CBC_MD5, 415 &response_data, salt, as_key); 416 417 if (defsalt.length) 418 krb5_xfree(defsalt.data); 419 420 if (ret) { 421 krb5_xfree(sam_challenge); 422 return(ret); 423 } 424 425 enc_sam_response_enc.sam_sad.length = 0; 426 } else { 427 /* Eventually, combine SAD with long-term key to get 428 encryption key. */ 429 return KRB5_PREAUTH_BAD_TYPE; 430 } 431 432 /* copy things from the challenge */ 433 sam_response.sam_nonce = sam_challenge->sam_nonce; 434 sam_response.sam_flags = sam_challenge->sam_flags; 435 sam_response.sam_track_id = sam_challenge->sam_track_id; 436 sam_response.sam_type = sam_challenge->sam_type; 437 sam_response.magic = KV5M_SAM_RESPONSE; 438 439 krb5_xfree(sam_challenge); 440 441 /* encode the encoded part of the response */ 442 if ((ret = encode_krb5_enc_sam_response_enc(&enc_sam_response_enc, 443 &scratch))) 444 return(ret); 445 446 /* 447 * Solaris Kerberos: 448 * Using new crypto interface now so we can get rid of the 449 * old modules. 450 */ 451 if ((ret = krb5_c_encrypt_length(context, as_key->enctype, 452 scratch->length, &enclen))) { 453 krb5_free_data(context, scratch); 454 return(ret); 455 } 456 457 enc_data = &sam_response.sam_enc_nonce_or_ts; 458 enc_data->magic = KV5M_ENC_DATA; 459 enc_data->kvno = 0; 460 enc_data->enctype = as_key->enctype; 461 enc_data->ciphertext.length = enclen; 462 463 if ((enc_data->ciphertext.data = MALLOC(enclen)) == NULL) { 464 enc_data->ciphertext.length = 0; 465 krb5_free_data(context, scratch); 466 return(ENOMEM); 467 } 468 469 if ((ret = krb5_c_encrypt(context, as_key, 0, 0, 470 scratch, enc_data))) { 471 FREE(enc_data->ciphertext.data, enclen); 472 enc_data->ciphertext.data = NULL; 473 enc_data->ciphertext.length = 0; 474 } 475 476 krb5_free_data(context, scratch); 477 478 if (ret) 479 return(ret); 480 481 /* sam_enc_key is reserved for future use */ 482 sam_response.sam_enc_key.ciphertext.length = 0; 483 484 if ((pa = malloc(sizeof(krb5_pa_data))) == NULL) 485 return(ENOMEM); 486 487 if ((ret = encode_krb5_sam_response(&sam_response, &scratch))) { 488 free(pa); 489 return(ret); 490 } 491 492 pa->magic = KV5M_PA_DATA; 493 pa->pa_type = KRB5_PADATA_SAM_RESPONSE; 494 pa->length = scratch->length; 495 pa->contents = (krb5_octet *) scratch->data; 496 497 *out_padata = pa; 498 499 return(0); 500 } 501 502 static 503 krb5_error_code pa_sam_2(krb5_context context, 504 krb5_kdc_req *request, 505 krb5_pa_data *in_padata, 506 krb5_pa_data **out_padata, 507 krb5_data *salt, 508 krb5_data *s2kparams, 509 krb5_enctype *etype, 510 krb5_keyblock *as_key, 511 krb5_prompter_fct prompter, 512 void *prompter_data, 513 krb5_gic_get_as_key_fct gak_fct, 514 void *gak_data) { 515 516 krb5_error_code retval; 517 krb5_sam_challenge_2 *sc2 = NULL; 518 krb5_sam_challenge_2_body *sc2b = NULL; 519 krb5_data tmp_data; 520 krb5_data response_data; 521 char name[100], banner[100], prompt[100], response[100]; 522 krb5_prompt kprompt; 523 krb5_prompt_type prompt_type; 524 krb5_data defsalt; 525 krb5_checksum **cksum; 526 krb5_data *scratch = NULL; 527 krb5_boolean valid_cksum = 0; 528 krb5_enc_sam_response_enc_2 enc_sam_response_enc_2; 529 krb5_sam_response_2 sr2; 530 size_t ciph_len; 531 krb5_pa_data *sam_padata; 532 533 if (prompter == NULL) 534 return KRB5_LIBOS_CANTREADPWD; 535 536 tmp_data.length = in_padata->length; 537 tmp_data.data = (char *)in_padata->contents; 538 539 if ((retval = decode_krb5_sam_challenge_2(&tmp_data, &sc2))) 540 return(retval); 541 542 retval = decode_krb5_sam_challenge_2_body(&sc2->sam_challenge_2_body, &sc2b); 543 544 if (retval) 545 return(retval); 546 547 if (!sc2->sam_cksum || ! *sc2->sam_cksum) { 548 krb5_free_sam_challenge_2(context, sc2); 549 krb5_free_sam_challenge_2_body(context, sc2b); 550 return(KRB5_SAM_NO_CHECKSUM); 551 } 552 553 if (sc2b->sam_flags & KRB5_SAM_MUST_PK_ENCRYPT_SAD) { 554 krb5_free_sam_challenge_2(context, sc2); 555 krb5_free_sam_challenge_2_body(context, sc2b); 556 return(KRB5_SAM_UNSUPPORTED); 557 } 558 559 if (!valid_enctype(sc2b->sam_etype)) { 560 krb5_free_sam_challenge_2(context, sc2); 561 krb5_free_sam_challenge_2_body(context, sc2b); 562 return(KRB5_SAM_INVALID_ETYPE); 563 } 564 565 /* All of the above error checks are KDC-specific, that is, they */ 566 /* assume a failure in the KDC reply. By returning anything other */ 567 /* than KRB5_KDC_UNREACH, KRB5_PREAUTH_FAILED, */ 568 /* KRB5_LIBOS_PWDINTR, or KRB5_REALM_CANT_RESOLVE, the client will */ 569 /* most likely go on to try the AS_REQ against master KDC */ 570 571 if (!(sc2b->sam_flags & KRB5_SAM_USE_SAD_AS_KEY)) { 572 /* We will need the password to obtain the key used for */ 573 /* the checksum, and encryption of the sam_response. */ 574 /* Go ahead and get it now, preserving the ordering of */ 575 /* prompts for the user. */ 576 577 retval = (gak_fct)(context, request->client, 578 sc2b->sam_etype, prompter, 579 prompter_data, salt, s2kparams, as_key, gak_data); 580 if (retval) { 581 krb5_free_sam_challenge_2(context, sc2); 582 krb5_free_sam_challenge_2_body(context, sc2b); 583 return(retval); 584 } 585 } 586 587 sprintf(name, "%.*s", 588 SAMDATA(sc2b->sam_type_name, "SAM Authentication", 589 sizeof(name) - 1)); 590 591 sprintf(banner, "%.*s", 592 SAMDATA(sc2b->sam_challenge_label, 593 sam_challenge_banner(sc2b->sam_type), 594 sizeof(banner)-1)); 595 596 sprintf(prompt, "%s%.*s%s%.*s", 597 sc2b->sam_challenge.length?"Challenge is [":"", 598 SAMDATA(sc2b->sam_challenge, "", 20), 599 sc2b->sam_challenge.length?"], ":"", 600 SAMDATA(sc2b->sam_response_prompt, "passcode", 55)); 601 602 response_data.data = response; 603 response_data.length = sizeof(response); 604 kprompt.prompt = prompt; 605 kprompt.hidden = 1; 606 kprompt.reply = &response_data; 607 608 prompt_type = KRB5_PROMPT_TYPE_PREAUTH; 609 krb5int_set_prompt_types(context, &prompt_type); 610 611 if ((retval = ((*prompter)(context, prompter_data, name, 612 banner, 1, &kprompt)))) { 613 krb5_free_sam_challenge_2(context, sc2); 614 krb5_free_sam_challenge_2_body(context, sc2b); 615 krb5int_set_prompt_types(context, 0); 616 return(retval); 617 } 618 619 krb5int_set_prompt_types(context, (krb5_prompt_type *)NULL); 620 621 /* Generate salt used by string_to_key() */ 622 if ((salt->length == -1) && (salt->data == NULL)) { 623 if ((retval = 624 krb5_principal2salt(context, request->client, &defsalt))) { 625 krb5_free_sam_challenge_2(context, sc2); 626 krb5_free_sam_challenge_2_body(context, sc2b); 627 return(retval); 628 } 629 salt = &defsalt; 630 } else { 631 defsalt.length = 0; 632 } 633 634 /* Get encryption key to be used for checksum and sam_response */ 635 if (!(sc2b->sam_flags & KRB5_SAM_USE_SAD_AS_KEY)) { 636 /* as_key = string_to_key(password) */ 637 638 if (as_key->length) { 639 krb5_free_keyblock_contents(context, as_key); 640 as_key->length = 0; 641 } 642 643 /* generate a key using the supplied password */ 644 retval = krb5_c_string_to_key(context, sc2b->sam_etype, 645 (krb5_data *)gak_data, salt, as_key); 646 647 if (retval) { 648 krb5_free_sam_challenge_2(context, sc2); 649 krb5_free_sam_challenge_2_body(context, sc2b); 650 if (defsalt.length) krb5_xfree(defsalt.data); 651 return(retval); 652 } 653 654 if (!(sc2b->sam_flags & KRB5_SAM_SEND_ENCRYPTED_SAD)) { 655 /* as_key = combine_key (as_key, string_to_key(SAD)) */ 656 krb5_keyblock tmp_kb; 657 658 retval = krb5_c_string_to_key(context, sc2b->sam_etype, 659 &response_data, salt, &tmp_kb); 660 661 if (retval) { 662 krb5_free_sam_challenge_2(context, sc2); 663 krb5_free_sam_challenge_2_body(context, sc2b); 664 if (defsalt.length) krb5_xfree(defsalt.data); 665 return(retval); 666 } 667 668 /* This should be a call to the crypto library some day */ 669 /* key types should already match the sam_etype */ 670 retval = krb5int_c_combine_keys(context, as_key, &tmp_kb, as_key); 671 672 if (retval) { 673 krb5_free_sam_challenge_2(context, sc2); 674 krb5_free_sam_challenge_2_body(context, sc2b); 675 if (defsalt.length) krb5_xfree(defsalt.data); 676 return(retval); 677 } 678 krb5_free_keyblock_contents(context, &tmp_kb); 679 } 680 if (defsalt.length) 681 krb5_xfree(defsalt.data); 682 683 } else { 684 /* as_key = string_to_key(SAD) */ 685 686 if (as_key->length) { 687 krb5_free_keyblock_contents(context, as_key); 688 as_key->length = 0; 689 } 690 691 /* generate a key using the supplied password */ 692 retval = krb5_c_string_to_key(context, sc2b->sam_etype, 693 &response_data, salt, as_key); 694 695 if (defsalt.length) 696 krb5_xfree(defsalt.data); 697 698 if (retval) { 699 krb5_free_sam_challenge_2(context, sc2); 700 krb5_free_sam_challenge_2_body(context, sc2b); 701 return(retval); 702 } 703 } 704 705 /* Now we have a key, verify the checksum on the sam_challenge */ 706 707 cksum = sc2->sam_cksum; 708 709 while (*cksum) { 710 /* Check this cksum */ 711 retval = krb5_c_verify_checksum(context, as_key, 712 KRB5_KEYUSAGE_PA_SAM_CHALLENGE_CKSUM, 713 &sc2->sam_challenge_2_body, 714 *cksum, &valid_cksum); 715 if (retval) { 716 krb5_free_data(context, scratch); 717 krb5_free_sam_challenge_2(context, sc2); 718 krb5_free_sam_challenge_2_body(context, sc2b); 719 return(retval); 720 } 721 if (valid_cksum) 722 break; 723 cksum++; 724 } 725 726 if (!valid_cksum) { 727 728 /* If KRB5_SAM_SEND_ENCRYPTED_SAD is set, then password is only */ 729 /* source for checksum key. Therefore, a bad checksum means a */ 730 /* bad password. Don't give that direct feedback to someone */ 731 /* trying to brute-force passwords. */ 732 733 if (!(sc2b->sam_flags & KRB5_SAM_SEND_ENCRYPTED_SAD)) 734 krb5_free_sam_challenge_2(context, sc2); 735 krb5_free_sam_challenge_2_body(context, sc2b); 736 /* 737 * Note: We return AP_ERR_BAD_INTEGRITY so upper-level applications 738 * can interpret that as "password incorrect", which is probably 739 * the best error we can return in this situation. 740 */ 741 return(KRB5KRB_AP_ERR_BAD_INTEGRITY); 742 } 743 744 /* fill in enc_sam_response_enc_2 */ 745 enc_sam_response_enc_2.magic = KV5M_ENC_SAM_RESPONSE_ENC_2; 746 enc_sam_response_enc_2.sam_nonce = sc2b->sam_nonce; 747 if (sc2b->sam_flags & KRB5_SAM_SEND_ENCRYPTED_SAD) { 748 enc_sam_response_enc_2.sam_sad = response_data; 749 } else { 750 enc_sam_response_enc_2.sam_sad.data = NULL; 751 enc_sam_response_enc_2.sam_sad.length = 0; 752 } 753 754 /* encode and encrypt enc_sam_response_enc_2 with as_key */ 755 retval = encode_krb5_enc_sam_response_enc_2(&enc_sam_response_enc_2, 756 &scratch); 757 if (retval) { 758 krb5_free_sam_challenge_2(context, sc2); 759 krb5_free_sam_challenge_2_body(context, sc2b); 760 return(retval); 761 } 762 763 /* Fill in sam_response_2 */ 764 memset(&sr2, 0, sizeof(sr2)); 765 sr2.sam_type = sc2b->sam_type; 766 sr2.sam_flags = sc2b->sam_flags; 767 sr2.sam_track_id = sc2b->sam_track_id; 768 sr2.sam_nonce = sc2b->sam_nonce; 769 770 /* Now take care of sr2.sam_enc_nonce_or_sad by encrypting encoded */ 771 /* enc_sam_response_enc_2 from above */ 772 773 retval = krb5_c_encrypt_length(context, as_key->enctype, scratch->length, 774 &ciph_len); 775 if (retval) { 776 krb5_free_sam_challenge_2(context, sc2); 777 krb5_free_sam_challenge_2_body(context, sc2b); 778 return(retval); 779 } 780 sr2.sam_enc_nonce_or_sad.ciphertext.length = ciph_len; 781 782 sr2.sam_enc_nonce_or_sad.ciphertext.data = 783 (char *)malloc(sr2.sam_enc_nonce_or_sad.ciphertext.length); 784 785 if (!sr2.sam_enc_nonce_or_sad.ciphertext.data) { 786 krb5_free_sam_challenge_2(context, sc2); 787 krb5_free_sam_challenge_2_body(context, sc2b); 788 return(ENOMEM); 789 } 790 791 retval = krb5_c_encrypt(context, as_key, KRB5_KEYUSAGE_PA_SAM_RESPONSE, 792 NULL, scratch, &sr2.sam_enc_nonce_or_sad); 793 if (retval) { 794 krb5_free_sam_challenge_2(context, sc2); 795 krb5_free_sam_challenge_2_body(context, sc2b); 796 krb5_free_data(context, scratch); 797 krb5_free_data_contents(context, &sr2.sam_enc_nonce_or_sad.ciphertext); 798 return(retval); 799 } 800 krb5_free_data(context, scratch); 801 scratch = NULL; 802 803 /* Encode the sam_response_2 */ 804 retval = encode_krb5_sam_response_2(&sr2, &scratch); 805 krb5_free_sam_challenge_2(context, sc2); 806 krb5_free_sam_challenge_2_body(context, sc2b); 807 krb5_free_data_contents(context, &sr2.sam_enc_nonce_or_sad.ciphertext); 808 809 if (retval) { 810 return (retval); 811 } 812 813 /* Almost there, just need to make padata ! */ 814 sam_padata = malloc(sizeof(krb5_pa_data)); 815 if (sam_padata == NULL) { 816 krb5_free_data(context, scratch); 817 return(ENOMEM); 818 } 819 820 sam_padata->magic = KV5M_PA_DATA; 821 sam_padata->pa_type = KRB5_PADATA_SAM_RESPONSE_2; 822 sam_padata->length = scratch->length; 823 sam_padata->contents = (krb5_octet *) scratch->data; 824 825 *out_padata = sam_padata; 826 827 return(0); 828 } 829 830 831 static pa_types_t pa_types[] = { 832 { 833 KRB5_PADATA_PW_SALT, 834 pa_salt, 835 PA_INFO, 836 }, 837 { 838 KRB5_PADATA_AFS3_SALT, 839 pa_salt, 840 PA_INFO, 841 }, 842 { 843 KRB5_PADATA_ENC_TIMESTAMP, 844 pa_enc_timestamp, 845 PA_REAL, 846 }, 847 { 848 KRB5_PADATA_SAM_CHALLENGE_2, 849 pa_sam_2, 850 PA_REAL, 851 }, 852 { 853 KRB5_PADATA_SAM_CHALLENGE, 854 pa_sam, 855 PA_REAL, 856 }, 857 { 858 -1, 859 NULL, 860 0, 861 }, 862 }; 863 864 krb5_error_code 865 krb5_do_preauth(krb5_context context, 866 krb5_kdc_req *request, 867 krb5_pa_data **in_padata, krb5_pa_data ***out_padata, 868 krb5_data *salt, krb5_data *s2kparams, 869 krb5_enctype *etype, 870 krb5_keyblock *as_key, 871 krb5_prompter_fct prompter, void *prompter_data, 872 krb5_gic_get_as_key_fct gak_fct, void *gak_data) 873 { 874 int h, i, j, out_pa_list_size; 875 int seen_etype_info2 = 0; 876 krb5_pa_data *out_pa = NULL, **out_pa_list = NULL; 877 krb5_data scratch; 878 krb5_etype_info etype_info = NULL; 879 krb5_error_code ret; 880 static const int paorder[] = { PA_INFO, PA_REAL }; 881 int realdone; 882 883 KRB5_LOG0(KRB5_INFO, "krb5_do_preauth() start"); 884 885 if (in_padata == NULL) { 886 *out_padata = NULL; 887 return(0); 888 } 889 890 #ifdef DEBUG 891 if (salt && salt->data && salt->length > 0) { 892 fprintf (stderr, "salt len=%d", salt->length); 893 if (salt->length > 0) 894 fprintf (stderr, " '%*s'", salt->length, salt->data); 895 fprintf (stderr, "; preauth data types:"); 896 for (i = 0; in_padata[i]; i++) { 897 fprintf (stderr, " %d", in_padata[i]->pa_type); 898 } 899 fprintf (stderr, "\n"); 900 } 901 #endif 902 903 out_pa_list = NULL; 904 out_pa_list_size = 0; 905 906 /* first do all the informational preauths, then the first real one */ 907 908 for (h=0; h<(sizeof(paorder)/sizeof(paorder[0])); h++) { 909 realdone = 0; 910 for (i=0; in_padata[i] && !realdone; i++) { 911 int k, l, etype_found, valid_etype_found; 912 /* 913 * This is really gross, but is necessary to prevent 914 * lossge when talking to a 1.0.x KDC, which returns an 915 * erroneous PA-PW-SALT when it returns a KRB-ERROR 916 * requiring additional preauth. 917 */ 918 switch (in_padata[i]->pa_type) { 919 case KRB5_PADATA_ETYPE_INFO: 920 case KRB5_PADATA_ETYPE_INFO2: 921 { 922 krb5_preauthtype pa_type = in_padata[i]->pa_type; 923 if (etype_info) { 924 if (seen_etype_info2 || pa_type != KRB5_PADATA_ETYPE_INFO2) 925 continue; 926 if (pa_type == KRB5_PADATA_ETYPE_INFO2) { 927 krb5_free_etype_info( context, etype_info); 928 etype_info = NULL; 929 } 930 } 931 932 scratch.length = in_padata[i]->length; 933 scratch.data = (char *) in_padata[i]->contents; 934 if (pa_type == KRB5_PADATA_ETYPE_INFO2) { 935 seen_etype_info2++; 936 ret = decode_krb5_etype_info2(&scratch, &etype_info); 937 } 938 else ret = decode_krb5_etype_info(&scratch, &etype_info); 939 if (ret) { 940 ret = 0; /*Ignore error and etype_info element*/ 941 krb5_free_etype_info( context, etype_info); 942 etype_info = NULL; 943 continue; 944 } 945 if (etype_info[0] == NULL) { 946 krb5_free_etype_info(context, etype_info); 947 etype_info = NULL; 948 break; 949 } 950 /* 951 * Select first etype in our request which is also in 952 * etype-info (preferring client request ktype order). 953 */ 954 for (etype_found = 0, valid_etype_found = 0, k = 0; 955 !etype_found && k < request->nktypes; k++) { 956 for (l = 0; etype_info[l]; l++) { 957 if (etype_info[l]->etype == request->ktype[k]) { 958 etype_found++; 959 break; 960 } 961 /* check if program has support for this etype for more 962 * precise error reporting. 963 */ 964 if (valid_enctype(etype_info[l]->etype)) 965 valid_etype_found++; 966 } 967 } 968 if (!etype_found) { 969 KRB5_LOG(KRB5_ERR, "error !etype_found, " 970 "valid_etype_found = %d", 971 valid_etype_found); 972 if (valid_etype_found) { 973 /* supported enctype but not requested */ 974 ret = KRB5_CONFIG_ETYPE_NOSUPP; 975 goto cleanup; 976 } 977 else { 978 /* unsupported enctype */ 979 ret = KRB5_PROG_ETYPE_NOSUPP; 980 goto cleanup; 981 } 982 983 } 984 scratch.data = (char *) etype_info[l]->salt; 985 scratch.length = etype_info[l]->length; 986 krb5_free_data_contents(context, salt); 987 if (scratch.length == KRB5_ETYPE_NO_SALT) 988 salt->data = NULL; 989 else 990 if ((ret = krb5int_copy_data_contents( context, 991 &scratch, salt)) != 0) 992 goto cleanup; 993 *etype = etype_info[l]->etype; 994 krb5_free_data_contents(context, s2kparams); 995 if ((ret = krb5int_copy_data_contents(context, 996 &etype_info[l]->s2kparams, 997 s2kparams)) != 0) 998 goto cleanup; 999 break; 1000 } 1001 case KRB5_PADATA_PW_SALT: 1002 case KRB5_PADATA_AFS3_SALT: 1003 if (etype_info) 1004 continue; 1005 break; 1006 default: 1007 ; 1008 } 1009 for (j=0; pa_types[j].type >= 0; j++) { 1010 if ((in_padata[i]->pa_type == pa_types[j].type) && 1011 (pa_types[j].flags & paorder[h])) { 1012 out_pa = NULL; 1013 1014 if ((ret = ((*pa_types[j].fct)(context, request, 1015 in_padata[i], &out_pa, 1016 salt, s2kparams, etype, as_key, 1017 prompter, prompter_data, 1018 gak_fct, gak_data)))) { 1019 goto cleanup; 1020 } 1021 1022 if (out_pa) { 1023 if (out_pa_list == NULL) { 1024 if ((out_pa_list = 1025 (krb5_pa_data **) 1026 malloc(2*sizeof(krb5_pa_data *))) 1027 == NULL) { 1028 ret = ENOMEM; 1029 goto cleanup; 1030 } 1031 } else { 1032 if ((out_pa_list = 1033 (krb5_pa_data **) 1034 realloc(out_pa_list, 1035 (out_pa_list_size+2)* 1036 sizeof(krb5_pa_data *))) 1037 == NULL) { 1038 /* XXX this will leak the pointers which 1039 have already been allocated. oh well. */ 1040 ret = ENOMEM; 1041 goto cleanup; 1042 } 1043 } 1044 1045 out_pa_list[out_pa_list_size++] = out_pa; 1046 } 1047 if (paorder[h] == PA_REAL) 1048 realdone = 1; 1049 } 1050 } 1051 } 1052 } 1053 1054 if (out_pa_list) 1055 out_pa_list[out_pa_list_size++] = NULL; 1056 1057 *out_padata = out_pa_list; 1058 if (etype_info) 1059 krb5_free_etype_info(context, etype_info); 1060 1061 KRB5_LOG0(KRB5_INFO, "krb5_do_preauth() end"); 1062 return(0); 1063 cleanup: 1064 if (out_pa_list) { 1065 out_pa_list[out_pa_list_size++] = NULL; 1066 krb5_free_pa_data(context, out_pa_list); 1067 } 1068 if (etype_info) 1069 krb5_free_etype_info(context, etype_info); 1070 1071 KRB5_LOG0(KRB5_INFO, "krb5_do_preauth() end"); 1072 return (ret); 1073 1074 } 1075