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
75static const char *objdirs[] = { KRB5_PLUGIN_BUNDLE_DIR, LIBDIR "/krb5/plugins/preauth", NULL }; /* should be a list */
76#else
77static 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
83typedef unsigned char des_cblock[8];	/* crypto-block size */
84#endif
85typedef des_cblock mit_des_cblock;
86extern void mit_des_fixup_key_parity (mit_des_cblock );
87extern int mit_des_is_weak_key (mit_des_cblock );
88
89typedef 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
102static 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
113static 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);
119static krb5_error_code
120get_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);
125static krb5_error_code
126etype_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
134static krb5_error_code
135return_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
146static krb5_error_code
147return_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
158static 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 */
171static 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
182static 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);
188static 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
200static 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
270static krb5_preauth_systems *preauth_systems;
271static int n_preauth_systems;
272static struct plugin_dir_handle preauth_plugins;
273
274krb5_error_code
275load_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
433krb5_error_code
434unload_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 */
458struct request_pa_context {
459    int n_contexts;
460    struct {
461	krb5_preauth_systems *pa_system;
462	void *pa_context;
463    } *contexts;
464};
465
466static krb5_error_code
467make_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 */
501krb5_error_code
502free_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. */
534static krb5_error_code
535get_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 */
567static krb5_error_code
568get_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
649static krb5_error_code
650find_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
663static krb5_error_code
664find_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 */
689static krb5_boolean
690pa_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}
699static void
700sort_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
770const 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
807void 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
862errout:
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 */
871static krb5_error_code
872add_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
924krb5_error_code
925check_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 */
1100krb5_error_code
1101return_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
1193cleanup:
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
1204static krb5_boolean
1205enctype_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
1223static krb5_boolean
1224request_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
1235static krb5_error_code
1236verify_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
1308cleanup:
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
1327static 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
1379fail:
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 */
1395static krb5_error_code
1396etype_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
1476cleanup:
1477    if (entry)
1478	krb5_free_etype_info(context, entry);
1479    return retval;
1480}
1481
1482static krb5_error_code
1483get_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
1499static krb5_error_code
1500get_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
1509static krb5_error_code
1510etype_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
1586static krb5_error_code
1587return_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
1603static krb5_error_code
1604return_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
1619static krb5_error_code
1620return_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
1704cleanup:
1705    free(padata);
1706    return retval;
1707}
1708
1709static krb5_error_code
1710return_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 */
1834cleanup:
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
1843static 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
1855static krb5_error_code
1856get_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! */
2161sc.sam_challenge_label.data = response;
2162sc.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
2237cleanup:
2238    krb5_free_keyblock_contents(context, &encrypting_key);
2239    return retval;
2240}
2241
2242static krb5_error_code
2243verify_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