xref: /titanic_52/usr/src/cmd/krb5/krb5kdc/kdc_preauth.c (revision 68c47f65208790c466e5e484f2293d3baed71c6a)
1 /*
2  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
3  * Use is subject to license terms.
4  */
5 
6 
7 /*
8  * kdc/kdc_preauth.c
9  *
10  * Copyright 1995, 2003 by the Massachusetts Institute of Technology.
11  * All Rights Reserved.
12  *
13  * Export of this software from the United States of America may
14  *   require a specific license from the United States Government.
15  *   It is the responsibility of any person or organization contemplating
16  *   export to obtain such a license before exporting.
17  *
18  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
19  * distribute this software and its documentation for any purpose and
20  * without fee is hereby granted, provided that the above copyright
21  * notice appear in all copies and that both that copyright notice and
22  * this permission notice appear in supporting documentation, and that
23  * the name of M.I.T. not be used in advertising or publicity pertaining
24  * to distribution of the software without specific, written prior
25  * permission.  Furthermore if you modify this software you must label
26  * your software as modified software and not distribute it in such a
27  * fashion that it might be confused with the original M.I.T. software.
28  * M.I.T. makes no representations about the suitability of
29  * this software for any purpose.  It is provided "as is" without express
30  * or implied warranty.
31  *
32  * Preauthentication routines for the KDC.
33  */
34 
35 /*
36  * Copyright (C) 1998 by the FundsXpress, INC.
37  *
38  * All rights reserved.
39  *
40  * Export of this software from the United States of America may require
41  * a specific license from the United States Government.  It is the
42  * responsibility of any person or organization contemplating export to
43  * obtain such a license before exporting.
44  *
45  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
46  * distribute this software and its documentation for any purpose and
47  * without fee is hereby granted, provided that the above copyright
48  * notice appear in all copies and that both that copyright notice and
49  * this permission notice appear in supporting documentation, and that
50  * the name of FundsXpress. not be used in advertising or publicity pertaining
51  * to distribution of the software without specific, written prior
52  * permission.  FundsXpress makes no representations about the suitability of
53  * this software for any purpose.  It is provided "as is" without express
54  * or implied warranty.
55  *
56  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
57  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
58  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
59  */
60 
61 #include "k5-int.h"
62 #include "kdc_util.h"
63 #include "extern.h"
64 #include "com_err.h"
65 #include <assert.h>
66 #include <stdio.h>
67 #include "adm_proto.h"
68 #include <libintl.h>
69 #include <syslog.h>
70 
71 #include <assert.h>
72 #include "preauth_plugin.h"
73 
74 #if TARGET_OS_MAC
75 static const char *objdirs[] = { KRB5_PLUGIN_BUNDLE_DIR, LIBDIR "/krb5/plugins/preauth", NULL }; /* should be a list */
76 #else
77 static const char *objdirs[] = { LIBDIR "/krb5/plugins/preauth", NULL };
78 #endif
79 
80 /* XXX This is ugly and should be in a header file somewhere */
81 #ifndef KRB5INT_DES_TYPES_DEFINED
82 #define KRB5INT_DES_TYPES_DEFINED
83 typedef unsigned char des_cblock[8];	/* crypto-block size */
84 #endif
85 typedef des_cblock mit_des_cblock;
86 extern void mit_des_fixup_key_parity (mit_des_cblock );
87 extern int mit_des_is_weak_key (mit_des_cblock );
88 
89 typedef struct _krb5_preauth_systems {
90     const char *name;
91     int		type;
92     int		flags;
93     void       *plugin_context;
94     preauth_server_init_proc	init;
95     preauth_server_fini_proc	fini;
96     preauth_server_edata_proc	get_edata;
97     preauth_server_verify_proc	verify_padata;
98     preauth_server_return_proc	return_padata;
99     preauth_server_free_reqcontext_proc	free_pa_reqctx;
100 } krb5_preauth_systems;
101 
102 static krb5_error_code verify_enc_timestamp
103     (krb5_context, krb5_db_entry *client,
104 		    krb5_data *req_pkt,
105 		    krb5_kdc_req *request,
106 		    krb5_enc_tkt_part * enc_tkt_reply, krb5_pa_data *data,
107 		    preauth_get_entry_data_proc get_entry_data,
108 		    void *pa_system_context,
109 		    void **pa_request_context,
110 		    krb5_data **e_data,
111 		    krb5_authdata ***authz_data);
112 
113 static krb5_error_code get_etype_info
114     (krb5_context, krb5_kdc_req *request,
115 		    krb5_db_entry *client, krb5_db_entry *server,
116 		    preauth_get_entry_data_proc get_entry_data,
117 		    void *pa_system_context,
118 		    krb5_pa_data *data);
119 static krb5_error_code
120 get_etype_info2(krb5_context context, krb5_kdc_req *request,
121 	        krb5_db_entry *client, krb5_db_entry *server,
122 		preauth_get_entry_data_proc get_entry_data,
123 		void *pa_system_context,
124 		krb5_pa_data *pa_data);
125 static krb5_error_code
126 etype_info_as_rep_helper(krb5_context context, krb5_pa_data * padata,
127 			 krb5_db_entry *client,
128 			 krb5_kdc_req *request, krb5_kdc_rep *reply,
129 			 krb5_key_data *client_key,
130 			 krb5_keyblock *encrypting_key,
131 			 krb5_pa_data **send_pa,
132 			 int etype_info2);
133 
134 static krb5_error_code
135 return_etype_info(krb5_context, krb5_pa_data * padata,
136 		  krb5_db_entry *client,
137 		  krb5_data *req_pkt,
138 		  krb5_kdc_req *request, krb5_kdc_rep *reply,
139 		  krb5_key_data *client_key,
140 		  krb5_keyblock *encrypting_key,
141 		  krb5_pa_data **send_pa,
142 		  preauth_get_entry_data_proc get_entry_data,
143 		  void *pa_system_context,
144 		  void **pa_request_context);
145 
146 static krb5_error_code
147 return_etype_info2(krb5_context, krb5_pa_data * padata,
148 		   krb5_db_entry *client,
149 		   krb5_data *req_pkt,
150 		   krb5_kdc_req *request, krb5_kdc_rep *reply,
151 		   krb5_key_data *client_key,
152 		   krb5_keyblock *encrypting_key,
153 		   krb5_pa_data **send_pa,
154 		   preauth_get_entry_data_proc get_entry_data,
155 		   void *pa_system_context,
156 		   void **pa_request_context);
157 
158 static krb5_error_code return_pw_salt
159     (krb5_context, krb5_pa_data * padata,
160 		    krb5_db_entry *client,
161 		    krb5_data *req_pkt,
162 		    krb5_kdc_req *request, krb5_kdc_rep *reply,
163 		    krb5_key_data *client_key,
164 		    krb5_keyblock *encrypting_key,
165 		    krb5_pa_data **send_pa,
166 		    preauth_get_entry_data_proc get_entry_data,
167 		    void *pa_system_context,
168 		    void **pa_request_context);
169 
170 /* SAM preauth support */
171 static krb5_error_code verify_sam_response
172     (krb5_context, krb5_db_entry *client,
173 		    krb5_data *req_pkt,
174 		    krb5_kdc_req *request,
175 		    krb5_enc_tkt_part * enc_tkt_reply, krb5_pa_data *data,
176 		    preauth_get_entry_data_proc get_entry_data,
177 		    void *pa_module_context,
178 		    void **pa_request_context,
179 		    krb5_data **e_data,
180 		    krb5_authdata ***authz_data);
181 
182 static krb5_error_code get_sam_edata
183     (krb5_context, krb5_kdc_req *request,
184 		    krb5_db_entry *client, krb5_db_entry *server,
185 		    preauth_get_entry_data_proc get_entry_data,
186 		    void *pa_module_context,
187 		    krb5_pa_data *data);
188 static krb5_error_code return_sam_data
189     (krb5_context, krb5_pa_data * padata,
190 		    krb5_db_entry *client,
191 		    krb5_data *req_pkt,
192 		    krb5_kdc_req *request, krb5_kdc_rep *reply,
193 		    krb5_key_data *client_key,
194 		    krb5_keyblock *encrypting_key,
195 		    krb5_pa_data **send_pa,
196 		    preauth_get_entry_data_proc get_entry_data,
197 		    void *pa_module_context,
198 		    void **pa_request_context);
199 
200 static krb5_preauth_systems static_preauth_systems[] = {
201     {
202 	"timestamp",
203         KRB5_PADATA_ENC_TIMESTAMP,
204         0,
205 	NULL,
206 	NULL,
207 	NULL,
208         0,
209 	verify_enc_timestamp,
210 	0
211     },
212     {
213 	"etype-info",
214 	KRB5_PADATA_ETYPE_INFO,
215 	0,
216 	NULL,
217 	NULL,
218 	NULL,
219 	get_etype_info,
220 	0,
221 	return_etype_info
222     },
223     {
224 	"etype-info2",
225 	KRB5_PADATA_ETYPE_INFO2,
226 	0,
227 	NULL,
228 	NULL,
229 	NULL,
230 	get_etype_info2,
231 	0,
232 	return_etype_info2
233     },
234     {
235 	"pw-salt",
236 	KRB5_PADATA_PW_SALT,
237 	PA_PSEUDO,		/* Don't include this in the error list */
238 	NULL,
239 	NULL,
240 	NULL,
241 	0,
242 	0,
243 	return_pw_salt
244     },
245     {
246 	"sam-response",
247 	KRB5_PADATA_SAM_RESPONSE,
248 	0,
249 	NULL,
250 	NULL,
251 	NULL,
252 	0,
253 	verify_sam_response,
254 	return_sam_data
255     },
256     {
257 	"sam-challenge",
258 	KRB5_PADATA_SAM_CHALLENGE,
259 	PA_HARDWARE,		/* causes get_preauth_hint_list to use this */
260 	NULL,
261 	NULL,
262 	NULL,
263 	get_sam_edata,
264 	0,
265 	0
266     },
267     { "[end]", -1,}
268 };
269 
270 static krb5_preauth_systems *preauth_systems;
271 static int n_preauth_systems;
272 static struct plugin_dir_handle preauth_plugins;
273 
274 krb5_error_code
275 load_preauth_plugins(krb5_context context)
276 {
277     struct errinfo err;
278     void **preauth_plugins_ftables;
279     struct krb5plugin_preauth_server_ftable_v1 *ftable;
280     int module_count, i, j, k;
281     void *plugin_context;
282     preauth_server_init_proc server_init_proc = NULL;
283     char **kdc_realm_names = NULL;
284 
285     memset(&err, 0, sizeof(err));
286 
287     /* Attempt to load all of the preauth plugins we can find. */
288     PLUGIN_DIR_INIT(&preauth_plugins);
289     if (PLUGIN_DIR_OPEN(&preauth_plugins) == 0) {
290 	if (krb5int_open_plugin_dirs(objdirs, NULL,
291 				     &preauth_plugins, &err) != 0) {
292 	    return KRB5_PLUGIN_NO_HANDLE;
293 	}
294     }
295 
296     /* Get the method tables provided by the loaded plugins. */
297     preauth_plugins_ftables = NULL;
298     if (krb5int_get_plugin_dir_data(&preauth_plugins,
299 				    "preauthentication_server_1",
300 				    &preauth_plugins_ftables, &err) != 0) {
301 	return KRB5_PLUGIN_NO_HANDLE;
302     }
303 
304     /* Count the valid modules. */
305     module_count = sizeof(static_preauth_systems)
306 		   / sizeof(static_preauth_systems[0]);
307     if (preauth_plugins_ftables != NULL) {
308 	for (i = 0; preauth_plugins_ftables[i] != NULL; i++) {
309 	    ftable = preauth_plugins_ftables[i];
310 	    if ((ftable->flags_proc == NULL) &&
311 		(ftable->edata_proc == NULL) &&
312 		(ftable->verify_proc == NULL) &&
313 		(ftable->return_proc == NULL)) {
314 		continue;
315 	    }
316 	    for (j = 0;
317 		 ftable->pa_type_list != NULL &&
318 		 ftable->pa_type_list[j] > 0;
319 		 j++) {
320 		module_count++;
321 	    }
322 	}
323     }
324 
325     /* Build the complete list of supported preauthentication options, and
326      * leave room for a terminator entry. */
327     preauth_systems = malloc(sizeof(krb5_preauth_systems) * (module_count + 1));
328     if (preauth_systems == NULL) {
329 	krb5int_free_plugin_dir_data(preauth_plugins_ftables);
330 	return ENOMEM;
331     }
332 
333     /* Build a list of the names of the supported realms for this KDC.
334      * The list of names is terminated with a NULL. */
335     kdc_realm_names = malloc(sizeof(char *) * (kdc_numrealms + 1));
336     if (kdc_realm_names == NULL) {
337 	krb5int_free_plugin_dir_data(preauth_plugins_ftables);
338 	return ENOMEM;
339     }
340     for (i = 0; i < kdc_numrealms; i++) {
341 	kdc_realm_names[i] = kdc_realmlist[i]->realm_name;
342     }
343     kdc_realm_names[i] = NULL;
344 
345     /* Add the locally-supplied mechanisms to the dynamic list first. */
346     for (i = 0, k = 0;
347 	 i < sizeof(static_preauth_systems) / sizeof(static_preauth_systems[0]);
348 	 i++) {
349 	if (static_preauth_systems[i].type == -1)
350 	    break;
351 	preauth_systems[k] = static_preauth_systems[i];
352 	/* Try to initialize the preauth system.  If it fails, we'll remove it
353 	 * from the list of systems we'll be using. */
354 	plugin_context = NULL;
355 	server_init_proc = static_preauth_systems[i].init;
356 	if ((server_init_proc != NULL) &&
357 	    ((*server_init_proc)(context, &plugin_context, (const char **)kdc_realm_names) != 0)) {
358 	    memset(&preauth_systems[k], 0, sizeof(preauth_systems[k]));
359 	    continue;
360 	}
361 	preauth_systems[k].plugin_context = plugin_context;
362 	k++;
363     }
364 
365     /* Now add the dynamically-loaded mechanisms to the list. */
366     if (preauth_plugins_ftables != NULL) {
367 	for (i = 0; preauth_plugins_ftables[i] != NULL; i++) {
368 	    ftable = preauth_plugins_ftables[i];
369 	    if ((ftable->flags_proc == NULL) &&
370 		(ftable->edata_proc == NULL) &&
371 		(ftable->verify_proc == NULL) &&
372 		(ftable->return_proc == NULL)) {
373 		continue;
374 	    }
375 	    plugin_context = NULL;
376 	    for (j = 0;
377 		 ftable->pa_type_list != NULL &&
378 		 ftable->pa_type_list[j] > 0;
379 		 j++) {
380 		/* Try to initialize the plugin.  If it fails, we'll remove it
381 		 * from the list of modules we'll be using. */
382 		if (j == 0) {
383 		    server_init_proc = ftable->init_proc;
384 		    if (server_init_proc != NULL) {
385 			krb5_error_code initerr;
386 			initerr = (*server_init_proc)(context, &plugin_context, (const char **)kdc_realm_names);
387 			if (initerr) {
388 			    const char *emsg;
389 			    emsg = krb5_get_error_message(context, initerr);
390 			    if (emsg) {
391 				krb5_klog_syslog(LOG_ERR,
392 					"preauth %s failed to initialize: %s",
393 					ftable->name, emsg);
394 				krb5_free_error_message(context, emsg);
395 			    }
396 			    memset(&preauth_systems[k], 0, sizeof(preauth_systems[k]));
397 
398 			    break;	/* skip all modules in this plugin */
399 			}
400 		    }
401 		}
402 		preauth_systems[k].name = ftable->name;
403 		preauth_systems[k].type = ftable->pa_type_list[j];
404 		if (ftable->flags_proc != NULL)
405 		    preauth_systems[k].flags = ftable->flags_proc(context, preauth_systems[k].type);
406 		else
407 		    preauth_systems[k].flags = 0;
408 		preauth_systems[k].plugin_context = plugin_context;
409 		preauth_systems[k].init = server_init_proc;
410 		/* Only call fini once for each plugin */
411 		if (j == 0)
412 		    preauth_systems[k].fini = ftable->fini_proc;
413 		else
414 		    preauth_systems[k].fini = NULL;
415 		preauth_systems[k].get_edata = ftable->edata_proc;
416 		preauth_systems[k].verify_padata = ftable->verify_proc;
417 		preauth_systems[k].return_padata = ftable->return_proc;
418 		preauth_systems[k].free_pa_reqctx =
419 		    ftable->freepa_reqcontext_proc;
420 		k++;
421 	    }
422 	}
423 	krb5int_free_plugin_dir_data(preauth_plugins_ftables);
424     }
425     free(kdc_realm_names);
426     n_preauth_systems = k;
427     /* Add the end-of-list marker. */
428     preauth_systems[k].name = "[end]";
429     preauth_systems[k].type = -1;
430     return 0;
431 }
432 
433 krb5_error_code
434 unload_preauth_plugins(krb5_context context)
435 {
436     int i;
437     if (preauth_systems != NULL) {
438 	for (i = 0; i < n_preauth_systems; i++) {
439 	    if (preauth_systems[i].fini != NULL) {
440 	        (*preauth_systems[i].fini)(context,
441 					   preauth_systems[i].plugin_context);
442 	    }
443 	    memset(&preauth_systems[i], 0, sizeof(preauth_systems[i]));
444 	}
445 	free(preauth_systems);
446 	preauth_systems = NULL;
447 	n_preauth_systems = 0;
448 	krb5int_close_plugin_dirs(&preauth_plugins);
449     }
450     return 0;
451 }
452 
453 /*
454  * The make_padata_context() function creates a space for storing any context
455  * information which will be needed by return_padata() later.  Each preauth
456  * type gets a context storage location of its own.
457  */
458 struct request_pa_context {
459     int n_contexts;
460     struct {
461 	krb5_preauth_systems *pa_system;
462 	void *pa_context;
463     } *contexts;
464 };
465 
466 static krb5_error_code
467 make_padata_context(krb5_context context, void **padata_context)
468 {
469     int i;
470     struct request_pa_context *ret;
471 
472     ret = malloc(sizeof(*ret));
473     if (ret == NULL) {
474 	return ENOMEM;
475     }
476 
477     ret->n_contexts = n_preauth_systems;
478     ret->contexts = malloc(sizeof(ret->contexts[0]) * ret->n_contexts);
479     if (ret->contexts == NULL) {
480 	free(ret);
481 	return ENOMEM;
482     }
483 
484     memset(ret->contexts, 0, sizeof(ret->contexts[0]) * ret->n_contexts);
485 
486     for (i = 0; i < ret->n_contexts; i++) {
487 	ret->contexts[i].pa_system = &preauth_systems[i];
488 	ret->contexts[i].pa_context = NULL;
489     }
490 
491     *padata_context = ret;
492 
493     return 0;
494 }
495 
496 /*
497  * The free_padata_context function frees any context information pointers
498  * which the check_padata() function created but which weren't already cleaned
499  * up by return_padata().
500  */
501 krb5_error_code
502 free_padata_context(krb5_context kcontext, void **padata_context)
503 {
504     struct request_pa_context *context;
505     krb5_preauth_systems *preauth_system;
506     void **pctx, *mctx;
507     int i;
508 
509     if (padata_context == NULL)
510 	return 0;
511 
512     context = *padata_context;
513 
514     for (i = 0; i < context->n_contexts; i++) {
515 	if (context->contexts[i].pa_context != NULL) {
516 	    preauth_system = context->contexts[i].pa_system;
517 	    mctx = preauth_system->plugin_context;
518 	    if (preauth_system->free_pa_reqctx != NULL) {
519 		pctx = &context->contexts[i].pa_context;
520 		(*preauth_system->free_pa_reqctx)(kcontext, mctx, pctx);
521 	    }
522 	    context->contexts[i].pa_context = NULL;
523 	}
524     }
525 
526     free(context->contexts);
527     free(context);
528 
529     return 0;
530 }
531 
532 /* Retrieve a specified tl_data item from the given entry, and return its
533  * contents in a new krb5_data, which must be freed by the caller. */
534 static krb5_error_code
535 get_entry_tl_data(krb5_context context, krb5_db_entry *entry,
536 		  krb5_int16 tl_data_type, krb5_data **result)
537 {
538     krb5_tl_data *tl;
539     for (tl = entry->tl_data; tl != NULL; tl = tl->tl_data_next) {
540 	if (tl->tl_data_type == tl_data_type) {
541 	    *result = malloc(sizeof(krb5_data));
542 	    if (*result == NULL) {
543 		return ENOMEM;
544 	    }
545 	    (*result)->magic = KV5M_DATA;
546 	    (*result)->data = malloc(tl->tl_data_length);
547 	    if ((*result)->data == NULL) {
548 		free(*result);
549 		*result = NULL;
550 		return ENOMEM;
551 	    }
552 	    memcpy((*result)->data, tl->tl_data_contents, tl->tl_data_length);
553 	    return 0;
554 	}
555     }
556     return ENOENT;
557 }
558 
559 /*
560  * Retrieve a specific piece of information pertaining to the entry or the
561  * request and return it in a new krb5_data item which the caller must free.
562  *
563  * This may require massaging data into a contrived format, but it will
564  * hopefully keep us from having to reveal library-internal functions to
565  * modules.
566  */
567 static krb5_error_code
568 get_entry_data(krb5_context context,
569 	       krb5_kdc_req *request, krb5_db_entry *entry,
570 	       krb5_int32  type,
571 	       krb5_data **result)
572 {
573     int i, k;
574     krb5_data *ret;
575     krb5_deltat *delta;
576     krb5_keyblock *keys;
577     krb5_key_data *entry_key;
578 
579     switch (type) {
580     case krb5plugin_preauth_entry_request_certificate:
581 	return get_entry_tl_data(context, entry,
582 				 KRB5_TL_USER_CERTIFICATE, result);
583 	break;
584     case krb5plugin_preauth_entry_max_time_skew:
585 	ret = malloc(sizeof(krb5_data));
586 	if (ret == NULL)
587 	    return ENOMEM;
588 	delta = malloc(sizeof(krb5_deltat));
589 	if (delta == NULL) {
590 	    free(ret);
591 	    return ENOMEM;
592 	}
593 	*delta = context->clockskew;
594 	ret->data = (char *) delta;
595 	ret->length = sizeof(*delta);
596 	*result = ret;
597 	return 0;
598 	break;
599     case krb5plugin_preauth_keys:
600 	ret = malloc(sizeof(krb5_data));
601 	if (ret == NULL)
602 	    return ENOMEM;
603 	keys = malloc(sizeof(krb5_keyblock) * (request->nktypes + 1));
604 	if (keys == NULL) {
605 	    free(ret);
606 	    return ENOMEM;
607 	}
608 	ret->data = (char *) keys;
609 	ret->length = sizeof(krb5_keyblock) * (request->nktypes + 1);
610 	memset(ret->data, 0, ret->length);
611 	k = 0;
612 	for (i = 0; i < request->nktypes; i++) {
613 	    entry_key = NULL;
614 	    if (krb5_dbe_find_enctype(context, entry, request->ktype[i],
615 				      -1, 0, &entry_key) != 0)
616 		continue;
617 	    if (krb5_dbekd_decrypt_key_data(context, &master_keyblock,
618 					    entry_key, &keys[k], NULL) != 0) {
619 		if (keys[k].contents != NULL)
620 		    krb5_free_keyblock_contents(context, &keys[k]);
621 		memset(&keys[k], 0, sizeof(keys[k]));
622 		continue;
623 	    }
624 	    k++;
625 	}
626 	if (k > 0) {
627 	    *result = ret;
628 	    return 0;
629 	} else {
630 	    free(keys);
631 	    free(ret);
632 	}
633 	break;
634     case krb5plugin_preauth_request_body:
635 	ret = NULL;
636 	encode_krb5_kdc_req_body(request, &ret);
637 	if (ret != NULL) {
638 	    *result = ret;
639 	    return 0;
640 	}
641 	return ASN1_PARSE_ERROR;
642 	break;
643     default:
644 	break;
645     }
646     return ENOENT;
647 }
648 
649 static krb5_error_code
650 find_pa_system(int type, krb5_preauth_systems **preauth)
651 {
652     krb5_preauth_systems *ap;
653 
654     ap = preauth_systems ? preauth_systems : static_preauth_systems;
655     while ((ap->type != -1) && (ap->type != type))
656 	ap++;
657     if (ap->type == -1)
658 	return(KRB5_PREAUTH_BAD_TYPE);
659     *preauth = ap;
660     return 0;
661 }
662 
663 static krb5_error_code
664 find_pa_context(krb5_preauth_systems *pa_sys,
665 		struct request_pa_context *context,
666 		void ***pa_context)
667 {
668     int i;
669 
670     *pa_context = 0;
671 
672     if (context == NULL)
673 	return KRB5KRB_ERR_GENERIC;
674 
675     for (i = 0; i < context->n_contexts; i++) {
676 	if (context->contexts[i].pa_system == pa_sys) {
677 	    *pa_context = &context->contexts[i].pa_context;
678 	    return 0;
679 	}
680     }
681 
682     return KRB5KRB_ERR_GENERIC;
683 }
684 
685 /*
686  * Create a list of indices into the preauth_systems array, sorted by order of
687  * preference.
688  */
689 static krb5_boolean
690 pa_list_includes(krb5_pa_data **pa_data, krb5_preauthtype pa_type)
691 {
692     while (*pa_data != NULL) {
693 	if ((*pa_data)->pa_type == pa_type)
694 	    return TRUE;
695 	pa_data++;
696     }
697     return FALSE;
698 }
699 static void
700 sort_pa_order(krb5_context context, krb5_kdc_req *request, int *pa_order)
701 {
702     int i, j, k, n_repliers, n_key_replacers;
703 
704     /* First, set up the default order. */
705     i = 0;
706     for (j = 0; j < n_preauth_systems; j++) {
707         if (preauth_systems[j].return_padata != NULL)
708 	    pa_order[i++] = j;
709     }
710     n_repliers = i;
711     pa_order[n_repliers] = -1;
712 
713     /* Reorder so that PA_REPLACES_KEY modules are listed first. */
714     for (i = 0; i < n_repliers; i++) {
715 	/* If this module replaces the key, then it's okay to leave it where it
716 	 * is in the order. */
717 	if (preauth_systems[pa_order[i]].flags & PA_REPLACES_KEY)
718 	    continue;
719 	/* If not, search for a module which does, and swap in the first one we
720 	 * find. */
721         for (j = i + 1; j < n_repliers; j++) {
722 	    if (preauth_systems[pa_order[j]].flags & PA_REPLACES_KEY) {
723                 k = pa_order[j];
724 		pa_order[j] = pa_order[i];
725 		pa_order[i] = k;
726 		break;
727 	    }
728         }
729     }
730 
731     if (request->padata != NULL) {
732 	/* Now reorder the subset of modules which replace the key,
733 	 * bubbling those which handle pa_data types provided by the
734 	 * client ahead of the others. */
735 	for (i = 0; preauth_systems[pa_order[i]].flags & PA_REPLACES_KEY; i++) {
736 	    continue;
737 	}
738 	n_key_replacers = i;
739 	for (i = 0; i < n_key_replacers; i++) {
740 	    if (pa_list_includes(request->padata,
741 				preauth_systems[pa_order[i]].type))
742 		continue;
743 	    for (j = i + 1; j < n_key_replacers; j++) {
744 		if (pa_list_includes(request->padata,
745 				    preauth_systems[pa_order[j]].type)) {
746 		    k = pa_order[j];
747 		    pa_order[j] = pa_order[i];
748 		    pa_order[i] = k;
749 		    break;
750 		}
751 	    }
752 	}
753     }
754 #ifdef DEBUG
755     krb5_klog_syslog(LOG_DEBUG, "original preauth mechanism list:");
756     for (i = 0; i < n_preauth_systems; i++) {
757 	if (preauth_systems[i].return_padata != NULL)
758             krb5_klog_syslog(LOG_DEBUG, "... %s(%d)", preauth_systems[i].name,
759 			     preauth_systems[i].type);
760     }
761     krb5_klog_syslog(LOG_DEBUG, "sorted preauth mechanism list:");
762     for (i = 0; pa_order[i] != -1; i++) {
763         krb5_klog_syslog(LOG_DEBUG, "... %s(%d)",
764 			 preauth_systems[pa_order[i]].name,
765 			 preauth_systems[pa_order[i]].type);
766     }
767 #endif
768 }
769 
770 const char *missing_required_preauth(krb5_db_entry *client,
771 				     krb5_db_entry *server,
772 				     krb5_enc_tkt_part *enc_tkt_reply)
773 {
774 #if 0
775     /*
776      * If this is the pwchange service, and the pre-auth bit is set,
777      * allow it even if the HW preauth would normally be required.
778      *
779      * Sandia national labs wanted this for some strange reason... we
780      * leave it disabled normally.
781      */
782     if (isflagset(server->attributes, KRB5_KDB_PWCHANGE_SERVICE) &&
783 	isflagset(enc_tkt_reply->flags, TKT_FLG_PRE_AUTH))
784 	return 0;
785 #endif
786 
787 #ifdef DEBUG
788     krb5_klog_syslog (LOG_DEBUG,
789 		      "client needs %spreauth, %shw preauth; request has %spreauth, %shw preauth",
790 		      isflagset (client->attributes, KRB5_KDB_REQUIRES_PRE_AUTH) ? "" : "no ",
791 		      isflagset (client->attributes, KRB5_KDB_REQUIRES_HW_AUTH) ? "" : "no ",
792 		      isflagset (enc_tkt_reply->flags, TKT_FLG_PRE_AUTH) ? "" : "no ",
793 		      isflagset (enc_tkt_reply->flags, TKT_FLG_HW_AUTH) ? "" : "no ");
794 #endif
795 
796     if (isflagset(client->attributes, KRB5_KDB_REQUIRES_PRE_AUTH) &&
797 	 !isflagset(enc_tkt_reply->flags, TKT_FLG_PRE_AUTH))
798 	return "NEEDED_PREAUTH";
799 
800     if (isflagset(client->attributes, KRB5_KDB_REQUIRES_HW_AUTH) &&
801 	!isflagset(enc_tkt_reply->flags, TKT_FLG_HW_AUTH))
802 	return "NEEDED_HW_PREAUTH";
803 
804     return 0;
805 }
806 
807 void get_preauth_hint_list(krb5_kdc_req *request, krb5_db_entry *client,
808 			   krb5_db_entry *server, krb5_data *e_data)
809 {
810     int hw_only;
811     krb5_preauth_systems *ap;
812     krb5_pa_data **pa_data, **pa;
813     krb5_data *edat;
814     krb5_error_code retval;
815 
816     /* Zero these out in case we need to abort */
817     e_data->length = 0;
818     e_data->data = 0;
819 
820     hw_only = isflagset(client->attributes, KRB5_KDB_REQUIRES_HW_AUTH);
821     pa_data = malloc(sizeof(krb5_pa_data *) * (n_preauth_systems+1));
822     if (pa_data == 0)
823 	return;
824     memset(pa_data, 0, sizeof(krb5_pa_data *) * (n_preauth_systems+1));
825     pa = pa_data;
826 
827     for (ap = preauth_systems; ap->type != -1; ap++) {
828 	if (hw_only && !(ap->flags & PA_HARDWARE))
829 	    continue;
830 	if (ap->flags & PA_PSEUDO)
831 	    continue;
832 	*pa = malloc(sizeof(krb5_pa_data));
833 	if (*pa == 0)
834 	    goto errout;
835 	memset(*pa, 0, sizeof(krb5_pa_data));
836 	(*pa)->magic = KV5M_PA_DATA;
837 	(*pa)->pa_type = ap->type;
838 	if (ap->get_edata) {
839 	  retval = (ap->get_edata)(kdc_context, request, client, server,
840 				   get_entry_data, ap->plugin_context, *pa);
841 	  if (retval) {
842 	    /* just failed on this type, continue */
843 	    free(*pa);
844 	    *pa = 0;
845 	    continue;
846 	  }
847 	}
848 	pa++;
849     }
850     if (pa_data[0] == 0) {
851 	krb5_klog_syslog (LOG_INFO,
852 			  "%spreauth required but hint list is empty",
853 			  hw_only ? "hw" : "");
854     }
855     retval = encode_krb5_padata_sequence((krb5_pa_data * const *) pa_data,
856 					 &edat);
857     if (retval)
858 	goto errout;
859     *e_data = *edat;
860     free(edat);
861 
862 errout:
863     krb5_free_pa_data(kdc_context, pa_data);
864     return;
865 }
866 
867 /*
868  * Add authorization data returned from preauth modules to the ticket
869  * It is assumed that ad is a "null-terminated" array of krb5_authdata ptrs
870  */
871 static krb5_error_code
872 add_authorization_data(krb5_enc_tkt_part *enc_tkt_part, krb5_authdata **ad)
873 {
874     krb5_authdata **newad;
875     int oldones, newones;
876     int i;
877 
878     if (enc_tkt_part == NULL || ad == NULL)
879 	return EINVAL;
880 
881     for (newones = 0; ad[newones] != NULL; newones++);
882     if (newones == 0)
883 	return 0;   /* nothing to add */
884 
885     if (enc_tkt_part->authorization_data == NULL)
886 	oldones = 0;
887     else
888 	for (oldones = 0;
889 	     enc_tkt_part->authorization_data[oldones] != NULL; oldones++);
890 
891     newad = malloc((oldones + newones + 1) * sizeof(krb5_authdata *));
892     if (newad == NULL)
893 	return ENOMEM;
894 
895     /* Copy any existing pointers */
896     for (i = 0; i < oldones; i++)
897 	newad[i] = enc_tkt_part->authorization_data[i];
898 
899     /* Add the new ones */
900     for (i = 0; i < newones; i++)
901 	newad[oldones+i] = ad[i];
902 
903     /* Terminate the new list */
904     newad[oldones+i] = NULL;
905 
906     /* Free any existing list */
907     if (enc_tkt_part->authorization_data != NULL)
908 	free(enc_tkt_part->authorization_data);
909 
910     /* Install our new list */
911     enc_tkt_part->authorization_data = newad;
912 
913     return 0;
914 }
915 
916 /*
917  * This routine is called to verify the preauthentication information
918  * for a V5 request.
919  *
920  * Returns 0 if the pre-authentication is valid, non-zero to indicate
921  * an error code of some sort.
922  */
923 
924 krb5_error_code
925 check_padata (krb5_context context, krb5_db_entry *client, krb5_data *req_pkt,
926 	      krb5_kdc_req *request, krb5_enc_tkt_part *enc_tkt_reply,
927 	      void **padata_context, krb5_data *e_data)
928 {
929     krb5_error_code retval = 0;
930     krb5_pa_data **padata;
931     krb5_preauth_systems *pa_sys;
932     void **pa_context;
933     krb5_data *pa_e_data = NULL, *tmp_e_data = NULL;
934     int	pa_ok = 0, pa_found = 0;
935     krb5_error_code saved_retval = 0;
936     int use_saved_retval = 0;
937     const char *emsg;
938     krb5_authdata **tmp_authz_data = NULL;
939 
940     if (request->padata == 0)
941 	return 0;
942 
943     if (make_padata_context(context, padata_context) != 0) {
944 	return KRB5KRB_ERR_GENERIC;
945     }
946 
947 #ifdef DEBUG
948     krb5_klog_syslog (LOG_DEBUG, "checking padata");
949 #endif
950     for (padata = request->padata; *padata; padata++) {
951 #ifdef DEBUG
952 	krb5_klog_syslog (LOG_DEBUG, ".. pa_type 0x%x", (*padata)->pa_type);
953 #endif
954 	if (find_pa_system((*padata)->pa_type, &pa_sys))
955 	    continue;
956 	if (find_pa_context(pa_sys, *padata_context, &pa_context))
957 	    continue;
958 #ifdef DEBUG
959 	krb5_klog_syslog (LOG_DEBUG, ".. pa_type %s", pa_sys->name);
960 #endif
961 	if (pa_sys->verify_padata == 0)
962 	    continue;
963 	pa_found++;
964 	retval = pa_sys->verify_padata(context, client, req_pkt, request,
965 				       enc_tkt_reply, *padata,
966 				       get_entry_data, pa_sys->plugin_context,
967 				       pa_context, &tmp_e_data, &tmp_authz_data);
968 	if (retval) {
969 	    emsg = krb5_get_error_message (context, retval);
970 	    krb5_klog_syslog (LOG_INFO, "preauth (%s) verify failure: %s",
971 			      pa_sys->name, emsg);
972 	    krb5_free_error_message (context, emsg);
973 	    /* Ignore authorization data returned from modules that fail */
974 	    if (tmp_authz_data != NULL) {
975 		krb5_free_authdata(context, tmp_authz_data);
976 		tmp_authz_data = NULL;
977 	    }
978 	    if (pa_sys->flags & PA_REQUIRED) {
979 		/* free up any previous edata we might have been saving */
980 		if (pa_e_data != NULL)
981 		    krb5_free_data(context, pa_e_data);
982 		pa_e_data = tmp_e_data;
983 		tmp_e_data = NULL;
984 		use_saved_retval = 0; /* Make sure we use the current retval */
985 		pa_ok = 0;
986 		break;
987 	    }
988 	    /*
989 	     * We'll return edata from either the first PA_REQUIRED module
990 	     * that fails, or the first non-PA_REQUIRED module that fails.
991 	     * Hang on to edata from the first non-PA_REQUIRED module.
992 	     * If we've already got one saved, simply discard this one.
993 	     */
994 	    if (tmp_e_data != NULL) {
995 		if (pa_e_data == NULL) {
996 		    /* save the first error code and e-data */
997 		    pa_e_data = tmp_e_data;
998 		    tmp_e_data = NULL;
999 		    saved_retval = retval;
1000 		    use_saved_retval = 1;
1001 		} else {
1002 		    /* discard this extra e-data from non-PA_REQUIRED module */
1003 		    krb5_free_data(context, tmp_e_data);
1004 		    tmp_e_data = NULL;
1005 		}
1006 	    }
1007 	} else {
1008 #ifdef DEBUG
1009 	    krb5_klog_syslog (LOG_DEBUG, ".. .. ok");
1010 #endif
1011 	    /* Ignore any edata returned on success */
1012 	    if (tmp_e_data != NULL) {
1013 	        krb5_free_data(context, tmp_e_data);
1014 		tmp_e_data = NULL;
1015 	    }
1016 	    /* Add any authorization data to the ticket */
1017 	    if (tmp_authz_data != NULL) {
1018 		add_authorization_data(enc_tkt_reply, tmp_authz_data);
1019 		free(tmp_authz_data);
1020 		tmp_authz_data = NULL;
1021 	    }
1022 	    pa_ok = 1;
1023 	    if (pa_sys->flags & PA_SUFFICIENT)
1024 		break;
1025 	}
1026     }
1027 
1028     /* Don't bother copying and returning e-data on success */
1029     if (pa_ok && pa_e_data != NULL) {
1030 	krb5_free_data(context, pa_e_data);
1031 	pa_e_data = NULL;
1032     }
1033     /* Return any e-data from the preauth that caused us to exit the loop */
1034     if (pa_e_data != NULL) {
1035 	e_data->data = malloc(pa_e_data->length);
1036 	if (e_data->data == NULL) {
1037 	    krb5_free_data(context, pa_e_data);
1038 	    /* Solaris Kerberos */
1039 	    return ENOMEM;
1040 	}
1041 	memcpy(e_data->data, pa_e_data->data, pa_e_data->length);
1042 	e_data->length = pa_e_data->length;
1043 	krb5_free_data(context, pa_e_data);
1044 	pa_e_data = NULL;
1045 	if (use_saved_retval != 0)
1046 	    retval = saved_retval;
1047     }
1048 
1049     if (pa_ok)
1050 	return 0;
1051 
1052     /* pa system was not found, but principal doesn't require preauth */
1053     if (!pa_found &&
1054 	!isflagset(client->attributes, KRB5_KDB_REQUIRES_PRE_AUTH) &&
1055 	!isflagset(client->attributes, KRB5_KDB_REQUIRES_HW_AUTH))
1056        return 0;
1057 
1058     if (!pa_found) {
1059 	emsg = krb5_get_error_message(context, retval);
1060 	krb5_klog_syslog (LOG_INFO, "no valid preauth type found: %s", emsg);
1061 	krb5_free_error_message(context, emsg);
1062     }
1063     /* The following switch statement allows us
1064      * to return some preauth system errors back to the client.
1065      */
1066     switch(retval) {
1067     case KRB5KRB_AP_ERR_BAD_INTEGRITY:
1068     case KRB5KRB_AP_ERR_SKEW:
1069     case KRB5KDC_ERR_ETYPE_NOSUPP:
1070     /* rfc 4556 */
1071     case KRB5KDC_ERR_CLIENT_NOT_TRUSTED:
1072     case KRB5KDC_ERR_INVALID_SIG:
1073     case KRB5KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED:
1074     case KRB5KDC_ERR_CANT_VERIFY_CERTIFICATE:
1075     case KRB5KDC_ERR_INVALID_CERTIFICATE:
1076     case KRB5KDC_ERR_REVOKED_CERTIFICATE:
1077     case KRB5KDC_ERR_REVOCATION_STATUS_UNKNOWN:
1078     case KRB5KDC_ERR_CLIENT_NAME_MISMATCH:
1079     case KRB5KDC_ERR_INCONSISTENT_KEY_PURPOSE:
1080     case KRB5KDC_ERR_DIGEST_IN_CERT_NOT_ACCEPTED:
1081     case KRB5KDC_ERR_PA_CHECKSUM_MUST_BE_INCLUDED:
1082     case KRB5KDC_ERR_DIGEST_IN_SIGNED_DATA_NOT_ACCEPTED:
1083     case KRB5KDC_ERR_PUBLIC_KEY_ENCRYPTION_NOT_SUPPORTED:
1084     /* earlier drafts of what became rfc 4556 */
1085     case KRB5KDC_ERR_CERTIFICATE_MISMATCH:
1086     case KRB5KDC_ERR_KDC_NOT_TRUSTED:
1087     case KRB5KDC_ERR_REVOCATION_STATUS_UNAVAILABLE:
1088     /* This value is shared with KRB5KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED. */
1089     /* case KRB5KDC_ERR_KEY_TOO_WEAK: */
1090 	return retval;
1091     default:
1092 	return KRB5KDC_ERR_PREAUTH_FAILED;
1093     }
1094 }
1095 
1096 /*
1097  * return_padata creates any necessary preauthentication
1098  * structures which should be returned by the KDC to the client
1099  */
1100 krb5_error_code
1101 return_padata(krb5_context context, krb5_db_entry *client, krb5_data *req_pkt,
1102 	      krb5_kdc_req *request, krb5_kdc_rep *reply,
1103 	      krb5_key_data *client_key, krb5_keyblock *encrypting_key,
1104 	      void **padata_context)
1105 {
1106     krb5_error_code		retval;
1107     krb5_pa_data **		padata;
1108     krb5_pa_data **		send_pa_list;
1109     krb5_pa_data **		send_pa;
1110     krb5_pa_data *		pa = 0;
1111     krb5_preauth_systems *	ap;
1112     int *			pa_order;
1113     int *			pa_type;
1114     int 			size = 0;
1115     void **			pa_context;
1116     krb5_boolean		key_modified;
1117     krb5_keyblock		original_key;
1118     if ((!*padata_context)&& (make_padata_context(context, padata_context) != 0)) {
1119 	return KRB5KRB_ERR_GENERIC;
1120     }
1121 
1122     for (ap = preauth_systems; ap->type != -1; ap++) {
1123 	if (ap->return_padata)
1124 	    size++;
1125     }
1126 
1127     if ((send_pa_list = malloc((size+1) * sizeof(krb5_pa_data *))) == NULL)
1128 	return ENOMEM;
1129     if ((pa_order = malloc((size+1) * sizeof(int))) == NULL) {
1130 	free(send_pa_list);
1131 	return ENOMEM;
1132     }
1133     sort_pa_order(context, request, pa_order);
1134 
1135     retval = krb5_copy_keyblock_contents(context, encrypting_key,
1136 					 &original_key);
1137     if (retval) {
1138 	free(send_pa_list);
1139 	free(pa_order);
1140 	return retval;
1141     }
1142     key_modified = FALSE;
1143 
1144     send_pa = send_pa_list;
1145     *send_pa = 0;
1146 
1147     for (pa_type = pa_order; *pa_type != -1; pa_type++) {
1148 	ap = &preauth_systems[*pa_type];
1149         if (!key_modified)
1150 	    if (original_key.enctype != encrypting_key->enctype)
1151                 key_modified = TRUE;
1152         if (!key_modified)
1153 	    if (original_key.length != encrypting_key->length)
1154                 key_modified = TRUE;
1155         if (!key_modified)
1156 	    if (memcmp(original_key.contents, encrypting_key->contents,
1157 		       original_key.length) != 0)
1158                 key_modified = TRUE;
1159 	if (key_modified && (ap->flags & PA_REPLACES_KEY))
1160 	    continue;
1161 	if (ap->return_padata == 0)
1162 	    continue;
1163 	if (find_pa_context(ap, *padata_context, &pa_context))
1164 	    continue;
1165 	pa = 0;
1166 	if (request->padata) {
1167 	    for (padata = request->padata; *padata; padata++) {
1168 		if ((*padata)->pa_type == ap->type) {
1169 		    pa = *padata;
1170 		    break;
1171 		}
1172 	    }
1173 	}
1174 	if ((retval = ap->return_padata(context, pa, client, req_pkt, request, reply,
1175 					client_key, encrypting_key, send_pa,
1176 					get_entry_data, ap->plugin_context,
1177 					pa_context))) {
1178 	    goto cleanup;
1179 	}
1180 
1181 	if (*send_pa)
1182 	    send_pa++;
1183 	*send_pa = 0;
1184     }
1185 
1186     retval = 0;
1187 
1188     if (send_pa_list[0]) {
1189 	reply->padata = send_pa_list;
1190 	send_pa_list = 0;
1191     }
1192 
1193 cleanup:
1194     if (send_pa_list)
1195 	krb5_free_pa_data(context, send_pa_list);
1196 
1197     /* Solaris Kerberos */
1198     krb5_free_keyblock_contents(context, &original_key);
1199     free(pa_order);
1200 
1201     return (retval);
1202 }
1203 
1204 static krb5_boolean
1205 enctype_requires_etype_info_2(krb5_enctype enctype)
1206 {
1207     switch(enctype) {
1208     case ENCTYPE_DES_CBC_CRC:
1209     case ENCTYPE_DES_CBC_MD4:
1210     case ENCTYPE_DES_CBC_MD5:
1211     case ENCTYPE_DES3_CBC_SHA1:
1212     case ENCTYPE_DES3_CBC_RAW:
1213     case ENCTYPE_ARCFOUR_HMAC:
1214     case ENCTYPE_ARCFOUR_HMAC_EXP :
1215 	return 0;
1216     default:
1217 	if (krb5_c_valid_enctype(enctype))
1218 	    return 1;
1219 	else return 0;
1220     }
1221 }
1222 
1223 static krb5_boolean
1224 request_contains_enctype (krb5_context context,  const krb5_kdc_req *request,
1225 			  krb5_enctype enctype)
1226 {
1227     int i;
1228     for (i =0; i < request->nktypes; i++)
1229 	if (request->ktype[i] == enctype)
1230 	    return 1;
1231     return 0;
1232 }
1233 
1234 
1235 static krb5_error_code
1236 verify_enc_timestamp(krb5_context context, krb5_db_entry *client,
1237 		     krb5_data *req_pkt,
1238 		     krb5_kdc_req *request, krb5_enc_tkt_part *enc_tkt_reply,
1239 		     krb5_pa_data *pa,
1240 		     preauth_get_entry_data_proc ets_get_entry_data,
1241 		     void *pa_system_context,
1242 		     void **pa_request_context,
1243 		     krb5_data **e_data,
1244 		     krb5_authdata ***authz_data)
1245 {
1246     krb5_pa_enc_ts *		pa_enc = 0;
1247     krb5_error_code		retval;
1248     krb5_data			scratch;
1249     krb5_data			enc_ts_data;
1250     krb5_enc_data 		*enc_data = 0;
1251     krb5_keyblock		key;
1252     krb5_key_data *		client_key;
1253     krb5_int32			start;
1254     krb5_timestamp		timenow;
1255     krb5_error_code		decrypt_err = 0;
1256 
1257     (void) memset(&key, 0, sizeof(krb5_keyblock));
1258     scratch.data = (char *) pa->contents;
1259     scratch.length = pa->length;
1260 
1261     enc_ts_data.data = 0;
1262 
1263     if ((retval = decode_krb5_enc_data(&scratch, &enc_data)) != 0)
1264 	goto cleanup;
1265 
1266     enc_ts_data.length = enc_data->ciphertext.length;
1267     if ((enc_ts_data.data = (char *) malloc(enc_ts_data.length)) == NULL)
1268 	goto cleanup;
1269 
1270     start = 0;
1271     decrypt_err = 0;
1272     while (1) {
1273 	if ((retval = krb5_dbe_search_enctype(context, client,
1274 					      &start, enc_data->enctype,
1275 					      -1, 0, &client_key)))
1276 	    goto cleanup;
1277 
1278 	if ((retval = krb5_dbekd_decrypt_key_data(context, &master_keyblock,
1279 						  client_key, &key, NULL)))
1280 	    goto cleanup;
1281 
1282 	key.enctype = enc_data->enctype;
1283 
1284 	retval = krb5_c_decrypt(context, &key, KRB5_KEYUSAGE_AS_REQ_PA_ENC_TS,
1285 				0, enc_data, &enc_ts_data);
1286 	krb5_free_keyblock_contents(context, &key);
1287 	if (retval == 0)
1288 	    break;
1289 	else
1290 	    decrypt_err = retval;
1291     }
1292 
1293     if ((retval = decode_krb5_pa_enc_ts(&enc_ts_data, &pa_enc)) != 0)
1294 	goto cleanup;
1295 
1296     if ((retval = krb5_timeofday(context, &timenow)) != 0)
1297 	goto cleanup;
1298 
1299     if (labs(timenow - pa_enc->patimestamp) > context->clockskew) {
1300 	retval = KRB5KRB_AP_ERR_SKEW;
1301 	goto cleanup;
1302     }
1303 
1304     setflag(enc_tkt_reply->flags, TKT_FLG_PRE_AUTH);
1305 
1306     retval = 0;
1307 
1308 cleanup:
1309     if (enc_data) {
1310 	krb5_free_data_contents(context, &enc_data->ciphertext);
1311 	free(enc_data);
1312     }
1313     krb5_free_data_contents(context, &enc_ts_data);
1314     if (pa_enc)
1315 	free(pa_enc);
1316     /*
1317      * If we get NO_MATCHING_KEY and decryption previously failed, and
1318      * we failed to find any other keys of the correct enctype after
1319      * that failed decryption, it probably means that the password was
1320      * incorrect.
1321      */
1322     if (retval == KRB5_KDB_NO_MATCHING_KEY && decrypt_err != 0)
1323 	retval = decrypt_err;
1324     return retval;
1325 }
1326 
1327 static krb5_error_code
1328 _make_etype_info_entry(krb5_context context,
1329 		       krb5_kdc_req *request, krb5_key_data *client_key,
1330 		       krb5_enctype etype, krb5_etype_info_entry **entry,
1331 		       int etype_info2)
1332 {
1333     krb5_data			salt;
1334     krb5_etype_info_entry *	tmp_entry;
1335     krb5_error_code		retval;
1336 
1337     if ((tmp_entry = malloc(sizeof(krb5_etype_info_entry))) == NULL)
1338        return ENOMEM;
1339 
1340     salt.data = 0;
1341 
1342     tmp_entry->magic = KV5M_ETYPE_INFO_ENTRY;
1343     tmp_entry->etype = etype;
1344     tmp_entry->length = KRB5_ETYPE_NO_SALT;
1345     tmp_entry->salt = 0;
1346     tmp_entry->s2kparams.data = NULL;
1347     tmp_entry->s2kparams.length = 0;
1348     retval = get_salt_from_key(context, request->client,
1349 			       client_key, &salt);
1350     if (retval)
1351 	goto fail;
1352     if (etype_info2 && client_key->key_data_ver > 1 &&
1353 	client_key->key_data_type[1] == KRB5_KDB_SALTTYPE_AFS3) {
1354 	switch (etype) {
1355 	case ENCTYPE_DES_CBC_CRC:
1356 	case ENCTYPE_DES_CBC_MD4:
1357 	case ENCTYPE_DES_CBC_MD5:
1358 	    tmp_entry->s2kparams.data = malloc(1);
1359 	    if (tmp_entry->s2kparams.data == NULL) {
1360 		retval = ENOMEM;
1361 		goto fail;
1362 	    }
1363 	    tmp_entry->s2kparams.length = 1;
1364 	    tmp_entry->s2kparams.data[0] = 1;
1365 	    break;
1366 	default:
1367 	    break;
1368 	}
1369     }
1370 
1371     if (salt.length >= 0) {
1372 	tmp_entry->length = salt.length;
1373 	tmp_entry->salt = (unsigned char *) salt.data;
1374 	salt.data = 0;
1375     }
1376     *entry = tmp_entry;
1377     return 0;
1378 
1379 fail:
1380     if (tmp_entry) {
1381 	if (tmp_entry->s2kparams.data)
1382 	    free(tmp_entry->s2kparams.data);
1383 	free(tmp_entry);
1384     }
1385     if (salt.data)
1386 	free(salt.data);
1387     return retval;
1388 }
1389 /*
1390  * This function returns the etype information for a particular
1391  * client, to be passed back in the preauth list in the KRB_ERROR
1392  * message.  It supports generating both etype_info  and etype_info2
1393  *  as most of the work is the same.
1394  */
1395 static krb5_error_code
1396 etype_info_helper(krb5_context context, krb5_kdc_req *request,
1397 	       krb5_db_entry *client, krb5_db_entry *server,
1398 	       krb5_pa_data *pa_data, int etype_info2)
1399 {
1400     krb5_etype_info_entry **	entry = 0;
1401     krb5_key_data		*client_key;
1402     krb5_error_code		retval;
1403     krb5_data *			scratch;
1404     krb5_enctype		db_etype;
1405     int 			i = 0;
1406     int 			start = 0;
1407     int				seen_des = 0;
1408 
1409     entry = malloc((client->n_key_data * 2 + 1) * sizeof(krb5_etype_info_entry *));
1410     if (entry == NULL)
1411 	return ENOMEM;
1412     entry[0] = NULL;
1413 
1414     while (1) {
1415 	retval = krb5_dbe_search_enctype(context, client, &start, -1,
1416 					 -1, 0, &client_key);
1417 	if (retval == KRB5_KDB_NO_MATCHING_KEY)
1418 	    break;
1419 	if (retval)
1420 	    goto cleanup;
1421 	db_etype = client_key->key_data_type[0];
1422 	if (db_etype == ENCTYPE_DES_CBC_MD4)
1423 	    db_etype = ENCTYPE_DES_CBC_MD5;
1424 
1425 	if (request_contains_enctype(context, request, db_etype)) {
1426 	    assert(etype_info2 ||
1427 		   !enctype_requires_etype_info_2(db_etype));
1428 	    if ((retval = _make_etype_info_entry(context, request, client_key,
1429 			    db_etype, &entry[i], etype_info2)) != 0) {
1430 		goto cleanup;
1431 	    }
1432 	    entry[i+1] = 0;
1433 	    i++;
1434 	}
1435 
1436 	/*
1437 	 * If there is a des key in the kdb, try the "similar" enctypes,
1438 	 * avoid duplicate entries.
1439 	 */
1440 	if (!seen_des) {
1441 	    switch (db_etype) {
1442 	    case ENCTYPE_DES_CBC_MD5:
1443 		db_etype = ENCTYPE_DES_CBC_CRC;
1444 		break;
1445 	    case ENCTYPE_DES_CBC_CRC:
1446 		db_etype = ENCTYPE_DES_CBC_MD5;
1447 		break;
1448 	    default:
1449 		continue;
1450 
1451 	    }
1452 	    if (request_contains_enctype(context, request, db_etype)) {
1453 		if ((retval = _make_etype_info_entry(context, request,
1454 				client_key, db_etype, &entry[i], etype_info2)) != 0) {
1455 		    goto cleanup;
1456 		}
1457 		entry[i+1] = 0;
1458 		i++;
1459 	    }
1460 	    seen_des++;
1461 	}
1462     }
1463     if (etype_info2)
1464 	retval = encode_krb5_etype_info2((krb5_etype_info_entry * const*) entry,
1465 				    &scratch);
1466     else 	retval = encode_krb5_etype_info((krb5_etype_info_entry * const*) entry,
1467 				    &scratch);
1468     if (retval)
1469 	goto cleanup;
1470     pa_data->contents = (unsigned char *)scratch->data;
1471     pa_data->length = scratch->length;
1472     free(scratch);
1473 
1474     retval = 0;
1475 
1476 cleanup:
1477     if (entry)
1478 	krb5_free_etype_info(context, entry);
1479     return retval;
1480 }
1481 
1482 static krb5_error_code
1483 get_etype_info(krb5_context context, krb5_kdc_req *request,
1484 	       krb5_db_entry *client, krb5_db_entry *server,
1485 	       preauth_get_entry_data_proc etype_get_entry_data,
1486 	       void *pa_system_context,
1487 	       krb5_pa_data *pa_data)
1488 {
1489   int i;
1490     for (i=0;  i < request->nktypes; i++) {
1491 	if (enctype_requires_etype_info_2(request->ktype[i]))
1492 	    return KRB5KDC_ERR_PADATA_TYPE_NOSUPP ;;;; /*Caller will
1493 							* skip this
1494 							* type*/
1495     }
1496     return etype_info_helper(context, request, client, server, pa_data, 0);
1497 }
1498 
1499 static krb5_error_code
1500 get_etype_info2(krb5_context context, krb5_kdc_req *request,
1501 	       krb5_db_entry *client, krb5_db_entry *server,
1502 	       preauth_get_entry_data_proc etype_get_entry_data,
1503 	       void *pa_system_context,
1504 	       krb5_pa_data *pa_data)
1505 {
1506     return etype_info_helper( context, request, client, server, pa_data, 1);
1507 }
1508 
1509 static krb5_error_code
1510 etype_info_as_rep_helper(krb5_context context, krb5_pa_data * padata,
1511 			 krb5_db_entry *client,
1512 			 krb5_kdc_req *request, krb5_kdc_rep *reply,
1513 			 krb5_key_data *client_key,
1514 			 krb5_keyblock *encrypting_key,
1515 			 krb5_pa_data **send_pa,
1516 			 int etype_info2)
1517 {
1518     int i;
1519     krb5_error_code retval;
1520     krb5_pa_data *tmp_padata;
1521     krb5_etype_info_entry **entry = NULL;
1522     krb5_data *scratch = NULL;
1523 
1524     /*
1525      * Skip PA-ETYPE-INFO completely if AS-REQ lists any "newer"
1526      * enctypes.
1527      */
1528     if (!etype_info2) {
1529 	for (i = 0; i < request->nktypes; i++) {
1530 	    if (enctype_requires_etype_info_2(request->ktype[i])) {
1531 		*send_pa = NULL;
1532 		return 0;
1533 	    }
1534 	}
1535     }
1536 
1537     tmp_padata = malloc( sizeof(krb5_pa_data));
1538     if (tmp_padata == NULL)
1539 	return ENOMEM;
1540     if (etype_info2)
1541 	tmp_padata->pa_type = KRB5_PADATA_ETYPE_INFO2;
1542     else
1543 	tmp_padata->pa_type = KRB5_PADATA_ETYPE_INFO;
1544 
1545     entry = malloc(2 * sizeof(krb5_etype_info_entry *));
1546     if (entry == NULL) {
1547 	retval = ENOMEM;
1548 	goto cleanup;
1549     }
1550     entry[0] = NULL;
1551     entry[1] = NULL;
1552     retval = _make_etype_info_entry(context, request,
1553 				    client_key, encrypting_key->enctype,
1554 				    entry, etype_info2);
1555     if (retval)
1556 	goto cleanup;
1557 
1558     if (etype_info2)
1559 	retval = encode_krb5_etype_info2((krb5_etype_info_entry * const*) entry, &scratch);
1560     else
1561 	retval = encode_krb5_etype_info((krb5_etype_info_entry * const*) entry, &scratch);
1562 
1563     if (retval)
1564 	goto cleanup;
1565     tmp_padata->contents = (uchar_t *)scratch->data;
1566     tmp_padata->length = scratch->length;
1567     *send_pa = tmp_padata;
1568 
1569     /* For cleanup - we no longer own the contents of the krb5_data
1570      * only to pointer to the krb5_data
1571      */
1572     scratch->data = 0;
1573 
1574  cleanup:
1575     if (entry)
1576 	krb5_free_etype_info(context, entry);
1577     if (retval) {
1578 	if (tmp_padata)
1579 	    free(tmp_padata);
1580     }
1581     if (scratch)
1582 	    krb5_free_data(context, scratch);
1583     return retval;
1584 }
1585 
1586 static krb5_error_code
1587 return_etype_info2(krb5_context context, krb5_pa_data * padata,
1588 		   krb5_db_entry *client,
1589 		   krb5_data *req_pkt,
1590 		   krb5_kdc_req *request, krb5_kdc_rep *reply,
1591 		   krb5_key_data *client_key,
1592 		   krb5_keyblock *encrypting_key,
1593 		   krb5_pa_data **send_pa,
1594 		   preauth_get_entry_data_proc etype_get_entry_data,
1595 	           void *pa_system_context,
1596 		   void **pa_request_context)
1597 {
1598     return etype_info_as_rep_helper(context, padata, client, request, reply,
1599 				    client_key, encrypting_key, send_pa, 1);
1600 }
1601 
1602 
1603 static krb5_error_code
1604 return_etype_info(krb5_context context, krb5_pa_data * padata,
1605 		  krb5_db_entry *client,
1606 		  krb5_data *req_pkt,
1607 		  krb5_kdc_req *request, krb5_kdc_rep *reply,
1608 		  krb5_key_data *client_key,
1609 		  krb5_keyblock *encrypting_key,
1610 		  krb5_pa_data **send_pa,
1611 		  preauth_get_entry_data_proc etypeget_entry_data,
1612 	          void *pa_system_context,
1613 		  void **pa_request_context)
1614 {
1615     return etype_info_as_rep_helper(context, padata, client, request, reply,
1616 				    client_key, encrypting_key, send_pa, 0);
1617 }
1618 
1619 static krb5_error_code
1620 return_pw_salt(krb5_context context, krb5_pa_data *in_padata,
1621 	       krb5_db_entry *client, krb5_data *req_pkt, krb5_kdc_req *request,
1622 	       krb5_kdc_rep *reply, krb5_key_data *client_key,
1623 	       krb5_keyblock *encrypting_key, krb5_pa_data **send_pa,
1624 	       preauth_get_entry_data_proc etype_get_entry_data,
1625 	       void *pa_system_context,
1626 	       void **pa_request_context)
1627 {
1628     krb5_error_code	retval;
1629     krb5_pa_data *	padata;
1630     krb5_data *		scratch;
1631     krb5_data		salt_data;
1632     int i;
1633 
1634     for (i = 0; i < request->nktypes; i++) {
1635 	if (enctype_requires_etype_info_2(request->ktype[i]))
1636 	    return 0;
1637     }
1638     if (client_key->key_data_ver == 1 ||
1639 	client_key->key_data_type[1] == KRB5_KDB_SALTTYPE_NORMAL)
1640 	return 0;
1641 
1642     if ((padata = malloc(sizeof(krb5_pa_data))) == NULL)
1643 	return ENOMEM;
1644     padata->magic = KV5M_PA_DATA;
1645     padata->pa_type = KRB5_PADATA_PW_SALT;
1646 
1647     switch (client_key->key_data_type[1]) {
1648     case KRB5_KDB_SALTTYPE_V4:
1649 	/* send an empty (V4) salt */
1650 	padata->contents = 0;
1651 	padata->length = 0;
1652 	break;
1653     case KRB5_KDB_SALTTYPE_NOREALM:
1654 	if ((retval = krb5_principal2salt_norealm(kdc_context,
1655 						   request->client,
1656 						   &salt_data)))
1657 	    goto cleanup;
1658 	padata->contents = (krb5_octet *)salt_data.data;
1659 	padata->length = salt_data.length;
1660 	break;
1661     case KRB5_KDB_SALTTYPE_AFS3:
1662 	/* send an AFS style realm-based salt */
1663 	/* for now, just pass the realm back and let the client
1664 	   do the work. In the future, add a kdc configuration
1665 	   variable that specifies the old cell name. */
1666 	padata->pa_type = KRB5_PADATA_AFS3_SALT;
1667 	/* it would be just like ONLYREALM, but we need to pass the 0 */
1668 	scratch = krb5_princ_realm(kdc_context, request->client);
1669 	if ((padata->contents = malloc(scratch->length+1)) == NULL) {
1670 	    retval = ENOMEM;
1671 	    goto cleanup;
1672 	}
1673 	memcpy(padata->contents, scratch->data, scratch->length);
1674 	padata->length = scratch->length+1;
1675 	padata->contents[scratch->length] = 0;
1676 	break;
1677     case KRB5_KDB_SALTTYPE_ONLYREALM:
1678 	scratch = krb5_princ_realm(kdc_context, request->client);
1679 	if ((padata->contents = malloc(scratch->length)) == NULL) {
1680 	    retval = ENOMEM;
1681 	    goto cleanup;
1682 	}
1683 	memcpy(padata->contents, scratch->data, scratch->length);
1684 	padata->length = scratch->length;
1685 	break;
1686     case KRB5_KDB_SALTTYPE_SPECIAL:
1687 	if ((padata->contents = malloc(client_key->key_data_length[1]))
1688 	    == NULL) {
1689 	    retval = ENOMEM;
1690 	    goto cleanup;
1691 	}
1692 	memcpy(padata->contents, client_key->key_data_contents[1],
1693 	       client_key->key_data_length[1]);
1694 	padata->length = client_key->key_data_length[1];
1695 	break;
1696     default:
1697 	free(padata);
1698 	return 0;
1699     }
1700 
1701     *send_pa = padata;
1702     return 0;
1703 
1704 cleanup:
1705     free(padata);
1706     return retval;
1707 }
1708 
1709 static krb5_error_code
1710 return_sam_data(krb5_context context, krb5_pa_data *in_padata,
1711 		krb5_db_entry *client, krb5_data *req_pkt, krb5_kdc_req *request,
1712 		krb5_kdc_rep *reply, krb5_key_data *client_key,
1713 		krb5_keyblock *encrypting_key, krb5_pa_data **send_pa,
1714 		preauth_get_entry_data_proc sam_get_entry_data,
1715 		void *pa_system_context,
1716 		void **pa_request_context)
1717 {
1718     krb5_error_code	retval;
1719     krb5_data		scratch;
1720     int			i;
1721 
1722     krb5_sam_response		*sr = 0;
1723     krb5_predicted_sam_response	*psr = 0;
1724 
1725     if (in_padata == 0)
1726 	return 0;
1727 
1728     /*
1729      * We start by doing the same thing verify_sam_response() does:
1730      * extract the psr from the padata (which is an sr). Nothing
1731      * here should generate errors! We've already successfully done
1732      * all this once.
1733      */
1734 
1735     scratch.data = (char *) in_padata->contents; /* SUNWresync121 XXX */
1736     scratch.length = in_padata->length;
1737 
1738     if ((retval = decode_krb5_sam_response(&scratch, &sr))) {
1739 	com_err("krb5kdc", retval,
1740 		gettext("return_sam_data(): decode_krb5_sam_response failed"));
1741 	goto cleanup;
1742     }
1743 
1744     {
1745 	krb5_enc_data tmpdata;
1746 
1747 	tmpdata.enctype = ENCTYPE_UNKNOWN;
1748 	tmpdata.ciphertext = sr->sam_track_id;
1749 
1750 	scratch.length = tmpdata.ciphertext.length;
1751 	if ((scratch.data = (char *) malloc(scratch.length)) == NULL) {
1752 	    retval = ENOMEM;
1753 	    goto cleanup;
1754 	}
1755 
1756 	if ((retval = krb5_c_decrypt(context, &psr_key, /* XXX */ 0, 0,
1757 				     &tmpdata, &scratch))) {
1758 	    com_err("krb5kdc", retval,
1759 		    gettext("return_sam_data(): decrypt track_id failed"));
1760 	    free(scratch.data);
1761 	    goto cleanup;
1762 	}
1763     }
1764 
1765     if ((retval = decode_krb5_predicted_sam_response(&scratch, &psr))) {
1766 	com_err("krb5kdc", retval,
1767 		gettext(
1768 		"return_sam_data(): decode_krb5_predicted_sam_response failed"));
1769 	free(scratch.data);
1770 	goto cleanup;
1771     }
1772 
1773     /* We could use sr->sam_flags, but it may be absent or altered. */
1774     if (psr->sam_flags & KRB5_SAM_MUST_PK_ENCRYPT_SAD) {
1775 	com_err("krb5kdc", retval = KRB5KDC_ERR_PREAUTH_FAILED,
1776 		gettext("Unsupported SAM flag must-pk-encrypt-sad"));
1777 	goto cleanup;
1778     }
1779     if (psr->sam_flags & KRB5_SAM_SEND_ENCRYPTED_SAD) {
1780 	/* No key munging */
1781 	goto cleanup;
1782     }
1783     if (psr->sam_flags & KRB5_SAM_USE_SAD_AS_KEY) {
1784 	/* Use sam_key instead of client key */
1785 	krb5_free_keyblock_contents(context, encrypting_key);
1786 	krb5_copy_keyblock_contents(context, &psr->sam_key, encrypting_key);
1787 	/* XXX Attach a useful pa_data */
1788 	goto cleanup;
1789     }
1790 
1791     /* Otherwise (no flags set), we XOR the keys */
1792     /* XXX The passwords-04 draft is underspecified here wrt different
1793 	   key types. We will do what I hope to get into the -05 draft. */
1794     {
1795 	krb5_octet *p = encrypting_key->contents;
1796 	krb5_octet *q = psr->sam_key.contents;
1797 	int length = ((encrypting_key->length < psr->sam_key.length)
1798 		      ? encrypting_key->length
1799 		      : psr->sam_key.length);
1800 
1801 	for (i = 0; i < length; i++)
1802 	    p[i] ^= q[i];
1803     }
1804 
1805     /* Post-mixing key correction */
1806     switch (encrypting_key->enctype) {
1807     case ENCTYPE_DES_CBC_CRC:
1808     case ENCTYPE_DES_CBC_MD4:
1809     case ENCTYPE_DES_CBC_MD5:
1810     case ENCTYPE_DES_CBC_RAW:
1811 	mit_des_fixup_key_parity(encrypting_key->contents);
1812 	if (mit_des_is_weak_key(encrypting_key->contents))
1813 	    ((krb5_octet *) encrypting_key->contents)[7] ^= 0xf0;
1814 	break;
1815 
1816     /* XXX case ENCTYPE_DES3_CBC_MD5: listed in 1510bis-04 draft */
1817     case ENCTYPE_DES3_CBC_SHA: /* XXX deprecated? */
1818     case ENCTYPE_DES3_CBC_RAW:
1819     case ENCTYPE_DES3_CBC_SHA1:
1820 	for (i = 0; i < 3; i++) {
1821 	    mit_des_fixup_key_parity(encrypting_key->contents + i * 8);
1822 	    if (mit_des_is_weak_key(encrypting_key->contents + i * 8))
1823 		((krb5_octet *) encrypting_key->contents)[7 + i * 8] ^= 0xf0;
1824 	}
1825 	break;
1826 
1827     default:
1828 	com_err("krb5kdc", retval = KRB5KDC_ERR_PREAUTH_FAILED,
1829 		gettext("Unimplemented keytype for SAM key mixing"));
1830 	goto cleanup;
1831     }
1832 
1833     /* XXX Attach a useful pa_data */
1834 cleanup:
1835     if (sr)
1836 	krb5_free_sam_response(context, sr);
1837     if (psr)
1838 	krb5_free_predicted_sam_response(context, psr);
1839 
1840     return retval;
1841 }
1842 
1843 static struct {
1844   char* name;
1845   int   sam_type;
1846 } *sam_ptr, sam_inst_map[] = {
1847 #if 0	/* SUNWresync121 - unsupported hardware and kdc.log annoyance */
1848   { "SNK4", PA_SAM_TYPE_DIGI_PATH, },
1849   { "SECURID", PA_SAM_TYPE_SECURID, },
1850   { "GRAIL", PA_SAM_TYPE_GRAIL, },
1851 #endif
1852   { 0, 0 },
1853 };
1854 
1855 static krb5_error_code
1856 get_sam_edata(krb5_context context, krb5_kdc_req *request,
1857 	      krb5_db_entry *client, krb5_db_entry *server,
1858 	      preauth_get_entry_data_proc sam_get_entry_data,
1859 	      void *pa_system_context, krb5_pa_data *pa_data)
1860 {
1861     krb5_error_code		retval;
1862     krb5_sam_challenge		sc;
1863     krb5_predicted_sam_response	psr;
1864     krb5_data *			scratch;
1865     krb5_keyblock encrypting_key;
1866     char response[9];
1867     char inputblock[8];
1868     krb5_data predict_response;
1869 
1870     (void) memset(&encrypting_key, 0, sizeof(krb5_keyblock));
1871     (void) memset(&sc, 0, sizeof(sc));
1872     (void) memset(&psr, 0, sizeof(psr));
1873 
1874     /* Given the client name we can figure out what type of preauth
1875        they need. The spec is currently for querying the database for
1876        names that match the types of preauth used. Later we should
1877        make this mapping show up in kdc.conf. In the meantime, we
1878        hardcode the following:
1879 		/SNK4 -- Digital Pathways SNK/4 preauth.
1880 		/GRAIL -- experimental preauth
1881        The first one found is used. See sam_inst_map above.
1882 
1883        For SNK4 in particular, the key in the database is the key for
1884        the device; kadmin needs a special interface for it.
1885      */
1886 
1887     {
1888       int npr = 1;
1889       krb5_boolean more;
1890       krb5_db_entry assoc;
1891       krb5_key_data  *assoc_key;
1892       krb5_principal newp;
1893       int probeslot;
1894 
1895       sc.sam_type = 0;
1896 
1897       retval = krb5_copy_principal(kdc_context, request->client, &newp);
1898       if (retval) {
1899 	com_err(gettext("krb5kdc"),
1900 		retval,
1901 		gettext("copying client name for preauth probe"));
1902 	return retval;
1903       }
1904 
1905       probeslot = krb5_princ_size(context, newp)++;
1906       krb5_princ_name(kdc_context, newp) =
1907 	realloc(krb5_princ_name(kdc_context, newp),
1908 		krb5_princ_size(context, newp) * sizeof(krb5_data));
1909 
1910       for(sam_ptr = sam_inst_map; sam_ptr->name; sam_ptr++) {
1911 	krb5_princ_component(kdc_context,newp,probeslot)->data = sam_ptr->name;
1912 	krb5_princ_component(kdc_context,newp,probeslot)->length =
1913 	  strlen(sam_ptr->name);
1914 	npr = 1;
1915 	retval = krb5_db_get_principal(kdc_context, newp, &assoc, &npr, (uint *)&more);
1916 	if(!retval && npr) {
1917 	  sc.sam_type = sam_ptr->sam_type;
1918 	  break;
1919 	}
1920       }
1921 
1922       krb5_princ_component(kdc_context,newp,probeslot)->data = 0;
1923       krb5_princ_component(kdc_context,newp,probeslot)->length = 0;
1924       krb5_princ_size(context, newp)--;
1925 
1926       krb5_free_principal(kdc_context, newp);
1927 
1928       /* if sc.sam_type is set, it worked */
1929       if (sc.sam_type) {
1930 	/* so use assoc to get the key out! */
1931 	{
1932 	  /* here's what do_tgs_req does */
1933 	  retval = krb5_dbe_find_enctype(kdc_context, &assoc,
1934 					 ENCTYPE_DES_CBC_RAW,
1935 					 KRB5_KDB_SALTTYPE_NORMAL,
1936 					 0,		/* Get highest kvno */
1937 					 &assoc_key);
1938 	  if (retval) {
1939 	    char *sname;
1940 	    krb5_unparse_name(kdc_context, request->client, &sname);
1941 	    com_err(gettext("krb5kdc"),
1942 		    retval,
1943 		    gettext("snk4 finding the enctype and key <%s>"),
1944 		    sname);
1945 	    free(sname);
1946 	    return retval;
1947 	  }
1948 	  /* convert server.key into a real key */
1949 	  retval = krb5_dbekd_decrypt_key_data(kdc_context,
1950 					       &master_keyblock,
1951 					       assoc_key, &encrypting_key,
1952 					       NULL);
1953 	  if (retval) {
1954 	    com_err(gettext("krb5kdc"),
1955 		    retval,
1956 		   gettext("snk4 pulling out key entry"));
1957 	    return retval;
1958 	  }
1959 	  /* now we can use encrypting_key... */
1960 	}
1961       } else {
1962 	/* SAM is not an option - so don't return as hint */
1963 	return KRB5_PREAUTH_BAD_TYPE;
1964       }
1965     }
1966     sc.magic = KV5M_SAM_CHALLENGE;
1967     psr.sam_flags = sc.sam_flags = KRB5_SAM_USE_SAD_AS_KEY;
1968 
1969     /* Replay prevention */
1970     if ((retval = krb5_copy_principal(context, request->client, &psr.client)))
1971 	return retval;
1972 #ifdef USE_RCACHE
1973     if ((retval = krb5_us_timeofday(context, &psr.stime, &psr.susec)))
1974 	return retval;
1975 #endif /* USE_RCACHE */
1976 
1977     switch (sc.sam_type) {
1978     case PA_SAM_TYPE_GRAIL:
1979       sc.sam_type_name.data = "Experimental System";
1980       sc.sam_type_name.length = strlen(sc.sam_type_name.data);
1981       sc.sam_challenge_label.data = "experimental challenge label";
1982       sc.sam_challenge_label.length = strlen(sc.sam_challenge_label.data);
1983       sc.sam_challenge.data = "12345";
1984       sc.sam_challenge.length = strlen(sc.sam_challenge.data);
1985 
1986 #if 0 /* Enable this to test "normal" (no flags set) mode.  */
1987       psr.sam_flags = sc.sam_flags = 0;
1988 #endif
1989 
1990       psr.magic = KV5M_PREDICTED_SAM_RESPONSE;
1991       /* string2key on sc.sam_challenge goes in here */
1992       /* eblock is just to set the enctype */
1993       {
1994 	const krb5_enctype type = ENCTYPE_DES_CBC_MD5;
1995 
1996 	if ((retval = krb5_c_string_to_key(context, type, &sc.sam_challenge,
1997 					   0 /* salt */, &psr.sam_key)))
1998 	    goto cleanup;
1999 
2000 	if ((retval = encode_krb5_predicted_sam_response(&psr, &scratch)))
2001 	    goto cleanup;
2002 
2003 	{
2004 	    size_t enclen;
2005 	    krb5_enc_data tmpdata;
2006 
2007 	    if ((retval = krb5_c_encrypt_length(context,
2008 						psr_key.enctype,
2009 						scratch->length, &enclen)))
2010 		goto cleanup;
2011 
2012 	    if ((tmpdata.ciphertext.data = (char *) malloc(enclen)) == NULL) {
2013 		retval = ENOMEM;
2014 		goto cleanup;
2015 	    }
2016 	    tmpdata.ciphertext.length = enclen;
2017 
2018 	    if ((retval = krb5_c_encrypt(context, &psr_key,
2019 					 /* XXX */ 0, 0, scratch, &tmpdata)))
2020 		goto cleanup;
2021 
2022 	    sc.sam_track_id = tmpdata.ciphertext;
2023 	}
2024       }
2025 
2026       sc.sam_response_prompt.data = "response prompt";
2027       sc.sam_response_prompt.length = strlen(sc.sam_response_prompt.data);
2028       sc.sam_pk_for_sad.length = 0;
2029       sc.sam_nonce = 0;
2030       /* Generate checksum */
2031       /*krb5_checksum_size(context, ctype)*/
2032       /*krb5_calculate_checksum(context,ctype,in,in_length,seed,
2033 	seed_length,outcksum) */
2034       /*krb5_verify_checksum(context,ctype,cksum,in,in_length,seed,
2035 	seed_length) */
2036 #if 0 /* XXX a) glue appears broken; b) this gives up the SAD */
2037       sc.sam_cksum.contents = (krb5_octet *)
2038 	malloc(krb5_checksum_size(context, CKSUMTYPE_RSA_MD5_DES));
2039       if (sc.sam_cksum.contents == NULL) return(ENOMEM);
2040 
2041       retval = krb5_calculate_checksum(context, CKSUMTYPE_RSA_MD5_DES,
2042 				       sc.sam_challenge.data,
2043 				       sc.sam_challenge.length,
2044 				       psr.sam_key.contents, /* key */
2045 				       psr.sam_key.length, /* key length */
2046 				       &sc.sam_cksum);
2047       if (retval) { free(sc.sam_cksum.contents); return(retval); }
2048 #endif /* 0 */
2049 
2050       retval = encode_krb5_sam_challenge(&sc, &scratch);
2051       if (retval) goto cleanup;
2052       pa_data->magic = KV5M_PA_DATA;
2053       pa_data->pa_type = KRB5_PADATA_SAM_CHALLENGE;
2054       pa_data->contents = (unsigned char *) scratch->data;
2055       pa_data->length = scratch->length;
2056 
2057       retval = 0;
2058       break;
2059     case PA_SAM_TYPE_DIGI_PATH:
2060       sc.sam_type_name.data = "Digital Pathways";
2061       sc.sam_type_name.length = strlen(sc.sam_type_name.data);
2062 #if 1
2063       sc.sam_challenge_label.data = "Enter the following on your keypad";
2064       sc.sam_challenge_label.length = strlen(sc.sam_challenge_label.data);
2065 #endif
2066       /* generate digit string, take it mod 1000000 (six digits.) */
2067       {
2068 	int j;
2069 	krb5_keyblock session_key;
2070 	char outputblock[8];
2071 	int i;
2072 
2073 	(void) memset(&session_key, 0, sizeof(krb5_keyblock));
2074 
2075 	(void) memset(inputblock, 0, 8);
2076 
2077 	retval = krb5_c_make_random_key(kdc_context, ENCTYPE_DES_CBC_CRC,
2078 					&session_key);
2079 
2080 	if (retval) {
2081 	  /* random key failed */
2082 	  com_err(gettext("krb5kdc"),
2083 		 retval,
2084 		gettext("generating random challenge for preauth"));
2085 	  return retval;
2086 	}
2087 	/* now session_key has a key which we can pick bits out of */
2088 	/* we need six decimal digits. Grab 6 bytes, div 2, mod 10 each. */
2089 	if (session_key.length != 8) {
2090 		 retval = KRB5KDC_ERR_ETYPE_NOSUPP,
2091 	  com_err(gettext("krb5kdc"),
2092 		 retval = KRB5KDC_ERR_ETYPE_NOSUPP,
2093 		 gettext("keytype didn't match code expectations"));
2094 	  return retval;
2095 	}
2096 	for(i = 0; i<6; i++) {
2097 	  inputblock[i] = '0' + ((session_key.contents[i]/2) % 10);
2098 	}
2099 	if (session_key.contents)
2100 	  krb5_free_keyblock_contents(kdc_context, &session_key);
2101 
2102 	/* retval = krb5_finish_key(kdc_context, &eblock); */
2103 	/* now we have inputblock containing the 8 byte input to DES... */
2104 	sc.sam_challenge.data = inputblock;
2105 	sc.sam_challenge.length = 6;
2106 
2107 	encrypting_key.enctype = ENCTYPE_DES_CBC_RAW;
2108 
2109 	if (retval) {
2110 	  com_err(gettext("krb5kdc"),
2111 		 retval,
2112 		gettext("snk4 processing key"));
2113 	}
2114 
2115 	{
2116 	    krb5_data plain;
2117 	    krb5_enc_data cipher;
2118 
2119 	    plain.length = 8;
2120 	    plain.data = inputblock;
2121 
2122 	    /* XXX I know this is enough because of the fixed raw enctype.
2123 	       if it's not, the underlying code will return a reasonable
2124 	       error, which should never happen */
2125 	    cipher.ciphertext.length = 8;
2126 	    cipher.ciphertext.data = outputblock;
2127 
2128 	    if ((retval = krb5_c_encrypt(kdc_context, &encrypting_key,
2129 					 /* XXX */ 0, 0, &plain, &cipher))) {
2130 		com_err(gettext("krb5kdc"),
2131 		retval,
2132 		gettext("snk4 response generation failed"));
2133 		return retval;
2134 	    }
2135 	}
2136 
2137 	/* now output block is the raw bits of the response; convert it
2138 	   to display form */
2139 	for (j=0; j<4; j++) {
2140 	  char n[2];
2141 	  int k;
2142 	  n[0] = outputblock[j] & 0xf;
2143 	  n[1] = (outputblock[j]>>4) & 0xf;
2144 	  for (k=0; k<2; k++) {
2145 	    if(n[k] > 9) n[k] = ((n[k]-1)>>2);
2146 	    /* This is equivalent to:
2147 	       if(n[k]>=0xa && n[k]<=0xc) n[k] = 2;
2148 	       if(n[k]>=0xd && n[k]<=0xf) n[k] = 3;
2149 	       */
2150 	  }
2151 	  /* for v4, we keygen: *(j+(char*)&key1) = (n[1]<<4) | n[0]; */
2152 	  /* for v5, we just generate a string */
2153 	  response[2*j+0] = '0' + n[1];
2154 	  response[2*j+1] = '0' + n[0];
2155 	  /* and now, response has what we work with. */
2156 	}
2157 	response[8] = 0;
2158 	predict_response.data = response;
2159 	predict_response.length = 8;
2160 #if 0				/* for debugging, hack the output too! */
2161 sc.sam_challenge_label.data = response;
2162 sc.sam_challenge_label.length = strlen(sc.sam_challenge_label.data);
2163 #endif
2164       }
2165 
2166       psr.magic = KV5M_PREDICTED_SAM_RESPONSE;
2167       /* string2key on sc.sam_challenge goes in here */
2168       /* eblock is just to set the enctype */
2169       {
2170 	retval = krb5_c_string_to_key(context, ENCTYPE_DES_CBC_MD5,
2171 				      &predict_response, 0 /* salt */,
2172 				      &psr.sam_key);
2173 	if (retval) goto cleanup;
2174 
2175 	retval = encode_krb5_predicted_sam_response(&psr, &scratch);
2176 	if (retval) goto cleanup;
2177 
2178 	{
2179 	    size_t enclen;
2180 	    krb5_enc_data tmpdata;
2181 
2182 	    if ((retval = krb5_c_encrypt_length(context,
2183 						psr_key.enctype,
2184 						scratch->length, &enclen)))
2185 		goto cleanup;
2186 
2187 	    if ((tmpdata.ciphertext.data = (char *) malloc(enclen)) == NULL) {
2188 		retval = ENOMEM;
2189 		goto cleanup;
2190 	    }
2191 	    tmpdata.ciphertext.length = enclen;
2192 
2193 	    if ((retval = krb5_c_encrypt(context, &psr_key,
2194 					 /* XXX */ 0, 0, scratch, &tmpdata)))
2195 		goto cleanup;
2196 
2197 	    sc.sam_track_id = tmpdata.ciphertext;
2198 	}
2199 	if (retval) goto cleanup;
2200       }
2201 
2202       sc.sam_response_prompt.data = "Enter the displayed response";
2203       sc.sam_response_prompt.length = strlen(sc.sam_response_prompt.data);
2204       sc.sam_pk_for_sad.length = 0;
2205       sc.sam_nonce = 0;
2206       /* Generate checksum */
2207       /*krb5_checksum_size(context, ctype)*/
2208       /*krb5_calculate_checksum(context,ctype,in,in_length,seed,
2209 	seed_length,outcksum) */
2210       /*krb5_verify_checksum(context,ctype,cksum,in,in_length,seed,
2211 	seed_length) */
2212 #if 0 /* XXX a) glue appears broken; b) this gives up the SAD */
2213       sc.sam_cksum.contents = (krb5_octet *)
2214 	malloc(krb5_checksum_size(context, CKSUMTYPE_RSA_MD5_DES));
2215       if (sc.sam_cksum.contents == NULL) return(ENOMEM);
2216 
2217       retval = krb5_calculate_checksum(context, CKSUMTYPE_RSA_MD5_DES,
2218 				       sc.sam_challenge.data,
2219 				       sc.sam_challenge.length,
2220 				       psr.sam_key.contents, /* key */
2221 				       psr.sam_key.length, /* key length */
2222 				       &sc.sam_cksum);
2223       if (retval) { free(sc.sam_cksum.contents); return(retval); }
2224 #endif /* 0 */
2225 
2226       retval = encode_krb5_sam_challenge(&sc, &scratch);
2227       if (retval) goto cleanup;
2228       pa_data->magic = KV5M_PA_DATA;
2229       pa_data->pa_type = KRB5_PADATA_SAM_CHALLENGE;
2230       pa_data->contents = (unsigned char *) scratch->data;
2231       pa_data->length = scratch->length;
2232 
2233       retval = 0;
2234       break;
2235     }
2236 
2237 cleanup:
2238     krb5_free_keyblock_contents(context, &encrypting_key);
2239     return retval;
2240 }
2241 
2242 static krb5_error_code
2243 verify_sam_response(krb5_context context, krb5_db_entry *client,
2244 		    krb5_data *req_pkt,
2245 		    krb5_kdc_req *request, krb5_enc_tkt_part *enc_tkt_reply,
2246 		    krb5_pa_data *pa,
2247 		    preauth_get_entry_data_proc sam_get_entry_data,
2248 		    void *pa_system_context,
2249 		    void **pa_request_context,
2250 		    krb5_data **e_data,
2251 		    krb5_authdata ***authz_data)
2252 {
2253     krb5_error_code		retval;
2254     krb5_data			scratch;
2255     krb5_sam_response		*sr = 0;
2256     krb5_predicted_sam_response	*psr = 0;
2257     krb5_enc_sam_response_enc	*esre = 0;
2258     krb5_timestamp		timenow;
2259     char			*princ_req = 0, *princ_psr = 0;
2260 
2261     scratch.data = (char *) pa->contents;
2262     scratch.length = pa->length;
2263 
2264     if ((retval = decode_krb5_sam_response(&scratch, &sr))) {
2265 	scratch.data = 0;
2266 	com_err("krb5kdc", retval, gettext("decode_krb5_sam_response failed"));
2267 	goto cleanup;
2268     }
2269 
2270     /* XXX We can only handle the challenge/response model of SAM.
2271 	   See passwords-04, par 4.1, 4.2 */
2272     {
2273       krb5_enc_data tmpdata;
2274 
2275       tmpdata.enctype = ENCTYPE_UNKNOWN;
2276       tmpdata.ciphertext = sr->sam_track_id;
2277 
2278       scratch.length = tmpdata.ciphertext.length;
2279       if ((scratch.data = (char *) malloc(scratch.length)) == NULL) {
2280 	  retval = ENOMEM;
2281 	  goto cleanup;
2282       }
2283 
2284       if ((retval = krb5_c_decrypt(context, &psr_key, /* XXX */ 0, 0,
2285 				   &tmpdata, &scratch))) {
2286 	  com_err(gettext("krb5kdc"),
2287 		retval,
2288 		gettext("decrypt track_id failed"));
2289 	  goto cleanup;
2290       }
2291     }
2292 
2293     if ((retval = decode_krb5_predicted_sam_response(&scratch, &psr))) {
2294 	com_err(gettext("krb5kdc"), retval,
2295 		gettext("decode_krb5_predicted_sam_response failed -- replay attack?"));
2296 	goto cleanup;
2297     }
2298 
2299     /* Replay detection */
2300     if ((retval = krb5_unparse_name(context, request->client, &princ_req)))
2301 	goto cleanup;
2302     if ((retval = krb5_unparse_name(context, psr->client, &princ_psr)))
2303 	goto cleanup;
2304     if (strcmp(princ_req, princ_psr) != 0) {
2305 	com_err("krb5kdc", retval = KRB5KDC_ERR_PREAUTH_FAILED,
2306 		gettext("Principal mismatch in SAM psr! -- replay attack?"));
2307 	goto cleanup;
2308     }
2309 
2310     if ((retval = krb5_timeofday(context, &timenow)))
2311 	goto cleanup;
2312 
2313 #ifdef USE_RCACHE
2314     {
2315 	krb5_donot_replay rep;
2316 	extern krb5_deltat rc_lifetime;
2317 	/*
2318 	 * Verify this response came back in a timely manner.
2319 	 * We do this b/c otherwise very old (expunged from the rcache)
2320 	 * psr's would be able to be replayed.
2321 	 */
2322 	if (timenow - psr->stime > rc_lifetime) {
2323 	    com_err("krb5kdc", retval = KRB5KDC_ERR_PREAUTH_FAILED,
2324 	    gettext("SAM psr came back too late! -- replay attack?"));
2325 	    goto cleanup;
2326 	}
2327 
2328 	/* Now check the replay cache. */
2329 	rep.client = princ_psr;
2330 	rep.server = "SAM/rc";  /* Should not match any principal name. */
2331 	rep.ctime = psr->stime;
2332 	rep.cusec = psr->susec;
2333 	retval = krb5_rc_store(kdc_context, kdc_rcache, &rep);
2334 	if (retval) {
2335 	    com_err("krb5kdc", retval, gettext("SAM psr replay attack!"));
2336 	    goto cleanup;
2337 	}
2338     }
2339 #endif /* USE_RCACHE */
2340 
2341 
2342     {
2343 	free(scratch.data);
2344 	scratch.length = sr->sam_enc_nonce_or_ts.ciphertext.length;
2345 	if ((scratch.data = (char *) malloc(scratch.length)) == NULL) {
2346 	    retval = ENOMEM;
2347 	    goto cleanup;
2348 	}
2349 
2350 	if ((retval = krb5_c_decrypt(context, &psr->sam_key, /* XXX */ 0,
2351 				     0, &sr->sam_enc_nonce_or_ts, &scratch))) {
2352 	    com_err("krb5kdc", retval, gettext("decrypt nonce_or_ts failed"));
2353 	    goto cleanup;
2354 	}
2355     }
2356 
2357     if ((retval = decode_krb5_enc_sam_response_enc(&scratch, &esre))) {
2358 	com_err("krb5kdc", retval, gettext("decode_krb5_enc_sam_response_enc failed"));
2359 	goto cleanup;
2360     }
2361 
2362     if (esre->sam_timestamp != sr->sam_patimestamp) {
2363       retval = KRB5KDC_ERR_PREAUTH_FAILED;
2364       goto cleanup;
2365     }
2366 
2367     if (labs(timenow - sr->sam_patimestamp) > context->clockskew) {
2368 	retval = KRB5KRB_AP_ERR_SKEW;
2369 	goto cleanup;
2370     }
2371 
2372     setflag(enc_tkt_reply->flags, TKT_FLG_HW_AUTH);
2373 
2374   cleanup:
2375     if (retval) com_err(gettext("krb5kdc"),
2376 			retval,
2377 			gettext("sam verify failure"));
2378     if (scratch.data) free(scratch.data);
2379     if (sr) free(sr);
2380     if (psr) free(psr);
2381     if (esre) free(esre);
2382     if (princ_psr) free(princ_psr);
2383     if (princ_req) free(princ_req);
2384 
2385     return retval;
2386 }
2387