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