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
krb5_init_preauth_context(krb5_context kcontext)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
krb5_clear_preauth_context_use_counts(krb5_context context)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
krb5_preauth_supply_preauth_data(krb5_context context,krb5_gic_opt_ext * opte,const char * attr,const char * value)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
krb5_free_preauth_context(krb5_context context)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
krb5_preauth_request_context_init(krb5_context context)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
krb5_preauth_request_context_fini(krb5_context context)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
grow_ktypes(krb5_enctype ** out_ktypes,int * out_nktypes,krb5_enctype ktype)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
grow_pa_list(krb5_pa_data *** out_pa_list,int * out_pa_list_size,krb5_pa_data ** addition,int num_addition)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
client_data_proc(krb5_context kcontext,krb5_preauth_client_rock * rock,krb5_int32 request_type,krb5_data ** retdata)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
krb5_preauth_prepare_request(krb5_context kcontext,krb5_gic_opt_ext * opte,krb5_kdc_req * request)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
krb5_run_preauth_plugins(krb5_context kcontext,int module_required_flags,krb5_kdc_req * request,krb5_data * encoded_request_body,krb5_data * encoded_previous_request,krb5_pa_data * in_padata,krb5_prompter_fct prompter,void * prompter_data,preauth_get_as_key_proc gak_fct,krb5_data * salt,krb5_data * s2kparams,void * gak_data,krb5_preauth_client_rock * get_data_rock,krb5_keyblock * as_key,krb5_pa_data *** out_pa_list,int * out_pa_list_size,int * module_ret,int * module_flags,krb5_gic_opt_ext * opte)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
pa_salt(krb5_context context,krb5_kdc_req * request,krb5_pa_data * in_padata,krb5_pa_data ** out_padata,krb5_data * salt,krb5_data * s2kparams,krb5_enctype * etype,krb5_keyblock * as_key,krb5_prompter_fct prompter,void * prompter_data,krb5_gic_get_as_key_fct gak_fct,void * gak_data)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
pa_enc_timestamp(krb5_context context,krb5_kdc_req * request,krb5_pa_data * in_padata,krb5_pa_data ** out_padata,krb5_data * salt,krb5_data * s2kparams,krb5_enctype * etype,krb5_keyblock * as_key,krb5_prompter_fct prompter,void * prompter_data,krb5_gic_get_as_key_fct gak_fct,void * gak_data)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
sam_challenge_banner(krb5_int32 sam_type)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
pa_sam(krb5_context context,krb5_kdc_req * request,krb5_pa_data * in_padata,krb5_pa_data ** out_padata,krb5_data * salt,krb5_data * s2kparams,krb5_enctype * etype,krb5_keyblock * as_key,krb5_prompter_fct prompter,void * prompter_data,krb5_gic_get_as_key_fct gak_fct,void * gak_data)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
pa_sam_2(krb5_context context,krb5_kdc_req * request,krb5_pa_data * in_padata,krb5_pa_data ** out_padata,krb5_data * salt,krb5_data * s2kparams,krb5_enctype * etype,krb5_keyblock * as_key,krb5_prompter_fct prompter,void * prompter_data,krb5_gic_get_as_key_fct gak_fct,void * gak_data)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
krb5_do_preauth_tryagain(krb5_context kcontext,krb5_kdc_req * request,krb5_data * encoded_request_body,krb5_data * encoded_previous_request,krb5_pa_data ** padata,krb5_pa_data *** return_padata,krb5_error * err_reply,krb5_data * salt,krb5_data * s2kparams,krb5_enctype * etype,krb5_keyblock * as_key,krb5_prompter_fct prompter,void * prompter_data,krb5_gic_get_as_key_fct gak_fct,void * gak_data,krb5_preauth_client_rock * get_data_rock,krb5_gic_opt_ext * opte)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
krb5_do_preauth(krb5_context context,krb5_kdc_req * request,krb5_data * encoded_request_body,krb5_data * encoded_previous_request,krb5_pa_data ** in_padata,krb5_pa_data *** out_padata,krb5_data * salt,krb5_data * s2kparams,krb5_enctype * etype,krb5_keyblock * as_key,krb5_prompter_fct prompter,void * prompter_data,krb5_gic_get_as_key_fct gak_fct,void * gak_data,krb5_preauth_client_rock * get_data_rock,krb5_gic_opt_ext * opte)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