xref: /titanic_44/usr/src/cmd/krb5/krb5kdc/kdc_preauth.c (revision 9df12a23948bd40cbe37ce88d84e272c3894e675)
1 /*
2  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
3  * Use is subject to license terms.
4  */
5 
6 #pragma ident	"%Z%%M%	%I%	%E% SMI"
7 
8 /*
9  * kdc/kdc_preauth.c
10  *
11  * Copyright 1995 by the Massachusetts Institute of Technology.
12  * All Rights Reserved.
13  *
14  * Export of this software from the United States of America may
15  *   require a specific license from the United States Government.
16  *   It is the responsibility of any person or organization contemplating
17  *   export to obtain such a license before exporting.
18  *
19  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
20  * distribute this software and its documentation for any purpose and
21  * without fee is hereby granted, provided that the above copyright
22  * notice appear in all copies and that both that copyright notice and
23  * this permission notice appear in supporting documentation, and that
24  * the name of M.I.T. not be used in advertising or publicity pertaining
25  * to distribution of the software without specific, written prior
26  * permission.  Furthermore if you modify this software you must label
27  * your software as modified software and not distribute it in such a
28  * fashion that it might be confused with the original M.I.T. software.
29  * M.I.T. makes no representations about the suitability of
30  * this software for any purpose.  It is provided "as is" without express
31  * or implied warranty.
32  *
33  * Preauthentication routines for the KDC.
34  */
35 
36 /*
37  * Copyright (C) 1998 by the FundsXpress, INC.
38  *
39  * All rights reserved.
40  *
41  * Export of this software from the United States of America may require
42  * a specific license from the United States Government.  It is the
43  * responsibility of any person or organization contemplating export to
44  * obtain such a license before exporting.
45  *
46  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
47  * distribute this software and its documentation for any purpose and
48  * without fee is hereby granted, provided that the above copyright
49  * notice appear in all copies and that both that copyright notice and
50  * this permission notice appear in supporting documentation, and that
51  * the name of FundsXpress. not be used in advertising or publicity pertaining
52  * to distribution of the software without specific, written prior
53  * permission.  FundsXpress makes no representations about the suitability of
54  * this software for any purpose.  It is provided "as is" without express
55  * or implied warranty.
56  *
57  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
58  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
59  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
60  */
61 
62 #include "k5-int.h"
63 #include "kdc_util.h"
64 #include "extern.h"
65 #include "com_err.h"
66 #include <assert.h>
67 #include <stdio.h>
68 #include <libintl.h>
69 #include <syslog.h>
70 
71 typedef krb5_error_code (*verify_proc)
72     (krb5_context, krb5_db_entry *client,
73 	    krb5_kdc_req *request,
74 	    krb5_enc_tkt_part * enc_tkt_reply, krb5_pa_data *data);
75 
76 typedef krb5_error_code (*edata_proc)
77     (krb5_context, krb5_kdc_req *request,
78 	    krb5_db_entry *client, krb5_db_entry *server,
79 	    krb5_pa_data *data);
80 
81 typedef krb5_error_code (*return_proc)
82     (krb5_context, krb5_pa_data * padata,
83 	    krb5_db_entry *client,
84 	    krb5_kdc_req *request, krb5_kdc_rep *reply,
85 	    krb5_key_data *client_key,
86 	    krb5_keyblock *encrypting_key,
87 	    krb5_pa_data **send_pa);
88 
89 typedef struct _krb5_preauth_systems {
90     char *	name;
91     int		type;
92     int		flags;
93     edata_proc	get_edata;
94     verify_proc	verify_padata;
95     return_proc return_padata;
96 } krb5_preauth_systems;
97 
98 static krb5_error_code verify_enc_timestamp
99     (krb5_context, krb5_db_entry *client,
100     krb5_kdc_req *request,
101     krb5_enc_tkt_part * enc_tkt_reply, krb5_pa_data *data);
102 
103 static krb5_error_code get_etype_info
104     (krb5_context, krb5_kdc_req *request,
105     krb5_db_entry *client, krb5_db_entry *server,
106     krb5_pa_data *data);
107 
108 static krb5_error_code
109 get_etype_info2(krb5_context context, krb5_kdc_req *request,
110 		krb5_db_entry *client, krb5_db_entry *server,
111 		  krb5_pa_data *pa_data);
112 
113 static krb5_error_code
114 return_etype_info2(krb5_context, krb5_pa_data * padata,
115                    krb5_db_entry *client,
116                    krb5_kdc_req *request, krb5_kdc_rep *reply,
117                    krb5_key_data *client_key,
118                    krb5_keyblock *encrypting_key,
119                    krb5_pa_data **send_pa);
120 
121 
122 static krb5_error_code return_pw_salt
123     (krb5_context, krb5_pa_data * padata,
124     krb5_db_entry *client,
125     krb5_kdc_req *request, krb5_kdc_rep *reply,
126     krb5_key_data *client_key,
127     krb5_keyblock *encrypting_key,
128     krb5_pa_data **send_pa);
129 
130 /* SAM preauth support */
131 static krb5_error_code verify_sam_response
132 	(krb5_context, krb5_db_entry *client,
133 	    krb5_kdc_req *request,
134 	    krb5_enc_tkt_part * enc_tkt_reply, krb5_pa_data *data);
135 
136 static krb5_error_code get_sam_edata
137     (krb5_context, krb5_kdc_req *request,
138     krb5_db_entry *client, krb5_db_entry *server,
139     krb5_pa_data *data);
140 
141 static krb5_error_code return_sam_data
142     (krb5_context, krb5_pa_data * padata,
143     krb5_db_entry *client,
144     krb5_kdc_req *request, krb5_kdc_rep *reply,
145     krb5_key_data *client_key,
146     krb5_keyblock *encrypting_key,
147     krb5_pa_data **send_pa);
148 /*
149  * Preauth property flags
150  */
151 #define PA_HARDWARE	0x00000001
152 #define PA_REQUIRED	0x00000002
153 #define PA_SUFFICIENT	0x00000004
154 	/* Not really a padata, so don't include it in the etype list*/
155 #define PA_PSEUDO	0x00000008
156 
157 static krb5_preauth_systems preauth_systems[] = {
158     {
159 	"timestamp",
160         KRB5_PADATA_ENC_TIMESTAMP,
161         0,
162         0,
163 	verify_enc_timestamp,
164 	0
165     },
166     {
167 	"etype-info",
168 	KRB5_PADATA_ETYPE_INFO,
169 	0,
170 	get_etype_info,
171 	0,
172 	0
173     },
174     {
175      	"etype-info2",
176 	KRB5_PADATA_ETYPE_INFO2,
177 	0,
178         get_etype_info2,
179 	0,
180         return_etype_info2
181     },
182     {
183 	"pw-salt",
184 	KRB5_PADATA_PW_SALT,
185 	PA_PSEUDO,		/* Don't include this in the error list */
186 	0,
187 	0,
188 	return_pw_salt
189     },
190     {
191 	"sam-response",
192 	KRB5_PADATA_SAM_RESPONSE,
193 	0,
194 	0,
195 	verify_sam_response,
196 	return_sam_data
197     },
198     {
199 	"sam-challenge",
200 	KRB5_PADATA_SAM_CHALLENGE,
201 	PA_HARDWARE,		/* causes get_preauth_hint_list to use this */
202 	get_sam_edata,
203 	0,
204 	0
205     },
206     { "[end]", -1,}
207 };
208 
209 #define MAX_PREAUTH_SYSTEMS (sizeof(preauth_systems)/sizeof(preauth_systems[0]))
210 
211 static krb5_error_code
212 find_pa_system(int type, krb5_preauth_systems **preauth)
213 {
214     krb5_preauth_systems 	*ap = preauth_systems;
215 
216     while ((ap->type != -1) && (ap->type != type))
217 	ap++;
218     if (ap->type == -1)
219 	return(KRB5_PREAUTH_BAD_TYPE);
220     *preauth = ap;
221     return 0;
222 }
223 
224 const char *missing_required_preauth(client, server, enc_tkt_reply)
225     krb5_db_entry *client, *server;
226     krb5_enc_tkt_part *enc_tkt_reply;
227 {
228 #if 0
229     /*
230      * If this is the pwchange service, and the pre-auth bit is set,
231      * allow it even if the HW preauth would normally be required.
232      *
233      * Sandia national labs wanted this for some strange reason... we
234      * leave it disabled normally.
235      */
236     if (isflagset(server->attributes, KRB5_KDB_PWCHANGE_SERVICE) &&
237 	isflagset(enc_tkt_reply->flags, TKT_FLG_PRE_AUTH))
238 	return 0;
239 #endif
240 
241 #ifdef DEBUG
242     krb5_klog_syslog (LOG_DEBUG,
243 		      "client needs %spreauth, %shw preauth; request has %spreauth, %shw preauth",
244 		      isflagset (client->attributes, KRB5_KDB_REQUIRES_PRE_AUTH) ? "" : "no ",
245 		      isflagset (client->attributes, KRB5_KDB_REQUIRES_HW_AUTH) ? "" : "no ",
246 		      isflagset (enc_tkt_reply->flags, TKT_FLG_PRE_AUTH) ? "" : "no ",
247 		      isflagset (enc_tkt_reply->flags, TKT_FLG_HW_AUTH) ? "" : "no ");
248 #endif
249 
250     if (isflagset(client->attributes, KRB5_KDB_REQUIRES_PRE_AUTH) &&
251 	 !isflagset(enc_tkt_reply->flags, TKT_FLG_PRE_AUTH))
252 	return "NEEDED_PREAUTH";
253 
254     if (isflagset(client->attributes, KRB5_KDB_REQUIRES_HW_AUTH) &&
255 	!isflagset(enc_tkt_reply->flags, TKT_FLG_HW_AUTH))
256 	return "NEEDED_HW_PREAUTH";
257 
258     return 0;
259 }
260 
261 void get_preauth_hint_list(
262     krb5_kdc_req *request,
263     krb5_db_entry *client,
264     krb5_db_entry *server,
265     krb5_data *e_data)
266 {
267     int hw_only;
268     krb5_preauth_systems *ap;
269     krb5_pa_data **pa_data, **pa;
270     krb5_data *edat;
271     krb5_error_code retval;
272 
273     /* Zero these out in case we need to abort */
274     e_data->length = 0;
275     e_data->data = 0;
276 
277     hw_only = isflagset(client->attributes, KRB5_KDB_REQUIRES_HW_AUTH);
278     pa_data = malloc(sizeof(krb5_pa_data *) * (MAX_PREAUTH_SYSTEMS+1));
279     if (pa_data == 0)
280 	return;
281     memset(pa_data, 0, sizeof(krb5_pa_data *) * (MAX_PREAUTH_SYSTEMS+1));
282     pa = pa_data;
283 
284     for (ap = preauth_systems; ap->type != -1; ap++) {
285 	if (hw_only && !(ap->flags & PA_HARDWARE))
286 	    continue;
287 	if (ap->flags & PA_PSEUDO)
288 	    continue;
289 	*pa = malloc(sizeof(krb5_pa_data));
290 	if (*pa == 0)
291 	    goto errout;
292 	memset(*pa, 0, sizeof(krb5_pa_data));
293 	(*pa)->magic = KV5M_PA_DATA;
294 	(*pa)->pa_type = ap->type;
295 	if (ap->get_edata) {
296 	  retval = (ap->get_edata)(kdc_context, request, client, server, *pa);
297 	  if (retval) {
298 	    /* just failed on this type, continue */
299 	    free(*pa);
300 	    *pa = 0;
301 	    continue;
302 	  }
303 	}
304 	pa++;
305     }
306     if (pa_data[0] == 0) {
307 	krb5_klog_syslog (LOG_INFO,
308 			  "%spreauth required but hint list is empty",
309 			  hw_only ? "hw" : "");
310     }
311     retval = encode_krb5_padata_sequence((const krb5_pa_data **) pa_data,
312 					 &edat);
313     if (retval)
314 	goto errout;
315     *e_data = *edat;
316     free(edat);
317 
318 errout:
319     krb5_free_pa_data(kdc_context, pa_data);
320     return;
321 }
322 
323 /*
324  * This routine is called to verify the preauthentication information
325  * for a V5 request.
326  *
327  * Returns 0 if the pre-authentication is valid, non-zero to indicate
328  * an error code of some sort.
329  */
330 
331 krb5_error_code
332 check_padata (
333     krb5_context	context,
334     krb5_db_entry *	client,
335     krb5_kdc_req *	request,
336     krb5_enc_tkt_part * enc_tkt_reply)
337 {
338     krb5_error_code retval = 0;
339     krb5_pa_data **padata;
340     krb5_preauth_systems *pa_sys;
341     int			pa_ok = 0, pa_found = 0;
342 
343     if (request->padata == 0)
344 	return 0;
345 
346 #ifdef DEBUG
347     krb5_klog_syslog (LOG_DEBUG, "checking padata");
348 #endif
349     for (padata = request->padata; *padata; padata++) {
350 #ifdef DEBUG
351 	krb5_klog_syslog (LOG_DEBUG, ".. pa_type 0x%x", (*padata)->pa_type);
352 #endif
353 	if (find_pa_system((*padata)->pa_type, &pa_sys))
354 	    continue;
355 #ifdef DEBUG
356 	krb5_klog_syslog (LOG_DEBUG, ".. pa_type %s", pa_sys->name);
357 #endif
358 	if (pa_sys->verify_padata == 0)
359 	    continue;
360 	pa_found++;
361 	retval = pa_sys->verify_padata(context, client, request,
362 				       enc_tkt_reply, *padata);
363 	if (retval) {
364 	    krb5_klog_syslog (LOG_INFO, "preauth (%s) verify failure: %s",
365 			      pa_sys->name, error_message (retval));
366 	    if (pa_sys->flags & PA_REQUIRED) {
367 		pa_ok = 0;
368 		break;
369 	    }
370 	} else {
371 #ifdef DEBUG
372 	    krb5_klog_syslog (LOG_DEBUG, ".. .. ok");
373 #endif
374 	    pa_ok = 1;
375 	    if (pa_sys->flags & PA_SUFFICIENT)
376 		break;
377 	}
378     }
379     if (pa_ok)
380 	return 0;
381 
382     /* pa system was not found, but principal doesn't require preauth */
383     if (!pa_found &&
384         !isflagset(client->attributes, KRB5_KDB_REQUIRES_PRE_AUTH) &&
385         !isflagset(client->attributes, KRB5_KDB_REQUIRES_HW_AUTH))
386        return 0;
387 
388     if (!pa_found)
389 	krb5_klog_syslog (LOG_INFO, "no valid preauth type found: %s",
390 			  error_message (retval));
391 
392     /* The following switch statement allows us
393      * to return some preauth system errors back to the client.
394      */
395     switch(retval) {
396 	case KRB5KRB_AP_ERR_BAD_INTEGRITY:
397         case KRB5KRB_AP_ERR_SKEW:
398 		return retval;
399         default:
400 		return KRB5KDC_ERR_PREAUTH_FAILED;
401     }
402 }
403 
404 /*
405  * return_padata creates any necessary preauthentication
406  * structures which should be returned by the KDC to the client
407  */
408 krb5_error_code
409 return_padata(
410     krb5_context	context,
411     krb5_db_entry *	client,
412     krb5_kdc_req *	request,
413     krb5_kdc_rep *	reply,
414     krb5_key_data *	client_key,
415     krb5_keyblock *	encrypting_key)
416 {
417     krb5_error_code		retval;
418     krb5_pa_data **		padata;
419     krb5_pa_data **		send_pa_list;
420     krb5_pa_data **		send_pa;
421     krb5_pa_data *		pa = 0;
422     krb5_preauth_systems *	ap;
423     int 			size = 0;
424 
425     for (ap = preauth_systems; ap->type != -1; ap++) {
426 	if (ap->return_padata)
427 	    size++;
428     }
429 
430     if ((send_pa_list = malloc((size+1) * sizeof(krb5_pa_data *))) == NULL)
431 	return ENOMEM;
432 
433     send_pa = send_pa_list;
434     *send_pa = 0;
435 
436     for (ap = preauth_systems; ap->type != -1; ap++) {
437 	if (ap->return_padata == 0)
438 	    continue;
439 	pa = 0;
440 	if (request->padata) {
441 	    for (padata = request->padata; *padata; padata++) {
442 		if ((*padata)->pa_type == ap->type) {
443 		    pa = *padata;
444 		    break;
445 		}
446 	    }
447 	}
448 	if ((retval = ap->return_padata(context, pa, client, request, reply,
449 					client_key, encrypting_key, send_pa)))
450 	    goto cleanup;
451 
452 	if (*send_pa)
453 	    send_pa++;
454 	*send_pa = 0;
455     }
456 
457     retval = 0;
458 
459     if (send_pa_list[0]) {
460 	reply->padata = send_pa_list;
461 	send_pa_list = 0;
462     }
463 
464 cleanup:
465     if (send_pa_list)
466 	krb5_free_pa_data(context, send_pa_list);
467     return (retval);
468 }
469 static krb5_boolean
470 enctype_requires_etype_info_2(krb5_enctype enctype)
471 {
472     switch(enctype) {
473     case ENCTYPE_DES_CBC_CRC:
474     case ENCTYPE_DES_CBC_MD4:
475     case ENCTYPE_DES_CBC_MD5:
476     case ENCTYPE_DES3_CBC_SHA1:
477     case ENCTYPE_DES3_CBC_RAW:
478     case ENCTYPE_ARCFOUR_HMAC:
479     case ENCTYPE_ARCFOUR_HMAC_EXP :
480 	return 0;
481     default:
482 	if (krb5_c_valid_enctype(enctype))
483             return 1;
484 	else return 0;
485     }
486 }
487 
488 static krb5_boolean
489 request_contains_enctype (krb5_context context,  const krb5_kdc_req *request,
490 			  krb5_enctype enctype)
491 {
492     int i;
493     for (i =0; i < request->nktypes; i++)
494 	if (request->ktype[i] == enctype)
495 	    return 1;
496     return 0;
497 }
498 
499 static krb5_error_code
500 verify_enc_timestamp(
501     krb5_context	context,
502     krb5_db_entry *	client,
503     krb5_kdc_req *	request,
504     krb5_enc_tkt_part * enc_tkt_reply,
505     krb5_pa_data *	pa)
506 {
507     krb5_pa_enc_ts *		pa_enc = 0;
508     krb5_error_code		retval;
509     krb5_data			scratch;
510     krb5_data			enc_ts_data;
511     krb5_enc_data 		*enc_data = 0;
512     krb5_keyblock		key;
513     krb5_key_data *		client_key;
514     krb5_int32			start;
515     krb5_timestamp		timenow;
516     krb5_error_code		decrypt_err;
517 
518     (void) memset(&key, 0, sizeof(krb5_keyblock));
519     scratch.data = (char *) pa->contents;
520     scratch.length = pa->length;
521 
522     enc_ts_data.data = 0;
523 
524     if ((retval = decode_krb5_enc_data(&scratch, &enc_data)) != 0)
525 	goto cleanup;
526 
527     enc_ts_data.length = enc_data->ciphertext.length;
528     if ((enc_ts_data.data = (char *) malloc(enc_ts_data.length)) == NULL)
529 	goto cleanup;
530 
531     start = 0;
532     decrypt_err = 0;
533     while (1) {
534 	if ((retval = krb5_dbe_search_enctype(context, client,
535 					      &start, enc_data->enctype,
536 					      -1, 0, &client_key)))
537 	    goto cleanup;
538 
539 	if ((retval = krb5_dbekd_decrypt_key_data(context, &master_keyblock,
540 						  client_key, &key, NULL)))
541 	    goto cleanup;
542 
543 	key.enctype = enc_data->enctype;
544 
545 	retval = krb5_c_decrypt(context, &key, KRB5_KEYUSAGE_AS_REQ_PA_ENC_TS,
546 				0, enc_data, &enc_ts_data);
547 	krb5_free_keyblock_contents(context, &key);
548 	if (retval == 0)
549 	    break;
550 	else
551 	    decrypt_err = retval;
552     }
553 
554     if ((retval = decode_krb5_pa_enc_ts(&enc_ts_data, &pa_enc)) != 0)
555 	goto cleanup;
556 
557     if ((retval = krb5_timeofday(context, &timenow)) != 0)
558 	goto cleanup;
559 
560     if (labs(timenow - pa_enc->patimestamp) > context->clockskew) {
561 	retval = KRB5KRB_AP_ERR_SKEW;
562 	goto cleanup;
563     }
564 
565     setflag(enc_tkt_reply->flags, TKT_FLG_PRE_AUTH);
566 
567     retval = 0;
568 
569 cleanup:
570     if (enc_data) {
571 	krb5_free_data_contents(context, &enc_data->ciphertext);
572 	free(enc_data);
573     }
574     krb5_free_data_contents(context, &enc_ts_data);
575     if (pa_enc)
576 	free(pa_enc);
577 
578     /*
579      * If we get NO_MATCHING_KEY and decryption previously failed, and
580      * we failed to find any other keys of the correct enctype after
581      * that failed decryption, it probably means that the password was
582      * incorrect.
583      */
584     if (retval == KRB5_KDB_NO_MATCHING_KEY && decrypt_err != 0)
585 	retval = decrypt_err;
586 
587     return retval;
588 }
589 
590 static krb5_error_code
591 _make_etype_info_entry(krb5_context context,
592 	krb5_kdc_req *request, krb5_key_data *client_key,
593 	krb5_enctype etype, krb5_etype_info_entry **entry,
594 	int etype_info2)
595 {
596     krb5_data			salt;
597     krb5_etype_info_entry *	tmp_entry;
598     krb5_error_code		retval;
599 
600     if ((tmp_entry = malloc(sizeof(krb5_etype_info_entry))) == NULL)
601        return ENOMEM;
602 
603     salt.data = 0;
604 
605     tmp_entry->magic = KV5M_ETYPE_INFO_ENTRY;
606     tmp_entry->etype = etype;
607     tmp_entry->length = KRB5_ETYPE_NO_SALT;
608     tmp_entry->salt = 0;
609     tmp_entry->s2kparams.data = NULL;
610     tmp_entry->s2kparams.length = 0;
611     retval = get_salt_from_key(context, request->client,
612 			       client_key, &salt);
613     if (retval)
614 	goto fail;
615     if (etype_info2 && client_key->key_data_ver > 1 &&
616 	client_key->key_data_type[1] == KRB5_KDB_SALTTYPE_AFS3) {
617 	switch (etype) {
618 	case ENCTYPE_DES_CBC_CRC:
619 	case ENCTYPE_DES_CBC_MD4:
620 	case ENCTYPE_DES_CBC_MD5:
621             tmp_entry->s2kparams.data = malloc(1);
622             if (tmp_entry->s2kparams.data == NULL) {
623 		retval = ENOMEM;
624 		goto fail;
625             }
626             tmp_entry->s2kparams.length = 1;
627             tmp_entry->s2kparams.data[0] = 1;
628             break;
629 	default:
630             break;
631 	}
632     }
633 
634     if (salt.length >= 0) {
635 	tmp_entry->length = salt.length;
636 	tmp_entry->salt = (unsigned char *) salt.data;
637 	salt.data = 0;
638     }
639     *entry = tmp_entry;
640     return 0;
641 
642 fail:
643     if (tmp_entry) {
644 	if (tmp_entry->s2kparams.data)
645             free(tmp_entry->s2kparams.data);
646 	free(tmp_entry);
647     }
648     if (salt.data)
649 	free(salt.data);
650     return retval;
651 }
652 /*
653  * This function returns the etype information for a particular
654  * client, to be passed back in the preauth list in the KRB_ERROR
655  * message.  It supports generating both etype_info  and etype_info2
656  *  as most of the work is the same.
657  */
658 static krb5_error_code
659 etype_info_helper(krb5_context context, krb5_kdc_req *request,
660 		krb5_db_entry *client, krb5_db_entry *server,
661 		krb5_pa_data *pa_data, int etype_info2)
662 {
663     krb5_etype_info_entry **	entry = 0;
664     krb5_key_data		*client_key;
665     krb5_error_code		retval;
666     krb5_data *			scratch;
667     krb5_enctype		db_etype;
668     int				i = 0;
669     int				start = 0;
670     int				seen_des = 0;
671 
672     entry = malloc((client->n_key_data * 2 + 1) *
673 		sizeof(krb5_etype_info_entry *));
674     if (entry == NULL)
675 	return ENOMEM;
676     entry[0] = NULL;
677 
678     while (1) {
679 	retval = krb5_dbe_search_enctype(context, client, &start, -1,
680                                          -1, 0, &client_key);
681 	if (retval == KRB5_KDB_NO_MATCHING_KEY)
682             break;
683 	if (retval)
684             goto cleanup;
685 	db_etype = client_key->key_data_type[0];
686 	if (db_etype == ENCTYPE_DES_CBC_MD4)
687             db_etype = ENCTYPE_DES_CBC_MD5;
688 	if (request_contains_enctype(context, request, db_etype)) {
689             assert(etype_info2 ||
690                    !enctype_requires_etype_info_2(db_etype));
691             if ((retval = _make_etype_info_entry(context, request, client_key,
692                             db_etype, &entry[i], etype_info2)) != 0) {
693 		goto cleanup;
694             }
695             entry[i+1] = 0;
696             i++;
697 	}
698 
699         /*
700          * If there is a des key in the kdb, try the "similar" enctypes,
701 	 * avoid duplicate entries.
702 	 */
703 	if (!seen_des) {
704             switch (db_etype) {
705             case ENCTYPE_DES_CBC_MD5:
706 		db_etype = ENCTYPE_DES_CBC_CRC;
707 		break;
708             case ENCTYPE_DES_CBC_CRC:
709 		db_etype = ENCTYPE_DES_CBC_MD5;
710 		break;
711             default:
712 		continue;
713 
714             }
715             if (request_contains_enctype(context, request, db_etype)) {
716 		if ((retval = _make_etype_info_entry(context, request,
717 		    client_key, db_etype, &entry[i], etype_info2)) != 0) {
718                     goto cleanup;
719 		}
720                 entry[i+1] = 0;
721 		i++;
722             }
723             seen_des++;
724 	}
725     }
726     if (etype_info2)
727 	retval = encode_krb5_etype_info2((const krb5_etype_info_entry **) entry,
728                                     &scratch);
729     else
730 	retval = encode_krb5_etype_info((const krb5_etype_info_entry **) entry,                                    &scratch);
731     if (retval)
732 	goto cleanup;
733     pa_data->contents = (unsigned char *)scratch->data;
734     pa_data->length = scratch->length;
735     /*
736      * note, don't free scratch->data as it is in use (don't use
737      * krb5_free_data() either).
738      */
739     free(scratch);
740 
741     retval = 0;
742 
743 cleanup:
744     if (entry)
745 	krb5_free_etype_info(context, entry);
746     return retval;
747 }
748 
749 static krb5_error_code
750 get_etype_info(krb5_context context, krb5_kdc_req *request,
751 		krb5_db_entry *client, krb5_db_entry *server,
752 		krb5_pa_data *pa_data)
753 {
754   int i;
755     for (i=0;  i < request->nktypes; i++) {
756 	if (enctype_requires_etype_info_2(request->ktype[i]))
757             return KRB5KDC_ERR_PADATA_TYPE_NOSUPP ;;;; /*Caller will
758                                                         * skip this
759                                                         * type*/
760     }
761     return etype_info_helper(context, request, client, server, pa_data, 0);
762 }
763 
764 static krb5_error_code
765 get_etype_info2(krb5_context context, krb5_kdc_req *request,
766 		krb5_db_entry *client, krb5_db_entry *server,
767 		krb5_pa_data *pa_data)
768 {
769     return etype_info_helper( context, request, client, server, pa_data, 1);
770 }
771 
772 static krb5_error_code
773 return_etype_info2(krb5_context context, krb5_pa_data * padata,
774                    krb5_db_entry *client,
775                    krb5_kdc_req *request, krb5_kdc_rep *reply,
776                    krb5_key_data *client_key,
777                    krb5_keyblock *encrypting_key,
778                    krb5_pa_data **send_pa)
779 {
780     krb5_error_code retval;
781     krb5_pa_data *tmp_padata;
782     krb5_etype_info_entry **entry = NULL;
783     krb5_data *scratch = NULL;
784 
785     tmp_padata = malloc( sizeof(krb5_pa_data));
786     if (tmp_padata == NULL)
787 	return ENOMEM;
788     tmp_padata->pa_type = KRB5_PADATA_ETYPE_INFO2;
789     entry = malloc(2 * sizeof(krb5_etype_info_entry *));
790     if (entry == NULL) {
791 	retval = ENOMEM;
792 	goto cleanup;
793     }
794     entry[0] = NULL;
795     entry[1] = NULL;
796     /* using encrypting_key->enctype as this is specified in rfc4120 */
797     retval = _make_etype_info_entry(context, request,
798 		client_key, encrypting_key->enctype,
799 		entry, 1);
800     if (retval)
801 	goto cleanup;
802 
803     retval = encode_krb5_etype_info2((const krb5_etype_info_entry **) entry,
804 	&scratch);
805     if (retval)
806 	goto cleanup;
807     tmp_padata->contents = (uchar_t *)scratch->data;
808     tmp_padata->length = scratch->length;
809     *send_pa = tmp_padata;
810 
811     /* For cleanup - we no longer own the contents of the krb5_data
812      * only to pointer to the krb5_data
813      */
814      scratch->data = 0;
815 
816  cleanup:
817     if (entry)
818 	krb5_free_etype_info(context, entry);
819     if (retval) {
820 	if (tmp_padata)
821             free(tmp_padata);
822     }
823     if (scratch)
824             krb5_free_data(context, scratch);
825     return retval;
826 }
827 
828 
829 static krb5_error_code
830 return_pw_salt(context, in_padata, client, request, reply, client_key,
831 	       encrypting_key, send_pa)
832     krb5_context	context;
833     krb5_pa_data *	in_padata;
834     krb5_db_entry *	client;
835     krb5_kdc_req *	request;
836     krb5_kdc_rep *	reply;
837     krb5_key_data *	client_key;
838     krb5_keyblock *	encrypting_key;
839     krb5_pa_data **	send_pa;
840 {
841     krb5_error_code	retval;
842     krb5_pa_data *	padata;
843     krb5_data *		scratch;
844     krb5_data		salt_data;
845     int i;
846 
847     for (i = 0; i < request->nktypes; i++) {
848 	if (enctype_requires_etype_info_2(request->ktype[i]))
849             return 0;
850     }
851 
852     if (client_key->key_data_ver == 1 ||
853 	client_key->key_data_type[1] == KRB5_KDB_SALTTYPE_NORMAL)
854 	return 0;
855 
856     if ((padata = malloc(sizeof(krb5_pa_data))) == NULL)
857 	return ENOMEM;
858     padata->magic = KV5M_PA_DATA;
859     padata->pa_type = KRB5_PADATA_PW_SALT;
860 
861     switch (client_key->key_data_type[1]) {
862     case KRB5_KDB_SALTTYPE_V4:
863 	/* send an empty (V4) salt */
864 	padata->contents = 0;
865 	padata->length = 0;
866 	break;
867     case KRB5_KDB_SALTTYPE_NOREALM:
868 	if ((retval = krb5_principal2salt_norealm(kdc_context,
869 						   request->client,
870 						   &salt_data)))
871 	    goto cleanup;
872 	padata->contents = (krb5_octet *)salt_data.data;
873 	padata->length = salt_data.length;
874 	break;
875     case KRB5_KDB_SALTTYPE_AFS3:
876 	/* send an AFS style realm-based salt */
877 	/* for now, just pass the realm back and let the client
878 	   do the work. In the future, add a kdc configuration
879 	   variable that specifies the old cell name. */
880 	padata->pa_type = KRB5_PADATA_AFS3_SALT;
881 	/* it would be just like ONLYREALM, but we need to pass the 0 */
882 	scratch = krb5_princ_realm(kdc_context, request->client);
883 	if ((padata->contents = malloc(scratch->length+1)) == NULL) {
884 	    retval = ENOMEM;
885 	    goto cleanup;
886 	}
887 	memcpy(padata->contents, scratch->data, scratch->length);
888 	padata->length = scratch->length+1;
889 	padata->contents[scratch->length] = 0;
890 	break;
891     case KRB5_KDB_SALTTYPE_ONLYREALM:
892 	scratch = krb5_princ_realm(kdc_context, request->client);
893 	if ((padata->contents = malloc(scratch->length)) == NULL) {
894 	    retval = ENOMEM;
895 	    goto cleanup;
896 	}
897 	memcpy(padata->contents, scratch->data, scratch->length);
898 	padata->length = scratch->length;
899 	break;
900     case KRB5_KDB_SALTTYPE_SPECIAL:
901 	if ((padata->contents = malloc(client_key->key_data_length[1]))
902 	    == NULL) {
903 	    retval = ENOMEM;
904 	    goto cleanup;
905 	}
906 	memcpy(padata->contents, client_key->key_data_contents[1],
907 	       client_key->key_data_length[1]);
908 	padata->length = client_key->key_data_length[1];
909 	break;
910     default:
911 	free(padata);
912 	return 0;
913     }
914 
915     *send_pa = padata;
916     return 0;
917 
918 cleanup:
919     free(padata);
920     return retval;
921 }
922 
923 static krb5_error_code
924 return_sam_data(context, in_padata, client, request, reply, client_key,
925 	        encrypting_key, send_pa)
926     krb5_context	context;
927     krb5_pa_data *	in_padata;
928     krb5_db_entry *	client;
929     krb5_kdc_req *	request;
930     krb5_kdc_rep *	reply;
931     krb5_key_data *	client_key;
932     krb5_keyblock *	encrypting_key;
933     krb5_pa_data **	send_pa;
934 {
935     krb5_error_code	retval;
936     krb5_data		scratch;
937     int			i;
938 
939     krb5_sam_response		*sr = 0;
940     krb5_predicted_sam_response	*psr = 0;
941 
942     if (in_padata == 0)
943 	return 0;
944 
945     /*
946      * We start by doing the same thing verify_sam_response() does:
947      * extract the psr from the padata (which is an sr). Nothing
948      * here should generate errors! We've already successfully done
949      * all this once.
950      */
951 
952     scratch.data = (char *) in_padata->contents; /* SUNWresync121 XXX */
953     scratch.length = in_padata->length;
954 
955     if ((retval = decode_krb5_sam_response(&scratch, &sr))) {
956 	com_err("krb5kdc", retval,
957 		gettext("return_sam_data(): decode_krb5_sam_response failed"));
958 	goto cleanup;
959     }
960 
961     {
962 	krb5_enc_data tmpdata;
963 
964 	tmpdata.enctype = ENCTYPE_UNKNOWN;
965 	tmpdata.ciphertext = sr->sam_track_id;
966 
967 	scratch.length = tmpdata.ciphertext.length;
968 	if ((scratch.data = (char *) malloc(scratch.length)) == NULL) {
969 	    retval = ENOMEM;
970 	    goto cleanup;
971 	}
972 
973 	if ((retval = krb5_c_decrypt(context, &psr_key, /* XXX */ 0, 0,
974 				     &tmpdata, &scratch))) {
975 	    com_err("krb5kdc", retval,
976 		    gettext("return_sam_data(): decrypt track_id failed"));
977 	    free(scratch.data);
978 	    goto cleanup;
979 	}
980     }
981 
982     if ((retval = decode_krb5_predicted_sam_response(&scratch, &psr))) {
983 	com_err("krb5kdc", retval,
984 		gettext(
985 		"return_sam_data(): decode_krb5_predicted_sam_response failed"));
986 	free(scratch.data);
987 	goto cleanup;
988     }
989 
990     /* We could use sr->sam_flags, but it may be absent or altered. */
991     if (psr->sam_flags & KRB5_SAM_MUST_PK_ENCRYPT_SAD) {
992 	com_err("krb5kdc", retval = KRB5KDC_ERR_PREAUTH_FAILED,
993 		gettext("Unsupported SAM flag must-pk-encrypt-sad"));
994 	goto cleanup;
995     }
996     if (psr->sam_flags & KRB5_SAM_SEND_ENCRYPTED_SAD) {
997 	/* No key munging */
998 	goto cleanup;
999     }
1000     if (psr->sam_flags & KRB5_SAM_USE_SAD_AS_KEY) {
1001 	/* Use sam_key instead of client key */
1002 	krb5_free_keyblock_contents(context, encrypting_key);
1003 	krb5_copy_keyblock_contents(context, &psr->sam_key, encrypting_key);
1004 	/* XXX Attach a useful pa_data */
1005 	goto cleanup;
1006     }
1007 
1008     /* Otherwise (no flags set), we XOR the keys */
1009     /* XXX The passwords-04 draft is underspecified here wrt different
1010 	   key types. We will do what I hope to get into the -05 draft. */
1011     {
1012 	krb5_octet *p = encrypting_key->contents;
1013 	krb5_octet *q = psr->sam_key.contents;
1014 	int length = ((encrypting_key->length < psr->sam_key.length)
1015 		      ? encrypting_key->length
1016 		      : psr->sam_key.length);
1017 
1018 	for (i = 0; i < length; i++)
1019 	    p[i] ^= q[i];
1020     }
1021 
1022     /* Post-mixing key correction */
1023     switch (encrypting_key->enctype) {
1024     case ENCTYPE_DES_CBC_CRC:
1025     case ENCTYPE_DES_CBC_MD4:
1026     case ENCTYPE_DES_CBC_MD5:
1027     case ENCTYPE_DES_CBC_RAW:
1028 	mit_des_fixup_key_parity(encrypting_key->contents);
1029 	if (mit_des_is_weak_key(encrypting_key->contents))
1030 	    ((krb5_octet *) encrypting_key->contents)[7] ^= 0xf0;
1031 	break;
1032 
1033     /* XXX case ENCTYPE_DES3_CBC_MD5: listed in 1510bis-04 draft */
1034     case ENCTYPE_DES3_CBC_SHA: /* XXX deprecated? */
1035     case ENCTYPE_DES3_CBC_RAW:
1036     case ENCTYPE_DES3_CBC_SHA1:
1037 	for (i = 0; i < 3; i++) {
1038 	    mit_des_fixup_key_parity(encrypting_key->contents + i * 8);
1039 	    if (mit_des_is_weak_key(encrypting_key->contents + i * 8))
1040 		((krb5_octet *) encrypting_key->contents)[7 + i * 8] ^= 0xf0;
1041 	}
1042 	break;
1043 
1044     default:
1045 	com_err("krb5kdc", retval = KRB5KDC_ERR_PREAUTH_FAILED,
1046 		gettext("Unimplemented keytype for SAM key mixing"));
1047 	goto cleanup;
1048     }
1049 
1050     /* XXX Attach a useful pa_data */
1051 cleanup:
1052     if (sr)
1053 	krb5_free_sam_response(context, sr);
1054     if (psr)
1055 	krb5_free_predicted_sam_response(context, psr);
1056 
1057     return retval;
1058 }
1059 
1060 static struct {
1061   char* name;
1062   int   sam_type;
1063 } *sam_ptr, sam_inst_map[] = {
1064 #if 0	/* SUNWresync121 - unsupported hardware and kdc.log annoyance */
1065   { "SNK4", PA_SAM_TYPE_DIGI_PATH, },
1066   { "SECURID", PA_SAM_TYPE_SECURID, },
1067   { "GRAIL", PA_SAM_TYPE_GRAIL, },
1068 #endif
1069   { 0, 0 },
1070 };
1071 
1072 static krb5_error_code
1073 get_sam_edata(context, request, client, server, pa_data)
1074     krb5_context 	context;
1075     krb5_kdc_req *	request;
1076     krb5_db_entry *	client;
1077     krb5_db_entry *	server;
1078     krb5_pa_data *	pa_data;
1079 {
1080     krb5_error_code		retval;
1081     krb5_sam_challenge		sc;
1082     krb5_predicted_sam_response	psr;
1083     krb5_data *			scratch;
1084     krb5_keyblock encrypting_key;
1085     char response[9];
1086     char inputblock[8];
1087     krb5_data predict_response;
1088 
1089     (void) memset(&encrypting_key, 0, sizeof(krb5_keyblock));
1090     (void) memset(&sc, 0, sizeof(sc));
1091     (void) memset(&psr, 0, sizeof(psr));
1092 
1093     /* Given the client name we can figure out what type of preauth
1094        they need. The spec is currently for querying the database for
1095        names that match the types of preauth used. Later we should
1096        make this mapping show up in kdc.conf. In the meantime, we
1097        hardcode the following:
1098 		/SNK4 -- Digital Pathways SNK/4 preauth.
1099 		/GRAIL -- experimental preauth
1100        The first one found is used. See sam_inst_map above.
1101 
1102        For SNK4 in particular, the key in the database is the key for
1103        the device; kadmin needs a special interface for it.
1104      */
1105 
1106     {
1107       int npr = 1, more;
1108       krb5_db_entry assoc;
1109       krb5_key_data  *assoc_key;
1110       krb5_principal newp;
1111       int probeslot;
1112 
1113       sc.sam_type = 0;
1114 
1115       retval = krb5_copy_principal(kdc_context, request->client, &newp);
1116       if (retval) {
1117 	com_err(gettext("krb5kdc"),
1118 		retval,
1119 		gettext("copying client name for preauth probe"));
1120 	return retval;
1121       }
1122 
1123       probeslot = krb5_princ_size(context, newp)++;
1124       krb5_princ_name(kdc_context, newp) =
1125 	realloc(krb5_princ_name(kdc_context, newp),
1126 		krb5_princ_size(context, newp) * sizeof(krb5_data));
1127 
1128       for(sam_ptr = sam_inst_map; sam_ptr->name; sam_ptr++) {
1129 	krb5_princ_component(kdc_context,newp,probeslot)->data = sam_ptr->name;
1130 	krb5_princ_component(kdc_context,newp,probeslot)->length =
1131 	  strlen(sam_ptr->name);
1132 	npr = 1;
1133 	retval = krb5_db_get_principal(kdc_context, newp, &assoc, &npr, (uint *)&more);
1134 	if(!retval) {
1135 	  sc.sam_type = sam_ptr->sam_type;
1136 	  break;
1137 	}
1138       }
1139 
1140       krb5_princ_component(kdc_context,newp,probeslot)->data = 0;
1141       krb5_princ_component(kdc_context,newp,probeslot)->length = 0;
1142       krb5_princ_size(context, newp)--;
1143 
1144       krb5_free_principal(kdc_context, newp);
1145 
1146       /* if sc.sam_type is set, it worked */
1147       if (sc.sam_type) {
1148 	/* so use assoc to get the key out! */
1149 	{
1150 	  /* here's what do_tgs_req does */
1151 	  retval = krb5_dbe_find_enctype(kdc_context, &assoc,
1152 					 ENCTYPE_DES_CBC_RAW,
1153 					 KRB5_KDB_SALTTYPE_NORMAL,
1154 					 0,		/* Get highest kvno */
1155 					 &assoc_key);
1156 	  if (retval) {
1157 	    char *sname;
1158 	    krb5_unparse_name(kdc_context, request->client, &sname);
1159 	    com_err(gettext("krb5kdc"),
1160 		    retval,
1161 		    gettext("snk4 finding the enctype and key <%s>"),
1162 		    sname);
1163 	    free(sname);
1164 	    return retval;
1165 	  }
1166 	  /* convert server.key into a real key */
1167 	  retval = krb5_dbekd_decrypt_key_data(kdc_context,
1168 					       &master_keyblock,
1169 					       assoc_key, &encrypting_key,
1170 					       NULL);
1171 	  if (retval) {
1172 	    com_err(gettext("krb5kdc"),
1173 		    retval,
1174 		   gettext("snk4 pulling out key entry"));
1175 	    return retval;
1176 	  }
1177 	  /* now we can use encrypting_key... */
1178 	}
1179       } else {
1180 	/* SAM is not an option - so don't return as hint */
1181 	return KRB5_PREAUTH_BAD_TYPE;
1182       }
1183     }
1184     sc.magic = KV5M_SAM_CHALLENGE;
1185     psr.sam_flags = sc.sam_flags = KRB5_SAM_USE_SAD_AS_KEY;
1186 
1187     /* Replay prevention */
1188     if ((retval = krb5_copy_principal(context, request->client, &psr.client)))
1189 	return retval;
1190 #ifdef USE_RCACHE
1191     if ((retval = krb5_us_timeofday(context, &psr.stime, &psr.susec)))
1192 	return retval;
1193 #endif /* USE_RCACHE */
1194 
1195     switch (sc.sam_type) {
1196     case PA_SAM_TYPE_GRAIL:
1197       sc.sam_type_name.data = "Experimental System";
1198       sc.sam_type_name.length = strlen(sc.sam_type_name.data);
1199       sc.sam_challenge_label.data = "experimental challenge label";
1200       sc.sam_challenge_label.length = strlen(sc.sam_challenge_label.data);
1201       sc.sam_challenge.data = "12345";
1202       sc.sam_challenge.length = strlen(sc.sam_challenge.data);
1203 
1204 #if 0 /* Enable this to test "normal" (no flags set) mode.  */
1205       psr.sam_flags = sc.sam_flags = 0;
1206 #endif
1207 
1208       psr.magic = KV5M_PREDICTED_SAM_RESPONSE;
1209       /* string2key on sc.sam_challenge goes in here */
1210       /* eblock is just to set the enctype */
1211       {
1212 	const krb5_enctype type = ENCTYPE_DES_CBC_MD5;
1213 
1214 	if ((retval = krb5_c_string_to_key(context, type, &sc.sam_challenge,
1215 					   0 /* salt */, &psr.sam_key)))
1216 	    goto cleanup;
1217 
1218 	if ((retval = encode_krb5_predicted_sam_response(&psr, &scratch)))
1219 	    goto cleanup;
1220 
1221 	{
1222 	    size_t enclen;
1223 	    krb5_enc_data tmpdata;
1224 
1225 	    if ((retval = krb5_c_encrypt_length(context,
1226 						psr_key.enctype,
1227 						scratch->length, &enclen)))
1228 		goto cleanup;
1229 
1230 	    if ((tmpdata.ciphertext.data = (char *) malloc(enclen)) == NULL) {
1231 		retval = ENOMEM;
1232 		goto cleanup;
1233 	    }
1234 	    tmpdata.ciphertext.length = enclen;
1235 
1236 	    if ((retval = krb5_c_encrypt(context, &psr_key,
1237 					 /* XXX */ 0, 0, scratch, &tmpdata)))
1238 		goto cleanup;
1239 
1240 	    sc.sam_track_id = tmpdata.ciphertext;
1241 	}
1242       }
1243 
1244       sc.sam_response_prompt.data = "response prompt";
1245       sc.sam_response_prompt.length = strlen(sc.sam_response_prompt.data);
1246       sc.sam_pk_for_sad.length = 0;
1247       sc.sam_nonce = 0;
1248       /* Generate checksum */
1249       /*krb5_checksum_size(context, ctype)*/
1250       /*krb5_calculate_checksum(context,ctype,in,in_length,seed,
1251 	seed_length,outcksum) */
1252       /*krb5_verify_checksum(context,ctype,cksum,in,in_length,seed,
1253 	seed_length) */
1254 #if 0 /* XXX a) glue appears broken; b) this gives up the SAD */
1255       sc.sam_cksum.contents = (krb5_octet *)
1256 	malloc(krb5_checksum_size(context, CKSUMTYPE_RSA_MD5_DES));
1257       if (sc.sam_cksum.contents == NULL) return(ENOMEM);
1258 
1259       retval = krb5_calculate_checksum(context, CKSUMTYPE_RSA_MD5_DES,
1260 				       sc.sam_challenge.data,
1261 				       sc.sam_challenge.length,
1262 				       psr.sam_key.contents, /* key */
1263 				       psr.sam_key.length, /* key length */
1264 				       &sc.sam_cksum);
1265       if (retval) { free(sc.sam_cksum.contents); return(retval); }
1266 #endif /* 0 */
1267 
1268       retval = encode_krb5_sam_challenge(&sc, &scratch);
1269       if (retval) goto cleanup;
1270       pa_data->magic = KV5M_PA_DATA;
1271       pa_data->pa_type = KRB5_PADATA_SAM_CHALLENGE;
1272       pa_data->contents = (unsigned char *) scratch->data;
1273       pa_data->length = scratch->length;
1274 
1275       retval = 0;
1276       break;
1277     case PA_SAM_TYPE_DIGI_PATH:
1278       sc.sam_type_name.data = "Digital Pathways";
1279       sc.sam_type_name.length = strlen(sc.sam_type_name.data);
1280 #if 1
1281       sc.sam_challenge_label.data = "Enter the following on your keypad";
1282       sc.sam_challenge_label.length = strlen(sc.sam_challenge_label.data);
1283 #endif
1284       /* generate digit string, take it mod 1000000 (six digits.) */
1285       {
1286 	int j;
1287 	krb5_keyblock session_key;
1288 	char outputblock[8];
1289 	int i;
1290 
1291 	(void) memset(&session_key, 0, sizeof(krb5_keyblock));
1292 	(void) memset(inputblock, 0, 8);
1293 
1294 	retval = krb5_c_make_random_key(kdc_context, ENCTYPE_DES_CBC_CRC,
1295 					&session_key);
1296 
1297 	if (retval) {
1298 	  /* random key failed */
1299 	  com_err(gettext("krb5kdc"),
1300 		 retval,
1301 		gettext("generating random challenge for preauth"));
1302 	  return retval;
1303 	}
1304 	/* now session_key has a key which we can pick bits out of */
1305 	/* we need six decimal digits. Grab 6 bytes, div 2, mod 10 each. */
1306 	if (session_key.length != 8) {
1307 		 retval = KRB5KDC_ERR_ETYPE_NOSUPP,
1308 	  com_err(gettext("krb5kdc"),
1309 		 retval = KRB5KDC_ERR_ETYPE_NOSUPP,
1310 		 gettext("keytype didn't match code expectations"));
1311 	  return retval;
1312 	}
1313 	for(i = 0; i<6; i++) {
1314 	  inputblock[i] = '0' + ((session_key.contents[i]/2) % 10);
1315 	}
1316 	if (session_key.contents)
1317 	  krb5_free_keyblock_contents(kdc_context, &session_key);
1318 
1319 	/* retval = krb5_finish_key(kdc_context, &eblock); */
1320 	/* now we have inputblock containing the 8 byte input to DES... */
1321 	sc.sam_challenge.data = inputblock;
1322 	sc.sam_challenge.length = 6;
1323 
1324 	encrypting_key.enctype = ENCTYPE_DES_CBC_RAW;
1325 
1326 	if (retval) {
1327 	  com_err(gettext("krb5kdc"),
1328 		 retval,
1329 		gettext("snk4 processing key"));
1330 	}
1331 
1332 	{
1333 	    krb5_data plain;
1334 	    krb5_enc_data cipher;
1335 
1336 	    plain.length = 8;
1337 	    plain.data = inputblock;
1338 
1339 	    /* XXX I know this is enough because of the fixed raw enctype.
1340 	       if it's not, the underlying code will return a reasonable
1341 	       error, which should never happen */
1342 	    cipher.ciphertext.length = 8;
1343 	    cipher.ciphertext.data = outputblock;
1344 
1345 	    if ((retval = krb5_c_encrypt(kdc_context, &encrypting_key,
1346 					 /* XXX */ 0, 0, &plain, &cipher))) {
1347 		com_err(gettext("krb5kdc"),
1348 		retval,
1349 		gettext("snk4 response generation failed"));
1350 		return retval;
1351 	    }
1352 	}
1353 
1354 	/* now output block is the raw bits of the response; convert it
1355 	   to display form */
1356 	for (j=0; j<4; j++) {
1357 	  char n[2];
1358 	  int k;
1359 	  n[0] = outputblock[j] & 0xf;
1360 	  n[1] = (outputblock[j]>>4) & 0xf;
1361 	  for (k=0; k<2; k++) {
1362 	    if(n[k] > 9) n[k] = ((n[k]-1)>>2);
1363 	    /* This is equivalent to:
1364 	       if(n[k]>=0xa && n[k]<=0xc) n[k] = 2;
1365 	       if(n[k]>=0xd && n[k]<=0xf) n[k] = 3;
1366 	       */
1367 	  }
1368 	  /* for v4, we keygen: *(j+(char*)&key1) = (n[1]<<4) | n[0]; */
1369 	  /* for v5, we just generate a string */
1370 	  response[2*j+0] = '0' + n[1];
1371 	  response[2*j+1] = '0' + n[0];
1372 	  /* and now, response has what we work with. */
1373 	}
1374 	response[8] = 0;
1375 	predict_response.data = response;
1376 	predict_response.length = 8;
1377 #if 0				/* for debugging, hack the output too! */
1378 sc.sam_challenge_label.data = response;
1379 sc.sam_challenge_label.length = strlen(sc.sam_challenge_label.data);
1380 #endif
1381       }
1382 
1383       psr.magic = KV5M_PREDICTED_SAM_RESPONSE;
1384       /* string2key on sc.sam_challenge goes in here */
1385       /* eblock is just to set the enctype */
1386       {
1387 	retval = krb5_c_string_to_key(context, ENCTYPE_DES_CBC_MD5,
1388 				      &predict_response, 0 /* salt */,
1389 				      &psr.sam_key);
1390 	if (retval) goto cleanup;
1391 
1392 	retval = encode_krb5_predicted_sam_response(&psr, &scratch);
1393 	if (retval) goto cleanup;
1394 
1395 	{
1396 	    size_t enclen;
1397 	    krb5_enc_data tmpdata;
1398 
1399 	    if ((retval = krb5_c_encrypt_length(context,
1400 						psr_key.enctype,
1401 						scratch->length, &enclen)))
1402 		goto cleanup;
1403 
1404 	    if ((tmpdata.ciphertext.data = (char *) malloc(enclen)) == NULL) {
1405 		retval = ENOMEM;
1406 		goto cleanup;
1407 	    }
1408 	    tmpdata.ciphertext.length = enclen;
1409 
1410 	    if ((retval = krb5_c_encrypt(context, &psr_key,
1411 					 /* XXX */ 0, 0, scratch, &tmpdata)))
1412 		goto cleanup;
1413 
1414 	    sc.sam_track_id = tmpdata.ciphertext;
1415 	}
1416 	if (retval) goto cleanup;
1417       }
1418 
1419       sc.sam_response_prompt.data = "Enter the displayed response";
1420       sc.sam_response_prompt.length = strlen(sc.sam_response_prompt.data);
1421       sc.sam_pk_for_sad.length = 0;
1422       sc.sam_nonce = 0;
1423       /* Generate checksum */
1424       /*krb5_checksum_size(context, ctype)*/
1425       /*krb5_calculate_checksum(context,ctype,in,in_length,seed,
1426 	seed_length,outcksum) */
1427       /*krb5_verify_checksum(context,ctype,cksum,in,in_length,seed,
1428 	seed_length) */
1429 #if 0 /* XXX a) glue appears broken; b) this gives up the SAD */
1430       sc.sam_cksum.contents = (krb5_octet *)
1431 	malloc(krb5_checksum_size(context, CKSUMTYPE_RSA_MD5_DES));
1432       if (sc.sam_cksum.contents == NULL) return(ENOMEM);
1433 
1434       retval = krb5_calculate_checksum(context, CKSUMTYPE_RSA_MD5_DES,
1435 				       sc.sam_challenge.data,
1436 				       sc.sam_challenge.length,
1437 				       psr.sam_key.contents, /* key */
1438 				       psr.sam_key.length, /* key length */
1439 				       &sc.sam_cksum);
1440       if (retval) { free(sc.sam_cksum.contents); return(retval); }
1441 #endif /* 0 */
1442 
1443       retval = encode_krb5_sam_challenge(&sc, &scratch);
1444       if (retval) goto cleanup;
1445       pa_data->magic = KV5M_PA_DATA;
1446       pa_data->pa_type = KRB5_PADATA_SAM_CHALLENGE;
1447       pa_data->contents = (unsigned char *) scratch->data;
1448       pa_data->length = scratch->length;
1449 
1450       retval = 0;
1451       break;
1452     }
1453 
1454 cleanup:
1455     krb5_free_keyblock_contents(context, &encrypting_key);
1456     return retval;
1457 }
1458 
1459 static krb5_error_code
1460 verify_sam_response(context, client, request, enc_tkt_reply, pa)
1461     krb5_context	context;
1462     krb5_db_entry *	client;
1463     krb5_kdc_req *	request;
1464     krb5_enc_tkt_part * enc_tkt_reply;
1465     krb5_pa_data *	pa;
1466 {
1467     krb5_error_code		retval;
1468     krb5_data			scratch;
1469     krb5_sam_response		*sr = 0;
1470     krb5_predicted_sam_response	*psr = 0;
1471     krb5_enc_sam_response_enc	*esre = 0;
1472     krb5_timestamp		timenow;
1473     char			*princ_req = 0, *princ_psr = 0;
1474 
1475     scratch.data = (char *) pa->contents;
1476     scratch.length = pa->length;
1477 
1478     if ((retval = decode_krb5_sam_response(&scratch, &sr))) {
1479 	scratch.data = 0;
1480 	com_err("krb5kdc", retval, gettext("decode_krb5_sam_response failed"));
1481 	goto cleanup;
1482     }
1483 
1484     /* XXX We can only handle the challenge/response model of SAM.
1485 	   See passwords-04, par 4.1, 4.2 */
1486     {
1487       krb5_enc_data tmpdata;
1488 
1489       tmpdata.enctype = ENCTYPE_UNKNOWN;
1490       tmpdata.ciphertext = sr->sam_track_id;
1491 
1492       scratch.length = tmpdata.ciphertext.length;
1493       if ((scratch.data = (char *) malloc(scratch.length)) == NULL) {
1494 	  retval = ENOMEM;
1495 	  goto cleanup;
1496       }
1497 
1498       if ((retval = krb5_c_decrypt(context, &psr_key, /* XXX */ 0, 0,
1499 				   &tmpdata, &scratch))) {
1500 	  com_err(gettext("krb5kdc"),
1501 		retval,
1502 		gettext("decrypt track_id failed"));
1503 	  goto cleanup;
1504       }
1505     }
1506 
1507     if ((retval = decode_krb5_predicted_sam_response(&scratch, &psr))) {
1508 	com_err(gettext("krb5kdc"), retval,
1509 		gettext("decode_krb5_predicted_sam_response failed -- replay attack?"));
1510 	goto cleanup;
1511     }
1512 
1513     /* Replay detection */
1514     if ((retval = krb5_unparse_name(context, request->client, &princ_req)))
1515 	goto cleanup;
1516     if ((retval = krb5_unparse_name(context, psr->client, &princ_psr)))
1517 	goto cleanup;
1518     if (strcmp(princ_req, princ_psr) != 0) {
1519 	com_err("krb5kdc", retval = KRB5KDC_ERR_PREAUTH_FAILED,
1520 		gettext("Principal mismatch in SAM psr! -- replay attack?"));
1521 	goto cleanup;
1522     }
1523 
1524     if ((retval = krb5_timeofday(context, &timenow)))
1525 	goto cleanup;
1526 
1527 #ifdef USE_RCACHE
1528     {
1529 	krb5_donot_replay rep;
1530 	extern krb5_deltat rc_lifetime;
1531 	/*
1532 	 * Verify this response came back in a timely manner.
1533 	 * We do this b/c otherwise very old (expunged from the rcache)
1534 	 * psr's would be able to be replayed.
1535 	 */
1536 	if (timenow - psr->stime > rc_lifetime) {
1537 	    com_err("krb5kdc", retval = KRB5KDC_ERR_PREAUTH_FAILED,
1538 	    gettext("SAM psr came back too late! -- replay attack?"));
1539 	    goto cleanup;
1540 	}
1541 
1542 	/* Now check the replay cache. */
1543 	rep.client = princ_psr;
1544 	rep.server = "SAM/rc";  /* Should not match any principal name. */
1545 	rep.ctime = psr->stime;
1546 	rep.cusec = psr->susec;
1547 	if (retval = krb5_rc_store(kdc_context, kdc_rcache, &rep)) {
1548 	    com_err("krb5kdc", retval, gettext("SAM psr replay attack!"));
1549 	    goto cleanup;
1550 	}
1551     }
1552 #endif /* USE_RCACHE */
1553 
1554 
1555     {
1556 	free(scratch.data);
1557 	scratch.length = sr->sam_enc_nonce_or_ts.ciphertext.length;
1558 	if ((scratch.data = (char *) malloc(scratch.length)) == NULL) {
1559 	    retval = ENOMEM;
1560 	    goto cleanup;
1561 	}
1562 
1563 	if ((retval = krb5_c_decrypt(context, &psr->sam_key, /* XXX */ 0,
1564 				     0, &sr->sam_enc_nonce_or_ts, &scratch))) {
1565 	    com_err("krb5kdc", retval, gettext("decrypt nonce_or_ts failed"));
1566 	    goto cleanup;
1567 	}
1568     }
1569 
1570     if ((retval = decode_krb5_enc_sam_response_enc(&scratch, &esre))) {
1571 	com_err("krb5kdc", retval, gettext("decode_krb5_enc_sam_response_enc failed"));
1572 	goto cleanup;
1573     }
1574 
1575     if (esre->sam_timestamp != sr->sam_patimestamp) {
1576       retval = KRB5KDC_ERR_PREAUTH_FAILED;
1577       goto cleanup;
1578     }
1579 
1580     if (labs(timenow - sr->sam_patimestamp) > context->clockskew) {
1581 	retval = KRB5KRB_AP_ERR_SKEW;
1582 	goto cleanup;
1583     }
1584 
1585     setflag(enc_tkt_reply->flags, TKT_FLG_HW_AUTH);
1586 
1587   cleanup:
1588     if (retval) com_err(gettext("krb5kdc"),
1589 			retval,
1590 			gettext("sam verify failure"));
1591     if (scratch.data) free(scratch.data);
1592     if (sr) free(sr);
1593     if (psr) free(psr);
1594     if (esre) free(esre);
1595 
1596     return retval;
1597 }
1598