xref: /illumos-gate/usr/src/cmd/krb5/krb5kdc/kdc_preauth.c (revision bdfc6d18da790deeec2e0eb09c625902defe2498)
1 /*
2  * Copyright 2004 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     retval = _make_etype_info_entry(context, request,
797 		client_key, client_key->key_data_type[0],
798 		entry, 1);
799     if (retval)
800 	goto cleanup;
801 
802     retval = encode_krb5_etype_info2((const krb5_etype_info_entry **) entry,
803 	&scratch);
804     if (retval)
805 	goto cleanup;
806     tmp_padata->contents = (uchar_t *)scratch->data;
807     tmp_padata->length = scratch->length;
808     *send_pa = tmp_padata;
809 
810     /* For cleanup - we no longer own the contents of the krb5_data
811      * only to pointer to the krb5_data
812      */
813      scratch->data = 0;
814 
815  cleanup:
816     if (entry)
817 	krb5_free_etype_info(context, entry);
818     if (retval) {
819 	if (tmp_padata)
820             free(tmp_padata);
821     }
822     if (scratch)
823             krb5_free_data(context, scratch);
824     return retval;
825 }
826 
827 
828 static krb5_error_code
829 return_pw_salt(context, in_padata, client, request, reply, client_key,
830 	       encrypting_key, send_pa)
831     krb5_context	context;
832     krb5_pa_data *	in_padata;
833     krb5_db_entry *	client;
834     krb5_kdc_req *	request;
835     krb5_kdc_rep *	reply;
836     krb5_key_data *	client_key;
837     krb5_keyblock *	encrypting_key;
838     krb5_pa_data **	send_pa;
839 {
840     krb5_error_code	retval;
841     krb5_pa_data *	padata;
842     krb5_data *		scratch;
843     krb5_data		salt_data;
844     int i;
845 
846     for (i = 0; i < request->nktypes; i++) {
847 	if (enctype_requires_etype_info_2(request->ktype[i]))
848             return 0;
849     }
850 
851     if (client_key->key_data_ver == 1 ||
852 	client_key->key_data_type[1] == KRB5_KDB_SALTTYPE_NORMAL)
853 	return 0;
854 
855     if ((padata = malloc(sizeof(krb5_pa_data))) == NULL)
856 	return ENOMEM;
857     padata->magic = KV5M_PA_DATA;
858     padata->pa_type = KRB5_PADATA_PW_SALT;
859 
860     switch (client_key->key_data_type[1]) {
861     case KRB5_KDB_SALTTYPE_V4:
862 	/* send an empty (V4) salt */
863 	padata->contents = 0;
864 	padata->length = 0;
865 	break;
866     case KRB5_KDB_SALTTYPE_NOREALM:
867 	if ((retval = krb5_principal2salt_norealm(kdc_context,
868 						   request->client,
869 						   &salt_data)))
870 	    goto cleanup;
871 	padata->contents = (krb5_octet *)salt_data.data;
872 	padata->length = salt_data.length;
873 	break;
874     case KRB5_KDB_SALTTYPE_AFS3:
875 	/* send an AFS style realm-based salt */
876 	/* for now, just pass the realm back and let the client
877 	   do the work. In the future, add a kdc configuration
878 	   variable that specifies the old cell name. */
879 	padata->pa_type = KRB5_PADATA_AFS3_SALT;
880 	/* it would be just like ONLYREALM, but we need to pass the 0 */
881 	scratch = krb5_princ_realm(kdc_context, request->client);
882 	if ((padata->contents = malloc(scratch->length+1)) == NULL) {
883 	    retval = ENOMEM;
884 	    goto cleanup;
885 	}
886 	memcpy(padata->contents, scratch->data, scratch->length);
887 	padata->length = scratch->length+1;
888 	padata->contents[scratch->length] = 0;
889 	break;
890     case KRB5_KDB_SALTTYPE_ONLYREALM:
891 	scratch = krb5_princ_realm(kdc_context, request->client);
892 	if ((padata->contents = malloc(scratch->length)) == NULL) {
893 	    retval = ENOMEM;
894 	    goto cleanup;
895 	}
896 	memcpy(padata->contents, scratch->data, scratch->length);
897 	padata->length = scratch->length;
898 	break;
899     case KRB5_KDB_SALTTYPE_SPECIAL:
900 	if ((padata->contents = malloc(client_key->key_data_length[1]))
901 	    == NULL) {
902 	    retval = ENOMEM;
903 	    goto cleanup;
904 	}
905 	memcpy(padata->contents, client_key->key_data_contents[1],
906 	       client_key->key_data_length[1]);
907 	padata->length = client_key->key_data_length[1];
908 	break;
909     default:
910 	free(padata);
911 	return 0;
912     }
913 
914     *send_pa = padata;
915     return 0;
916 
917 cleanup:
918     free(padata);
919     return retval;
920 }
921 
922 static krb5_error_code
923 return_sam_data(context, in_padata, client, request, reply, client_key,
924 	        encrypting_key, send_pa)
925     krb5_context	context;
926     krb5_pa_data *	in_padata;
927     krb5_db_entry *	client;
928     krb5_kdc_req *	request;
929     krb5_kdc_rep *	reply;
930     krb5_key_data *	client_key;
931     krb5_keyblock *	encrypting_key;
932     krb5_pa_data **	send_pa;
933 {
934     krb5_error_code	retval;
935     krb5_data		scratch;
936     int			i;
937 
938     krb5_sam_response		*sr = 0;
939     krb5_predicted_sam_response	*psr = 0;
940 
941     if (in_padata == 0)
942 	return 0;
943 
944     /*
945      * We start by doing the same thing verify_sam_response() does:
946      * extract the psr from the padata (which is an sr). Nothing
947      * here should generate errors! We've already successfully done
948      * all this once.
949      */
950 
951     scratch.data = (char *) in_padata->contents; /* SUNWresync121 XXX */
952     scratch.length = in_padata->length;
953 
954     if ((retval = decode_krb5_sam_response(&scratch, &sr))) {
955 	com_err("krb5kdc", retval,
956 		gettext("return_sam_data(): decode_krb5_sam_response failed"));
957 	goto cleanup;
958     }
959 
960     {
961 	krb5_enc_data tmpdata;
962 
963 	tmpdata.enctype = ENCTYPE_UNKNOWN;
964 	tmpdata.ciphertext = sr->sam_track_id;
965 
966 	scratch.length = tmpdata.ciphertext.length;
967 	if ((scratch.data = (char *) malloc(scratch.length)) == NULL) {
968 	    retval = ENOMEM;
969 	    goto cleanup;
970 	}
971 
972 	if ((retval = krb5_c_decrypt(context, &psr_key, /* XXX */ 0, 0,
973 				     &tmpdata, &scratch))) {
974 	    com_err("krb5kdc", retval,
975 		    gettext("return_sam_data(): decrypt track_id failed"));
976 	    free(scratch.data);
977 	    goto cleanup;
978 	}
979     }
980 
981     if ((retval = decode_krb5_predicted_sam_response(&scratch, &psr))) {
982 	com_err("krb5kdc", retval,
983 		gettext(
984 		"return_sam_data(): decode_krb5_predicted_sam_response failed"));
985 	free(scratch.data);
986 	goto cleanup;
987     }
988 
989     /* We could use sr->sam_flags, but it may be absent or altered. */
990     if (psr->sam_flags & KRB5_SAM_MUST_PK_ENCRYPT_SAD) {
991 	com_err("krb5kdc", retval = KRB5KDC_ERR_PREAUTH_FAILED,
992 		gettext("Unsupported SAM flag must-pk-encrypt-sad"));
993 	goto cleanup;
994     }
995     if (psr->sam_flags & KRB5_SAM_SEND_ENCRYPTED_SAD) {
996 	/* No key munging */
997 	goto cleanup;
998     }
999     if (psr->sam_flags & KRB5_SAM_USE_SAD_AS_KEY) {
1000 	/* Use sam_key instead of client key */
1001 	krb5_free_keyblock_contents(context, encrypting_key);
1002 	krb5_copy_keyblock_contents(context, &psr->sam_key, encrypting_key);
1003 	/* XXX Attach a useful pa_data */
1004 	goto cleanup;
1005     }
1006 
1007     /* Otherwise (no flags set), we XOR the keys */
1008     /* XXX The passwords-04 draft is underspecified here wrt different
1009 	   key types. We will do what I hope to get into the -05 draft. */
1010     {
1011 	krb5_octet *p = encrypting_key->contents;
1012 	krb5_octet *q = psr->sam_key.contents;
1013 	int length = ((encrypting_key->length < psr->sam_key.length)
1014 		      ? encrypting_key->length
1015 		      : psr->sam_key.length);
1016 
1017 	for (i = 0; i < length; i++)
1018 	    p[i] ^= q[i];
1019     }
1020 
1021     /* Post-mixing key correction */
1022     switch (encrypting_key->enctype) {
1023     case ENCTYPE_DES_CBC_CRC:
1024     case ENCTYPE_DES_CBC_MD4:
1025     case ENCTYPE_DES_CBC_MD5:
1026     case ENCTYPE_DES_CBC_RAW:
1027 	mit_des_fixup_key_parity(encrypting_key->contents);
1028 	if (mit_des_is_weak_key(encrypting_key->contents))
1029 	    ((krb5_octet *) encrypting_key->contents)[7] ^= 0xf0;
1030 	break;
1031 
1032     /* XXX case ENCTYPE_DES3_CBC_MD5: listed in 1510bis-04 draft */
1033     case ENCTYPE_DES3_CBC_SHA: /* XXX deprecated? */
1034     case ENCTYPE_DES3_CBC_RAW:
1035     case ENCTYPE_DES3_CBC_SHA1:
1036 	for (i = 0; i < 3; i++) {
1037 	    mit_des_fixup_key_parity(encrypting_key->contents + i * 8);
1038 	    if (mit_des_is_weak_key(encrypting_key->contents + i * 8))
1039 		((krb5_octet *) encrypting_key->contents)[7 + i * 8] ^= 0xf0;
1040 	}
1041 	break;
1042 
1043     default:
1044 	com_err("krb5kdc", retval = KRB5KDC_ERR_PREAUTH_FAILED,
1045 		gettext("Unimplemented keytype for SAM key mixing"));
1046 	goto cleanup;
1047     }
1048 
1049     /* XXX Attach a useful pa_data */
1050 cleanup:
1051     if (sr)
1052 	krb5_free_sam_response(context, sr);
1053     if (psr)
1054 	krb5_free_predicted_sam_response(context, psr);
1055 
1056     return retval;
1057 }
1058 
1059 static struct {
1060   char* name;
1061   int   sam_type;
1062 } *sam_ptr, sam_inst_map[] = {
1063 #if 0	/* SUNWresync121 - unsupported hardware and kdc.log annoyance */
1064   { "SNK4", PA_SAM_TYPE_DIGI_PATH, },
1065   { "SECURID", PA_SAM_TYPE_SECURID, },
1066   { "GRAIL", PA_SAM_TYPE_GRAIL, },
1067 #endif
1068   { 0, 0 },
1069 };
1070 
1071 static krb5_error_code
1072 get_sam_edata(context, request, client, server, pa_data)
1073     krb5_context 	context;
1074     krb5_kdc_req *	request;
1075     krb5_db_entry *	client;
1076     krb5_db_entry *	server;
1077     krb5_pa_data *	pa_data;
1078 {
1079     krb5_error_code		retval;
1080     krb5_sam_challenge		sc;
1081     krb5_predicted_sam_response	psr;
1082     krb5_data *			scratch;
1083     krb5_keyblock encrypting_key;
1084     char response[9];
1085     char inputblock[8];
1086     krb5_data predict_response;
1087 
1088     (void) memset(&encrypting_key, 0, sizeof(krb5_keyblock));
1089     (void) memset(&sc, 0, sizeof(sc));
1090     (void) memset(&psr, 0, sizeof(psr));
1091 
1092     /* Given the client name we can figure out what type of preauth
1093        they need. The spec is currently for querying the database for
1094        names that match the types of preauth used. Later we should
1095        make this mapping show up in kdc.conf. In the meantime, we
1096        hardcode the following:
1097 		/SNK4 -- Digital Pathways SNK/4 preauth.
1098 		/GRAIL -- experimental preauth
1099        The first one found is used. See sam_inst_map above.
1100 
1101        For SNK4 in particular, the key in the database is the key for
1102        the device; kadmin needs a special interface for it.
1103      */
1104 
1105     {
1106       int npr = 1, more;
1107       krb5_db_entry assoc;
1108       krb5_key_data  *assoc_key;
1109       krb5_principal newp;
1110       int probeslot;
1111 
1112       sc.sam_type = 0;
1113 
1114       retval = krb5_copy_principal(kdc_context, request->client, &newp);
1115       if (retval) {
1116 	com_err(gettext("krb5kdc"),
1117 		retval,
1118 		gettext("copying client name for preauth probe"));
1119 	return retval;
1120       }
1121 
1122       probeslot = krb5_princ_size(context, newp)++;
1123       krb5_princ_name(kdc_context, newp) =
1124 	realloc(krb5_princ_name(kdc_context, newp),
1125 		krb5_princ_size(context, newp) * sizeof(krb5_data));
1126 
1127       for(sam_ptr = sam_inst_map; sam_ptr->name; sam_ptr++) {
1128 	krb5_princ_component(kdc_context,newp,probeslot)->data = sam_ptr->name;
1129 	krb5_princ_component(kdc_context,newp,probeslot)->length =
1130 	  strlen(sam_ptr->name);
1131 	npr = 1;
1132 	retval = krb5_db_get_principal(kdc_context, newp, &assoc, &npr, (uint *)&more);
1133 	if(!retval) {
1134 	  sc.sam_type = sam_ptr->sam_type;
1135 	  break;
1136 	}
1137       }
1138 
1139       krb5_princ_component(kdc_context,newp,probeslot)->data = 0;
1140       krb5_princ_component(kdc_context,newp,probeslot)->length = 0;
1141       krb5_princ_size(context, newp)--;
1142 
1143       krb5_free_principal(kdc_context, newp);
1144 
1145       /* if sc.sam_type is set, it worked */
1146       if (sc.sam_type) {
1147 	/* so use assoc to get the key out! */
1148 	{
1149 	  /* here's what do_tgs_req does */
1150 	  retval = krb5_dbe_find_enctype(kdc_context, &assoc,
1151 					 ENCTYPE_DES_CBC_RAW,
1152 					 KRB5_KDB_SALTTYPE_NORMAL,
1153 					 0,		/* Get highest kvno */
1154 					 &assoc_key);
1155 	  if (retval) {
1156 	    char *sname;
1157 	    krb5_unparse_name(kdc_context, request->client, &sname);
1158 	    com_err(gettext("krb5kdc"),
1159 		    retval,
1160 		    gettext("snk4 finding the enctype and key <%s>"),
1161 		    sname);
1162 	    free(sname);
1163 	    return retval;
1164 	  }
1165 	  /* convert server.key into a real key */
1166 	  retval = krb5_dbekd_decrypt_key_data(kdc_context,
1167 					       &master_keyblock,
1168 					       assoc_key, &encrypting_key,
1169 					       NULL);
1170 	  if (retval) {
1171 	    com_err(gettext("krb5kdc"),
1172 		    retval,
1173 		   gettext("snk4 pulling out key entry"));
1174 	    return retval;
1175 	  }
1176 	  /* now we can use encrypting_key... */
1177 	}
1178       } else {
1179 	/* SAM is not an option - so don't return as hint */
1180 	return KRB5_PREAUTH_BAD_TYPE;
1181       }
1182     }
1183     sc.magic = KV5M_SAM_CHALLENGE;
1184     psr.sam_flags = sc.sam_flags = KRB5_SAM_USE_SAD_AS_KEY;
1185 
1186     /* Replay prevention */
1187     if ((retval = krb5_copy_principal(context, request->client, &psr.client)))
1188 	return retval;
1189 #ifdef USE_RCACHE
1190     if ((retval = krb5_us_timeofday(context, &psr.stime, &psr.susec)))
1191 	return retval;
1192 #endif /* USE_RCACHE */
1193 
1194     switch (sc.sam_type) {
1195     case PA_SAM_TYPE_GRAIL:
1196       sc.sam_type_name.data = "Experimental System";
1197       sc.sam_type_name.length = strlen(sc.sam_type_name.data);
1198       sc.sam_challenge_label.data = "experimental challenge label";
1199       sc.sam_challenge_label.length = strlen(sc.sam_challenge_label.data);
1200       sc.sam_challenge.data = "12345";
1201       sc.sam_challenge.length = strlen(sc.sam_challenge.data);
1202 
1203 #if 0 /* Enable this to test "normal" (no flags set) mode.  */
1204       psr.sam_flags = sc.sam_flags = 0;
1205 #endif
1206 
1207       psr.magic = KV5M_PREDICTED_SAM_RESPONSE;
1208       /* string2key on sc.sam_challenge goes in here */
1209       /* eblock is just to set the enctype */
1210       {
1211 	const krb5_enctype type = ENCTYPE_DES_CBC_MD5;
1212 
1213 	if ((retval = krb5_c_string_to_key(context, type, &sc.sam_challenge,
1214 					   0 /* salt */, &psr.sam_key)))
1215 	    goto cleanup;
1216 
1217 	if ((retval = encode_krb5_predicted_sam_response(&psr, &scratch)))
1218 	    goto cleanup;
1219 
1220 	{
1221 	    size_t enclen;
1222 	    krb5_enc_data tmpdata;
1223 
1224 	    if ((retval = krb5_c_encrypt_length(context,
1225 						psr_key.enctype,
1226 						scratch->length, &enclen)))
1227 		goto cleanup;
1228 
1229 	    if ((tmpdata.ciphertext.data = (char *) malloc(enclen)) == NULL) {
1230 		retval = ENOMEM;
1231 		goto cleanup;
1232 	    }
1233 	    tmpdata.ciphertext.length = enclen;
1234 
1235 	    if ((retval = krb5_c_encrypt(context, &psr_key,
1236 					 /* XXX */ 0, 0, scratch, &tmpdata)))
1237 		goto cleanup;
1238 
1239 	    sc.sam_track_id = tmpdata.ciphertext;
1240 	}
1241       }
1242 
1243       sc.sam_response_prompt.data = "response prompt";
1244       sc.sam_response_prompt.length = strlen(sc.sam_response_prompt.data);
1245       sc.sam_pk_for_sad.length = 0;
1246       sc.sam_nonce = 0;
1247       /* Generate checksum */
1248       /*krb5_checksum_size(context, ctype)*/
1249       /*krb5_calculate_checksum(context,ctype,in,in_length,seed,
1250 	seed_length,outcksum) */
1251       /*krb5_verify_checksum(context,ctype,cksum,in,in_length,seed,
1252 	seed_length) */
1253 #if 0 /* XXX a) glue appears broken; b) this gives up the SAD */
1254       sc.sam_cksum.contents = (krb5_octet *)
1255 	malloc(krb5_checksum_size(context, CKSUMTYPE_RSA_MD5_DES));
1256       if (sc.sam_cksum.contents == NULL) return(ENOMEM);
1257 
1258       retval = krb5_calculate_checksum(context, CKSUMTYPE_RSA_MD5_DES,
1259 				       sc.sam_challenge.data,
1260 				       sc.sam_challenge.length,
1261 				       psr.sam_key.contents, /* key */
1262 				       psr.sam_key.length, /* key length */
1263 				       &sc.sam_cksum);
1264       if (retval) { free(sc.sam_cksum.contents); return(retval); }
1265 #endif /* 0 */
1266 
1267       retval = encode_krb5_sam_challenge(&sc, &scratch);
1268       if (retval) goto cleanup;
1269       pa_data->magic = KV5M_PA_DATA;
1270       pa_data->pa_type = KRB5_PADATA_SAM_CHALLENGE;
1271       pa_data->contents = (unsigned char *) scratch->data;
1272       pa_data->length = scratch->length;
1273 
1274       retval = 0;
1275       break;
1276     case PA_SAM_TYPE_DIGI_PATH:
1277       sc.sam_type_name.data = "Digital Pathways";
1278       sc.sam_type_name.length = strlen(sc.sam_type_name.data);
1279 #if 1
1280       sc.sam_challenge_label.data = "Enter the following on your keypad";
1281       sc.sam_challenge_label.length = strlen(sc.sam_challenge_label.data);
1282 #endif
1283       /* generate digit string, take it mod 1000000 (six digits.) */
1284       {
1285 	int j;
1286 	krb5_keyblock session_key;
1287 	char outputblock[8];
1288 	int i;
1289 
1290 	(void) memset(&session_key, 0, sizeof(krb5_keyblock));
1291 	(void) memset(inputblock, 0, 8);
1292 
1293 	retval = krb5_c_make_random_key(kdc_context, ENCTYPE_DES_CBC_CRC,
1294 					&session_key);
1295 
1296 	if (retval) {
1297 	  /* random key failed */
1298 	  com_err(gettext("krb5kdc"),
1299 		 retval,
1300 		gettext("generating random challenge for preauth"));
1301 	  return retval;
1302 	}
1303 	/* now session_key has a key which we can pick bits out of */
1304 	/* we need six decimal digits. Grab 6 bytes, div 2, mod 10 each. */
1305 	if (session_key.length != 8) {
1306 		 retval = KRB5KDC_ERR_ETYPE_NOSUPP,
1307 	  com_err(gettext("krb5kdc"),
1308 		 retval = KRB5KDC_ERR_ETYPE_NOSUPP,
1309 		 gettext("keytype didn't match code expectations"));
1310 	  return retval;
1311 	}
1312 	for(i = 0; i<6; i++) {
1313 	  inputblock[i] = '0' + ((session_key.contents[i]/2) % 10);
1314 	}
1315 	if (session_key.contents)
1316 	  krb5_free_keyblock_contents(kdc_context, &session_key);
1317 
1318 	/* retval = krb5_finish_key(kdc_context, &eblock); */
1319 	/* now we have inputblock containing the 8 byte input to DES... */
1320 	sc.sam_challenge.data = inputblock;
1321 	sc.sam_challenge.length = 6;
1322 
1323 	encrypting_key.enctype = ENCTYPE_DES_CBC_RAW;
1324 
1325 	if (retval) {
1326 	  com_err(gettext("krb5kdc"),
1327 		 retval,
1328 		gettext("snk4 processing key"));
1329 	}
1330 
1331 	{
1332 	    krb5_data plain;
1333 	    krb5_enc_data cipher;
1334 
1335 	    plain.length = 8;
1336 	    plain.data = inputblock;
1337 
1338 	    /* XXX I know this is enough because of the fixed raw enctype.
1339 	       if it's not, the underlying code will return a reasonable
1340 	       error, which should never happen */
1341 	    cipher.ciphertext.length = 8;
1342 	    cipher.ciphertext.data = outputblock;
1343 
1344 	    if ((retval = krb5_c_encrypt(kdc_context, &encrypting_key,
1345 					 /* XXX */ 0, 0, &plain, &cipher))) {
1346 		com_err(gettext("krb5kdc"),
1347 		retval,
1348 		gettext("snk4 response generation failed"));
1349 		return retval;
1350 	    }
1351 	}
1352 
1353 	/* now output block is the raw bits of the response; convert it
1354 	   to display form */
1355 	for (j=0; j<4; j++) {
1356 	  char n[2];
1357 	  int k;
1358 	  n[0] = outputblock[j] & 0xf;
1359 	  n[1] = (outputblock[j]>>4) & 0xf;
1360 	  for (k=0; k<2; k++) {
1361 	    if(n[k] > 9) n[k] = ((n[k]-1)>>2);
1362 	    /* This is equivalent to:
1363 	       if(n[k]>=0xa && n[k]<=0xc) n[k] = 2;
1364 	       if(n[k]>=0xd && n[k]<=0xf) n[k] = 3;
1365 	       */
1366 	  }
1367 	  /* for v4, we keygen: *(j+(char*)&key1) = (n[1]<<4) | n[0]; */
1368 	  /* for v5, we just generate a string */
1369 	  response[2*j+0] = '0' + n[1];
1370 	  response[2*j+1] = '0' + n[0];
1371 	  /* and now, response has what we work with. */
1372 	}
1373 	response[8] = 0;
1374 	predict_response.data = response;
1375 	predict_response.length = 8;
1376 #if 0				/* for debugging, hack the output too! */
1377 sc.sam_challenge_label.data = response;
1378 sc.sam_challenge_label.length = strlen(sc.sam_challenge_label.data);
1379 #endif
1380       }
1381 
1382       psr.magic = KV5M_PREDICTED_SAM_RESPONSE;
1383       /* string2key on sc.sam_challenge goes in here */
1384       /* eblock is just to set the enctype */
1385       {
1386 	retval = krb5_c_string_to_key(context, ENCTYPE_DES_CBC_MD5,
1387 				      &predict_response, 0 /* salt */,
1388 				      &psr.sam_key);
1389 	if (retval) goto cleanup;
1390 
1391 	retval = encode_krb5_predicted_sam_response(&psr, &scratch);
1392 	if (retval) goto cleanup;
1393 
1394 	{
1395 	    size_t enclen;
1396 	    krb5_enc_data tmpdata;
1397 
1398 	    if ((retval = krb5_c_encrypt_length(context,
1399 						psr_key.enctype,
1400 						scratch->length, &enclen)))
1401 		goto cleanup;
1402 
1403 	    if ((tmpdata.ciphertext.data = (char *) malloc(enclen)) == NULL) {
1404 		retval = ENOMEM;
1405 		goto cleanup;
1406 	    }
1407 	    tmpdata.ciphertext.length = enclen;
1408 
1409 	    if ((retval = krb5_c_encrypt(context, &psr_key,
1410 					 /* XXX */ 0, 0, scratch, &tmpdata)))
1411 		goto cleanup;
1412 
1413 	    sc.sam_track_id = tmpdata.ciphertext;
1414 	}
1415 	if (retval) goto cleanup;
1416       }
1417 
1418       sc.sam_response_prompt.data = "Enter the displayed response";
1419       sc.sam_response_prompt.length = strlen(sc.sam_response_prompt.data);
1420       sc.sam_pk_for_sad.length = 0;
1421       sc.sam_nonce = 0;
1422       /* Generate checksum */
1423       /*krb5_checksum_size(context, ctype)*/
1424       /*krb5_calculate_checksum(context,ctype,in,in_length,seed,
1425 	seed_length,outcksum) */
1426       /*krb5_verify_checksum(context,ctype,cksum,in,in_length,seed,
1427 	seed_length) */
1428 #if 0 /* XXX a) glue appears broken; b) this gives up the SAD */
1429       sc.sam_cksum.contents = (krb5_octet *)
1430 	malloc(krb5_checksum_size(context, CKSUMTYPE_RSA_MD5_DES));
1431       if (sc.sam_cksum.contents == NULL) return(ENOMEM);
1432 
1433       retval = krb5_calculate_checksum(context, CKSUMTYPE_RSA_MD5_DES,
1434 				       sc.sam_challenge.data,
1435 				       sc.sam_challenge.length,
1436 				       psr.sam_key.contents, /* key */
1437 				       psr.sam_key.length, /* key length */
1438 				       &sc.sam_cksum);
1439       if (retval) { free(sc.sam_cksum.contents); return(retval); }
1440 #endif /* 0 */
1441 
1442       retval = encode_krb5_sam_challenge(&sc, &scratch);
1443       if (retval) goto cleanup;
1444       pa_data->magic = KV5M_PA_DATA;
1445       pa_data->pa_type = KRB5_PADATA_SAM_CHALLENGE;
1446       pa_data->contents = (unsigned char *) scratch->data;
1447       pa_data->length = scratch->length;
1448 
1449       retval = 0;
1450       break;
1451     }
1452 
1453 cleanup:
1454     krb5_free_keyblock_contents(context, &encrypting_key);
1455     return retval;
1456 }
1457 
1458 static krb5_error_code
1459 verify_sam_response(context, client, request, enc_tkt_reply, pa)
1460     krb5_context	context;
1461     krb5_db_entry *	client;
1462     krb5_kdc_req *	request;
1463     krb5_enc_tkt_part * enc_tkt_reply;
1464     krb5_pa_data *	pa;
1465 {
1466     krb5_error_code		retval;
1467     krb5_data			scratch;
1468     krb5_sam_response		*sr = 0;
1469     krb5_predicted_sam_response	*psr = 0;
1470     krb5_enc_sam_response_enc	*esre = 0;
1471     krb5_timestamp		timenow;
1472     char			*princ_req = 0, *princ_psr = 0;
1473 
1474     scratch.data = (char *) pa->contents;
1475     scratch.length = pa->length;
1476 
1477     if ((retval = decode_krb5_sam_response(&scratch, &sr))) {
1478 	scratch.data = 0;
1479 	com_err("krb5kdc", retval, gettext("decode_krb5_sam_response failed"));
1480 	goto cleanup;
1481     }
1482 
1483     /* XXX We can only handle the challenge/response model of SAM.
1484 	   See passwords-04, par 4.1, 4.2 */
1485     {
1486       krb5_enc_data tmpdata;
1487 
1488       tmpdata.enctype = ENCTYPE_UNKNOWN;
1489       tmpdata.ciphertext = sr->sam_track_id;
1490 
1491       scratch.length = tmpdata.ciphertext.length;
1492       if ((scratch.data = (char *) malloc(scratch.length)) == NULL) {
1493 	  retval = ENOMEM;
1494 	  goto cleanup;
1495       }
1496 
1497       if ((retval = krb5_c_decrypt(context, &psr_key, /* XXX */ 0, 0,
1498 				   &tmpdata, &scratch))) {
1499 	  com_err(gettext("krb5kdc"),
1500 		retval,
1501 		gettext("decrypt track_id failed"));
1502 	  goto cleanup;
1503       }
1504     }
1505 
1506     if ((retval = decode_krb5_predicted_sam_response(&scratch, &psr))) {
1507 	com_err(gettext("krb5kdc"), retval,
1508 		gettext("decode_krb5_predicted_sam_response failed -- replay attack?"));
1509 	goto cleanup;
1510     }
1511 
1512     /* Replay detection */
1513     if ((retval = krb5_unparse_name(context, request->client, &princ_req)))
1514 	goto cleanup;
1515     if ((retval = krb5_unparse_name(context, psr->client, &princ_psr)))
1516 	goto cleanup;
1517     if (strcmp(princ_req, princ_psr) != 0) {
1518 	com_err("krb5kdc", retval = KRB5KDC_ERR_PREAUTH_FAILED,
1519 		gettext("Principal mismatch in SAM psr! -- replay attack?"));
1520 	goto cleanup;
1521     }
1522 
1523     if ((retval = krb5_timeofday(context, &timenow)))
1524 	goto cleanup;
1525 
1526 #ifdef USE_RCACHE
1527     {
1528 	krb5_donot_replay rep;
1529 	extern krb5_deltat rc_lifetime;
1530 	/*
1531 	 * Verify this response came back in a timely manner.
1532 	 * We do this b/c otherwise very old (expunged from the rcache)
1533 	 * psr's would be able to be replayed.
1534 	 */
1535 	if (timenow - psr->stime > rc_lifetime) {
1536 	    com_err("krb5kdc", retval = KRB5KDC_ERR_PREAUTH_FAILED,
1537 	    gettext("SAM psr came back too late! -- replay attack?"));
1538 	    goto cleanup;
1539 	}
1540 
1541 	/* Now check the replay cache. */
1542 	rep.client = princ_psr;
1543 	rep.server = "SAM/rc";  /* Should not match any principal name. */
1544 	rep.ctime = psr->stime;
1545 	rep.cusec = psr->susec;
1546 	if (retval = krb5_rc_store(kdc_context, kdc_rcache, &rep)) {
1547 	    com_err("krb5kdc", retval, gettext("SAM psr replay attack!"));
1548 	    goto cleanup;
1549 	}
1550     }
1551 #endif /* USE_RCACHE */
1552 
1553 
1554     {
1555 	free(scratch.data);
1556 	scratch.length = sr->sam_enc_nonce_or_ts.ciphertext.length;
1557 	if ((scratch.data = (char *) malloc(scratch.length)) == NULL) {
1558 	    retval = ENOMEM;
1559 	    goto cleanup;
1560 	}
1561 
1562 	if ((retval = krb5_c_decrypt(context, &psr->sam_key, /* XXX */ 0,
1563 				     0, &sr->sam_enc_nonce_or_ts, &scratch))) {
1564 	    com_err("krb5kdc", retval, gettext("decrypt nonce_or_ts failed"));
1565 	    goto cleanup;
1566 	}
1567     }
1568 
1569     if ((retval = decode_krb5_enc_sam_response_enc(&scratch, &esre))) {
1570 	com_err("krb5kdc", retval, gettext("decode_krb5_enc_sam_response_enc failed"));
1571 	goto cleanup;
1572     }
1573 
1574     if (esre->sam_timestamp != sr->sam_patimestamp) {
1575       retval = KRB5KDC_ERR_PREAUTH_FAILED;
1576       goto cleanup;
1577     }
1578 
1579     if (labs(timenow - sr->sam_patimestamp) > context->clockskew) {
1580 	retval = KRB5KRB_AP_ERR_SKEW;
1581 	goto cleanup;
1582     }
1583 
1584     setflag(enc_tkt_reply->flags, TKT_FLG_HW_AUTH);
1585 
1586   cleanup:
1587     if (retval) com_err(gettext("krb5kdc"),
1588 			retval,
1589 			gettext("sam verify failure"));
1590     if (scratch.data) free(scratch.data);
1591     if (sr) free(sr);
1592     if (psr) free(psr);
1593     if (esre) free(esre);
1594 
1595     return retval;
1596 }
1597