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