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  * Copyright 1995 by the Massachusetts Institute of Technology.  All
10  * Rights Reserved.
11  *
12  * Export of this software from the United States of America may
13  *   require a specific license from the United States Government.
14  *   It is the responsibility of any person or organization contemplating
15  *   export to obtain such a license before exporting.
16  *
17  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
18  * distribute this software and its documentation for any purpose and
19  * without fee is hereby granted, provided that the above copyright
20  * notice appear in all copies and that both that copyright notice and
21  * this permission notice appear in supporting documentation, and that
22  * the name of M.I.T. not be used in advertising or publicity pertaining
23  * to distribution of the software without specific, written prior
24  * permission.  Furthermore if you modify this software you must label
25  * your software as modified software and not distribute it in such a
26  * fashion that it might be confused with the original M.I.T. software.
27  * M.I.T. makes no representations about the suitability of
28  * this software for any purpose.  It is provided "as is" without express
29  * or implied warranty.
30  *
31  */
32 
33 /*
34  * This file contains routines for establishing, verifying, and any other
35  * necessary functions, for utilizing the pre-authentication field of the
36  * kerberos kdc request, with various hardware/software verification devices.
37  */
38 
39 #include <k5-int.h>
40 
41 typedef krb5_error_code (*pa_function)(krb5_context,
42 				       krb5_kdc_req *request,
43 				       krb5_pa_data *in_padata,
44 				       krb5_pa_data **out_padata,
45 				       krb5_data *salt,
46 				       krb5_data *s2kparams,
47 				       krb5_enctype *etype,
48 				       krb5_keyblock *as_key,
49 				       krb5_prompter_fct prompter_fct,
50 				       void *prompter_data,
51 				       krb5_gic_get_as_key_fct gak_fct,
52 				       void *gak_data);
53 
54 typedef struct _pa_types_t {
55     krb5_preauthtype type;
56     pa_function fct;
57     int flags;
58 } pa_types_t;
59 
60 #define PA_REAL 0x0001
61 #define PA_INFO 0x0002
62 
63 /*ARGSUSED*/
64 static
65 krb5_error_code pa_salt(krb5_context context,
66 			krb5_kdc_req *request,
67 			krb5_pa_data *in_padata,
68 			krb5_pa_data **out_padata,
69 			krb5_data *salt,
70 			krb5_data *s2kparams,
71 			krb5_enctype *etype,
72 			krb5_keyblock *as_key,
73 			krb5_prompter_fct prompter, void *prompter_data,
74 			krb5_gic_get_as_key_fct gak_fct, void *gak_data)
75 {
76     krb5_data tmp;
77 
78     tmp.data = (char *)in_padata->contents;
79     tmp.length = in_padata->length;
80     krb5_free_data_contents(context, salt);
81     krb5int_copy_data_contents(context, &tmp, salt);
82 
83     if (in_padata->pa_type == KRB5_PADATA_AFS3_SALT)
84 	salt->length = -1;
85 
86     return(0);
87 }
88 
89 /*ARGSUSED*/
90 static
91 krb5_error_code pa_enc_timestamp(krb5_context context,
92 				 krb5_kdc_req *request,
93 				 krb5_pa_data *in_padata,
94 				 krb5_pa_data **out_padata,
95 				 krb5_data *salt,
96 				 krb5_data *s2kparams,
97 				 krb5_enctype *etype,
98 				 krb5_keyblock *as_key,
99 				 krb5_prompter_fct prompter,
100 				 void *prompter_data,
101 				 krb5_gic_get_as_key_fct gak_fct,
102 				 void *gak_data)
103 {
104     krb5_error_code ret;
105     krb5_pa_enc_ts pa_enc;
106     krb5_data *tmp;
107     krb5_enc_data enc_data;
108     krb5_pa_data *pa;
109 
110     if (as_key->length == 0) {
111 #ifdef DEBUG
112 	if (salt != NULL && salt->data != NULL) {
113 	    fprintf (stderr, "%s:%d: salt len=%d", __FILE__, __LINE__,
114 		 salt->length);
115 	    if (salt->length > 0)
116 	        fprintf (stderr, " '%*s'", salt->length, salt->data);
117 	    fprintf (stderr, "; *etype=%d request->ktype[0]=%d\n",
118 		 *etype, request->ktype[0]);
119 	}
120 #endif
121        if (ret = ((*gak_fct)(context, request->client,
122 			     *etype ? *etype : request->ktype[0],
123 			     prompter, prompter_data,
124 			     salt, s2kparams, as_key, gak_data)))
125            return(ret);
126     }
127 
128     /* now get the time of day, and encrypt it accordingly */
129 
130     if (ret = krb5_us_timeofday(context, &pa_enc.patimestamp, &pa_enc.pausec))
131 	return(ret);
132 
133     if (ret = encode_krb5_pa_enc_ts(&pa_enc, &tmp))
134 	return(ret);
135 
136 #ifdef DEBUG
137     fprintf (stderr, "key type %d bytes %02x %02x ...\n",
138 	     as_key->enctype,
139 	     as_key->contents[0], as_key->contents[1]);
140 #endif
141     ret = krb5_encrypt_helper(context, as_key,
142 			      KRB5_KEYUSAGE_AS_REQ_PA_ENC_TS,
143 			      tmp, &enc_data);
144 #ifdef DEBUG
145     fprintf (stderr, "enc data { type=%d kvno=%d data=%02x %02x ... }\n",
146 	     enc_data.enctype, enc_data.kvno,
147 	     0xff & enc_data.ciphertext.data[0],
148 	     0xff & enc_data.ciphertext.data[1]);
149 #endif
150 
151     krb5_free_data(context, tmp);
152 
153     if (ret) {
154 	krb5_xfree(enc_data.ciphertext.data);
155 	return(ret);
156     }
157 
158     ret = encode_krb5_enc_data(&enc_data, &tmp);
159 
160     krb5_xfree(enc_data.ciphertext.data);
161 
162     if (ret)
163 	return(ret);
164 
165     if ((pa = (krb5_pa_data *) malloc(sizeof(krb5_pa_data))) == NULL) {
166 	krb5_free_data(context, tmp);
167 	return(ENOMEM);
168     }
169 
170     pa->magic = KV5M_PA_DATA;
171     pa->pa_type = KRB5_PADATA_ENC_TIMESTAMP;
172     pa->length = tmp->length;
173     pa->contents = (krb5_octet *) tmp->data;
174 
175     *out_padata = pa;
176 
177     krb5_xfree(tmp);
178 
179     return(0);
180 }
181 
182 static
183 char *sam_challenge_banner(krb5_int32 sam_type)
184 {
185     char *label;
186 
187     switch (sam_type) {
188     case PA_SAM_TYPE_ENIGMA:	/* Enigma Logic */
189 	label = "Challenge for Enigma Logic mechanism";
190 	break;
191     case PA_SAM_TYPE_DIGI_PATH: /*  Digital Pathways */
192     case PA_SAM_TYPE_DIGI_PATH_HEX: /*  Digital Pathways */
193 	label = "Challenge for Digital Pathways mechanism";
194 	break;
195     case PA_SAM_TYPE_ACTIVCARD_DEC: /*  Digital Pathways */
196     case PA_SAM_TYPE_ACTIVCARD_HEX: /*  Digital Pathways */
197 	label = "Challenge for Activcard mechanism";
198 	break;
199     case PA_SAM_TYPE_SKEY_K0:	/*  S/key where  KDC has key 0 */
200 	label = "Challenge for Enhanced S/Key mechanism";
201 	break;
202     case PA_SAM_TYPE_SKEY:	/*  Traditional S/Key */
203 	label = "Challenge for Traditional S/Key mechanism";
204 	break;
205     case PA_SAM_TYPE_SECURID:	/*  Security Dynamics */
206 	label = "Challenge for Security Dynamics mechanism";
207 	break;
208     case PA_SAM_TYPE_SECURID_PREDICT:	/* predictive Security Dynamics */
209 	label = "Challenge for Security Dynamics mechanism";
210 	break;
211     default:
212 	label = "Challenge from authentication server";
213 	break;
214     }
215 
216     return(label);
217 }
218 
219 /* this macro expands to the int,ptr necessary for "%.*s" in an sprintf */
220 
221 #define SAMDATA(kdata, str, maxsize) \
222 	(int)((kdata.length)? \
223 	      ((((kdata.length)<=(maxsize))?(kdata.length):strlen(str))): \
224 	      strlen(str)), \
225 	(kdata.length)? \
226 	((((kdata.length)<=(maxsize))?(kdata.data):(str))):(str)
227 
228 /* XXX Danger! This code is not in sync with the kerberos-password-02
229    draft.  This draft cannot be implemented as written.  This code is
230    compatible with earlier versions of mit krb5 and cygnus kerbnet. */
231 
232 /*ARGSUSED*/
233 static
234 krb5_error_code pa_sam(krb5_context context,
235 		       krb5_kdc_req *request,
236 		       krb5_pa_data *in_padata,
237 		       krb5_pa_data **out_padata,
238 		       krb5_data *salt,
239 		       krb5_data *s2kparams,
240 		       krb5_enctype *etype,
241 		       krb5_keyblock *as_key,
242 		       krb5_prompter_fct prompter,
243 		       void *prompter_data,
244 		       krb5_gic_get_as_key_fct gak_fct,
245 		       void *gak_data)
246 {
247     krb5_error_code		ret;
248     krb5_data			tmpsam;
249     char			name[100], banner[100];
250     char			prompt[100], response[100];
251     krb5_data			response_data;
252     krb5_prompt			kprompt;
253     krb5_prompt_type		prompt_type;
254     krb5_data			defsalt;
255     krb5_sam_challenge		*sam_challenge = 0;
256     krb5_sam_response		sam_response;
257     /* these two get encrypted and stuffed in to sam_response */
258     krb5_enc_sam_response_enc	enc_sam_response_enc;
259     krb5_data *			scratch;
260     krb5_pa_data *		pa;
261     krb5_enc_data *		enc_data;
262     size_t			enclen;
263 
264     if (prompter == NULL)
265 	return (EIO);
266 
267     tmpsam.length = in_padata->length;
268     tmpsam.data = (char *) in_padata->contents;
269     if (ret = decode_krb5_sam_challenge(&tmpsam, &sam_challenge))
270 	return(ret);
271 
272     if (sam_challenge->sam_flags & KRB5_SAM_MUST_PK_ENCRYPT_SAD) {
273 	krb5_xfree(sam_challenge);
274 	return(KRB5_SAM_UNSUPPORTED);
275     }
276     /* If we need the password from the user (USE_SAD_AS_KEY not set),	*/
277     /* then get it here.  Exception for "old" KDCs with CryptoCard	*/
278     /* support which uses the USE_SAD_AS_KEY flag, but still needs pwd	*/
279 
280     if (!(sam_challenge->sam_flags & KRB5_SAM_USE_SAD_AS_KEY) ||
281 	(sam_challenge->sam_type == PA_SAM_TYPE_CRYPTOCARD)) {
282 
283 	/* etype has either been set by caller or by KRB5_PADATA_ETYPE_INFO */
284 	/* message from the KDC.  If it is not set, pick an enctype that we */
285 	/* think the KDC will have for us.                                  */
286 
287 	if (etype && *etype == 0)
288            *etype = ENCTYPE_DES_CBC_CRC;
289 
290 	if ((ret = (gak_fct)(context, request->client, *etype, prompter,
291 			prompter_data, salt, s2kparams, as_key, gak_data)))
292 	   return(ret);
293     }
294 
295     sprintf(name, "%.*s",
296 	    SAMDATA(sam_challenge->sam_type_name, "SAM Authentication",
297 		    sizeof(name) - 1));
298 
299     sprintf(banner, "%.*s",
300 	    SAMDATA(sam_challenge->sam_challenge_label,
301 		    sam_challenge_banner(sam_challenge->sam_type),
302 		    sizeof(banner)-1));
303 
304     /* sprintf(prompt, "Challenge is [%s], %s: ", challenge, prompt); */
305     sprintf(prompt, "%s%.*s%s%.*s",
306 	    sam_challenge->sam_challenge.length?"Challenge is [":"",
307 	    SAMDATA(sam_challenge->sam_challenge, "", 20),
308 	    sam_challenge->sam_challenge.length?"], ":"",
309 	    SAMDATA(sam_challenge->sam_response_prompt, "passcode", 55));
310 
311     response_data.data = response;
312     response_data.length = sizeof(response);
313 
314     kprompt.prompt = prompt;
315     kprompt.hidden = sam_challenge->sam_challenge.length?0:1;
316     kprompt.reply = &response_data;
317     prompt_type = KRB5_PROMPT_TYPE_PREAUTH;
318 
319     /* PROMPTER_INVOCATION */
320     krb5int_set_prompt_types(context, &prompt_type);
321     if (ret = ((*prompter)(context, prompter_data, name,
322 			   banner, 1, &kprompt))) {
323 	krb5_xfree(sam_challenge);
324 	krb5int_set_prompt_types(context, 0);
325 	return(ret);
326     }
327     krb5int_set_prompt_types(context, 0);
328 
329     enc_sam_response_enc.sam_nonce = sam_challenge->sam_nonce;
330     if (sam_challenge->sam_nonce == 0) {
331 	if (ret = krb5_us_timeofday(context,
332 				&enc_sam_response_enc.sam_timestamp,
333 				&enc_sam_response_enc.sam_usec)) {
334 		krb5_xfree(sam_challenge);
335 		return(ret);
336 	}
337 
338 	sam_response.sam_patimestamp = enc_sam_response_enc.sam_timestamp;
339     }
340 
341     /* XXX What if more than one flag is set?  */
342     if (sam_challenge->sam_flags & KRB5_SAM_SEND_ENCRYPTED_SAD) {
343 
344 	/* Most of this should be taken care of before we get here.  We */
345 	/* will need the user's password and as_key to encrypt the SAD	*/
346 	/* and we want to preserve ordering of user prompts (first	*/
347 	/* password, then SAM data) so that user's won't be confused.	*/
348 
349 	if (as_key->length) {
350 	    krb5_free_keyblock_contents(context, as_key);
351 	    as_key->length = 0;
352 	}
353 
354 	/* generate a salt using the requested principal */
355 
356 	if ((salt->length == -1) && (salt->data == NULL)) {
357 	    if (ret = krb5_principal2salt(context, request->client,
358 					  &defsalt)) {
359 		krb5_xfree(sam_challenge);
360 		return(ret);
361 	    }
362 
363 	    salt = &defsalt;
364 	} else {
365 	    defsalt.length = 0;
366 	}
367 
368 	/* generate a key using the supplied password */
369 
370 	ret = krb5_c_string_to_key(context, ENCTYPE_DES_CBC_MD5,
371 				   (krb5_data *)gak_data, salt, as_key);
372 
373 	if (defsalt.length)
374 	    krb5_xfree(defsalt.data);
375 
376 	if (ret) {
377 	    krb5_xfree(sam_challenge);
378 	    return(ret);
379 	}
380 
381 	/* encrypt the passcode with the key from above */
382 
383 	enc_sam_response_enc.sam_sad = response_data;
384     } else if (sam_challenge->sam_flags & KRB5_SAM_USE_SAD_AS_KEY) {
385 
386 	/* process the key as password */
387 
388 	if (as_key->length) {
389 	    krb5_free_keyblock_contents(context, as_key);
390 	    as_key->length = 0;
391 	}
392 
393 #if 0
394 	if ((salt->length == -1) && (salt->data == NULL)) {
395 	    if (ret = krb5_principal2salt(context, request->client,
396 					  &defsalt)) {
397 		krb5_xfree(sam_challenge);
398 		return(ret);
399 	    }
400 
401 	    salt = &defsalt;
402 	} else {
403 	    defsalt.length = 0;
404 	}
405 #else
406 	defsalt.length = 0;
407 	salt = NULL;
408 #endif
409 
410 	/* XXX As of the passwords-04 draft, no enctype is specified,
411 	   the server uses ENCTYPE_DES_CBC_MD5. In the future the
412 	   server should send a PA-SAM-ETYPE-INFO containing the enctype. */
413 
414 	ret = krb5_c_string_to_key(context, ENCTYPE_DES_CBC_MD5,
415 				   &response_data, salt, as_key);
416 
417 	if (defsalt.length)
418 	    krb5_xfree(defsalt.data);
419 
420 	if (ret) {
421 	    krb5_xfree(sam_challenge);
422 	    return(ret);
423 	}
424 
425 	enc_sam_response_enc.sam_sad.length = 0;
426     } else {
427 	/* Eventually, combine SAD with long-term key to get
428 	   encryption key.  */
429 	return KRB5_PREAUTH_BAD_TYPE;
430     }
431 
432     /* copy things from the challenge */
433     sam_response.sam_nonce = sam_challenge->sam_nonce;
434     sam_response.sam_flags = sam_challenge->sam_flags;
435     sam_response.sam_track_id = sam_challenge->sam_track_id;
436     sam_response.sam_type = sam_challenge->sam_type;
437     sam_response.magic = KV5M_SAM_RESPONSE;
438 
439     krb5_xfree(sam_challenge);
440 
441     /* encode the encoded part of the response */
442     if (ret = encode_krb5_enc_sam_response_enc(&enc_sam_response_enc,
443 					       &scratch))
444 	return(ret);
445 
446     /*
447      * Solaris Kerberos:
448      * Using new crypto interface now so we can get rid of the
449      * old modules.
450      */
451     if ((ret = krb5_c_encrypt_length(context, as_key->enctype,
452 				scratch->length, &enclen))) {
453 	krb5_free_data(context, scratch);
454 	return(ret);
455     }
456 
457     enc_data = &sam_response.sam_enc_nonce_or_ts;
458     enc_data->magic = KV5M_ENC_DATA;
459     enc_data->kvno = 0;
460     enc_data->enctype = as_key->enctype;
461     enc_data->ciphertext.length = enclen;
462 
463     if ((enc_data->ciphertext.data = MALLOC(enclen)) == NULL) {
464 	enc_data->ciphertext.length = 0;
465 	krb5_free_data(context, scratch);
466 	return(ENOMEM);
467     }
468 
469     if ((ret = krb5_c_encrypt(context, as_key, 0, 0,
470 	scratch, enc_data))) {
471 	FREE(enc_data->ciphertext.data, enclen);
472 	enc_data->ciphertext.data = NULL;
473 	enc_data->ciphertext.length = 0;
474     }
475 
476     krb5_free_data(context, scratch);
477 
478     if (ret)
479 	return(ret);
480 
481     /* sam_enc_key is reserved for future use */
482     sam_response.sam_enc_key.ciphertext.length = 0;
483 
484     if ((pa = malloc(sizeof(krb5_pa_data))) == NULL)
485 	return(ENOMEM);
486 
487     if (ret = encode_krb5_sam_response(&sam_response, &scratch)) {
488 	free(pa);
489 	return(ret);
490     }
491 
492     pa->magic = KV5M_PA_DATA;
493     pa->pa_type = KRB5_PADATA_SAM_RESPONSE;
494     pa->length = scratch->length;
495     pa->contents = (krb5_octet *) scratch->data;
496 
497     *out_padata = pa;
498 
499     return(0);
500 }
501 
502 static
503 krb5_error_code pa_sam_2(krb5_context context,
504 				krb5_kdc_req *request,
505 				krb5_pa_data *in_padata,
506 				krb5_pa_data **out_padata,
507 				krb5_data *salt,
508 			 krb5_data *s2kparams,
509 				krb5_enctype *etype,
510 				krb5_keyblock *as_key,
511 				krb5_prompter_fct prompter,
512 				void *prompter_data,
513 				krb5_gic_get_as_key_fct gak_fct,
514 				void *gak_data) {
515 
516    krb5_error_code retval;
517    krb5_sam_challenge_2 *sc2 = NULL;
518    krb5_sam_challenge_2_body *sc2b = NULL;
519    krb5_data tmp_data;
520    krb5_data response_data;
521    char name[100], banner[100], prompt[100], response[100];
522    krb5_prompt kprompt;
523    krb5_prompt_type prompt_type;
524    krb5_data defsalt;
525    krb5_checksum **cksum;
526    krb5_data *scratch = NULL;
527    krb5_boolean valid_cksum = 0;
528    krb5_enc_sam_response_enc_2 enc_sam_response_enc_2;
529    krb5_sam_response_2 sr2;
530    size_t ciph_len;
531    krb5_pa_data *sam_padata;
532 
533    if (prompter == NULL)
534 	return KRB5_LIBOS_CANTREADPWD;
535 
536    tmp_data.length = in_padata->length;
537    tmp_data.data = (char *)in_padata->contents;
538 
539    if ((retval = decode_krb5_sam_challenge_2(&tmp_data, &sc2)))
540 	return(retval);
541 
542    retval = decode_krb5_sam_challenge_2_body(&sc2->sam_challenge_2_body, &sc2b);
543 
544    if (retval)
545 	return(retval);
546 
547    if (!sc2->sam_cksum || ! *sc2->sam_cksum) {
548 	krb5_free_sam_challenge_2(context, sc2);
549 	krb5_free_sam_challenge_2_body(context, sc2b);
550 	return(KRB5_SAM_NO_CHECKSUM);
551    }
552 
553    if (sc2b->sam_flags & KRB5_SAM_MUST_PK_ENCRYPT_SAD) {
554 	krb5_free_sam_challenge_2(context, sc2);
555 	krb5_free_sam_challenge_2_body(context, sc2b);
556 	return(KRB5_SAM_UNSUPPORTED);
557    }
558 
559    if (!valid_enctype(sc2b->sam_etype)) {
560 	krb5_free_sam_challenge_2(context, sc2);
561 	krb5_free_sam_challenge_2_body(context, sc2b);
562 	return(KRB5_SAM_INVALID_ETYPE);
563    }
564 
565    /* All of the above error checks are KDC-specific, that is, they	*/
566    /* assume a failure in the KDC reply.  By returning anything other	*/
567    /* than KRB5_KDC_UNREACH, KRB5_PREAUTH_FAILED,		*/
568    /* KRB5_LIBOS_PWDINTR, or KRB5_REALM_CANT_RESOLVE, the client will	*/
569    /* most likely go on to try the AS_REQ against master KDC		*/
570 
571    if (!(sc2b->sam_flags & KRB5_SAM_USE_SAD_AS_KEY)) {
572 	/* We will need the password to obtain the key used for */
573 	/* the checksum, and encryption of the sam_response.	*/
574 	/* Go ahead and get it now, preserving the ordering of	*/
575 	/* prompts for the user.				*/
576 
577 	retval = (gak_fct)(context, request->client,
578 			sc2b->sam_etype, prompter,
579 			prompter_data, salt, s2kparams, as_key, gak_data);
580 	if (retval) {
581 	   krb5_free_sam_challenge_2(context, sc2);
582 	   krb5_free_sam_challenge_2_body(context, sc2b);
583 	   return(retval);
584 	}
585    }
586 
587    sprintf(name, "%.*s",
588 	SAMDATA(sc2b->sam_type_name, "SAM Authentication",
589 	sizeof(name) - 1));
590 
591    sprintf(banner, "%.*s",
592 	SAMDATA(sc2b->sam_challenge_label,
593 	sam_challenge_banner(sc2b->sam_type),
594 	sizeof(banner)-1));
595 
596    sprintf(prompt, "%s%.*s%s%.*s",
597 	sc2b->sam_challenge.length?"Challenge is [":"",
598 	SAMDATA(sc2b->sam_challenge, "", 20),
599 	sc2b->sam_challenge.length?"], ":"",
600 	SAMDATA(sc2b->sam_response_prompt, "passcode", 55));
601 
602    response_data.data = response;
603    response_data.length = sizeof(response);
604    kprompt.prompt = prompt;
605    kprompt.hidden = 1;
606    kprompt.reply = &response_data;
607 
608    prompt_type = KRB5_PROMPT_TYPE_PREAUTH;
609    krb5int_set_prompt_types(context, &prompt_type);
610 
611    if ((retval = ((*prompter)(context, prompter_data, name,
612 				banner, 1, &kprompt)))) {
613 	krb5_free_sam_challenge_2(context, sc2);
614 	krb5_free_sam_challenge_2_body(context, sc2b);
615 	krb5int_set_prompt_types(context, 0);
616 	return(retval);
617    }
618 
619    krb5int_set_prompt_types(context, (krb5_prompt_type *)NULL);
620 
621    /* Generate salt used by string_to_key() */
622    if ((salt->length == -1) && (salt->data == NULL)) {
623 	if ((retval =
624              krb5_principal2salt(context, request->client, &defsalt))) {
625 	   krb5_free_sam_challenge_2(context, sc2);
626 	   krb5_free_sam_challenge_2_body(context, sc2b);
627 	   return(retval);
628 	}
629         salt = &defsalt;
630    } else {
631 	defsalt.length = 0;
632    }
633 
634    /* Get encryption key to be used for checksum and sam_response */
635    if (!(sc2b->sam_flags & KRB5_SAM_USE_SAD_AS_KEY)) {
636 	/* as_key = string_to_key(password) */
637 
638 	if (as_key->length) {
639 	   krb5_free_keyblock_contents(context, as_key);
640 	   as_key->length = 0;
641 	}
642 
643         /* generate a key using the supplied password */
644 	retval = krb5_c_string_to_key(context, sc2b->sam_etype,
645                                    (krb5_data *)gak_data, salt, as_key);
646 
647 	if (retval) {
648 	   krb5_free_sam_challenge_2(context, sc2);
649 	   krb5_free_sam_challenge_2_body(context, sc2b);
650 	   if (defsalt.length) krb5_xfree(defsalt.data);
651 	   return(retval);
652 	}
653 
654         if (!(sc2b->sam_flags & KRB5_SAM_SEND_ENCRYPTED_SAD)) {
655 	   /* as_key = combine_key (as_key, string_to_key(SAD)) */
656 	   krb5_keyblock tmp_kb;
657 
658            retval = krb5_c_string_to_key(context, sc2b->sam_etype,
659 				&response_data, salt, &tmp_kb);
660 
661            if (retval) {
662 		krb5_free_sam_challenge_2(context, sc2);
663 		krb5_free_sam_challenge_2_body(context, sc2b);
664 		if (defsalt.length) krb5_xfree(defsalt.data);
665 		return(retval);
666 	   }
667 
668            /* This should be a call to the crypto library some day */
669 	   /* key types should already match the sam_etype */
670 	   retval = krb5int_c_combine_keys(context, as_key, &tmp_kb, as_key);
671 
672            if (retval) {
673 		krb5_free_sam_challenge_2(context, sc2);
674 		krb5_free_sam_challenge_2_body(context, sc2b);
675 		if (defsalt.length) krb5_xfree(defsalt.data);
676 		return(retval);
677 	   }
678            krb5_free_keyblock_contents(context, &tmp_kb);
679 	}
680         if (defsalt.length)
681 	   krb5_xfree(defsalt.data);
682 
683    } else {
684 	/* as_key = string_to_key(SAD) */
685 
686 	if (as_key->length) {
687 	   krb5_free_keyblock_contents(context, as_key);
688 	   as_key->length = 0;
689 	}
690 
691         /* generate a key using the supplied password */
692 	retval = krb5_c_string_to_key(context, sc2b->sam_etype,
693 				&response_data, salt, as_key);
694 
695 	if (defsalt.length)
696 	   krb5_xfree(defsalt.data);
697 
698 	if (retval) {
699 	   krb5_free_sam_challenge_2(context, sc2);
700 	   krb5_free_sam_challenge_2_body(context, sc2b);
701 	   return(retval);
702 	}
703    }
704 
705    /* Now we have a key, verify the checksum on the sam_challenge */
706 
707    cksum = sc2->sam_cksum;
708 
709    while (*cksum) {
710 	/* Check this cksum */
711 	retval = krb5_c_verify_checksum(context, as_key,
712 			KRB5_KEYUSAGE_PA_SAM_CHALLENGE_CKSUM,
713 			&sc2->sam_challenge_2_body,
714 			*cksum, &valid_cksum);
715 	if (retval) {
716 	   krb5_free_data(context, scratch);
717 	   krb5_free_sam_challenge_2(context, sc2);
718 	   krb5_free_sam_challenge_2_body(context, sc2b);
719 	   return(retval);
720 	}
721         if (valid_cksum)
722 	   break;
723 	cksum++;
724    }
725 
726    if (!valid_cksum) {
727 
728 	/* If KRB5_SAM_SEND_ENCRYPTED_SAD is set, then password is only */
729 	/* source for checksum key.  Therefore, a bad checksum means a	*/
730 	/* bad password.  Don't give that direct feedback to someone	*/
731 	/* trying to brute-force passwords.				*/
732 
733 	if (!(sc2b->sam_flags & KRB5_SAM_SEND_ENCRYPTED_SAD))
734 	krb5_free_sam_challenge_2(context, sc2);
735 	krb5_free_sam_challenge_2_body(context, sc2b);
736 	/*
737          * Note: We return AP_ERR_BAD_INTEGRITY so upper-level applications
738 	 * can interpret that as "password incorrect", which is probably
739 	 * the best error we can return in this situation.
740 	 */
741 	return(KRB5KRB_AP_ERR_BAD_INTEGRITY);
742    }
743 
744    /* fill in enc_sam_response_enc_2 */
745    enc_sam_response_enc_2.magic = KV5M_ENC_SAM_RESPONSE_ENC_2;
746    enc_sam_response_enc_2.sam_nonce = sc2b->sam_nonce;
747    if (sc2b->sam_flags & KRB5_SAM_SEND_ENCRYPTED_SAD) {
748 	enc_sam_response_enc_2.sam_sad = response_data;
749    } else {
750 	enc_sam_response_enc_2.sam_sad.data = NULL;
751 	enc_sam_response_enc_2.sam_sad.length = 0;
752    }
753 
754    /* encode and encrypt enc_sam_response_enc_2 with as_key */
755    retval = encode_krb5_enc_sam_response_enc_2(&enc_sam_response_enc_2,
756 		&scratch);
757    if (retval) {
758 	krb5_free_sam_challenge_2(context, sc2);
759 	krb5_free_sam_challenge_2_body(context, sc2b);
760 	return(retval);
761    }
762 
763    /* Fill in sam_response_2 */
764    memset(&sr2, 0, sizeof(sr2));
765    sr2.sam_type = sc2b->sam_type;
766    sr2.sam_flags = sc2b->sam_flags;
767    sr2.sam_track_id = sc2b->sam_track_id;
768    sr2.sam_nonce = sc2b->sam_nonce;
769 
770    /* Now take care of sr2.sam_enc_nonce_or_sad by encrypting encoded	*/
771    /* enc_sam_response_enc_2 from above */
772 
773    retval = krb5_c_encrypt_length(context, as_key->enctype, scratch->length,
774                                   &ciph_len);
775    if (retval) {
776 	krb5_free_sam_challenge_2(context, sc2);
777 	krb5_free_sam_challenge_2_body(context, sc2b);
778 	return(retval);
779    }
780    sr2.sam_enc_nonce_or_sad.ciphertext.length = ciph_len;
781 
782    sr2.sam_enc_nonce_or_sad.ciphertext.data =
783 	(char *)malloc(sr2.sam_enc_nonce_or_sad.ciphertext.length);
784 
785    if (!sr2.sam_enc_nonce_or_sad.ciphertext.data) {
786 	krb5_free_sam_challenge_2(context, sc2);
787 	krb5_free_sam_challenge_2_body(context, sc2b);
788 	return(ENOMEM);
789    }
790 
791    retval = krb5_c_encrypt(context, as_key, KRB5_KEYUSAGE_PA_SAM_RESPONSE,
792 		NULL, scratch, &sr2.sam_enc_nonce_or_sad);
793    if (retval) {
794 	krb5_free_sam_challenge_2(context, sc2);
795 	krb5_free_sam_challenge_2_body(context, sc2b);
796 	krb5_free_data(context, scratch);
797 	krb5_free_data_contents(context, &sr2.sam_enc_nonce_or_sad.ciphertext);
798 	return(retval);
799    }
800    krb5_free_data(context, scratch);
801    scratch = NULL;
802 
803    /* Encode the sam_response_2 */
804    retval = encode_krb5_sam_response_2(&sr2, &scratch);
805    krb5_free_sam_challenge_2(context, sc2);
806    krb5_free_sam_challenge_2_body(context, sc2b);
807    krb5_free_data_contents(context, &sr2.sam_enc_nonce_or_sad.ciphertext);
808 
809    if (retval) {
810 	return (retval);
811    }
812 
813    /* Almost there, just need to make padata !	*/
814    sam_padata = malloc(sizeof(krb5_pa_data));
815    if (sam_padata == NULL) {
816 	krb5_free_data(context, scratch);
817 	return(ENOMEM);
818    }
819 
820    sam_padata->magic = KV5M_PA_DATA;
821    sam_padata->pa_type = KRB5_PADATA_SAM_RESPONSE_2;
822    sam_padata->length = scratch->length;
823    sam_padata->contents = (krb5_octet *) scratch->data;
824 
825    *out_padata = sam_padata;
826 
827    return(0);
828 }
829 
830 
831 static pa_types_t pa_types[] = {
832     {
833 	KRB5_PADATA_PW_SALT,
834 	pa_salt,
835 	PA_INFO,
836     },
837     {
838 	KRB5_PADATA_AFS3_SALT,
839 	pa_salt,
840 	PA_INFO,
841     },
842     {
843 	KRB5_PADATA_ENC_TIMESTAMP,
844 	pa_enc_timestamp,
845 	PA_REAL,
846     },
847     {
848      	KRB5_PADATA_SAM_CHALLENGE_2,
849 	pa_sam_2,
850 	PA_REAL,
851     },
852     {
853 	KRB5_PADATA_SAM_CHALLENGE,
854 	pa_sam,
855 	PA_REAL,
856     },
857     {
858 	-1,
859 	NULL,
860 	0,
861     },
862 };
863 
864 krb5_error_code
865 krb5_do_preauth(krb5_context context,
866 		krb5_kdc_req *request,
867 		krb5_pa_data **in_padata, krb5_pa_data ***out_padata,
868 		krb5_data *salt, krb5_data *s2kparams,
869 		krb5_enctype *etype,
870 		krb5_keyblock *as_key,
871 		krb5_prompter_fct prompter, void *prompter_data,
872 		krb5_gic_get_as_key_fct gak_fct, void *gak_data)
873 {
874     int h, i, j, out_pa_list_size;
875     int seen_etype_info2 = 0;
876     krb5_pa_data *out_pa = NULL, **out_pa_list = NULL;
877     krb5_data scratch;
878     krb5_etype_info etype_info = NULL;
879     krb5_error_code ret;
880     static const int paorder[] = { PA_INFO, PA_REAL };
881     int realdone;
882 
883     KRB5_LOG0(KRB5_INFO, "krb5_do_preauth() start");
884 
885     if (in_padata == NULL) {
886 	*out_padata = NULL;
887 	return(0);
888     }
889 
890 #ifdef DEBUG
891     if (salt && salt->data && salt->length > 0) {
892     	fprintf (stderr, "salt len=%d", salt->length);
893 	    if (salt->length > 0)
894 		fprintf (stderr, " '%*s'", salt->length, salt->data);
895 	    fprintf (stderr, "; preauth data types:");
896 	    for (i = 0; in_padata[i]; i++) {
897 		fprintf (stderr, " %d", in_padata[i]->pa_type);
898     	}
899     	fprintf (stderr, "\n");
900     }
901 #endif
902 
903     out_pa_list = NULL;
904     out_pa_list_size = 0;
905 
906     /* first do all the informational preauths, then the first real one */
907 
908     for (h=0; h<(sizeof(paorder)/sizeof(paorder[0])); h++) {
909 	realdone = 0;
910 	for (i=0; in_padata[i] && !realdone; i++) {
911 	    int k, l, etype_found, valid_etype_found;
912 	    /*
913 	     * This is really gross, but is necessary to prevent
914 	     * lossge when talking to a 1.0.x KDC, which returns an
915 	     * erroneous PA-PW-SALT when it returns a KRB-ERROR
916 	     * requiring additional preauth.
917 	     */
918 	    switch (in_padata[i]->pa_type) {
919 	    case KRB5_PADATA_ETYPE_INFO:
920 	    case KRB5_PADATA_ETYPE_INFO2:
921 	    {
922 		krb5_preauthtype pa_type = in_padata[i]->pa_type;
923 		if (etype_info) {
924 		    if (seen_etype_info2 || pa_type != KRB5_PADATA_ETYPE_INFO2)
925 			continue;
926 		    if (pa_type == KRB5_PADATA_ETYPE_INFO2) {
927                         krb5_free_etype_info( context, etype_info);
928 			etype_info = NULL;
929                     }
930 		}
931 
932 		scratch.length = in_padata[i]->length;
933 		scratch.data = (char *) in_padata[i]->contents;
934 		if (pa_type == KRB5_PADATA_ETYPE_INFO2) {
935                     seen_etype_info2++;
936                     ret = decode_krb5_etype_info2(&scratch, &etype_info);
937 		}
938 		else ret = decode_krb5_etype_info(&scratch, &etype_info);
939 		if (ret) {
940                     ret = 0; /*Ignore error and etype_info element*/
941                     krb5_free_etype_info( context, etype_info);
942                     etype_info = NULL;
943                     continue;
944 		}
945 		if (etype_info[0] == NULL) {
946 		    krb5_free_etype_info(context, etype_info);
947 		    etype_info = NULL;
948 		    break;
949 		}
950 		/*
951 		 * Select first etype in our request which is also in
952 		 * etype-info (preferring client request ktype order).
953 		 */
954 		for (etype_found = 0, valid_etype_found = 0, k = 0;
955 		       	!etype_found && k < request->nktypes; k++) {
956 		    for (l = 0; etype_info[l]; l++) {
957 			if (etype_info[l]->etype == request->ktype[k]) {
958 			    etype_found++;
959 			    break;
960 			}
961 			/* check if program has support for this etype for more
962 			 * precise error reporting.
963 			 */
964 			if (valid_enctype(etype_info[l]->etype))
965 			    valid_etype_found++;
966 		    }
967 		}
968 		if (!etype_found) {
969 		    KRB5_LOG(KRB5_ERR, "error !etype_found, "
970 				"valid_etype_found = %d",
971 				valid_etype_found);
972 		    if (valid_etype_found) {
973 			/* supported enctype but not requested */
974 			ret = KRB5_CONFIG_ETYPE_NOSUPP;
975 			goto cleanup;
976 		    }
977 		    else {
978 			/* unsupported enctype */
979 			ret = KRB5_PROG_ETYPE_NOSUPP;
980 			goto cleanup;
981 		    }
982 
983 		}
984 		scratch.data = (char *) etype_info[l]->salt;
985 		scratch.length = etype_info[l]->length;
986 		krb5_free_data_contents(context, salt);
987 		if (scratch.length == KRB5_ETYPE_NO_SALT)
988 		  salt->data = NULL;
989 		else
990                     if ((ret = krb5int_copy_data_contents( context,
991 				&scratch, salt)) != 0)
992 			goto cleanup;
993 		*etype = etype_info[l]->etype;
994 		krb5_free_data_contents(context, s2kparams);
995 		if ((ret = krb5int_copy_data_contents(context,
996 				&etype_info[l]->s2kparams,
997 				s2kparams)) != 0)
998 		  goto cleanup;
999 		break;
1000 	    }
1001 	    case KRB5_PADATA_PW_SALT:
1002 	    case KRB5_PADATA_AFS3_SALT:
1003 		if (etype_info)
1004 		    continue;
1005 		break;
1006 	    default:
1007 		;
1008 	    }
1009 	    for (j=0; pa_types[j].type >= 0; j++) {
1010 		if ((in_padata[i]->pa_type == pa_types[j].type) &&
1011 		    (pa_types[j].flags & paorder[h])) {
1012 		    out_pa = NULL;
1013 
1014 		    if (ret = ((*pa_types[j].fct)(context, request,
1015 					in_padata[i], &out_pa,
1016 					salt, s2kparams, etype, as_key,
1017 					prompter, prompter_data,
1018 					gak_fct, gak_data))) {
1019 			goto cleanup;
1020 		    }
1021 
1022 		    if (out_pa) {
1023 			if (out_pa_list == NULL) {
1024 			    if ((out_pa_list =
1025 				 (krb5_pa_data **)
1026 				 malloc(2*sizeof(krb5_pa_data *)))
1027 				== NULL) {
1028 				ret = ENOMEM;
1029 				goto cleanup;
1030 			     }
1031 			} else {
1032 			    if ((out_pa_list =
1033 				 (krb5_pa_data **)
1034 				 realloc(out_pa_list,
1035 					 (out_pa_list_size+2)*
1036 					 sizeof(krb5_pa_data *)))
1037 				== NULL) {
1038 				/* XXX this will leak the pointers which
1039 				   have already been allocated.  oh well. */
1040 				ret = ENOMEM;
1041 				goto cleanup;
1042 			    }
1043 			}
1044 
1045 			out_pa_list[out_pa_list_size++] = out_pa;
1046 		    }
1047 		    if (paorder[h] == PA_REAL)
1048 			realdone = 1;
1049 		}
1050 	    }
1051 	}
1052     }
1053 
1054     if (out_pa_list)
1055 	out_pa_list[out_pa_list_size++] = NULL;
1056 
1057     *out_padata = out_pa_list;
1058     if (etype_info)
1059 	krb5_free_etype_info(context, etype_info);
1060 
1061     KRB5_LOG0(KRB5_INFO, "krb5_do_preauth() end");
1062     return(0);
1063 cleanup:
1064     if (out_pa_list) {
1065 	out_pa_list[out_pa_list_size++] = NULL;
1066 	krb5_free_pa_data(context, out_pa_list);
1067     }
1068     if (etype_info)
1069 	krb5_free_etype_info(context, etype_info);
1070 
1071     KRB5_LOG0(KRB5_INFO, "krb5_do_preauth() end");
1072     return (ret);
1073 
1074 }
1075