1 /* 2 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 3 * Use is subject to license terms. 4 */ 5 6 7 /* 8 * Copyright 1995, 2003 by the Massachusetts Institute of Technology. All 9 * 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 32 /* 33 * This file contains routines for establishing, verifying, and any other 34 * necessary functions, for utilizing the pre-authentication field of the 35 * kerberos kdc request, with various hardware/software verification devices. 36 */ 37 38 #include "k5-int.h" 39 #include "osconf.h" 40 #include <preauth_plugin.h> 41 #include "int-proto.h" 42 43 #if !defined(_WIN32) 44 #include <unistd.h> 45 #endif 46 47 #if TARGET_OS_MAC 48 static const char *objdirs[] = { KRB5_PLUGIN_BUNDLE_DIR, LIBDIR "/krb5/plugins/preauth", NULL }; /* should be a list */ 49 #else 50 /* Solaris Kerberos */ 51 static const char *objdirs[] = { LIBDIR "/krb5/plugins/preauth", NULL }; 52 #endif 53 54 typedef krb5_error_code (*pa_function)(krb5_context, 55 krb5_kdc_req *request, 56 krb5_pa_data *in_padata, 57 krb5_pa_data **out_padata, 58 krb5_data *salt, krb5_data *s2kparams, 59 krb5_enctype *etype, 60 krb5_keyblock *as_key, 61 krb5_prompter_fct prompter_fct, 62 void *prompter_data, 63 krb5_gic_get_as_key_fct gak_fct, 64 void *gak_data); 65 66 typedef struct _pa_types_t { 67 krb5_preauthtype type; 68 pa_function fct; 69 int flags; 70 } pa_types_t; 71 72 /* Create the per-krb5_context context. This means loading the modules 73 * if we haven't done that yet (applications which never obtain initial 74 * credentials should never hit this routine), breaking up the module's 75 * list of support pa_types so that we can iterate over the modules more 76 * easily, and copying over the relevant parts of the module's table. */ 77 void KRB5_CALLCONV 78 krb5_init_preauth_context(krb5_context kcontext) 79 { 80 int n_modules, n_tables, i, j, k; 81 void **tables; 82 struct krb5plugin_preauth_client_ftable_v1 *table; 83 krb5_preauth_context *context = NULL; 84 void *plugin_context; 85 krb5_preauthtype pa_type; 86 void **rcpp; 87 88 /* Only do this once for each krb5_context */ 89 if (kcontext->preauth_context != NULL) 90 return; 91 92 /* load the plugins for the current context */ 93 if (PLUGIN_DIR_OPEN(&kcontext->preauth_plugins) == 0) { 94 if (krb5int_open_plugin_dirs(objdirs, NULL, 95 &kcontext->preauth_plugins, 96 &kcontext->err) != 0) { 97 return; 98 } 99 } 100 101 /* pull out the module function tables for all of the modules */ 102 tables = NULL; 103 if (krb5int_get_plugin_dir_data(&kcontext->preauth_plugins, 104 "preauthentication_client_1", 105 &tables, 106 &kcontext->err) != 0) { 107 return; 108 } 109 if (tables == NULL) { 110 return; 111 } 112 113 /* count how many modules we ended up loading, and how many preauth 114 * types we may claim to support as a result */ 115 n_modules = 0; 116 for (n_tables = 0; 117 (tables != NULL) && (tables[n_tables] != NULL); 118 n_tables++) { 119 table = tables[n_tables]; 120 if ((table->pa_type_list != NULL) && (table->process != NULL)) { 121 for (j = 0; table->pa_type_list[j] > 0; j++) { 122 n_modules++; 123 } 124 } 125 } 126 127 /* allocate the space we need */ 128 context = malloc(sizeof(*context)); 129 if (context == NULL) { 130 krb5int_free_plugin_dir_data(tables); 131 return; 132 } 133 context->modules = malloc(sizeof(context->modules[0]) * n_modules); 134 if (context->modules == NULL) { 135 krb5int_free_plugin_dir_data(tables); 136 free(context); 137 return; 138 } 139 memset(context->modules, 0, sizeof(context->modules[0]) * n_modules); 140 context->n_modules = n_modules; 141 142 /* fill in the structure */ 143 k = 0; 144 for (i = 0; i < n_tables; i++) { 145 table = tables[i]; 146 if ((table->pa_type_list != NULL) && (table->process != NULL)) { 147 plugin_context = NULL; 148 if ((table->init != NULL) && 149 ((*table->init)(kcontext, &plugin_context) != 0)) { 150 #ifdef DEBUG 151 fprintf (stderr, "init err, skipping module \"%s\"\n", 152 table->name); 153 #endif 154 continue; 155 } 156 157 rcpp = NULL; 158 for (j = 0; table->pa_type_list[j] > 0; j++) { 159 pa_type = table->pa_type_list[j]; 160 context->modules[k].pa_type = pa_type; 161 context->modules[k].enctypes = table->enctype_list; 162 context->modules[k].plugin_context = plugin_context; 163 /* Only call client_fini once per plugin */ 164 if (j == 0) 165 context->modules[k].client_fini = table->fini; 166 else 167 context->modules[k].client_fini = NULL; 168 context->modules[k].ftable = table; 169 context->modules[k].name = table->name; 170 context->modules[k].flags = (*table->flags)(kcontext, pa_type); 171 context->modules[k].use_count = 0; 172 context->modules[k].client_process = table->process; 173 context->modules[k].client_tryagain = table->tryagain; 174 if (j == 0) 175 context->modules[k].client_supply_gic_opts = table->gic_opts; 176 else 177 context->modules[k].client_supply_gic_opts = NULL; 178 context->modules[k].request_context = NULL; 179 /* 180 * Only call request_init and request_fini once per plugin. 181 * Only the first module within each plugin will ever 182 * have request_context filled in. Every module within 183 * the plugin will have its request_context_pp pointing 184 * to that entry's request_context. That way all the 185 * modules within the plugin share the same request_context 186 */ 187 if (j == 0) { 188 context->modules[k].client_req_init = table->request_init; 189 context->modules[k].client_req_fini = table->request_fini; 190 rcpp = &context->modules[k].request_context; 191 } else { 192 context->modules[k].client_req_init = NULL; 193 context->modules[k].client_req_fini = NULL; 194 } 195 context->modules[k].request_context_pp = rcpp; 196 #ifdef DEBUG 197 fprintf (stderr, "init module \"%s\", pa_type %d, flag %d\n", 198 context->modules[k].name, 199 context->modules[k].pa_type, 200 context->modules[k].flags); 201 #endif 202 k++; 203 } 204 } 205 } 206 krb5int_free_plugin_dir_data(tables); 207 208 /* return the result */ 209 kcontext->preauth_context = context; 210 } 211 212 /* Zero the use counts for the modules herein. Usually used before we 213 * start processing any data from the server, at which point every module 214 * will again be able to take a crack at whatever the server sent. */ 215 void KRB5_CALLCONV 216 krb5_clear_preauth_context_use_counts(krb5_context context) 217 { 218 int i; 219 if (context->preauth_context != NULL) { 220 for (i = 0; i < context->preauth_context->n_modules; i++) { 221 context->preauth_context->modules[i].use_count = 0; 222 } 223 } 224 } 225 226 /* 227 * Give all the preauth plugins a look at the preauth option which 228 * has just been set 229 */ 230 krb5_error_code 231 krb5_preauth_supply_preauth_data(krb5_context context, 232 krb5_gic_opt_ext *opte, 233 const char *attr, 234 const char *value) 235 { 236 krb5_error_code retval; 237 int i; 238 void *pctx; 239 const char *emsg = NULL; 240 241 if (context->preauth_context == NULL) 242 krb5_init_preauth_context(context); 243 if (context->preauth_context == NULL) { 244 retval = EINVAL; 245 krb5int_set_error(&context->err, retval, 246 "krb5_preauth_supply_preauth_data: " 247 "Unable to initialize preauth context"); 248 return retval; 249 } 250 251 /* 252 * Go down the list of preauth modules, and supply them with the 253 * attribute/value pair. 254 */ 255 for (i = 0; i < context->preauth_context->n_modules; i++) { 256 if (context->preauth_context->modules[i].client_supply_gic_opts == NULL) 257 continue; 258 pctx = context->preauth_context->modules[i].plugin_context; 259 retval = (*context->preauth_context->modules[i].client_supply_gic_opts) 260 (context, pctx, 261 (krb5_get_init_creds_opt *)opte, attr, value); 262 if (retval) { 263 emsg = krb5_get_error_message(context, retval); 264 krb5int_set_error(&context->err, retval, "Preauth plugin %s: %s", 265 context->preauth_context->modules[i].name, emsg); 266 break; 267 } 268 } 269 return retval; 270 } 271 272 /* Free the per-krb5_context preauth_context. This means clearing any 273 * plugin-specific context which may have been created, and then 274 * freeing the context itself. */ 275 void KRB5_CALLCONV 276 krb5_free_preauth_context(krb5_context context) 277 { 278 int i; 279 void *pctx; 280 if (context->preauth_context != NULL) { 281 for (i = 0; i < context->preauth_context->n_modules; i++) { 282 pctx = context->preauth_context->modules[i].plugin_context; 283 if (context->preauth_context->modules[i].client_fini != NULL) { 284 (*context->preauth_context->modules[i].client_fini)(context, pctx); 285 } 286 memset(&context->preauth_context->modules[i], 0, 287 sizeof(context->preauth_context->modules[i])); 288 } 289 if (context->preauth_context->modules != NULL) { 290 free(context->preauth_context->modules); 291 context->preauth_context->modules = NULL; 292 } 293 free(context->preauth_context); 294 context->preauth_context = NULL; 295 } 296 } 297 298 /* Initialize the per-AS-REQ context. This means calling the client_req_init 299 * function to give the plugin a chance to allocate a per-request context. */ 300 void KRB5_CALLCONV 301 krb5_preauth_request_context_init(krb5_context context) 302 { 303 int i; 304 void *rctx, *pctx; 305 306 /* Limit this to only one attempt per context? */ 307 if (context->preauth_context == NULL) 308 krb5_init_preauth_context(context); 309 if (context->preauth_context != NULL) { 310 for (i = 0; i < context->preauth_context->n_modules; i++) { 311 pctx = context->preauth_context->modules[i].plugin_context; 312 if (context->preauth_context->modules[i].client_req_init != NULL) { 313 rctx = context->preauth_context->modules[i].request_context_pp; 314 (*context->preauth_context->modules[i].client_req_init) (context, pctx, rctx); 315 } 316 } 317 } 318 } 319 320 /* Free the per-AS-REQ context. This means clearing any request-specific 321 * context which the plugin may have created. */ 322 void KRB5_CALLCONV 323 krb5_preauth_request_context_fini(krb5_context context) 324 { 325 int i; 326 void *rctx, *pctx; 327 if (context->preauth_context != NULL) { 328 for (i = 0; i < context->preauth_context->n_modules; i++) { 329 pctx = context->preauth_context->modules[i].plugin_context; 330 rctx = context->preauth_context->modules[i].request_context; 331 if (rctx != NULL) { 332 if (context->preauth_context->modules[i].client_req_fini != NULL) { 333 (*context->preauth_context->modules[i].client_req_fini)(context, pctx, rctx); 334 } 335 context->preauth_context->modules[i].request_context = NULL; 336 } 337 } 338 } 339 } 340 341 /* Add the named encryption type to the existing list of ktypes. */ 342 static void 343 grow_ktypes(krb5_enctype **out_ktypes, int *out_nktypes, krb5_enctype ktype) 344 { 345 int i; 346 krb5_enctype *ktypes; 347 for (i = 0; i < *out_nktypes; i++) { 348 if ((*out_ktypes)[i] == ktype) 349 return; 350 } 351 ktypes = malloc((*out_nktypes + 2) * sizeof(ktype)); 352 if (ktypes) { 353 for (i = 0; i < *out_nktypes; i++) 354 ktypes[i] = (*out_ktypes)[i]; 355 ktypes[i++] = ktype; 356 ktypes[i] = 0; 357 free(*out_ktypes); 358 *out_ktypes = ktypes; 359 *out_nktypes = i; 360 } 361 } 362 363 /* 364 * Add the given list of pa_data items to the existing list of items. 365 * Factored out here to make reading the do_preauth logic easier to read. 366 */ 367 static int 368 grow_pa_list(krb5_pa_data ***out_pa_list, int *out_pa_list_size, 369 krb5_pa_data **addition, int num_addition) 370 { 371 krb5_pa_data **pa_list; 372 int i, j; 373 374 if (out_pa_list == NULL || addition == NULL) { 375 return EINVAL; 376 } 377 378 if (*out_pa_list == NULL) { 379 /* Allocate room for the new additions and a NULL terminator. */ 380 pa_list = malloc((num_addition + 1) * sizeof(krb5_pa_data *)); 381 if (pa_list == NULL) 382 return ENOMEM; 383 for (i = 0; i < num_addition; i++) 384 pa_list[i] = addition[i]; 385 pa_list[i] = NULL; 386 *out_pa_list = pa_list; 387 *out_pa_list_size = num_addition; 388 } else { 389 /* 390 * Allocate room for the existing entries plus 391 * the new additions and a NULL terminator. 392 */ 393 pa_list = malloc((*out_pa_list_size + num_addition + 1) 394 * sizeof(krb5_pa_data *)); 395 if (pa_list == NULL) 396 return ENOMEM; 397 for (i = 0; i < *out_pa_list_size; i++) 398 pa_list[i] = (*out_pa_list)[i]; 399 for (j = 0; j < num_addition;) 400 pa_list[i++] = addition[j++]; 401 pa_list[i] = NULL; 402 free(*out_pa_list); 403 *out_pa_list = pa_list; 404 *out_pa_list_size = i; 405 } 406 return 0; 407 } 408 409 /* 410 * Retrieve a specific piece of information required by the plugin and 411 * return it in a new krb5_data item. There are separate request_types 412 * to obtain the data and free it. 413 * 414 * This may require massaging data into a contrived format, but it will 415 * hopefully keep us from having to reveal library-internal functions 416 * or data to the plugin modules. 417 */ 418 419 static krb5_error_code 420 client_data_proc(krb5_context kcontext, 421 krb5_preauth_client_rock *rock, 422 krb5_int32 request_type, 423 krb5_data **retdata) 424 { 425 krb5_data *ret; 426 char *data; 427 428 if (rock->magic != CLIENT_ROCK_MAGIC) 429 return EINVAL; 430 if (retdata == NULL) 431 return EINVAL; 432 433 switch (request_type) { 434 case krb5plugin_preauth_client_get_etype: 435 { 436 krb5_enctype *eptr; 437 if (rock->as_reply == NULL) 438 return ENOENT; 439 ret = malloc(sizeof(krb5_data)); 440 if (ret == NULL) 441 return ENOMEM; 442 data = malloc(sizeof(krb5_enctype)); 443 if (data == NULL) { 444 free(ret); 445 return ENOMEM; 446 } 447 ret->data = data; 448 ret->length = sizeof(krb5_enctype); 449 eptr = (krb5_enctype *)data; 450 *eptr = rock->as_reply->enc_part.enctype; 451 *retdata = ret; 452 return 0; 453 } 454 break; 455 case krb5plugin_preauth_client_free_etype: 456 ret = *retdata; 457 if (ret == NULL) 458 return 0; 459 if (ret->data) 460 free(ret->data); 461 free(ret); 462 return 0; 463 break; 464 default: 465 return EINVAL; 466 } 467 } 468 469 /* Tweak the request body, for now adding any enctypes which the module claims 470 * to add support for to the list, but in the future perhaps doing more 471 * involved things. */ 472 void KRB5_CALLCONV 473 krb5_preauth_prepare_request(krb5_context kcontext, 474 krb5_gic_opt_ext *opte, 475 krb5_kdc_req *request) 476 { 477 int i, j; 478 479 if (kcontext->preauth_context == NULL) { 480 return; 481 } 482 /* Add the module-specific enctype list to the request, but only if 483 * it's something we can safely modify. */ 484 if (!(opte && (opte->flags & KRB5_GET_INIT_CREDS_OPT_ETYPE_LIST))) { 485 for (i = 0; i < kcontext->preauth_context->n_modules; i++) { 486 if (kcontext->preauth_context->modules[i].enctypes == NULL) 487 continue; 488 for (j = 0; kcontext->preauth_context->modules[i].enctypes[j] != 0; j++) { 489 grow_ktypes(&request->ktype, &request->nktypes, 490 kcontext->preauth_context->modules[i].enctypes[j]); 491 } 492 } 493 } 494 } 495 496 /* Find the first module which provides for the named preauth type which also 497 * hasn't had a chance to run yet (INFO modules don't count, because as a rule 498 * they don't generate preauth data), and run it. */ 499 static krb5_error_code 500 krb5_run_preauth_plugins(krb5_context kcontext, 501 int module_required_flags, 502 krb5_kdc_req *request, 503 krb5_data *encoded_request_body, 504 krb5_data *encoded_previous_request, 505 krb5_pa_data *in_padata, 506 krb5_prompter_fct prompter, 507 void *prompter_data, 508 preauth_get_as_key_proc gak_fct, 509 krb5_data *salt, 510 krb5_data *s2kparams, 511 void *gak_data, 512 krb5_preauth_client_rock *get_data_rock, 513 krb5_keyblock *as_key, 514 krb5_pa_data ***out_pa_list, 515 int *out_pa_list_size, 516 int *module_ret, 517 int *module_flags, 518 krb5_gic_opt_ext *opte) 519 { 520 int i; 521 krb5_pa_data **out_pa_data; 522 krb5_error_code ret; 523 struct _krb5_preauth_context_module *module; 524 525 if (kcontext->preauth_context == NULL) { 526 return ENOENT; 527 } 528 /* iterate over all loaded modules */ 529 for (i = 0; i < kcontext->preauth_context->n_modules; i++) { 530 module = &kcontext->preauth_context->modules[i]; 531 /* skip over those which don't match the preauth type */ 532 if (module->pa_type != in_padata->pa_type) 533 continue; 534 /* skip over those which don't match the flags (INFO vs REAL, mainly) */ 535 if ((module->flags & module_required_flags) == 0) 536 continue; 537 /* if it's a REAL module, try to call it only once per library call */ 538 if (module_required_flags & PA_REAL) { 539 if (module->use_count > 0) { 540 #ifdef DEBUG 541 fprintf(stderr, "skipping already-used module \"%s\"(%d)\n", 542 module->name, module->pa_type); 543 #endif 544 continue; 545 } 546 module->use_count++; 547 } 548 /* run the module's callback function */ 549 out_pa_data = NULL; 550 #ifdef DEBUG 551 fprintf(stderr, "using module \"%s\" (%d), flags = %d\n", 552 module->name, module->pa_type, module->flags); 553 #endif 554 ret = module->client_process(kcontext, 555 module->plugin_context, 556 *module->request_context_pp, 557 (krb5_get_init_creds_opt *)opte, 558 client_data_proc, 559 get_data_rock, 560 request, 561 encoded_request_body, 562 encoded_previous_request, 563 in_padata, 564 prompter, prompter_data, 565 gak_fct, gak_data, salt, s2kparams, 566 as_key, 567 &out_pa_data); 568 /* Make note of the module's flags and status. */ 569 *module_flags = module->flags; 570 *module_ret = ret; 571 /* Save the new preauth data item. */ 572 if (out_pa_data != NULL) { 573 int j; 574 for (j = 0; out_pa_data[j] != NULL; j++); 575 ret = grow_pa_list(out_pa_list, out_pa_list_size, out_pa_data, j); 576 free(out_pa_data); 577 if (ret != 0) 578 return ret; 579 } 580 break; 581 } 582 if (i >= kcontext->preauth_context->n_modules) { 583 return ENOENT; 584 } 585 return 0; 586 } 587 588 static 589 krb5_error_code pa_salt(krb5_context context, 590 krb5_kdc_req *request, 591 krb5_pa_data *in_padata, 592 krb5_pa_data **out_padata, 593 krb5_data *salt, krb5_data *s2kparams, 594 krb5_enctype *etype, 595 krb5_keyblock *as_key, 596 krb5_prompter_fct prompter, void *prompter_data, 597 krb5_gic_get_as_key_fct gak_fct, void *gak_data) 598 { 599 krb5_data tmp; 600 601 /* Solaris Kerberos - resync */ 602 tmp.data = (char *)in_padata->contents; 603 tmp.length = in_padata->length; 604 krb5_free_data_contents(context, salt); 605 krb5int_copy_data_contents(context, &tmp, salt); 606 607 608 if (in_padata->pa_type == KRB5_PADATA_AFS3_SALT) 609 salt->length = SALT_TYPE_AFS_LENGTH; 610 611 return(0); 612 } 613 614 /*ARGSUSED*/ 615 static 616 krb5_error_code pa_enc_timestamp(krb5_context context, 617 krb5_kdc_req *request, 618 krb5_pa_data *in_padata, 619 krb5_pa_data **out_padata, 620 krb5_data *salt, 621 krb5_data *s2kparams, 622 krb5_enctype *etype, 623 krb5_keyblock *as_key, 624 krb5_prompter_fct prompter, 625 void *prompter_data, 626 krb5_gic_get_as_key_fct gak_fct, 627 void *gak_data) 628 { 629 krb5_error_code ret; 630 krb5_pa_enc_ts pa_enc; 631 krb5_data *tmp; 632 krb5_enc_data enc_data; 633 krb5_pa_data *pa; 634 635 if (as_key->length == 0) { 636 #ifdef DEBUG 637 /* Solaris Kerberos */ 638 if (salt != NULL && salt->data != NULL) { 639 fprintf (stderr, "%s:%d: salt len=%d", __FILE__, __LINE__, 640 salt->length); 641 if ((int) salt->length > 0) 642 fprintf (stderr, " '%.*s'", salt->length, salt->data); 643 fprintf (stderr, "; *etype=%d request->ktype[0]=%d\n", 644 *etype, request->ktype[0]); 645 } 646 #endif 647 if ((ret = ((*gak_fct)(context, request->client, 648 *etype ? *etype : request->ktype[0], 649 prompter, prompter_data, 650 salt, s2kparams, as_key, gak_data)))) 651 return(ret); 652 } 653 654 /* now get the time of day, and encrypt it accordingly */ 655 656 if ((ret = krb5_us_timeofday(context, &pa_enc.patimestamp, &pa_enc.pausec))) 657 return(ret); 658 659 if ((ret = encode_krb5_pa_enc_ts(&pa_enc, &tmp))) 660 return(ret); 661 662 #ifdef DEBUG 663 fprintf (stderr, "key type %d bytes %02x %02x ...\n", 664 as_key->enctype, 665 as_key->contents[0], as_key->contents[1]); 666 #endif 667 ret = krb5_encrypt_helper(context, as_key, 668 KRB5_KEYUSAGE_AS_REQ_PA_ENC_TS, 669 tmp, &enc_data); 670 #ifdef DEBUG 671 fprintf (stderr, "enc data { type=%d kvno=%d data=%02x %02x ... }\n", 672 enc_data.enctype, enc_data.kvno, 673 0xff & enc_data.ciphertext.data[0], 674 0xff & enc_data.ciphertext.data[1]); 675 #endif 676 677 krb5_free_data(context, tmp); 678 679 if (ret) { 680 krb5_xfree(enc_data.ciphertext.data); 681 return(ret); 682 } 683 684 ret = encode_krb5_enc_data(&enc_data, &tmp); 685 686 krb5_xfree(enc_data.ciphertext.data); 687 688 if (ret) 689 return(ret); 690 691 if ((pa = (krb5_pa_data *) malloc(sizeof(krb5_pa_data))) == NULL) { 692 krb5_free_data(context, tmp); 693 return(ENOMEM); 694 } 695 696 pa->magic = KV5M_PA_DATA; 697 pa->pa_type = KRB5_PADATA_ENC_TIMESTAMP; 698 pa->length = tmp->length; 699 pa->contents = (krb5_octet *) tmp->data; 700 701 *out_padata = pa; 702 703 krb5_xfree(tmp); 704 705 return(0); 706 } 707 708 static 709 char *sam_challenge_banner(krb5_int32 sam_type) 710 { 711 char *label; 712 713 switch (sam_type) { 714 case PA_SAM_TYPE_ENIGMA: /* Enigma Logic */ 715 label = "Challenge for Enigma Logic mechanism"; 716 break; 717 case PA_SAM_TYPE_DIGI_PATH: /* Digital Pathways */ 718 case PA_SAM_TYPE_DIGI_PATH_HEX: /* Digital Pathways */ 719 label = "Challenge for Digital Pathways mechanism"; 720 break; 721 case PA_SAM_TYPE_ACTIVCARD_DEC: /* Digital Pathways */ 722 case PA_SAM_TYPE_ACTIVCARD_HEX: /* Digital Pathways */ 723 label = "Challenge for Activcard mechanism"; 724 break; 725 case PA_SAM_TYPE_SKEY_K0: /* S/key where KDC has key 0 */ 726 label = "Challenge for Enhanced S/Key mechanism"; 727 break; 728 case PA_SAM_TYPE_SKEY: /* Traditional S/Key */ 729 label = "Challenge for Traditional S/Key mechanism"; 730 break; 731 case PA_SAM_TYPE_SECURID: /* Security Dynamics */ 732 label = "Challenge for Security Dynamics mechanism"; 733 break; 734 case PA_SAM_TYPE_SECURID_PREDICT: /* predictive Security Dynamics */ 735 label = "Challenge for Security Dynamics mechanism"; 736 break; 737 default: 738 label = "Challenge from authentication server"; 739 break; 740 } 741 742 return(label); 743 } 744 745 /* this macro expands to the int,ptr necessary for "%.*s" in an sprintf */ 746 747 #define SAMDATA(kdata, str, maxsize) \ 748 (int)((kdata.length)? \ 749 ((((kdata.length)<=(maxsize))?(kdata.length):strlen(str))): \ 750 strlen(str)), \ 751 (kdata.length)? \ 752 ((((kdata.length)<=(maxsize))?(kdata.data):(str))):(str) 753 754 /* XXX Danger! This code is not in sync with the kerberos-password-02 755 draft. This draft cannot be implemented as written. This code is 756 compatible with earlier versions of mit krb5 and cygnus kerbnet. */ 757 758 /*ARGSUSED*/ 759 static 760 krb5_error_code pa_sam(krb5_context context, 761 krb5_kdc_req *request, 762 krb5_pa_data *in_padata, 763 krb5_pa_data **out_padata, 764 krb5_data *salt, 765 krb5_data *s2kparams, 766 krb5_enctype *etype, 767 krb5_keyblock *as_key, 768 krb5_prompter_fct prompter, 769 void *prompter_data, 770 krb5_gic_get_as_key_fct gak_fct, 771 void *gak_data) 772 { 773 krb5_error_code ret; 774 krb5_data tmpsam; 775 char name[100], banner[100]; 776 char prompt[100], response[100]; 777 krb5_data response_data; 778 krb5_prompt kprompt; 779 krb5_prompt_type prompt_type; 780 krb5_data defsalt; 781 krb5_sam_challenge *sam_challenge = 0; 782 krb5_sam_response sam_response; 783 /* these two get encrypted and stuffed in to sam_response */ 784 krb5_enc_sam_response_enc enc_sam_response_enc; 785 krb5_data * scratch; 786 krb5_pa_data * pa; 787 788 /* Solaris Kerberos */ 789 krb5_enc_data * enc_data; 790 size_t enclen; 791 792 if (prompter == NULL) 793 return EIO; 794 795 tmpsam.length = in_padata->length; 796 tmpsam.data = (char *) in_padata->contents; 797 if ((ret = decode_krb5_sam_challenge(&tmpsam, &sam_challenge))) 798 return(ret); 799 800 if (sam_challenge->sam_flags & KRB5_SAM_MUST_PK_ENCRYPT_SAD) { 801 krb5_xfree(sam_challenge); 802 return(KRB5_SAM_UNSUPPORTED); 803 } 804 805 /* If we need the password from the user (USE_SAD_AS_KEY not set), */ 806 /* then get it here. Exception for "old" KDCs with CryptoCard */ 807 /* support which uses the USE_SAD_AS_KEY flag, but still needs pwd */ 808 809 if (!(sam_challenge->sam_flags & KRB5_SAM_USE_SAD_AS_KEY) || 810 (sam_challenge->sam_type == PA_SAM_TYPE_CRYPTOCARD)) { 811 812 /* etype has either been set by caller or by KRB5_PADATA_ETYPE_INFO */ 813 /* message from the KDC. If it is not set, pick an enctype that we */ 814 /* think the KDC will have for us. */ 815 816 if (etype && *etype == 0) 817 *etype = ENCTYPE_DES_CBC_CRC; 818 819 if ((ret = (gak_fct)(context, request->client, *etype, prompter, 820 prompter_data, salt, s2kparams, as_key, gak_data))) 821 return(ret); 822 } 823 sprintf(name, "%.*s", 824 SAMDATA(sam_challenge->sam_type_name, "SAM Authentication", 825 sizeof(name) - 1)); 826 827 sprintf(banner, "%.*s", 828 SAMDATA(sam_challenge->sam_challenge_label, 829 sam_challenge_banner(sam_challenge->sam_type), 830 sizeof(banner)-1)); 831 832 /* sprintf(prompt, "Challenge is [%s], %s: ", challenge, prompt); */ 833 sprintf(prompt, "%s%.*s%s%.*s", 834 sam_challenge->sam_challenge.length?"Challenge is [":"", 835 SAMDATA(sam_challenge->sam_challenge, "", 20), 836 sam_challenge->sam_challenge.length?"], ":"", 837 SAMDATA(sam_challenge->sam_response_prompt, "passcode", 55)); 838 839 response_data.data = response; 840 response_data.length = sizeof(response); 841 842 kprompt.prompt = prompt; 843 kprompt.hidden = 1; 844 kprompt.reply = &response_data; 845 prompt_type = KRB5_PROMPT_TYPE_PREAUTH; 846 847 /* PROMPTER_INVOCATION */ 848 krb5int_set_prompt_types(context, &prompt_type); 849 if ((ret = ((*prompter)(context, prompter_data, name, 850 banner, 1, &kprompt)))) { 851 krb5_xfree(sam_challenge); 852 krb5int_set_prompt_types(context, 0); 853 return(ret); 854 } 855 krb5int_set_prompt_types(context, 0); 856 857 enc_sam_response_enc.sam_nonce = sam_challenge->sam_nonce; 858 if (sam_challenge->sam_nonce == 0) { 859 if ((ret = krb5_us_timeofday(context, 860 &enc_sam_response_enc.sam_timestamp, 861 &enc_sam_response_enc.sam_usec))) { 862 krb5_xfree(sam_challenge); 863 return(ret); 864 } 865 866 sam_response.sam_patimestamp = enc_sam_response_enc.sam_timestamp; 867 } 868 869 /* XXX What if more than one flag is set? */ 870 if (sam_challenge->sam_flags & KRB5_SAM_SEND_ENCRYPTED_SAD) { 871 872 /* Most of this should be taken care of before we get here. We */ 873 /* will need the user's password and as_key to encrypt the SAD */ 874 /* and we want to preserve ordering of user prompts (first */ 875 /* password, then SAM data) so that user's won't be confused. */ 876 877 if (as_key->length) { 878 krb5_free_keyblock_contents(context, as_key); 879 as_key->length = 0; 880 } 881 882 /* generate a salt using the requested principal */ 883 884 if ((salt->length == -1 || salt->length == SALT_TYPE_AFS_LENGTH) && (salt->data == NULL)) { 885 if ((ret = krb5_principal2salt(context, request->client, 886 &defsalt))) { 887 krb5_xfree(sam_challenge); 888 return(ret); 889 } 890 891 salt = &defsalt; 892 } else { 893 defsalt.length = 0; 894 } 895 896 /* generate a key using the supplied password */ 897 898 ret = krb5_c_string_to_key(context, ENCTYPE_DES_CBC_MD5, 899 (krb5_data *)gak_data, salt, as_key); 900 901 if (defsalt.length) 902 krb5_xfree(defsalt.data); 903 904 if (ret) { 905 krb5_xfree(sam_challenge); 906 return(ret); 907 } 908 909 /* encrypt the passcode with the key from above */ 910 911 enc_sam_response_enc.sam_sad = response_data; 912 } else if (sam_challenge->sam_flags & KRB5_SAM_USE_SAD_AS_KEY) { 913 914 /* process the key as password */ 915 916 if (as_key->length) { 917 krb5_free_keyblock_contents(context, as_key); 918 as_key->length = 0; 919 } 920 921 #if 0 922 if ((salt->length == SALT_TYPE_AFS_LENGTH) && (salt->data == NULL)) { 923 if (ret = krb5_principal2salt(context, request->client, 924 &defsalt)) { 925 krb5_xfree(sam_challenge); 926 return(ret); 927 } 928 929 salt = &defsalt; 930 } else { 931 defsalt.length = 0; 932 } 933 #else 934 defsalt.length = 0; 935 salt = NULL; 936 #endif 937 938 /* XXX As of the passwords-04 draft, no enctype is specified, 939 the server uses ENCTYPE_DES_CBC_MD5. In the future the 940 server should send a PA-SAM-ETYPE-INFO containing the enctype. */ 941 942 ret = krb5_c_string_to_key(context, ENCTYPE_DES_CBC_MD5, 943 &response_data, salt, as_key); 944 945 if (defsalt.length) 946 krb5_xfree(defsalt.data); 947 948 if (ret) { 949 krb5_xfree(sam_challenge); 950 return(ret); 951 } 952 953 enc_sam_response_enc.sam_sad.length = 0; 954 } else { 955 /* Eventually, combine SAD with long-term key to get 956 encryption key. */ 957 return KRB5_PREAUTH_BAD_TYPE; 958 } 959 960 /* copy things from the challenge */ 961 sam_response.sam_nonce = sam_challenge->sam_nonce; 962 sam_response.sam_flags = sam_challenge->sam_flags; 963 sam_response.sam_track_id = sam_challenge->sam_track_id; 964 sam_response.sam_type = sam_challenge->sam_type; 965 sam_response.magic = KV5M_SAM_RESPONSE; 966 967 krb5_xfree(sam_challenge); 968 969 /* encode the encoded part of the response */ 970 if ((ret = encode_krb5_enc_sam_response_enc(&enc_sam_response_enc, 971 &scratch))) 972 return(ret); 973 974 /* 975 * Solaris Kerberos: 976 * Using new crypto interface now so we can get rid of the 977 * old modules. 978 */ 979 if ((ret = krb5_c_encrypt_length(context, as_key->enctype, 980 scratch->length, &enclen))) { 981 krb5_free_data(context, scratch); 982 return(ret); 983 } 984 985 enc_data = &sam_response.sam_enc_nonce_or_ts; 986 enc_data->magic = KV5M_ENC_DATA; 987 enc_data->kvno = 0; 988 enc_data->enctype = as_key->enctype; 989 enc_data->ciphertext.length = enclen; 990 991 if ((enc_data->ciphertext.data = MALLOC(enclen)) == NULL) { 992 enc_data->ciphertext.length = 0; 993 krb5_free_data(context, scratch); 994 return(ENOMEM); 995 } 996 997 if ((ret = krb5_c_encrypt(context, as_key, 0, 0, 998 scratch, enc_data))) { 999 FREE(enc_data->ciphertext.data, enclen); 1000 enc_data->ciphertext.data = NULL; 1001 enc_data->ciphertext.length = 0; 1002 } 1003 1004 krb5_free_data(context, scratch); 1005 1006 if (ret) 1007 return(ret); 1008 1009 /* sam_enc_key is reserved for future use */ 1010 sam_response.sam_enc_key.ciphertext.length = 0; 1011 1012 if ((pa = malloc(sizeof(krb5_pa_data))) == NULL) 1013 return(ENOMEM); 1014 1015 if ((ret = encode_krb5_sam_response(&sam_response, &scratch))) { 1016 free(pa); 1017 return(ret); 1018 } 1019 1020 pa->magic = KV5M_PA_DATA; 1021 pa->pa_type = KRB5_PADATA_SAM_RESPONSE; 1022 pa->length = scratch->length; 1023 pa->contents = (krb5_octet *) scratch->data; 1024 1025 *out_padata = pa; 1026 1027 return(0); 1028 } 1029 1030 static 1031 krb5_error_code pa_sam_2(krb5_context context, 1032 krb5_kdc_req *request, 1033 krb5_pa_data *in_padata, 1034 krb5_pa_data **out_padata, 1035 krb5_data *salt, 1036 krb5_data *s2kparams, 1037 krb5_enctype *etype, 1038 krb5_keyblock *as_key, 1039 krb5_prompter_fct prompter, 1040 void *prompter_data, 1041 krb5_gic_get_as_key_fct gak_fct, 1042 void *gak_data) { 1043 1044 krb5_error_code retval; 1045 krb5_sam_challenge_2 *sc2 = NULL; 1046 krb5_sam_challenge_2_body *sc2b = NULL; 1047 krb5_data tmp_data; 1048 krb5_data response_data; 1049 char name[100], banner[100], prompt[100], response[100]; 1050 krb5_prompt kprompt; 1051 krb5_prompt_type prompt_type; 1052 krb5_data defsalt; 1053 krb5_checksum **cksum; 1054 krb5_data *scratch = NULL; 1055 krb5_boolean valid_cksum = 0; 1056 krb5_enc_sam_response_enc_2 enc_sam_response_enc_2; 1057 krb5_sam_response_2 sr2; 1058 size_t ciph_len; 1059 krb5_pa_data *sam_padata; 1060 1061 if (prompter == NULL) 1062 return KRB5_LIBOS_CANTREADPWD; 1063 1064 tmp_data.length = in_padata->length; 1065 tmp_data.data = (char *)in_padata->contents; 1066 1067 if ((retval = decode_krb5_sam_challenge_2(&tmp_data, &sc2))) 1068 return(retval); 1069 1070 retval = decode_krb5_sam_challenge_2_body(&sc2->sam_challenge_2_body, &sc2b); 1071 1072 if (retval) 1073 return(retval); 1074 1075 if (!sc2->sam_cksum || ! *sc2->sam_cksum) { 1076 krb5_free_sam_challenge_2(context, sc2); 1077 krb5_free_sam_challenge_2_body(context, sc2b); 1078 return(KRB5_SAM_NO_CHECKSUM); 1079 } 1080 1081 if (sc2b->sam_flags & KRB5_SAM_MUST_PK_ENCRYPT_SAD) { 1082 krb5_free_sam_challenge_2(context, sc2); 1083 krb5_free_sam_challenge_2_body(context, sc2b); 1084 return(KRB5_SAM_UNSUPPORTED); 1085 } 1086 1087 if (!valid_enctype(sc2b->sam_etype)) { 1088 krb5_free_sam_challenge_2(context, sc2); 1089 krb5_free_sam_challenge_2_body(context, sc2b); 1090 return(KRB5_SAM_INVALID_ETYPE); 1091 } 1092 1093 /* All of the above error checks are KDC-specific, that is, they */ 1094 /* assume a failure in the KDC reply. By returning anything other */ 1095 /* than KRB5_KDC_UNREACH, KRB5_PREAUTH_FAILED, */ 1096 /* KRB5_LIBOS_PWDINTR, or KRB5_REALM_CANT_RESOLVE, the client will */ 1097 /* most likely go on to try the AS_REQ against master KDC */ 1098 1099 if (!(sc2b->sam_flags & KRB5_SAM_USE_SAD_AS_KEY)) { 1100 /* We will need the password to obtain the key used for */ 1101 /* the checksum, and encryption of the sam_response. */ 1102 /* Go ahead and get it now, preserving the ordering of */ 1103 /* prompts for the user. */ 1104 1105 retval = (gak_fct)(context, request->client, 1106 sc2b->sam_etype, prompter, 1107 prompter_data, salt, s2kparams, as_key, gak_data); 1108 if (retval) { 1109 krb5_free_sam_challenge_2(context, sc2); 1110 krb5_free_sam_challenge_2_body(context, sc2b); 1111 return(retval); 1112 } 1113 } 1114 1115 sprintf(name, "%.*s", 1116 SAMDATA(sc2b->sam_type_name, "SAM Authentication", 1117 sizeof(name) - 1)); 1118 1119 sprintf(banner, "%.*s", 1120 SAMDATA(sc2b->sam_challenge_label, 1121 sam_challenge_banner(sc2b->sam_type), 1122 sizeof(banner)-1)); 1123 1124 sprintf(prompt, "%s%.*s%s%.*s", 1125 sc2b->sam_challenge.length?"Challenge is [":"", 1126 SAMDATA(sc2b->sam_challenge, "", 20), 1127 sc2b->sam_challenge.length?"], ":"", 1128 SAMDATA(sc2b->sam_response_prompt, "passcode", 55)); 1129 1130 response_data.data = response; 1131 response_data.length = sizeof(response); 1132 kprompt.prompt = prompt; 1133 kprompt.hidden = 1; 1134 kprompt.reply = &response_data; 1135 1136 prompt_type = KRB5_PROMPT_TYPE_PREAUTH; 1137 krb5int_set_prompt_types(context, &prompt_type); 1138 1139 if ((retval = ((*prompter)(context, prompter_data, name, 1140 banner, 1, &kprompt)))) { 1141 krb5_free_sam_challenge_2(context, sc2); 1142 krb5_free_sam_challenge_2_body(context, sc2b); 1143 krb5int_set_prompt_types(context, 0); 1144 return(retval); 1145 } 1146 1147 krb5int_set_prompt_types(context, (krb5_prompt_type *)NULL); 1148 1149 /* Generate salt used by string_to_key() */ 1150 if ((salt->length == -1) && (salt->data == NULL)) { 1151 if ((retval = 1152 krb5_principal2salt(context, request->client, &defsalt))) { 1153 krb5_free_sam_challenge_2(context, sc2); 1154 krb5_free_sam_challenge_2_body(context, sc2b); 1155 return(retval); 1156 } 1157 salt = &defsalt; 1158 } else { 1159 defsalt.length = 0; 1160 } 1161 1162 /* Get encryption key to be used for checksum and sam_response */ 1163 if (!(sc2b->sam_flags & KRB5_SAM_USE_SAD_AS_KEY)) { 1164 /* as_key = string_to_key(password) */ 1165 1166 if (as_key->length) { 1167 krb5_free_keyblock_contents(context, as_key); 1168 as_key->length = 0; 1169 } 1170 1171 /* generate a key using the supplied password */ 1172 retval = krb5_c_string_to_key(context, sc2b->sam_etype, 1173 (krb5_data *)gak_data, salt, as_key); 1174 1175 if (retval) { 1176 krb5_free_sam_challenge_2(context, sc2); 1177 krb5_free_sam_challenge_2_body(context, sc2b); 1178 if (defsalt.length) krb5_xfree(defsalt.data); 1179 return(retval); 1180 } 1181 1182 if (!(sc2b->sam_flags & KRB5_SAM_SEND_ENCRYPTED_SAD)) { 1183 /* as_key = combine_key (as_key, string_to_key(SAD)) */ 1184 krb5_keyblock tmp_kb; 1185 1186 retval = krb5_c_string_to_key(context, sc2b->sam_etype, 1187 &response_data, salt, &tmp_kb); 1188 1189 if (retval) { 1190 krb5_free_sam_challenge_2(context, sc2); 1191 krb5_free_sam_challenge_2_body(context, sc2b); 1192 if (defsalt.length) krb5_xfree(defsalt.data); 1193 return(retval); 1194 } 1195 1196 /* This should be a call to the crypto library some day */ 1197 /* key types should already match the sam_etype */ 1198 retval = krb5int_c_combine_keys(context, as_key, &tmp_kb, as_key); 1199 1200 if (retval) { 1201 krb5_free_sam_challenge_2(context, sc2); 1202 krb5_free_sam_challenge_2_body(context, sc2b); 1203 if (defsalt.length) krb5_xfree(defsalt.data); 1204 return(retval); 1205 } 1206 krb5_free_keyblock_contents(context, &tmp_kb); 1207 } 1208 1209 if (defsalt.length) 1210 krb5_xfree(defsalt.data); 1211 1212 } else { 1213 /* as_key = string_to_key(SAD) */ 1214 1215 if (as_key->length) { 1216 krb5_free_keyblock_contents(context, as_key); 1217 as_key->length = 0; 1218 } 1219 1220 /* generate a key using the supplied password */ 1221 retval = krb5_c_string_to_key(context, sc2b->sam_etype, 1222 &response_data, salt, as_key); 1223 1224 if (defsalt.length) 1225 krb5_xfree(defsalt.data); 1226 1227 if (retval) { 1228 krb5_free_sam_challenge_2(context, sc2); 1229 krb5_free_sam_challenge_2_body(context, sc2b); 1230 return(retval); 1231 } 1232 } 1233 1234 /* Now we have a key, verify the checksum on the sam_challenge */ 1235 1236 cksum = sc2->sam_cksum; 1237 1238 while (*cksum) { 1239 /* Check this cksum */ 1240 retval = krb5_c_verify_checksum(context, as_key, 1241 KRB5_KEYUSAGE_PA_SAM_CHALLENGE_CKSUM, 1242 &sc2->sam_challenge_2_body, 1243 *cksum, &valid_cksum); 1244 if (retval) { 1245 krb5_free_data(context, scratch); 1246 krb5_free_sam_challenge_2(context, sc2); 1247 krb5_free_sam_challenge_2_body(context, sc2b); 1248 return(retval); 1249 } 1250 if (valid_cksum) 1251 break; 1252 cksum++; 1253 } 1254 1255 if (!valid_cksum) { 1256 1257 /* If KRB5_SAM_SEND_ENCRYPTED_SAD is set, then password is only */ 1258 /* source for checksum key. Therefore, a bad checksum means a */ 1259 /* bad password. Don't give that direct feedback to someone */ 1260 /* trying to brute-force passwords. */ 1261 1262 if (!(sc2b->sam_flags & KRB5_SAM_SEND_ENCRYPTED_SAD)) 1263 krb5_free_sam_challenge_2(context, sc2); 1264 krb5_free_sam_challenge_2_body(context, sc2b); 1265 /* 1266 * Note: We return AP_ERR_BAD_INTEGRITY so upper-level applications 1267 * can interpret that as "password incorrect", which is probably 1268 * the best error we can return in this situation. 1269 */ 1270 return(KRB5KRB_AP_ERR_BAD_INTEGRITY); 1271 } 1272 1273 /* fill in enc_sam_response_enc_2 */ 1274 enc_sam_response_enc_2.magic = KV5M_ENC_SAM_RESPONSE_ENC_2; 1275 enc_sam_response_enc_2.sam_nonce = sc2b->sam_nonce; 1276 if (sc2b->sam_flags & KRB5_SAM_SEND_ENCRYPTED_SAD) { 1277 enc_sam_response_enc_2.sam_sad = response_data; 1278 } else { 1279 enc_sam_response_enc_2.sam_sad.data = NULL; 1280 enc_sam_response_enc_2.sam_sad.length = 0; 1281 } 1282 1283 /* encode and encrypt enc_sam_response_enc_2 with as_key */ 1284 retval = encode_krb5_enc_sam_response_enc_2(&enc_sam_response_enc_2, 1285 &scratch); 1286 if (retval) { 1287 krb5_free_sam_challenge_2(context, sc2); 1288 krb5_free_sam_challenge_2_body(context, sc2b); 1289 return(retval); 1290 } 1291 1292 /* Fill in sam_response_2 */ 1293 memset(&sr2, 0, sizeof(sr2)); 1294 sr2.sam_type = sc2b->sam_type; 1295 sr2.sam_flags = sc2b->sam_flags; 1296 sr2.sam_track_id = sc2b->sam_track_id; 1297 sr2.sam_nonce = sc2b->sam_nonce; 1298 1299 /* Now take care of sr2.sam_enc_nonce_or_sad by encrypting encoded */ 1300 /* enc_sam_response_enc_2 from above */ 1301 1302 retval = krb5_c_encrypt_length(context, as_key->enctype, scratch->length, 1303 &ciph_len); 1304 if (retval) { 1305 krb5_free_sam_challenge_2(context, sc2); 1306 krb5_free_sam_challenge_2_body(context, sc2b); 1307 return(retval); 1308 } 1309 sr2.sam_enc_nonce_or_sad.ciphertext.length = ciph_len; 1310 1311 sr2.sam_enc_nonce_or_sad.ciphertext.data = 1312 (char *)malloc(sr2.sam_enc_nonce_or_sad.ciphertext.length); 1313 1314 if (!sr2.sam_enc_nonce_or_sad.ciphertext.data) { 1315 krb5_free_sam_challenge_2(context, sc2); 1316 krb5_free_sam_challenge_2_body(context, sc2b); 1317 return(ENOMEM); 1318 } 1319 1320 retval = krb5_c_encrypt(context, as_key, KRB5_KEYUSAGE_PA_SAM_RESPONSE, 1321 NULL, scratch, &sr2.sam_enc_nonce_or_sad); 1322 if (retval) { 1323 krb5_free_sam_challenge_2(context, sc2); 1324 krb5_free_sam_challenge_2_body(context, sc2b); 1325 krb5_free_data(context, scratch); 1326 krb5_free_data_contents(context, &sr2.sam_enc_nonce_or_sad.ciphertext); 1327 return(retval); 1328 } 1329 krb5_free_data(context, scratch); 1330 scratch = NULL; 1331 1332 /* Encode the sam_response_2 */ 1333 retval = encode_krb5_sam_response_2(&sr2, &scratch); 1334 krb5_free_sam_challenge_2(context, sc2); 1335 krb5_free_sam_challenge_2_body(context, sc2b); 1336 krb5_free_data_contents(context, &sr2.sam_enc_nonce_or_sad.ciphertext); 1337 1338 if (retval) { 1339 return (retval); 1340 } 1341 1342 /* Almost there, just need to make padata ! */ 1343 sam_padata = malloc(sizeof(krb5_pa_data)); 1344 if (sam_padata == NULL) { 1345 krb5_free_data(context, scratch); 1346 return(ENOMEM); 1347 } 1348 1349 sam_padata->magic = KV5M_PA_DATA; 1350 sam_padata->pa_type = KRB5_PADATA_SAM_RESPONSE_2; 1351 sam_padata->length = scratch->length; 1352 sam_padata->contents = (krb5_octet *) scratch->data; 1353 1354 *out_padata = sam_padata; 1355 1356 return(0); 1357 } 1358 1359 static const pa_types_t pa_types[] = { 1360 { 1361 KRB5_PADATA_PW_SALT, 1362 pa_salt, 1363 PA_INFO, 1364 }, 1365 { 1366 KRB5_PADATA_AFS3_SALT, 1367 pa_salt, 1368 PA_INFO, 1369 }, 1370 { 1371 KRB5_PADATA_ENC_TIMESTAMP, 1372 pa_enc_timestamp, 1373 PA_REAL, 1374 }, 1375 { 1376 KRB5_PADATA_SAM_CHALLENGE_2, 1377 pa_sam_2, 1378 PA_REAL, 1379 }, 1380 { 1381 KRB5_PADATA_SAM_CHALLENGE, 1382 pa_sam, 1383 PA_REAL, 1384 }, 1385 { 1386 -1, 1387 NULL, 1388 0, 1389 }, 1390 }; 1391 1392 /* 1393 * If one of the modules can adjust its AS_REQ data using the contents of the 1394 * err_reply, return 0. If it's the sort of correction which requires that we 1395 * ask the user another question, we let the calling application deal with it. 1396 */ 1397 krb5_error_code KRB5_CALLCONV 1398 krb5_do_preauth_tryagain(krb5_context kcontext, 1399 krb5_kdc_req *request, 1400 krb5_data *encoded_request_body, 1401 krb5_data *encoded_previous_request, 1402 krb5_pa_data **padata, 1403 krb5_pa_data ***return_padata, 1404 krb5_error *err_reply, 1405 krb5_data *salt, krb5_data *s2kparams, 1406 krb5_enctype *etype, 1407 krb5_keyblock *as_key, 1408 krb5_prompter_fct prompter, void *prompter_data, 1409 krb5_gic_get_as_key_fct gak_fct, void *gak_data, 1410 krb5_preauth_client_rock *get_data_rock, 1411 krb5_gic_opt_ext *opte) 1412 { 1413 krb5_error_code ret; 1414 krb5_pa_data **out_padata; 1415 krb5_preauth_context *context; 1416 struct _krb5_preauth_context_module *module; 1417 int i, j; 1418 int out_pa_list_size = 0; 1419 1420 ret = KRB5KRB_ERR_GENERIC; 1421 if (kcontext->preauth_context == NULL) { 1422 return KRB5KRB_ERR_GENERIC; 1423 } 1424 context = kcontext->preauth_context; 1425 if (context == NULL) { 1426 return KRB5KRB_ERR_GENERIC; 1427 } 1428 1429 for (i = 0; padata[i] != NULL && padata[i]->pa_type != 0; i++) { 1430 out_padata = NULL; 1431 for (j = 0; j < context->n_modules; j++) { 1432 module = &context->modules[j]; 1433 if (module->pa_type != padata[i]->pa_type) { 1434 continue; 1435 } 1436 if (module->client_tryagain == NULL) { 1437 continue; 1438 } 1439 if ((*module->client_tryagain)(kcontext, 1440 module->plugin_context, 1441 *module->request_context_pp, 1442 (krb5_get_init_creds_opt *)opte, 1443 client_data_proc, 1444 get_data_rock, 1445 request, 1446 encoded_request_body, 1447 encoded_previous_request, 1448 padata[i], 1449 err_reply, 1450 prompter, prompter_data, 1451 gak_fct, gak_data, salt, s2kparams, 1452 as_key, 1453 &out_padata) == 0) { 1454 if (out_padata != NULL) { 1455 int k; 1456 for (k = 0; out_padata[k] != NULL; k++); 1457 grow_pa_list(return_padata, &out_pa_list_size, 1458 out_padata, k); 1459 free(out_padata); 1460 return 0; 1461 } 1462 } 1463 } 1464 } 1465 return ret; 1466 } 1467 1468 krb5_error_code KRB5_CALLCONV 1469 krb5_do_preauth(krb5_context context, 1470 krb5_kdc_req *request, 1471 krb5_data *encoded_request_body, 1472 krb5_data *encoded_previous_request, 1473 krb5_pa_data **in_padata, krb5_pa_data ***out_padata, 1474 krb5_data *salt, krb5_data *s2kparams, 1475 krb5_enctype *etype, 1476 krb5_keyblock *as_key, 1477 krb5_prompter_fct prompter, void *prompter_data, 1478 krb5_gic_get_as_key_fct gak_fct, void *gak_data, 1479 krb5_preauth_client_rock *get_data_rock, 1480 krb5_gic_opt_ext *opte) 1481 { 1482 int h, i, j, out_pa_list_size; 1483 int seen_etype_info2 = 0; 1484 krb5_pa_data *out_pa = NULL, **out_pa_list = NULL; 1485 krb5_data scratch; 1486 krb5_etype_info etype_info = NULL; 1487 krb5_error_code ret; 1488 static const int paorder[] = { PA_INFO, PA_REAL }; 1489 int realdone; 1490 1491 /* Solaris Kerberos */ 1492 KRB5_LOG0(KRB5_INFO, "krb5_do_preauth() start"); 1493 1494 if (in_padata == NULL) { 1495 *out_padata = NULL; 1496 return(0); 1497 } 1498 1499 #ifdef DEBUG 1500 /* Solaris Kerberos */ 1501 if (salt && salt->data && salt->length > 0) { 1502 fprintf (stderr, "salt len=%d", salt->length); 1503 if ((int) salt->length > 0) 1504 fprintf (stderr, " '%*s'", salt->length, salt->data); 1505 fprintf (stderr, "; preauth data types:"); 1506 for (i = 0; in_padata[i]; i++) { 1507 fprintf (stderr, " %d", in_padata[i]->pa_type); 1508 } 1509 fprintf (stderr, "\n"); 1510 } 1511 #endif 1512 1513 out_pa_list = NULL; 1514 out_pa_list_size = 0; 1515 1516 /* first do all the informational preauths, then the first real one */ 1517 1518 for (h=0; h<(sizeof(paorder)/sizeof(paorder[0])); h++) { 1519 realdone = 0; 1520 for (i=0; in_padata[i] && !realdone; i++) { 1521 int k, l, etype_found, valid_etype_found; 1522 /* 1523 * This is really gross, but is necessary to prevent 1524 * lossage when talking to a 1.0.x KDC, which returns an 1525 * erroneous PA-PW-SALT when it returns a KRB-ERROR 1526 * requiring additional preauth. 1527 */ 1528 switch (in_padata[i]->pa_type) { 1529 case KRB5_PADATA_ETYPE_INFO: 1530 case KRB5_PADATA_ETYPE_INFO2: 1531 { 1532 krb5_preauthtype pa_type = in_padata[i]->pa_type; 1533 if (etype_info) { 1534 if (seen_etype_info2 || pa_type != KRB5_PADATA_ETYPE_INFO2) 1535 continue; 1536 if (pa_type == KRB5_PADATA_ETYPE_INFO2) { 1537 krb5_free_etype_info( context, etype_info); 1538 etype_info = NULL; 1539 } 1540 } 1541 1542 scratch.length = in_padata[i]->length; 1543 scratch.data = (char *) in_padata[i]->contents; 1544 if (pa_type == KRB5_PADATA_ETYPE_INFO2) { 1545 seen_etype_info2++; 1546 ret = decode_krb5_etype_info2(&scratch, &etype_info); 1547 } 1548 else ret = decode_krb5_etype_info(&scratch, &etype_info); 1549 if (ret) { 1550 ret = 0; /*Ignore error and etype_info element*/ 1551 if (etype_info) 1552 krb5_free_etype_info( context, etype_info); 1553 etype_info = NULL; 1554 continue; 1555 } 1556 if (etype_info[0] == NULL) { 1557 krb5_free_etype_info(context, etype_info); 1558 etype_info = NULL; 1559 break; 1560 } 1561 /* 1562 * Select first etype in our request which is also in 1563 * etype-info (preferring client request ktype order). 1564 */ 1565 for (etype_found = 0, valid_etype_found = 0, k = 0; 1566 !etype_found && k < request->nktypes; k++) { 1567 for (l = 0; etype_info[l]; l++) { 1568 if (etype_info[l]->etype == request->ktype[k]) { 1569 etype_found++; 1570 break; 1571 } 1572 /* check if program has support for this etype for more 1573 * precise error reporting. 1574 */ 1575 if (valid_enctype(etype_info[l]->etype)) 1576 valid_etype_found++; 1577 } 1578 } 1579 if (!etype_found) { 1580 /* Solaris Kerberos */ 1581 KRB5_LOG(KRB5_ERR, "error !etype_found, " 1582 "valid_etype_found = %d", 1583 valid_etype_found); 1584 if (valid_etype_found) { 1585 /* supported enctype but not requested */ 1586 ret = KRB5_CONFIG_ETYPE_NOSUPP; 1587 goto cleanup; 1588 } 1589 else { 1590 /* unsupported enctype */ 1591 ret = KRB5_PROG_ETYPE_NOSUPP; 1592 goto cleanup; 1593 } 1594 1595 } 1596 scratch.data = (char *) etype_info[l]->salt; 1597 scratch.length = etype_info[l]->length; 1598 krb5_free_data_contents(context, salt); 1599 if (scratch.length == KRB5_ETYPE_NO_SALT) 1600 salt->data = NULL; 1601 else 1602 if ((ret = krb5int_copy_data_contents( context, &scratch, salt)) != 0) 1603 goto cleanup; 1604 *etype = etype_info[l]->etype; 1605 krb5_free_data_contents(context, s2kparams); 1606 if ((ret = krb5int_copy_data_contents(context, 1607 &etype_info[l]->s2kparams, 1608 s2kparams)) != 0) 1609 goto cleanup; 1610 #ifdef DEBUG 1611 for (j = 0; etype_info[j]; j++) { 1612 krb5_etype_info_entry *e = etype_info[j]; 1613 fprintf (stderr, "etype info %d: etype %d salt len=%d", 1614 j, e->etype, e->length); 1615 if (e->length > 0 && e->length != KRB5_ETYPE_NO_SALT) 1616 fprintf (stderr, " '%.*s'", e->length, e->salt); 1617 fprintf (stderr, "\n"); 1618 } 1619 #endif 1620 break; 1621 } 1622 case KRB5_PADATA_PW_SALT: 1623 case KRB5_PADATA_AFS3_SALT: 1624 if (etype_info) 1625 continue; 1626 break; 1627 default: 1628 ; 1629 } 1630 /* Try the internally-provided preauth type list. */ 1631 if (!realdone) for (j=0; pa_types[j].type >= 0; j++) { 1632 if ((in_padata[i]->pa_type == pa_types[j].type) && 1633 (pa_types[j].flags & paorder[h])) { 1634 #ifdef DEBUG 1635 fprintf (stderr, "calling internal function for pa_type " 1636 "%d, flag %d\n", pa_types[j].type, paorder[h]); 1637 #endif 1638 out_pa = NULL; 1639 1640 if ((ret = ((*pa_types[j].fct)(context, request, 1641 in_padata[i], &out_pa, 1642 salt, s2kparams, etype, as_key, 1643 prompter, prompter_data, 1644 gak_fct, gak_data)))) { 1645 goto cleanup; 1646 } 1647 1648 ret = grow_pa_list(&out_pa_list, &out_pa_list_size, 1649 &out_pa, 1); 1650 if (ret != 0) { 1651 goto cleanup; 1652 } 1653 if (paorder[h] == PA_REAL) 1654 realdone = 1; 1655 } 1656 } 1657 1658 /* Try to use plugins now. */ 1659 if (!realdone) { 1660 krb5_init_preauth_context(context); 1661 if (context->preauth_context != NULL) { 1662 int module_ret, module_flags; 1663 #ifdef DEBUG 1664 fprintf (stderr, "trying modules for pa_type %d, flag %d\n", 1665 in_padata[i]->pa_type, paorder[h]); 1666 #endif 1667 ret = krb5_run_preauth_plugins(context, 1668 paorder[h], 1669 request, 1670 encoded_request_body, 1671 encoded_previous_request, 1672 in_padata[i], 1673 prompter, 1674 prompter_data, 1675 gak_fct, 1676 salt, s2kparams, 1677 gak_data, 1678 get_data_rock, 1679 as_key, 1680 &out_pa_list, 1681 &out_pa_list_size, 1682 &module_ret, 1683 &module_flags, 1684 opte); 1685 if (ret == 0) { 1686 if (module_ret == 0) { 1687 if (paorder[h] == PA_REAL) { 1688 realdone = 1; 1689 } 1690 } 1691 } 1692 } 1693 } 1694 } 1695 } 1696 1697 *out_padata = out_pa_list; 1698 if (etype_info) 1699 krb5_free_etype_info(context, etype_info); 1700 1701 /* Solaris Kerberos */ 1702 KRB5_LOG0(KRB5_INFO, "krb5_do_preauth() end"); 1703 return(0); 1704 cleanup: 1705 if (out_pa_list) { 1706 out_pa_list[out_pa_list_size++] = NULL; 1707 krb5_free_pa_data(context, out_pa_list); 1708 } 1709 if (etype_info) 1710 krb5_free_etype_info(context, etype_info); 1711 1712 /* Solaris Kerberos */ 1713 KRB5_LOG0(KRB5_INFO, "krb5_do_preauth() end"); 1714 return (ret); 1715 } 1716