1 /* 2 * Copyright 1995 by the Massachusetts Institute of Technology. All 3 * Rights Reserved. 4 * 5 * Export of this software from the United States of America may 6 * require a specific license from the United States Government. 7 * It is the responsibility of any person or organization contemplating 8 * export to obtain such a license before exporting. 9 * 10 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and 11 * distribute this software and its documentation for any purpose and 12 * without fee is hereby granted, provided that the above copyright 13 * notice appear in all copies and that both that copyright notice and 14 * this permission notice appear in supporting documentation, and that 15 * the name of M.I.T. not be used in advertising or publicity pertaining 16 * to distribution of the software without specific, written prior 17 * permission. Furthermore if you modify this software you must label 18 * your software as modified software and not distribute it in such a 19 * fashion that it might be confused with the original M.I.T. software. 20 * M.I.T. makes no representations about the suitability of 21 * this software for any purpose. It is provided "as is" without express 22 * or implied warranty. 23 * 24 */ 25 26 /* 27 * This file contains routines for establishing, verifying, and any other 28 * necessary functions, for utilizing the pre-authentication field of the 29 * kerberos kdc request, with various hardware/software verification devices. 30 */ 31 32 #include "k5-int.h" 33 #include <stdio.h> 34 #include <time.h> 35 36 static krb5_error_code obtain_enc_ts_padata 37 (krb5_context, 38 krb5_pa_data *, 39 krb5_etype_info, 40 krb5_keyblock *, 41 krb5_error_code ( * )(krb5_context, 42 const krb5_enctype, 43 krb5_data *, 44 krb5_const_pointer, 45 krb5_keyblock **), 46 krb5_const_pointer, 47 krb5_creds *, 48 krb5_kdc_req *, 49 krb5_pa_data **); 50 51 static krb5_error_code process_pw_salt 52 (krb5_context, 53 krb5_pa_data *, 54 krb5_kdc_req *, 55 krb5_kdc_rep *, 56 krb5_error_code ( * )(krb5_context, 57 const krb5_enctype, 58 krb5_data *, 59 krb5_const_pointer, 60 krb5_keyblock **), 61 krb5_const_pointer, 62 krb5_error_code ( * )(krb5_context, 63 const krb5_keyblock *, 64 krb5_const_pointer, 65 krb5_kdc_rep * ), 66 krb5_keyblock **, 67 krb5_creds *, 68 krb5_int32 *, 69 krb5_int32 *); 70 71 static krb5_error_code obtain_sam_padata 72 (krb5_context, 73 krb5_pa_data *, 74 krb5_etype_info, 75 krb5_keyblock *, 76 krb5_error_code ( * )(krb5_context, 77 const krb5_enctype, 78 krb5_data *, 79 krb5_const_pointer, 80 krb5_keyblock **), 81 krb5_const_pointer, 82 krb5_creds *, 83 krb5_kdc_req *, 84 krb5_pa_data **); 85 86 static const krb5_preauth_ops preauth_systems[] = { 87 { 88 KV5M_PREAUTH_OPS, 89 KRB5_PADATA_ENC_TIMESTAMP, 90 0, 91 obtain_enc_ts_padata, 92 0, 93 }, 94 { 95 KV5M_PREAUTH_OPS, 96 KRB5_PADATA_PW_SALT, 97 0, 98 0, 99 process_pw_salt, 100 }, 101 { 102 KV5M_PREAUTH_OPS, 103 KRB5_PADATA_AFS3_SALT, 104 0, 105 0, 106 process_pw_salt, 107 }, 108 { 109 KV5M_PREAUTH_OPS, 110 KRB5_PADATA_SAM_CHALLENGE, 111 0, 112 obtain_sam_padata, 113 0, 114 }, 115 { KV5M_PREAUTH_OPS, -1 } 116 }; 117 118 static krb5_error_code find_pa_system 119 (krb5_preauthtype type, const krb5_preauth_ops **Preauth_proc); 120 121 /* some typedef's for the function args to make things look a bit cleaner */ 122 123 typedef krb5_error_code (*git_key_proc) (krb5_context, 124 const krb5_enctype, 125 krb5_data *, 126 krb5_const_pointer, 127 krb5_keyblock **); 128 129 typedef krb5_error_code (*git_decrypt_proc) (krb5_context, 130 const krb5_keyblock *, 131 krb5_const_pointer, 132 krb5_kdc_rep *); 133 134 krb5_error_code krb5_obtain_padata(krb5_context context, krb5_pa_data **preauth_to_use, git_key_proc key_proc, krb5_const_pointer key_seed, krb5_creds *creds, krb5_kdc_req *request) 135 { 136 krb5_error_code retval; 137 krb5_etype_info etype_info = 0; 138 krb5_pa_data ** pa; 139 krb5_pa_data ** send_pa_list; 140 krb5_pa_data ** send_pa; 141 const krb5_preauth_ops *ops; 142 krb5_keyblock * def_enc_key = 0; 143 krb5_enctype enctype; 144 krb5_data salt; 145 krb5_data scratch; 146 int size; 147 int f_salt = 0; 148 149 if (preauth_to_use == NULL) 150 return 0; 151 152 for (pa = preauth_to_use, size=0; *pa; pa++, size++) { 153 if ((*pa)->pa_type == KRB5_PADATA_ETYPE_INFO) { 154 /* XXX use the first one. Is there another way to disambiguate? */ 155 if (etype_info) 156 continue; 157 158 scratch.length = (*pa)->length; 159 scratch.data = (char *) (*pa)->contents; 160 retval = decode_krb5_etype_info(&scratch, &etype_info); 161 if (retval) 162 return retval; 163 if (etype_info[0] == NULL) { 164 krb5_free_etype_info(context, etype_info); 165 etype_info = NULL; 166 } 167 } 168 } 169 170 if ((send_pa_list = malloc((size+1) * sizeof(krb5_pa_data *))) == NULL) 171 return ENOMEM; 172 173 send_pa = send_pa_list; 174 *send_pa = 0; 175 176 enctype = request->ktype[0]; 177 salt.data = 0; 178 salt.length = SALT_TYPE_NO_LENGTH; 179 if (etype_info) { 180 enctype = etype_info[0]->etype; 181 salt.data = (char *) etype_info[0]->salt; 182 if(etype_info[0]->length == KRB5_ETYPE_NO_SALT) 183 salt.length = SALT_TYPE_NO_LENGTH; /* XXX */ 184 else 185 salt.length = etype_info[0]->length; 186 } 187 if (salt.length == SALT_TYPE_NO_LENGTH) { 188 /* 189 * This will set the salt length 190 */ 191 if ((retval = krb5_principal2salt(context, request->client, &salt))) 192 return(retval); 193 f_salt = 1; 194 } 195 196 if ((retval = (*key_proc)(context, enctype, &salt, key_seed, 197 &def_enc_key))) 198 goto cleanup; 199 200 201 for (pa = preauth_to_use; *pa; pa++) { 202 if (find_pa_system((*pa)->pa_type, &ops)) 203 continue; 204 205 if (ops->obtain == 0) 206 continue; 207 208 retval = ((ops)->obtain)(context, *pa, etype_info, def_enc_key, 209 key_proc, key_seed, creds, 210 request, send_pa); 211 if (retval) 212 goto cleanup; 213 214 if (*send_pa) 215 send_pa++; 216 *send_pa = 0; 217 } 218 219 retval = 0; 220 221 if (send_pa_list[0]) { 222 request->padata = send_pa_list; 223 send_pa_list = 0; 224 } 225 226 cleanup: 227 if (etype_info) 228 krb5_free_etype_info(context, etype_info); 229 if (f_salt) 230 krb5_xfree(salt.data); 231 if (send_pa_list) 232 krb5_free_pa_data(context, send_pa_list); 233 if (def_enc_key) 234 krb5_free_keyblock(context, def_enc_key); 235 return retval; 236 237 } 238 239 krb5_error_code 240 krb5_process_padata(krb5_context context, krb5_kdc_req *request, krb5_kdc_rep *as_reply, git_key_proc key_proc, krb5_const_pointer keyseed, git_decrypt_proc decrypt_proc, krb5_keyblock **decrypt_key, krb5_creds *creds, krb5_int32 *do_more) 241 { 242 krb5_error_code retval = 0; 243 const krb5_preauth_ops * ops; 244 krb5_pa_data ** pa; 245 krb5_int32 done = 0; 246 247 *do_more = 0; /* By default, we don't need to repeat... */ 248 if (as_reply->padata == 0) 249 return 0; 250 251 for (pa = as_reply->padata; *pa; pa++) { 252 if (find_pa_system((*pa)->pa_type, &ops)) 253 continue; 254 255 if (ops->process == 0) 256 continue; 257 258 retval = ((ops)->process)(context, *pa, request, as_reply, 259 key_proc, keyseed, decrypt_proc, 260 decrypt_key, creds, do_more, &done); 261 if (retval) 262 goto cleanup; 263 if (done) 264 break; 265 } 266 267 cleanup: 268 return retval; 269 } 270 271 /* 272 * This routine is the "obtain" function for the ENC_TIMESTAMP 273 * preauthentication type. It take the current time and encrypts it 274 * in the user's key. 275 */ 276 static krb5_error_code 277 obtain_enc_ts_padata(krb5_context context, krb5_pa_data *in_padata, krb5_etype_info etype_info, krb5_keyblock *def_enc_key, git_key_proc key_proc, krb5_const_pointer key_seed, krb5_creds *creds, krb5_kdc_req *request, krb5_pa_data **out_padata) 278 { 279 krb5_pa_enc_ts pa_enc; 280 krb5_error_code retval; 281 krb5_data * scratch; 282 krb5_enc_data enc_data; 283 krb5_pa_data * pa; 284 285 retval = krb5_us_timeofday(context, &pa_enc.patimestamp, &pa_enc.pausec); 286 if (retval) 287 return retval; 288 289 if ((retval = encode_krb5_pa_enc_ts(&pa_enc, &scratch)) != 0) 290 return retval; 291 292 enc_data.ciphertext.data = 0; 293 294 if ((retval = krb5_encrypt_helper(context, def_enc_key, 295 KRB5_KEYUSAGE_AS_REQ_PA_ENC_TS, 296 scratch, &enc_data))) 297 goto cleanup; 298 299 krb5_free_data(context, scratch); 300 scratch = 0; 301 302 if ((retval = encode_krb5_enc_data(&enc_data, &scratch)) != 0) 303 goto cleanup; 304 305 if ((pa = malloc(sizeof(krb5_pa_data))) == NULL) { 306 retval = ENOMEM; 307 goto cleanup; 308 } 309 310 pa->magic = KV5M_PA_DATA; 311 pa->pa_type = KRB5_PADATA_ENC_TIMESTAMP; 312 pa->length = scratch->length; 313 pa->contents = (krb5_octet *) scratch->data; 314 315 *out_padata = pa; 316 317 krb5_xfree(scratch); 318 scratch = 0; 319 320 retval = 0; 321 322 cleanup: 323 if (scratch) 324 krb5_free_data(context, scratch); 325 if (enc_data.ciphertext.data) 326 krb5_xfree(enc_data.ciphertext.data); 327 return retval; 328 } 329 330 static krb5_error_code 331 process_pw_salt(krb5_context context, krb5_pa_data *padata, krb5_kdc_req *request, krb5_kdc_rep *as_reply, git_key_proc key_proc, krb5_const_pointer keyseed, git_decrypt_proc decrypt_proc, krb5_keyblock **decrypt_key, krb5_creds *creds, krb5_int32 *do_more, krb5_int32 *done) 332 { 333 krb5_error_code retval; 334 krb5_data salt; 335 336 if (*decrypt_key != 0) 337 return 0; 338 339 salt.data = (char *) padata->contents; 340 salt.length = 341 (padata->pa_type == KRB5_PADATA_AFS3_SALT)?(SALT_TYPE_AFS_LENGTH):(padata->length); 342 343 if ((retval = (*key_proc)(context, as_reply->enc_part.enctype, 344 &salt, keyseed, decrypt_key))) { 345 *decrypt_key = 0; 346 return retval; 347 } 348 349 return 0; 350 } 351 352 static krb5_error_code 353 find_pa_system(krb5_preauthtype type, const krb5_preauth_ops **preauth) 354 { 355 const krb5_preauth_ops *ap = preauth_systems; 356 357 while ((ap->type != -1) && (ap->type != type)) 358 ap++; 359 if (ap->type == -1) 360 return(KRB5_PREAUTH_BAD_TYPE); 361 *preauth = ap; 362 return 0; 363 } 364 365 366 extern const char *krb5_default_pwd_prompt1; 367 368 static krb5_error_code 369 sam_get_pass_from_user(krb5_context context, krb5_etype_info etype_info, git_key_proc key_proc, krb5_const_pointer key_seed, krb5_kdc_req *request, krb5_keyblock **new_enc_key, const char *prompt) 370 { 371 krb5_enctype enctype; 372 krb5_error_code retval; 373 const char *oldprompt; 374 375 /* enctype = request->ktype[0]; */ 376 enctype = ENCTYPE_DES_CBC_MD5; 377 /* hack with this first! */ 378 oldprompt = krb5_default_pwd_prompt1; 379 krb5_default_pwd_prompt1 = prompt; 380 { 381 krb5_data newpw; 382 newpw.data = 0; newpw.length = 0; 383 /* we don't keep the new password, just the key... */ 384 retval = (*key_proc)(context, enctype, 0, 385 (krb5_const_pointer)&newpw, new_enc_key); 386 krb5_xfree(newpw.data); 387 } 388 krb5_default_pwd_prompt1 = oldprompt; 389 return retval; 390 } 391 static 392 char *handle_sam_labels(krb5_sam_challenge *sc) 393 { 394 char *label = sc->sam_challenge_label.data; 395 unsigned int label_len = sc->sam_challenge_label.length; 396 char *prompt = sc->sam_response_prompt.data; 397 unsigned int prompt_len = sc->sam_response_prompt.length; 398 char *challenge = sc->sam_challenge.data; 399 unsigned int challenge_len = sc->sam_challenge.length; 400 char *prompt1, *p; 401 char *sep1 = ": ["; 402 char *sep2 = "]\n"; 403 char *sep3 = ": "; 404 405 if (sc->sam_cksum.length == 0) { 406 /* or invalid -- but lets just handle presence now XXX */ 407 switch (sc->sam_type) { 408 case PA_SAM_TYPE_ENIGMA: /* Enigma Logic */ 409 label = "Challenge for Enigma Logic mechanism"; 410 break; 411 case PA_SAM_TYPE_DIGI_PATH: /* Digital Pathways */ 412 case PA_SAM_TYPE_DIGI_PATH_HEX: /* Digital Pathways */ 413 label = "Challenge for Digital Pathways mechanism"; 414 break; 415 case PA_SAM_TYPE_ACTIVCARD_DEC: /* Digital Pathways */ 416 case PA_SAM_TYPE_ACTIVCARD_HEX: /* Digital Pathways */ 417 label = "Challenge for Activcard mechanism"; 418 break; 419 case PA_SAM_TYPE_SKEY_K0: /* S/key where KDC has key 0 */ 420 label = "Challenge for Enhanced S/Key mechanism"; 421 break; 422 case PA_SAM_TYPE_SKEY: /* Traditional S/Key */ 423 label = "Challenge for Traditional S/Key mechanism"; 424 break; 425 case PA_SAM_TYPE_SECURID: /* Security Dynamics */ 426 label = "Challenge for Security Dynamics mechanism"; 427 break; 428 case PA_SAM_TYPE_SECURID_PREDICT: /* predictive Security Dynamics */ 429 label = "Challenge for Security Dynamics mechanism"; 430 break; 431 } 432 prompt = "Passcode"; 433 label_len = strlen(label); 434 prompt_len = strlen(prompt); 435 } 436 437 /* example: 438 Challenge for Digital Pathways mechanism: [134591] 439 Passcode: 440 */ 441 p = prompt1 = malloc(label_len + strlen(sep1) + 442 challenge_len + strlen(sep2) + 443 prompt_len+ strlen(sep3) + 1); 444 if (p == NULL) 445 return NULL; 446 if (challenge_len) { 447 strncpy(p, label, label_len); p += label_len; 448 strcpy(p, sep1); p += strlen(sep1); 449 strncpy(p, challenge, challenge_len); p += challenge_len; 450 strcpy(p, sep2); p += strlen(sep2); 451 } 452 strncpy(p, prompt, prompt_len); p += prompt_len; 453 strcpy(p, sep3); /* p += strlen(sep3); */ 454 return prompt1; 455 } 456 457 /* 458 * This routine is the "obtain" function for the SAM_CHALLENGE 459 * preauthentication type. It presents the challenge... 460 */ 461 static krb5_error_code 462 obtain_sam_padata(krb5_context context, krb5_pa_data *in_padata, krb5_etype_info etype_info, krb5_keyblock *def_enc_key, git_key_proc key_proc, krb5_const_pointer key_seed, krb5_creds *creds, krb5_kdc_req *request, krb5_pa_data **out_padata) 463 { 464 krb5_error_code retval; 465 krb5_data * scratch; 466 krb5_data tmpsam; 467 krb5_pa_data * pa; 468 krb5_sam_challenge *sam_challenge = 0; 469 krb5_sam_response sam_response; 470 /* these two get encrypted and stuffed in to sam_response */ 471 krb5_enc_sam_response_enc enc_sam_response_enc; 472 krb5_keyblock * sam_use_key = 0; 473 char * prompt; 474 475 tmpsam.length = in_padata->length; 476 tmpsam.data = (char *) in_padata->contents; 477 retval = decode_krb5_sam_challenge(&tmpsam, &sam_challenge); 478 if (retval) 479 return retval; 480 481 if (sam_challenge->sam_flags & KRB5_SAM_MUST_PK_ENCRYPT_SAD) { 482 return KRB5_SAM_UNSUPPORTED; 483 } 484 485 enc_sam_response_enc.sam_nonce = sam_challenge->sam_nonce; 486 if (!sam_challenge->sam_nonce) { 487 retval = krb5_us_timeofday(context, 488 &enc_sam_response_enc.sam_timestamp, 489 &enc_sam_response_enc.sam_usec); 490 sam_response.sam_patimestamp = enc_sam_response_enc.sam_timestamp; 491 } 492 if (retval) 493 return retval; 494 if (sam_challenge->sam_flags & KRB5_SAM_SEND_ENCRYPTED_SAD) { 495 /* encrypt passcode in key by stuffing it here */ 496 unsigned int pcsize = 256; 497 char *passcode = malloc(pcsize+1); 498 if (passcode == NULL) 499 return ENOMEM; 500 prompt = handle_sam_labels(sam_challenge); 501 if (prompt == NULL) { 502 free(passcode); 503 return ENOMEM; 504 } 505 retval = krb5_read_password(context, prompt, 0, passcode, &pcsize); 506 free(prompt); 507 508 if (retval) { 509 free(passcode); 510 return retval; 511 } 512 enc_sam_response_enc.sam_sad.data = passcode; 513 enc_sam_response_enc.sam_sad.length = pcsize; 514 } else if (sam_challenge->sam_flags & KRB5_SAM_USE_SAD_AS_KEY) { 515 prompt = handle_sam_labels(sam_challenge); 516 if (prompt == NULL) 517 return ENOMEM; 518 retval = sam_get_pass_from_user(context, etype_info, key_proc, 519 key_seed, request, &sam_use_key, 520 prompt); 521 free(prompt); 522 if (retval) 523 return retval; 524 enc_sam_response_enc.sam_sad.length = 0; 525 } else { 526 /* what *was* it? */ 527 return KRB5_SAM_UNSUPPORTED; 528 } 529 530 /* so at this point, either sam_use_key is generated from the passcode 531 * or enc_sam_response_enc.sam_sad is set to it, and we use 532 * def_enc_key instead. */ 533 /* encode the encoded part of the response */ 534 if ((retval = encode_krb5_enc_sam_response_enc(&enc_sam_response_enc, 535 &scratch)) != 0) 536 return retval; 537 538 if ((retval = krb5_encrypt_data(context, 539 sam_use_key?sam_use_key:def_enc_key, 540 0, scratch, 541 &sam_response.sam_enc_nonce_or_ts))) 542 goto cleanup; 543 544 krb5_free_data(context, scratch); 545 scratch = 0; 546 547 /* sam_enc_key is reserved for future use */ 548 sam_response.sam_enc_key.ciphertext.length = 0; 549 550 /* copy things from the challenge */ 551 sam_response.sam_nonce = sam_challenge->sam_nonce; 552 sam_response.sam_flags = sam_challenge->sam_flags; 553 sam_response.sam_track_id = sam_challenge->sam_track_id; 554 sam_response.sam_type = sam_challenge->sam_type; 555 sam_response.magic = KV5M_SAM_RESPONSE; 556 557 if ((retval = encode_krb5_sam_response(&sam_response, &scratch)) != 0) 558 return retval; 559 560 if ((pa = malloc(sizeof(krb5_pa_data))) == NULL) { 561 retval = ENOMEM; 562 goto cleanup; 563 } 564 565 pa->magic = KV5M_PA_DATA; 566 pa->pa_type = KRB5_PADATA_SAM_RESPONSE; 567 pa->length = scratch->length; 568 pa->contents = (krb5_octet *) scratch->data; 569 scratch = 0; /* so we don't free it! */ 570 571 *out_padata = pa; 572 573 retval = 0; 574 575 cleanup: 576 if (scratch) 577 krb5_free_data(context, scratch); 578 if (sam_challenge) 579 krb5_xfree(sam_challenge); 580 return retval; 581 } 582