dmake: do not set MAKEFLAGS=k
[unleashed/tickless.git] / usr / src / lib / gss_mechs / mech_krb5 / krb5 / krb / preauth2.c
blob2e55fab9325651b64e86237359c71c9f745c0cb6
1 /*
2 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
3 * Use is subject to license terms.
4 */
7 /*
8 * Copyright 1995, 2003 by the Massachusetts Institute of Technology. All
9 * Rights Reserved.
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.
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.
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.
38 #include "k5-int.h"
39 #include "osconf.h"
40 #include <preauth_plugin.h>
41 #include "int-proto.h"
43 #if !defined(_WIN32)
44 #include <unistd.h>
45 #endif
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
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);
66 typedef struct _pa_types_t {
67 krb5_preauthtype type;
68 pa_function fct;
69 int flags;
70 } pa_types_t;
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)
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;
88 /* Only do this once for each krb5_context */
89 if (kcontext->preauth_context != NULL)
90 return;
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;
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;
109 if (tables == NULL) {
110 return;
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++;
127 /* allocate the space we need */
128 context = malloc(sizeof(*context));
129 if (context == NULL) {
130 krb5int_free_plugin_dir_data(tables);
131 return;
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;
139 memset(context->modules, 0, sizeof(context->modules[0]) * n_modules);
140 context->n_modules = n_modules;
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;
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;
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
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;
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++;
206 krb5int_free_plugin_dir_data(tables);
208 /* return the result */
209 kcontext->preauth_context = context;
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)
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;
227 * Give all the preauth plugins a look at the preauth option which
228 * has just been set
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)
236 krb5_error_code retval;
237 int i;
238 void *pctx;
239 const char *emsg = NULL;
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;
252 * Go down the list of preauth modules, and supply them with the
253 * attribute/value pair.
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;
269 return retval;
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)
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);
286 memset(&context->preauth_context->modules[i], 0,
287 sizeof(context->preauth_context->modules[i]));
289 if (context->preauth_context->modules != NULL) {
290 free(context->preauth_context->modules);
291 context->preauth_context->modules = NULL;
293 free(context->preauth_context);
294 context->preauth_context = NULL;
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)
303 int i;
304 void *rctx, *pctx;
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);
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)
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);
335 context->preauth_context->modules[i].request_context = NULL;
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)
345 int i;
346 krb5_enctype *ktypes;
347 for (i = 0; i < *out_nktypes; i++) {
348 if ((*out_ktypes)[i] == ktype)
349 return;
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;
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.
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)
371 krb5_pa_data **pa_list;
372 int i, j;
374 if (out_pa_list == NULL || addition == NULL) {
375 return EINVAL;
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 {
390 * Allocate room for the existing entries plus
391 * the new additions and a NULL terminator.
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;
406 return 0;
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.
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.
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)
425 krb5_data *ret;
426 char *data;
428 if (rock->magic != CLIENT_ROCK_MAGIC)
429 return EINVAL;
430 if (retdata == NULL)
431 return EINVAL;
433 switch (request_type) {
434 case krb5plugin_preauth_client_get_etype:
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;
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;
454 break;
455 case krb5plugin_preauth_client_free_etype:
456 ret = *retdata;
457 if (ret == NULL)
458 return 0;
459 free(ret->data);
460 free(ret);
461 return 0;
462 break;
463 default:
464 return EINVAL;
468 /* Tweak the request body, for now adding any enctypes which the module claims
469 * to add support for to the list, but in the future perhaps doing more
470 * involved things. */
471 void KRB5_CALLCONV
472 krb5_preauth_prepare_request(krb5_context kcontext,
473 krb5_gic_opt_ext *opte,
474 krb5_kdc_req *request)
476 int i, j;
478 if (kcontext->preauth_context == NULL) {
479 return;
481 /* Add the module-specific enctype list to the request, but only if
482 * it's something we can safely modify. */
483 if (!(opte && (opte->flags & KRB5_GET_INIT_CREDS_OPT_ETYPE_LIST))) {
484 for (i = 0; i < kcontext->preauth_context->n_modules; i++) {
485 if (kcontext->preauth_context->modules[i].enctypes == NULL)
486 continue;
487 for (j = 0; kcontext->preauth_context->modules[i].enctypes[j] != 0; j++) {
488 grow_ktypes(&request->ktype, &request->nktypes,
489 kcontext->preauth_context->modules[i].enctypes[j]);
495 /* Find the first module which provides for the named preauth type which also
496 * hasn't had a chance to run yet (INFO modules don't count, because as a rule
497 * they don't generate preauth data), and run it. */
498 static krb5_error_code
499 krb5_run_preauth_plugins(krb5_context kcontext,
500 int module_required_flags,
501 krb5_kdc_req *request,
502 krb5_data *encoded_request_body,
503 krb5_data *encoded_previous_request,
504 krb5_pa_data *in_padata,
505 krb5_prompter_fct prompter,
506 void *prompter_data,
507 preauth_get_as_key_proc gak_fct,
508 krb5_data *salt,
509 krb5_data *s2kparams,
510 void *gak_data,
511 krb5_preauth_client_rock *get_data_rock,
512 krb5_keyblock *as_key,
513 krb5_pa_data ***out_pa_list,
514 int *out_pa_list_size,
515 int *module_ret,
516 int *module_flags,
517 krb5_gic_opt_ext *opte)
519 int i;
520 krb5_pa_data **out_pa_data;
521 krb5_error_code ret;
522 struct _krb5_preauth_context_module *module;
524 if (kcontext->preauth_context == NULL) {
525 return ENOENT;
527 /* iterate over all loaded modules */
528 for (i = 0; i < kcontext->preauth_context->n_modules; i++) {
529 module = &kcontext->preauth_context->modules[i];
530 /* skip over those which don't match the preauth type */
531 if (module->pa_type != in_padata->pa_type)
532 continue;
533 /* skip over those which don't match the flags (INFO vs REAL, mainly) */
534 if ((module->flags & module_required_flags) == 0)
535 continue;
536 /* if it's a REAL module, try to call it only once per library call */
537 if (module_required_flags & PA_REAL) {
538 if (module->use_count > 0) {
539 #ifdef DEBUG
540 fprintf(stderr, "skipping already-used module \"%s\"(%d)\n",
541 module->name, module->pa_type);
542 #endif
543 continue;
545 module->use_count++;
547 /* run the module's callback function */
548 out_pa_data = NULL;
549 #ifdef DEBUG
550 fprintf(stderr, "using module \"%s\" (%d), flags = %d\n",
551 module->name, module->pa_type, module->flags);
552 #endif
553 ret = module->client_process(kcontext,
554 module->plugin_context,
555 *module->request_context_pp,
556 (krb5_get_init_creds_opt *)opte,
557 client_data_proc,
558 get_data_rock,
559 request,
560 encoded_request_body,
561 encoded_previous_request,
562 in_padata,
563 prompter, prompter_data,
564 gak_fct, gak_data, salt, s2kparams,
565 as_key,
566 &out_pa_data);
567 /* Make note of the module's flags and status. */
568 *module_flags = module->flags;
569 *module_ret = ret;
570 /* Save the new preauth data item. */
571 if (out_pa_data != NULL) {
572 int j;
573 for (j = 0; out_pa_data[j] != NULL; j++);
574 ret = grow_pa_list(out_pa_list, out_pa_list_size, out_pa_data, j);
575 free(out_pa_data);
576 if (ret != 0)
577 return ret;
579 break;
581 if (i >= kcontext->preauth_context->n_modules) {
582 return ENOENT;
584 return 0;
587 static
588 krb5_error_code pa_salt(krb5_context context,
589 krb5_kdc_req *request,
590 krb5_pa_data *in_padata,
591 krb5_pa_data **out_padata,
592 krb5_data *salt, krb5_data *s2kparams,
593 krb5_enctype *etype,
594 krb5_keyblock *as_key,
595 krb5_prompter_fct prompter, void *prompter_data,
596 krb5_gic_get_as_key_fct gak_fct, void *gak_data)
598 krb5_data tmp;
600 /* Solaris Kerberos - resync */
601 tmp.data = (char *)in_padata->contents;
602 tmp.length = in_padata->length;
603 krb5_free_data_contents(context, salt);
604 krb5int_copy_data_contents(context, &tmp, salt);
607 if (in_padata->pa_type == KRB5_PADATA_AFS3_SALT)
608 salt->length = SALT_TYPE_AFS_LENGTH;
610 return(0);
613 /*ARGSUSED*/
614 static
615 krb5_error_code pa_enc_timestamp(krb5_context context,
616 krb5_kdc_req *request,
617 krb5_pa_data *in_padata,
618 krb5_pa_data **out_padata,
619 krb5_data *salt,
620 krb5_data *s2kparams,
621 krb5_enctype *etype,
622 krb5_keyblock *as_key,
623 krb5_prompter_fct prompter,
624 void *prompter_data,
625 krb5_gic_get_as_key_fct gak_fct,
626 void *gak_data)
628 krb5_error_code ret;
629 krb5_pa_enc_ts pa_enc;
630 krb5_data *tmp;
631 krb5_enc_data enc_data;
632 krb5_pa_data *pa;
634 if (as_key->length == 0) {
635 #ifdef DEBUG
636 /* Solaris Kerberos */
637 if (salt != NULL && salt->data != NULL) {
638 fprintf (stderr, "%s:%d: salt len=%d", __FILE__, __LINE__,
639 salt->length);
640 if ((int) salt->length > 0)
641 fprintf (stderr, " '%.*s'", salt->length, salt->data);
642 fprintf (stderr, "; *etype=%d request->ktype[0]=%d\n",
643 *etype, request->ktype[0]);
645 #endif
646 if ((ret = ((*gak_fct)(context, request->client,
647 *etype ? *etype : request->ktype[0],
648 prompter, prompter_data,
649 salt, s2kparams, as_key, gak_data))))
650 return(ret);
653 /* now get the time of day, and encrypt it accordingly */
655 if ((ret = krb5_us_timeofday(context, &pa_enc.patimestamp, &pa_enc.pausec)))
656 return(ret);
658 if ((ret = encode_krb5_pa_enc_ts(&pa_enc, &tmp)))
659 return(ret);
661 #ifdef DEBUG
662 fprintf (stderr, "key type %d bytes %02x %02x ...\n",
663 as_key->enctype,
664 as_key->contents[0], as_key->contents[1]);
665 #endif
666 ret = krb5_encrypt_helper(context, as_key,
667 KRB5_KEYUSAGE_AS_REQ_PA_ENC_TS,
668 tmp, &enc_data);
669 #ifdef DEBUG
670 fprintf (stderr, "enc data { type=%d kvno=%d data=%02x %02x ... }\n",
671 enc_data.enctype, enc_data.kvno,
672 0xff & enc_data.ciphertext.data[0],
673 0xff & enc_data.ciphertext.data[1]);
674 #endif
676 krb5_free_data(context, tmp);
678 if (ret) {
679 krb5_xfree(enc_data.ciphertext.data);
680 return(ret);
683 ret = encode_krb5_enc_data(&enc_data, &tmp);
685 krb5_xfree(enc_data.ciphertext.data);
687 if (ret)
688 return(ret);
690 if ((pa = (krb5_pa_data *) malloc(sizeof(krb5_pa_data))) == NULL) {
691 krb5_free_data(context, tmp);
692 return(ENOMEM);
695 pa->magic = KV5M_PA_DATA;
696 pa->pa_type = KRB5_PADATA_ENC_TIMESTAMP;
697 pa->length = tmp->length;
698 pa->contents = (krb5_octet *) tmp->data;
700 *out_padata = pa;
702 krb5_xfree(tmp);
704 return(0);
707 static
708 char *sam_challenge_banner(krb5_int32 sam_type)
710 char *label;
712 switch (sam_type) {
713 case PA_SAM_TYPE_ENIGMA: /* Enigma Logic */
714 label = "Challenge for Enigma Logic mechanism";
715 break;
716 case PA_SAM_TYPE_DIGI_PATH: /* Digital Pathways */
717 case PA_SAM_TYPE_DIGI_PATH_HEX: /* Digital Pathways */
718 label = "Challenge for Digital Pathways mechanism";
719 break;
720 case PA_SAM_TYPE_ACTIVCARD_DEC: /* Digital Pathways */
721 case PA_SAM_TYPE_ACTIVCARD_HEX: /* Digital Pathways */
722 label = "Challenge for Activcard mechanism";
723 break;
724 case PA_SAM_TYPE_SKEY_K0: /* S/key where KDC has key 0 */
725 label = "Challenge for Enhanced S/Key mechanism";
726 break;
727 case PA_SAM_TYPE_SKEY: /* Traditional S/Key */
728 label = "Challenge for Traditional S/Key mechanism";
729 break;
730 case PA_SAM_TYPE_SECURID: /* Security Dynamics */
731 label = "Challenge for Security Dynamics mechanism";
732 break;
733 case PA_SAM_TYPE_SECURID_PREDICT: /* predictive Security Dynamics */
734 label = "Challenge for Security Dynamics mechanism";
735 break;
736 default:
737 label = "Challenge from authentication server";
738 break;
741 return(label);
744 /* this macro expands to the int,ptr necessary for "%.*s" in an sprintf */
746 #define SAMDATA(kdata, str, maxsize) \
747 (int)((kdata.length)? \
748 ((((kdata.length)<=(maxsize))?(kdata.length):strlen(str))): \
749 strlen(str)), \
750 (kdata.length)? \
751 ((((kdata.length)<=(maxsize))?(kdata.data):(str))):(str)
753 /* XXX Danger! This code is not in sync with the kerberos-password-02
754 draft. This draft cannot be implemented as written. This code is
755 compatible with earlier versions of mit krb5 and cygnus kerbnet. */
757 /*ARGSUSED*/
758 static
759 krb5_error_code pa_sam(krb5_context context,
760 krb5_kdc_req *request,
761 krb5_pa_data *in_padata,
762 krb5_pa_data **out_padata,
763 krb5_data *salt,
764 krb5_data *s2kparams,
765 krb5_enctype *etype,
766 krb5_keyblock *as_key,
767 krb5_prompter_fct prompter,
768 void *prompter_data,
769 krb5_gic_get_as_key_fct gak_fct,
770 void *gak_data)
772 krb5_error_code ret;
773 krb5_data tmpsam;
774 char name[100], banner[100];
775 char prompt[100], response[100];
776 krb5_data response_data;
777 krb5_prompt kprompt;
778 krb5_prompt_type prompt_type;
779 krb5_data defsalt;
780 krb5_sam_challenge *sam_challenge = 0;
781 krb5_sam_response sam_response;
782 /* these two get encrypted and stuffed in to sam_response */
783 krb5_enc_sam_response_enc enc_sam_response_enc;
784 krb5_data * scratch;
785 krb5_pa_data * pa;
787 /* Solaris Kerberos */
788 krb5_enc_data * enc_data;
789 size_t enclen;
791 if (prompter == NULL)
792 return EIO;
794 tmpsam.length = in_padata->length;
795 tmpsam.data = (char *) in_padata->contents;
796 if ((ret = decode_krb5_sam_challenge(&tmpsam, &sam_challenge)))
797 return(ret);
799 if (sam_challenge->sam_flags & KRB5_SAM_MUST_PK_ENCRYPT_SAD) {
800 krb5_xfree(sam_challenge);
801 return(KRB5_SAM_UNSUPPORTED);
804 /* If we need the password from the user (USE_SAD_AS_KEY not set), */
805 /* then get it here. Exception for "old" KDCs with CryptoCard */
806 /* support which uses the USE_SAD_AS_KEY flag, but still needs pwd */
808 if (!(sam_challenge->sam_flags & KRB5_SAM_USE_SAD_AS_KEY) ||
809 (sam_challenge->sam_type == PA_SAM_TYPE_CRYPTOCARD)) {
811 /* etype has either been set by caller or by KRB5_PADATA_ETYPE_INFO */
812 /* message from the KDC. If it is not set, pick an enctype that we */
813 /* think the KDC will have for us. */
815 if (etype && *etype == 0)
816 *etype = ENCTYPE_DES_CBC_CRC;
818 if ((ret = (gak_fct)(context, request->client, *etype, prompter,
819 prompter_data, salt, s2kparams, as_key, gak_data)))
820 return(ret);
822 sprintf(name, "%.*s",
823 SAMDATA(sam_challenge->sam_type_name, "SAM Authentication",
824 sizeof(name) - 1));
826 sprintf(banner, "%.*s",
827 SAMDATA(sam_challenge->sam_challenge_label,
828 sam_challenge_banner(sam_challenge->sam_type),
829 sizeof(banner)-1));
831 /* sprintf(prompt, "Challenge is [%s], %s: ", challenge, prompt); */
832 sprintf(prompt, "%s%.*s%s%.*s",
833 sam_challenge->sam_challenge.length?"Challenge is [":"",
834 SAMDATA(sam_challenge->sam_challenge, "", 20),
835 sam_challenge->sam_challenge.length?"], ":"",
836 SAMDATA(sam_challenge->sam_response_prompt, "passcode", 55));
838 response_data.data = response;
839 response_data.length = sizeof(response);
841 kprompt.prompt = prompt;
842 kprompt.hidden = 1;
843 kprompt.reply = &response_data;
844 prompt_type = KRB5_PROMPT_TYPE_PREAUTH;
846 /* PROMPTER_INVOCATION */
847 krb5int_set_prompt_types(context, &prompt_type);
848 if ((ret = ((*prompter)(context, prompter_data, name,
849 banner, 1, &kprompt)))) {
850 krb5_xfree(sam_challenge);
851 krb5int_set_prompt_types(context, 0);
852 return(ret);
854 krb5int_set_prompt_types(context, 0);
856 enc_sam_response_enc.sam_nonce = sam_challenge->sam_nonce;
857 if (sam_challenge->sam_nonce == 0) {
858 if ((ret = krb5_us_timeofday(context,
859 &enc_sam_response_enc.sam_timestamp,
860 &enc_sam_response_enc.sam_usec))) {
861 krb5_xfree(sam_challenge);
862 return(ret);
865 sam_response.sam_patimestamp = enc_sam_response_enc.sam_timestamp;
868 /* XXX What if more than one flag is set? */
869 if (sam_challenge->sam_flags & KRB5_SAM_SEND_ENCRYPTED_SAD) {
871 /* Most of this should be taken care of before we get here. We */
872 /* will need the user's password and as_key to encrypt the SAD */
873 /* and we want to preserve ordering of user prompts (first */
874 /* password, then SAM data) so that user's won't be confused. */
876 if (as_key->length) {
877 krb5_free_keyblock_contents(context, as_key);
878 as_key->length = 0;
881 /* generate a salt using the requested principal */
883 if ((salt->length == -1 || salt->length == SALT_TYPE_AFS_LENGTH) && (salt->data == NULL)) {
884 if ((ret = krb5_principal2salt(context, request->client,
885 &defsalt))) {
886 krb5_xfree(sam_challenge);
887 return(ret);
890 salt = &defsalt;
891 } else {
892 defsalt.length = 0;
895 /* generate a key using the supplied password */
897 ret = krb5_c_string_to_key(context, ENCTYPE_DES_CBC_MD5,
898 (krb5_data *)gak_data, salt, as_key);
900 if (defsalt.length)
901 krb5_xfree(defsalt.data);
903 if (ret) {
904 krb5_xfree(sam_challenge);
905 return(ret);
908 /* encrypt the passcode with the key from above */
910 enc_sam_response_enc.sam_sad = response_data;
911 } else if (sam_challenge->sam_flags & KRB5_SAM_USE_SAD_AS_KEY) {
913 /* process the key as password */
915 if (as_key->length) {
916 krb5_free_keyblock_contents(context, as_key);
917 as_key->length = 0;
920 #if 0
921 if ((salt->length == SALT_TYPE_AFS_LENGTH) && (salt->data == NULL)) {
922 if (ret = krb5_principal2salt(context, request->client,
923 &defsalt)) {
924 krb5_xfree(sam_challenge);
925 return(ret);
928 salt = &defsalt;
929 } else {
930 defsalt.length = 0;
932 #else
933 defsalt.length = 0;
934 salt = NULL;
935 #endif
937 /* XXX As of the passwords-04 draft, no enctype is specified,
938 the server uses ENCTYPE_DES_CBC_MD5. In the future the
939 server should send a PA-SAM-ETYPE-INFO containing the enctype. */
941 ret = krb5_c_string_to_key(context, ENCTYPE_DES_CBC_MD5,
942 &response_data, salt, as_key);
944 if (defsalt.length)
945 krb5_xfree(defsalt.data);
947 if (ret) {
948 krb5_xfree(sam_challenge);
949 return(ret);
952 enc_sam_response_enc.sam_sad.length = 0;
953 } else {
954 /* Eventually, combine SAD with long-term key to get
955 encryption key. */
956 return KRB5_PREAUTH_BAD_TYPE;
959 /* copy things from the challenge */
960 sam_response.sam_nonce = sam_challenge->sam_nonce;
961 sam_response.sam_flags = sam_challenge->sam_flags;
962 sam_response.sam_track_id = sam_challenge->sam_track_id;
963 sam_response.sam_type = sam_challenge->sam_type;
964 sam_response.magic = KV5M_SAM_RESPONSE;
966 krb5_xfree(sam_challenge);
968 /* encode the encoded part of the response */
969 if ((ret = encode_krb5_enc_sam_response_enc(&enc_sam_response_enc,
970 &scratch)))
971 return(ret);
974 * Solaris Kerberos:
975 * Using new crypto interface now so we can get rid of the
976 * old modules.
978 if ((ret = krb5_c_encrypt_length(context, as_key->enctype,
979 scratch->length, &enclen))) {
980 krb5_free_data(context, scratch);
981 return(ret);
984 enc_data = &sam_response.sam_enc_nonce_or_ts;
985 enc_data->magic = KV5M_ENC_DATA;
986 enc_data->kvno = 0;
987 enc_data->enctype = as_key->enctype;
988 enc_data->ciphertext.length = enclen;
990 if ((enc_data->ciphertext.data = MALLOC(enclen)) == NULL) {
991 enc_data->ciphertext.length = 0;
992 krb5_free_data(context, scratch);
993 return(ENOMEM);
996 if ((ret = krb5_c_encrypt(context, as_key, 0, 0,
997 scratch, enc_data))) {
998 FREE(enc_data->ciphertext.data, enclen);
999 enc_data->ciphertext.data = NULL;
1000 enc_data->ciphertext.length = 0;
1003 krb5_free_data(context, scratch);
1005 if (ret)
1006 return(ret);
1008 /* sam_enc_key is reserved for future use */
1009 sam_response.sam_enc_key.ciphertext.length = 0;
1011 if ((pa = malloc(sizeof(krb5_pa_data))) == NULL)
1012 return(ENOMEM);
1014 if ((ret = encode_krb5_sam_response(&sam_response, &scratch))) {
1015 free(pa);
1016 return(ret);
1019 pa->magic = KV5M_PA_DATA;
1020 pa->pa_type = KRB5_PADATA_SAM_RESPONSE;
1021 pa->length = scratch->length;
1022 pa->contents = (krb5_octet *) scratch->data;
1024 *out_padata = pa;
1026 return(0);
1029 static
1030 krb5_error_code pa_sam_2(krb5_context context,
1031 krb5_kdc_req *request,
1032 krb5_pa_data *in_padata,
1033 krb5_pa_data **out_padata,
1034 krb5_data *salt,
1035 krb5_data *s2kparams,
1036 krb5_enctype *etype,
1037 krb5_keyblock *as_key,
1038 krb5_prompter_fct prompter,
1039 void *prompter_data,
1040 krb5_gic_get_as_key_fct gak_fct,
1041 void *gak_data) {
1043 krb5_error_code retval;
1044 krb5_sam_challenge_2 *sc2 = NULL;
1045 krb5_sam_challenge_2_body *sc2b = NULL;
1046 krb5_data tmp_data;
1047 krb5_data response_data;
1048 char name[100], banner[100], prompt[100], response[100];
1049 krb5_prompt kprompt;
1050 krb5_prompt_type prompt_type;
1051 krb5_data defsalt;
1052 krb5_checksum **cksum;
1053 krb5_data *scratch = NULL;
1054 krb5_boolean valid_cksum = 0;
1055 krb5_enc_sam_response_enc_2 enc_sam_response_enc_2;
1056 krb5_sam_response_2 sr2;
1057 size_t ciph_len;
1058 krb5_pa_data *sam_padata;
1060 if (prompter == NULL)
1061 return KRB5_LIBOS_CANTREADPWD;
1063 tmp_data.length = in_padata->length;
1064 tmp_data.data = (char *)in_padata->contents;
1066 if ((retval = decode_krb5_sam_challenge_2(&tmp_data, &sc2)))
1067 return(retval);
1069 retval = decode_krb5_sam_challenge_2_body(&sc2->sam_challenge_2_body, &sc2b);
1071 if (retval)
1072 return(retval);
1074 if (!sc2->sam_cksum || ! *sc2->sam_cksum) {
1075 krb5_free_sam_challenge_2(context, sc2);
1076 krb5_free_sam_challenge_2_body(context, sc2b);
1077 return(KRB5_SAM_NO_CHECKSUM);
1080 if (sc2b->sam_flags & KRB5_SAM_MUST_PK_ENCRYPT_SAD) {
1081 krb5_free_sam_challenge_2(context, sc2);
1082 krb5_free_sam_challenge_2_body(context, sc2b);
1083 return(KRB5_SAM_UNSUPPORTED);
1086 if (!valid_enctype(sc2b->sam_etype)) {
1087 krb5_free_sam_challenge_2(context, sc2);
1088 krb5_free_sam_challenge_2_body(context, sc2b);
1089 return(KRB5_SAM_INVALID_ETYPE);
1092 /* All of the above error checks are KDC-specific, that is, they */
1093 /* assume a failure in the KDC reply. By returning anything other */
1094 /* than KRB5_KDC_UNREACH, KRB5_PREAUTH_FAILED, */
1095 /* KRB5_LIBOS_PWDINTR, or KRB5_REALM_CANT_RESOLVE, the client will */
1096 /* most likely go on to try the AS_REQ against master KDC */
1098 if (!(sc2b->sam_flags & KRB5_SAM_USE_SAD_AS_KEY)) {
1099 /* We will need the password to obtain the key used for */
1100 /* the checksum, and encryption of the sam_response. */
1101 /* Go ahead and get it now, preserving the ordering of */
1102 /* prompts for the user. */
1104 retval = (gak_fct)(context, request->client,
1105 sc2b->sam_etype, prompter,
1106 prompter_data, salt, s2kparams, as_key, gak_data);
1107 if (retval) {
1108 krb5_free_sam_challenge_2(context, sc2);
1109 krb5_free_sam_challenge_2_body(context, sc2b);
1110 return(retval);
1114 sprintf(name, "%.*s",
1115 SAMDATA(sc2b->sam_type_name, "SAM Authentication",
1116 sizeof(name) - 1));
1118 sprintf(banner, "%.*s",
1119 SAMDATA(sc2b->sam_challenge_label,
1120 sam_challenge_banner(sc2b->sam_type),
1121 sizeof(banner)-1));
1123 sprintf(prompt, "%s%.*s%s%.*s",
1124 sc2b->sam_challenge.length?"Challenge is [":"",
1125 SAMDATA(sc2b->sam_challenge, "", 20),
1126 sc2b->sam_challenge.length?"], ":"",
1127 SAMDATA(sc2b->sam_response_prompt, "passcode", 55));
1129 response_data.data = response;
1130 response_data.length = sizeof(response);
1131 kprompt.prompt = prompt;
1132 kprompt.hidden = 1;
1133 kprompt.reply = &response_data;
1135 prompt_type = KRB5_PROMPT_TYPE_PREAUTH;
1136 krb5int_set_prompt_types(context, &prompt_type);
1138 if ((retval = ((*prompter)(context, prompter_data, name,
1139 banner, 1, &kprompt)))) {
1140 krb5_free_sam_challenge_2(context, sc2);
1141 krb5_free_sam_challenge_2_body(context, sc2b);
1142 krb5int_set_prompt_types(context, 0);
1143 return(retval);
1146 krb5int_set_prompt_types(context, (krb5_prompt_type *)NULL);
1148 /* Generate salt used by string_to_key() */
1149 if ((salt->length == -1) && (salt->data == NULL)) {
1150 if ((retval =
1151 krb5_principal2salt(context, request->client, &defsalt))) {
1152 krb5_free_sam_challenge_2(context, sc2);
1153 krb5_free_sam_challenge_2_body(context, sc2b);
1154 return(retval);
1156 salt = &defsalt;
1157 } else {
1158 defsalt.length = 0;
1161 /* Get encryption key to be used for checksum and sam_response */
1162 if (!(sc2b->sam_flags & KRB5_SAM_USE_SAD_AS_KEY)) {
1163 /* as_key = string_to_key(password) */
1165 if (as_key->length) {
1166 krb5_free_keyblock_contents(context, as_key);
1167 as_key->length = 0;
1170 /* generate a key using the supplied password */
1171 retval = krb5_c_string_to_key(context, sc2b->sam_etype,
1172 (krb5_data *)gak_data, salt, as_key);
1174 if (retval) {
1175 krb5_free_sam_challenge_2(context, sc2);
1176 krb5_free_sam_challenge_2_body(context, sc2b);
1177 if (defsalt.length) krb5_xfree(defsalt.data);
1178 return(retval);
1181 if (!(sc2b->sam_flags & KRB5_SAM_SEND_ENCRYPTED_SAD)) {
1182 /* as_key = combine_key (as_key, string_to_key(SAD)) */
1183 krb5_keyblock tmp_kb;
1185 retval = krb5_c_string_to_key(context, sc2b->sam_etype,
1186 &response_data, salt, &tmp_kb);
1188 if (retval) {
1189 krb5_free_sam_challenge_2(context, sc2);
1190 krb5_free_sam_challenge_2_body(context, sc2b);
1191 if (defsalt.length) krb5_xfree(defsalt.data);
1192 return(retval);
1195 /* This should be a call to the crypto library some day */
1196 /* key types should already match the sam_etype */
1197 retval = krb5int_c_combine_keys(context, as_key, &tmp_kb, as_key);
1199 if (retval) {
1200 krb5_free_sam_challenge_2(context, sc2);
1201 krb5_free_sam_challenge_2_body(context, sc2b);
1202 if (defsalt.length) krb5_xfree(defsalt.data);
1203 return(retval);
1205 krb5_free_keyblock_contents(context, &tmp_kb);
1208 if (defsalt.length)
1209 krb5_xfree(defsalt.data);
1211 } else {
1212 /* as_key = string_to_key(SAD) */
1214 if (as_key->length) {
1215 krb5_free_keyblock_contents(context, as_key);
1216 as_key->length = 0;
1219 /* generate a key using the supplied password */
1220 retval = krb5_c_string_to_key(context, sc2b->sam_etype,
1221 &response_data, salt, as_key);
1223 if (defsalt.length)
1224 krb5_xfree(defsalt.data);
1226 if (retval) {
1227 krb5_free_sam_challenge_2(context, sc2);
1228 krb5_free_sam_challenge_2_body(context, sc2b);
1229 return(retval);
1233 /* Now we have a key, verify the checksum on the sam_challenge */
1235 cksum = sc2->sam_cksum;
1237 while (*cksum) {
1238 /* Check this cksum */
1239 retval = krb5_c_verify_checksum(context, as_key,
1240 KRB5_KEYUSAGE_PA_SAM_CHALLENGE_CKSUM,
1241 &sc2->sam_challenge_2_body,
1242 *cksum, &valid_cksum);
1243 if (retval) {
1244 krb5_free_data(context, scratch);
1245 krb5_free_sam_challenge_2(context, sc2);
1246 krb5_free_sam_challenge_2_body(context, sc2b);
1247 return(retval);
1249 if (valid_cksum)
1250 break;
1251 cksum++;
1254 if (!valid_cksum) {
1256 /* If KRB5_SAM_SEND_ENCRYPTED_SAD is set, then password is only */
1257 /* source for checksum key. Therefore, a bad checksum means a */
1258 /* bad password. Don't give that direct feedback to someone */
1259 /* trying to brute-force passwords. */
1261 if (!(sc2b->sam_flags & KRB5_SAM_SEND_ENCRYPTED_SAD))
1262 krb5_free_sam_challenge_2(context, sc2);
1263 krb5_free_sam_challenge_2_body(context, sc2b);
1265 * Note: We return AP_ERR_BAD_INTEGRITY so upper-level applications
1266 * can interpret that as "password incorrect", which is probably
1267 * the best error we can return in this situation.
1269 return(KRB5KRB_AP_ERR_BAD_INTEGRITY);
1272 /* fill in enc_sam_response_enc_2 */
1273 enc_sam_response_enc_2.magic = KV5M_ENC_SAM_RESPONSE_ENC_2;
1274 enc_sam_response_enc_2.sam_nonce = sc2b->sam_nonce;
1275 if (sc2b->sam_flags & KRB5_SAM_SEND_ENCRYPTED_SAD) {
1276 enc_sam_response_enc_2.sam_sad = response_data;
1277 } else {
1278 enc_sam_response_enc_2.sam_sad.data = NULL;
1279 enc_sam_response_enc_2.sam_sad.length = 0;
1282 /* encode and encrypt enc_sam_response_enc_2 with as_key */
1283 retval = encode_krb5_enc_sam_response_enc_2(&enc_sam_response_enc_2,
1284 &scratch);
1285 if (retval) {
1286 krb5_free_sam_challenge_2(context, sc2);
1287 krb5_free_sam_challenge_2_body(context, sc2b);
1288 return(retval);
1291 /* Fill in sam_response_2 */
1292 memset(&sr2, 0, sizeof(sr2));
1293 sr2.sam_type = sc2b->sam_type;
1294 sr2.sam_flags = sc2b->sam_flags;
1295 sr2.sam_track_id = sc2b->sam_track_id;
1296 sr2.sam_nonce = sc2b->sam_nonce;
1298 /* Now take care of sr2.sam_enc_nonce_or_sad by encrypting encoded */
1299 /* enc_sam_response_enc_2 from above */
1301 retval = krb5_c_encrypt_length(context, as_key->enctype, scratch->length,
1302 &ciph_len);
1303 if (retval) {
1304 krb5_free_sam_challenge_2(context, sc2);
1305 krb5_free_sam_challenge_2_body(context, sc2b);
1306 return(retval);
1308 sr2.sam_enc_nonce_or_sad.ciphertext.length = ciph_len;
1310 sr2.sam_enc_nonce_or_sad.ciphertext.data =
1311 (char *)malloc(sr2.sam_enc_nonce_or_sad.ciphertext.length);
1313 if (!sr2.sam_enc_nonce_or_sad.ciphertext.data) {
1314 krb5_free_sam_challenge_2(context, sc2);
1315 krb5_free_sam_challenge_2_body(context, sc2b);
1316 return(ENOMEM);
1319 retval = krb5_c_encrypt(context, as_key, KRB5_KEYUSAGE_PA_SAM_RESPONSE,
1320 NULL, scratch, &sr2.sam_enc_nonce_or_sad);
1321 if (retval) {
1322 krb5_free_sam_challenge_2(context, sc2);
1323 krb5_free_sam_challenge_2_body(context, sc2b);
1324 krb5_free_data(context, scratch);
1325 krb5_free_data_contents(context, &sr2.sam_enc_nonce_or_sad.ciphertext);
1326 return(retval);
1328 krb5_free_data(context, scratch);
1329 scratch = NULL;
1331 /* Encode the sam_response_2 */
1332 retval = encode_krb5_sam_response_2(&sr2, &scratch);
1333 krb5_free_sam_challenge_2(context, sc2);
1334 krb5_free_sam_challenge_2_body(context, sc2b);
1335 krb5_free_data_contents(context, &sr2.sam_enc_nonce_or_sad.ciphertext);
1337 if (retval) {
1338 return (retval);
1341 /* Almost there, just need to make padata ! */
1342 sam_padata = malloc(sizeof(krb5_pa_data));
1343 if (sam_padata == NULL) {
1344 krb5_free_data(context, scratch);
1345 return(ENOMEM);
1348 sam_padata->magic = KV5M_PA_DATA;
1349 sam_padata->pa_type = KRB5_PADATA_SAM_RESPONSE_2;
1350 sam_padata->length = scratch->length;
1351 sam_padata->contents = (krb5_octet *) scratch->data;
1353 *out_padata = sam_padata;
1355 return(0);
1358 static const pa_types_t pa_types[] = {
1360 KRB5_PADATA_PW_SALT,
1361 pa_salt,
1362 PA_INFO,
1365 KRB5_PADATA_AFS3_SALT,
1366 pa_salt,
1367 PA_INFO,
1370 KRB5_PADATA_ENC_TIMESTAMP,
1371 pa_enc_timestamp,
1372 PA_REAL,
1375 KRB5_PADATA_SAM_CHALLENGE_2,
1376 pa_sam_2,
1377 PA_REAL,
1380 KRB5_PADATA_SAM_CHALLENGE,
1381 pa_sam,
1382 PA_REAL,
1386 NULL,
1392 * If one of the modules can adjust its AS_REQ data using the contents of the
1393 * err_reply, return 0. If it's the sort of correction which requires that we
1394 * ask the user another question, we let the calling application deal with it.
1396 krb5_error_code KRB5_CALLCONV
1397 krb5_do_preauth_tryagain(krb5_context kcontext,
1398 krb5_kdc_req *request,
1399 krb5_data *encoded_request_body,
1400 krb5_data *encoded_previous_request,
1401 krb5_pa_data **padata,
1402 krb5_pa_data ***return_padata,
1403 krb5_error *err_reply,
1404 krb5_data *salt, krb5_data *s2kparams,
1405 krb5_enctype *etype,
1406 krb5_keyblock *as_key,
1407 krb5_prompter_fct prompter, void *prompter_data,
1408 krb5_gic_get_as_key_fct gak_fct, void *gak_data,
1409 krb5_preauth_client_rock *get_data_rock,
1410 krb5_gic_opt_ext *opte)
1412 krb5_error_code ret;
1413 krb5_pa_data **out_padata;
1414 krb5_preauth_context *context;
1415 struct _krb5_preauth_context_module *module;
1416 int i, j;
1417 int out_pa_list_size = 0;
1419 ret = KRB5KRB_ERR_GENERIC;
1420 if (kcontext->preauth_context == NULL) {
1421 return KRB5KRB_ERR_GENERIC;
1423 context = kcontext->preauth_context;
1424 if (context == NULL) {
1425 return KRB5KRB_ERR_GENERIC;
1428 for (i = 0; padata[i] != NULL && padata[i]->pa_type != 0; i++) {
1429 out_padata = NULL;
1430 for (j = 0; j < context->n_modules; j++) {
1431 module = &context->modules[j];
1432 if (module->pa_type != padata[i]->pa_type) {
1433 continue;
1435 if (module->client_tryagain == NULL) {
1436 continue;
1438 if ((*module->client_tryagain)(kcontext,
1439 module->plugin_context,
1440 *module->request_context_pp,
1441 (krb5_get_init_creds_opt *)opte,
1442 client_data_proc,
1443 get_data_rock,
1444 request,
1445 encoded_request_body,
1446 encoded_previous_request,
1447 padata[i],
1448 err_reply,
1449 prompter, prompter_data,
1450 gak_fct, gak_data, salt, s2kparams,
1451 as_key,
1452 &out_padata) == 0) {
1453 if (out_padata != NULL) {
1454 int k;
1455 for (k = 0; out_padata[k] != NULL; k++);
1456 grow_pa_list(return_padata, &out_pa_list_size,
1457 out_padata, k);
1458 free(out_padata);
1459 return 0;
1464 return ret;
1467 krb5_error_code KRB5_CALLCONV
1468 krb5_do_preauth(krb5_context context,
1469 krb5_kdc_req *request,
1470 krb5_data *encoded_request_body,
1471 krb5_data *encoded_previous_request,
1472 krb5_pa_data **in_padata, krb5_pa_data ***out_padata,
1473 krb5_data *salt, krb5_data *s2kparams,
1474 krb5_enctype *etype,
1475 krb5_keyblock *as_key,
1476 krb5_prompter_fct prompter, void *prompter_data,
1477 krb5_gic_get_as_key_fct gak_fct, void *gak_data,
1478 krb5_preauth_client_rock *get_data_rock,
1479 krb5_gic_opt_ext *opte)
1481 int h, i, j, out_pa_list_size;
1482 int seen_etype_info2 = 0;
1483 krb5_pa_data *out_pa = NULL, **out_pa_list = NULL;
1484 krb5_data scratch;
1485 krb5_etype_info etype_info = NULL;
1486 krb5_error_code ret;
1487 static const int paorder[] = { PA_INFO, PA_REAL };
1488 int realdone;
1490 /* Solaris Kerberos */
1491 KRB5_LOG0(KRB5_INFO, "krb5_do_preauth() start");
1493 if (in_padata == NULL) {
1494 *out_padata = NULL;
1495 return(0);
1498 #ifdef DEBUG
1499 /* Solaris Kerberos */
1500 if (salt && salt->data && salt->length > 0) {
1501 fprintf (stderr, "salt len=%d", salt->length);
1502 if ((int) salt->length > 0)
1503 fprintf (stderr, " '%*s'", salt->length, salt->data);
1504 fprintf (stderr, "; preauth data types:");
1505 for (i = 0; in_padata[i]; i++) {
1506 fprintf (stderr, " %d", in_padata[i]->pa_type);
1508 fprintf (stderr, "\n");
1510 #endif
1512 out_pa_list = NULL;
1513 out_pa_list_size = 0;
1515 /* first do all the informational preauths, then the first real one */
1517 for (h=0; h<(sizeof(paorder)/sizeof(paorder[0])); h++) {
1518 realdone = 0;
1519 for (i=0; in_padata[i] && !realdone; i++) {
1520 int k, l, etype_found, valid_etype_found;
1522 * This is really gross, but is necessary to prevent
1523 * lossage when talking to a 1.0.x KDC, which returns an
1524 * erroneous PA-PW-SALT when it returns a KRB-ERROR
1525 * requiring additional preauth.
1527 switch (in_padata[i]->pa_type) {
1528 case KRB5_PADATA_ETYPE_INFO:
1529 case KRB5_PADATA_ETYPE_INFO2:
1531 krb5_preauthtype pa_type = in_padata[i]->pa_type;
1532 if (etype_info) {
1533 if (seen_etype_info2 || pa_type != KRB5_PADATA_ETYPE_INFO2)
1534 continue;
1535 if (pa_type == KRB5_PADATA_ETYPE_INFO2) {
1536 krb5_free_etype_info( context, etype_info);
1537 etype_info = NULL;
1541 scratch.length = in_padata[i]->length;
1542 scratch.data = (char *) in_padata[i]->contents;
1543 if (pa_type == KRB5_PADATA_ETYPE_INFO2) {
1544 seen_etype_info2++;
1545 ret = decode_krb5_etype_info2(&scratch, &etype_info);
1547 else ret = decode_krb5_etype_info(&scratch, &etype_info);
1548 if (ret) {
1549 ret = 0; /*Ignore error and etype_info element*/
1550 if (etype_info)
1551 krb5_free_etype_info( context, etype_info);
1552 etype_info = NULL;
1553 continue;
1555 if (etype_info[0] == NULL) {
1556 krb5_free_etype_info(context, etype_info);
1557 etype_info = NULL;
1558 break;
1561 * Select first etype in our request which is also in
1562 * etype-info (preferring client request ktype order).
1564 for (etype_found = 0, valid_etype_found = 0, k = 0;
1565 !etype_found && k < request->nktypes; k++) {
1566 for (l = 0; etype_info[l]; l++) {
1567 if (etype_info[l]->etype == request->ktype[k]) {
1568 etype_found++;
1569 break;
1571 /* check if program has support for this etype for more
1572 * precise error reporting.
1574 if (valid_enctype(etype_info[l]->etype))
1575 valid_etype_found++;
1578 if (!etype_found) {
1579 /* Solaris Kerberos */
1580 KRB5_LOG(KRB5_ERR, "error !etype_found, "
1581 "valid_etype_found = %d",
1582 valid_etype_found);
1583 if (valid_etype_found) {
1584 /* supported enctype but not requested */
1585 ret = KRB5_CONFIG_ETYPE_NOSUPP;
1586 goto cleanup;
1588 else {
1589 /* unsupported enctype */
1590 ret = KRB5_PROG_ETYPE_NOSUPP;
1591 goto cleanup;
1595 scratch.data = (char *) etype_info[l]->salt;
1596 scratch.length = etype_info[l]->length;
1597 krb5_free_data_contents(context, salt);
1598 if (scratch.length == KRB5_ETYPE_NO_SALT)
1599 salt->data = NULL;
1600 else
1601 if ((ret = krb5int_copy_data_contents( context, &scratch, salt)) != 0)
1602 goto cleanup;
1603 *etype = etype_info[l]->etype;
1604 krb5_free_data_contents(context, s2kparams);
1605 if ((ret = krb5int_copy_data_contents(context,
1606 &etype_info[l]->s2kparams,
1607 s2kparams)) != 0)
1608 goto cleanup;
1609 #ifdef DEBUG
1610 for (j = 0; etype_info[j]; j++) {
1611 krb5_etype_info_entry *e = etype_info[j];
1612 fprintf (stderr, "etype info %d: etype %d salt len=%d",
1613 j, e->etype, e->length);
1614 if (e->length > 0 && e->length != KRB5_ETYPE_NO_SALT)
1615 fprintf (stderr, " '%.*s'", e->length, e->salt);
1616 fprintf (stderr, "\n");
1618 #endif
1619 break;
1621 case KRB5_PADATA_PW_SALT:
1622 case KRB5_PADATA_AFS3_SALT:
1623 if (etype_info)
1624 continue;
1625 break;
1626 default:
1629 /* Try the internally-provided preauth type list. */
1630 if (!realdone) for (j=0; pa_types[j].type >= 0; j++) {
1631 if ((in_padata[i]->pa_type == pa_types[j].type) &&
1632 (pa_types[j].flags & paorder[h])) {
1633 #ifdef DEBUG
1634 fprintf (stderr, "calling internal function for pa_type "
1635 "%d, flag %d\n", pa_types[j].type, paorder[h]);
1636 #endif
1637 out_pa = NULL;
1639 if ((ret = ((*pa_types[j].fct)(context, request,
1640 in_padata[i], &out_pa,
1641 salt, s2kparams, etype, as_key,
1642 prompter, prompter_data,
1643 gak_fct, gak_data)))) {
1644 goto cleanup;
1647 ret = grow_pa_list(&out_pa_list, &out_pa_list_size,
1648 &out_pa, 1);
1649 if (ret != 0) {
1650 goto cleanup;
1652 if (paorder[h] == PA_REAL)
1653 realdone = 1;
1657 /* Try to use plugins now. */
1658 if (!realdone) {
1659 krb5_init_preauth_context(context);
1660 if (context->preauth_context != NULL) {
1661 int module_ret, module_flags;
1662 #ifdef DEBUG
1663 fprintf (stderr, "trying modules for pa_type %d, flag %d\n",
1664 in_padata[i]->pa_type, paorder[h]);
1665 #endif
1666 ret = krb5_run_preauth_plugins(context,
1667 paorder[h],
1668 request,
1669 encoded_request_body,
1670 encoded_previous_request,
1671 in_padata[i],
1672 prompter,
1673 prompter_data,
1674 gak_fct,
1675 salt, s2kparams,
1676 gak_data,
1677 get_data_rock,
1678 as_key,
1679 &out_pa_list,
1680 &out_pa_list_size,
1681 &module_ret,
1682 &module_flags,
1683 opte);
1684 if (ret == 0) {
1685 if (module_ret == 0) {
1686 if (paorder[h] == PA_REAL) {
1687 realdone = 1;
1696 *out_padata = out_pa_list;
1697 if (etype_info)
1698 krb5_free_etype_info(context, etype_info);
1700 /* Solaris Kerberos */
1701 KRB5_LOG0(KRB5_INFO, "krb5_do_preauth() end");
1702 return(0);
1703 cleanup:
1704 if (out_pa_list) {
1705 out_pa_list[out_pa_list_size++] = NULL;
1706 krb5_free_pa_data(context, out_pa_list);
1708 if (etype_info)
1709 krb5_free_etype_info(context, etype_info);
1711 /* Solaris Kerberos */
1712 KRB5_LOG0(KRB5_INFO, "krb5_do_preauth() end");
1713 return (ret);