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