9807eeafd059a97c19d050a8d54eacb5cd737be0
[krb5.git] / src / lib / krb5 / krb / preauth2.c
1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /*
3  * Copyright 1995, 2003, 2008 by the Massachusetts Institute of Technology.  All
4  * Rights Reserved.
5  *
6  * Export of this software from the United States of America may
7  *   require a specific license from the United States Government.
8  *   It is the responsibility of any person or organization contemplating
9  *   export to obtain such a license before exporting.
10  *
11  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
12  * distribute this software and its documentation for any purpose and
13  * without fee is hereby granted, provided that the above copyright
14  * notice appear in all copies and that both that copyright notice and
15  * this permission notice appear in supporting documentation, and that
16  * the name of M.I.T. not be used in advertising or publicity pertaining
17  * to distribution of the software without specific, written prior
18  * permission.  Furthermore if you modify this software you must label
19  * your software as modified software and not distribute it in such a
20  * fashion that it might be confused with the original M.I.T. software.
21  * M.I.T. makes no representations about the suitability of
22  * this software for any purpose.  It is provided "as is" without express
23  * or implied warranty.
24  *
25  */
26
27 /*
28  * This file contains routines for establishing, verifying, and any other
29  * necessary functions, for utilizing the pre-authentication field of the
30  * kerberos kdc request, with various hardware/software verification devices.
31  */
32
33 #include "k5-int.h"
34 #if APPLE_PKINIT
35 #include "pkinit_client.h"
36 #include "pkinit_cert_store.h"
37 #endif /* APPLE_PKINIT */
38 #include "osconf.h"
39 #include <krb5/preauth_plugin.h>
40 #include "int-proto.h"
41 #include "fast.h"
42
43 #if !defined(_WIN32)
44 #include <unistd.h>
45 #endif
46
47 /* This structure lets us keep track of all of the modules which are loaded,
48  * turning the list of modules and their lists of implemented preauth types
49  * into a single list which we can walk easily. */
50 struct krb5_preauth_context_st {
51     int n_modules;
52     struct krb5_preauth_context_module_st {
53         /* Which of the possibly more than one preauth types which the
54          * module supports we're using at this point in the list. */
55         krb5_preauthtype pa_type;
56         /* Encryption types which the client claims to support -- we
57          * copy them directly into the krb5_kdc_req structure during
58          * krb5_preauth_prepare_request(). */
59         krb5_enctype *enctypes;
60         /* The plugin's module data and a function to clear it. */
61         krb5_clpreauth_moddata moddata;
62         krb5_clpreauth_fini_fn client_fini;
63         /* The module's table, and some of its members, copied here for
64          * convenience when we populated the list. */
65         const char *name;
66         int flags, use_count;
67         krb5_clpreauth_process_fn client_process;
68         krb5_clpreauth_tryagain_fn client_tryagain;
69         krb5_clpreauth_supply_gic_opts_fn client_supply_gic_opts;
70         krb5_clpreauth_request_init_fn client_req_init;
71         krb5_clpreauth_request_fini_fn client_req_fini;
72         /* The per-request context which the client_req_init() function
73          * might allocate, which we'll need to clean up later by
74          * calling the client_req_fini() function. */
75         krb5_clpreauth_modreq modreq;
76         /* A pointer to the request_context pointer.  All modules within
77          * a plugin will point at the request_context of the first
78          * module within the plugin. */
79         krb5_clpreauth_modreq *modreq_p;
80     } *modules;
81 };
82
83 typedef krb5_error_code (*pa_function)(krb5_context,
84                                        krb5_kdc_req *request,
85                                        krb5_pa_data *in_padata,
86                                        krb5_pa_data **out_padata,
87                                        krb5_data *salt, krb5_data *s2kparams,
88                                        krb5_enctype *etype,
89                                        krb5_keyblock *as_key,
90                                        krb5_prompter_fct prompter_fct,
91                                        void *prompter_data,
92                                        krb5_gic_get_as_key_fct gak_fct,
93                                        void *gak_data);
94
95 typedef struct _pa_types_t {
96     krb5_preauthtype type;
97     pa_function fct;
98     int flags;
99 } pa_types_t;
100
101 /* Create the per-krb5_context context. This means loading the modules
102  * if we haven't done that yet (applications which never obtain initial
103  * credentials should never hit this routine), breaking up the module's
104  * list of support pa_types so that we can iterate over the modules more
105  * easily, and copying over the relevant parts of the module's table. */
106 void KRB5_CALLCONV
107 krb5_init_preauth_context(krb5_context kcontext)
108 {
109     int n_tables, n_modules, i, count;
110     krb5_plugin_initvt_fn *plugins = NULL, *pl;
111     struct krb5_clpreauth_vtable_st *vtables = NULL, *vt;
112     struct krb5_preauth_context_module_st *mod;
113     krb5_preauth_context *context = NULL;
114     krb5_clpreauth_moddata moddata;
115     krb5_preauthtype pa_type, *pat;
116     krb5_boolean first;
117     krb5_clpreauth_modreq *rcpp;
118
119     /* Only do this once for each krb5_context */
120     if (kcontext->preauth_context != NULL)
121         return;
122
123     /* Auto-register encrypted challenge and (if possible) pkinit. */
124     k5_plugin_register_dyn(kcontext, PLUGIN_INTERFACE_CLPREAUTH, "pkinit",
125                            "preauth");
126     k5_plugin_register(kcontext, PLUGIN_INTERFACE_CLPREAUTH,
127                        "encrypted_challenge",
128                        clpreauth_encrypted_challenge_initvt);
129
130     /* Get all available clpreauth vtables. */
131     if (k5_plugin_load_all(kcontext, PLUGIN_INTERFACE_CLPREAUTH, &plugins))
132         return;
133     for (count = 0; plugins[count] != NULL; count++);
134     vtables = calloc(count, sizeof(*vtables));
135     if (vtables == NULL)
136         goto cleanup;
137     for (pl = plugins, n_tables = 0; *pl != NULL; pl++) {
138         if ((*pl)(kcontext, 1, 1, (krb5_plugin_vtable)&vtables[n_tables]) == 0)
139             n_tables++;
140     }
141
142     /* Count how many modules we ended up loading, and how many preauth
143      * types we may claim to support as a result. */
144     n_modules = 0;
145     for (i = 0; i < n_tables; i++) {
146         for (count = 0; vtables[i].pa_type_list[count] > 0; count++);
147         n_modules += count;
148     }
149
150     /* Allocate the space we need. */
151     context = malloc(sizeof(*context));
152     if (context == NULL)
153         goto cleanup;
154     context->modules = calloc(n_modules, sizeof(*context->modules));
155     if (context->modules == NULL)
156         goto cleanup;
157
158     /* fill in the structure */
159     n_modules = 0;
160     for (i = 0; i < n_tables; i++) {
161         vt = &vtables[i];
162         if ((vt->pa_type_list != NULL) && (vt->process != NULL)) {
163             moddata = NULL;
164             if (vt->init != NULL && vt->init(kcontext, &moddata) != 0) {
165 #ifdef DEBUG
166                 fprintf(stderr, "init err, skipping module \"%s\"\n",
167                         vt->name);
168 #endif
169                 continue;
170             }
171
172             rcpp = NULL;
173             for (pat = vt->pa_type_list, first = TRUE; *pat > 0;
174                  pat++, first = FALSE) {
175                 pa_type = *pat;
176                 mod = &context->modules[n_modules];
177                 mod->pa_type = pa_type;
178                 mod->enctypes = vt->enctype_list;
179                 mod->moddata = moddata;
180                 /* Only call client_fini once per plugin */
181                 if (first)
182                     mod->client_fini = vt->fini;
183                 else
184                     mod->client_fini = NULL;
185                 mod->name = vt->name;
186                 mod->flags = (*vt->flags)(kcontext, pa_type);
187                 mod->use_count = 0;
188                 mod->client_process = vt->process;
189                 mod->client_tryagain = vt->tryagain;
190                 mod->client_supply_gic_opts = first ? vt->gic_opts : NULL;
191                 mod->modreq = NULL;
192                 /*
193                  * Only call request_init and request_fini once per plugin.
194                  * Only the first module within each plugin will ever
195                  * have request_context filled in.  Every module within
196                  * the plugin will have its request_context_pp pointing
197                  * to that entry's request_context.  That way all the
198                  * modules within the plugin share the same request_context
199                  */
200                 if (first) {
201                     mod->client_req_init = vt->request_init;
202                     mod->client_req_fini = vt->request_fini;
203                     rcpp = &mod->modreq;
204                 } else {
205                     mod->client_req_init = NULL;
206                     mod->client_req_fini = NULL;
207                 }
208                 mod->modreq_p = rcpp;
209 #ifdef DEBUG
210                 fprintf(stderr, "init module \"%s\", pa_type %d, flag %d\n",
211                         mod->name, mod->pa_type, mod->flags);
212 #endif
213                 n_modules++;
214             }
215         }
216     }
217     context->n_modules = n_modules;
218
219     /* Place the constructed preauth context into the krb5 context. */
220     kcontext->preauth_context = context;
221     context = NULL;
222
223 cleanup:
224     if (context)
225         free(context->modules);
226     free(context);
227     k5_plugin_free_modules(kcontext, plugins);
228     free(vtables);
229 }
230
231 /* Zero the use counts for the modules herein.  Usually used before we
232  * start processing any data from the server, at which point every module
233  * will again be able to take a crack at whatever the server sent. */
234 void KRB5_CALLCONV
235 krb5_clear_preauth_context_use_counts(krb5_context context)
236 {
237     int i;
238     if (context->preauth_context != NULL) {
239         for (i = 0; i < context->preauth_context->n_modules; i++) {
240             context->preauth_context->modules[i].use_count = 0;
241         }
242     }
243 }
244
245
246 /* Free the per-krb5_context preauth_context. This means clearing any
247  * plugin-specific context which may have been created, and then
248  * freeing the context itself. */
249 void KRB5_CALLCONV
250 krb5_free_preauth_context(krb5_context context)
251 {
252     int i;
253     struct krb5_preauth_context_module_st *mod;
254
255     if (context == NULL || context->preauth_context == NULL)
256         return;
257     for (i = 0; i < context->preauth_context->n_modules; i++) {
258         mod = &context->preauth_context->modules[i];
259         if (mod->client_fini != NULL)
260             mod->client_fini(context, mod->moddata);
261         zap(mod, sizeof(*mod));
262     }
263     free(context->preauth_context->modules);
264     free(context->preauth_context);
265     context->preauth_context = NULL;
266 }
267
268 /* Initialize the per-AS-REQ context. This means calling the client_req_init
269  * function to give the plugin a chance to allocate a per-request context. */
270 void KRB5_CALLCONV
271 krb5_preauth_request_context_init(krb5_context context)
272 {
273     int i;
274     struct krb5_preauth_context_module_st *mod;
275
276     if (context->preauth_context == NULL)
277         krb5_init_preauth_context(context);
278     if (context->preauth_context == NULL)
279         return;
280     for (i = 0; i < context->preauth_context->n_modules; i++) {
281         mod = &context->preauth_context->modules[i];
282         if (mod->client_req_init != NULL)
283             mod->client_req_init(context, mod->moddata, mod->modreq_p);
284     }
285 }
286
287 /* Free the per-AS-REQ context. This means clearing any request-specific
288  * context which the plugin may have created. */
289 void KRB5_CALLCONV
290 krb5_preauth_request_context_fini(krb5_context context)
291 {
292     int i;
293     struct krb5_preauth_context_module_st *mod;
294
295     if (context->preauth_context == NULL)
296         return;
297     for (i = 0; i < context->preauth_context->n_modules; i++) {
298         mod = &context->preauth_context->modules[i];
299         if (mod->modreq != NULL) {
300             if (mod->client_req_fini != NULL)
301                 mod->client_req_fini(context, mod->moddata, mod->modreq);
302             mod->modreq = NULL;
303         }
304     }
305 }
306
307 /* Add the named encryption type to the existing list of ktypes. */
308 static void
309 grow_ktypes(krb5_enctype **out_ktypes, int *out_nktypes, krb5_enctype ktype)
310 {
311     int i;
312     krb5_enctype *ktypes;
313     for (i = 0; i < *out_nktypes; i++) {
314         if ((*out_ktypes)[i] == ktype)
315             return;
316     }
317     ktypes = malloc((*out_nktypes + 2) * sizeof(ktype));
318     if (ktypes) {
319         for (i = 0; i < *out_nktypes; i++)
320             ktypes[i] = (*out_ktypes)[i];
321         ktypes[i++] = ktype;
322         ktypes[i] = 0;
323         free(*out_ktypes);
324         *out_ktypes = ktypes;
325         *out_nktypes = i;
326     }
327 }
328
329 /*
330  * Add the given list of pa_data items to the existing list of items.
331  * Factored out here to make reading the do_preauth logic easier to read.
332  */
333 static int
334 grow_pa_list(krb5_pa_data ***out_pa_list, int *out_pa_list_size,
335              krb5_pa_data **addition, int num_addition)
336 {
337     krb5_pa_data **pa_list;
338     int i, j;
339
340     if (out_pa_list == NULL || addition == NULL) {
341         return EINVAL;
342     }
343
344     if (*out_pa_list == NULL) {
345         /* Allocate room for the new additions and a NULL terminator. */
346         pa_list = malloc((num_addition + 1) * sizeof(krb5_pa_data *));
347         if (pa_list == NULL)
348             return ENOMEM;
349         for (i = 0; i < num_addition; i++)
350             pa_list[i] = addition[i];
351         pa_list[i] = NULL;
352         *out_pa_list = pa_list;
353         *out_pa_list_size = num_addition;
354     } else {
355         /*
356          * Allocate room for the existing entries plus
357          * the new additions and a NULL terminator.
358          */
359         pa_list = malloc((*out_pa_list_size + num_addition + 1)
360                          * sizeof(krb5_pa_data *));
361         if (pa_list == NULL)
362             return ENOMEM;
363         for (i = 0; i < *out_pa_list_size; i++)
364             pa_list[i] = (*out_pa_list)[i];
365         for (j = 0; j < num_addition;)
366             pa_list[i++] = addition[j++];
367         pa_list[i] = NULL;
368         free(*out_pa_list);
369         *out_pa_list = pa_list;
370         *out_pa_list_size = i;
371     }
372     return 0;
373 }
374
375 /*
376  * Retrieve a specific piece of information required by the plugin and
377  * return it in a new krb5_data item.  There are separate request_types
378  * to obtain the data and free it.
379  *
380  * This may require massaging data into a contrived format, but it will
381  * hopefully keep us from having to reveal library-internal functions
382  * or data to the plugin modules.
383  */
384
385 static krb5_error_code
386 client_data_proc(krb5_context kcontext, krb5_clpreauth_rock rock,
387                  krb5_int32 request_type, krb5_data **retdata)
388 {
389     krb5_data *ret;
390     krb5_error_code retval;
391     char *data;
392
393     if (rock->magic != CLIENT_ROCK_MAGIC)
394         return EINVAL;
395     if (retdata == NULL)
396         return EINVAL;
397
398     switch (request_type) {
399     case krb5_clpreauth_get_etype:
400     {
401         krb5_enctype *eptr;
402         ret = malloc(sizeof(krb5_data));
403         if (ret == NULL)
404             return ENOMEM;
405         data = malloc(sizeof(krb5_enctype));
406         if (data == NULL) {
407             free(ret);
408             return ENOMEM;
409         }
410         ret->data = data;
411         ret->length = sizeof(krb5_enctype);
412         eptr = (krb5_enctype *)data;
413         *eptr = *rock->etype;
414         *retdata = ret;
415         return 0;
416     }
417     break;
418     case krb5_clpreauth_free_etype:
419         ret = *retdata;
420         if (ret == NULL)
421             return 0;
422         if (ret->data)
423             free(ret->data);
424         free(ret);
425         return 0;
426         break;
427     case krb5_clpreauth_fast_armor: {
428         krb5_keyblock *key = NULL;
429         ret = calloc(1, sizeof(krb5_data));
430         if (ret == NULL)
431             return ENOMEM;
432         retval = 0;
433         if (rock->fast_state->armor_key)
434             retval = krb5_copy_keyblock(kcontext, rock->fast_state->armor_key,
435                                         &key);
436         if (retval == 0) {
437             ret->data = (char *) key;
438             ret->length = key?sizeof(krb5_keyblock):0;
439             key = NULL;
440         }
441         if (retval == 0) {
442             *retdata = ret;
443             ret = NULL;
444         }
445         if (ret)
446             free(ret);
447         return retval;
448     }
449     case krb5_clpreauth_free_fast_armor:
450         ret = *retdata;
451         if (ret) {
452             if (ret->data)
453                 krb5_free_keyblock(kcontext, (krb5_keyblock *) ret->data);
454             free(ret);
455             *retdata = NULL;
456         }
457         return 0;
458     default:
459         return EINVAL;
460     }
461 }
462
463 /* Tweak the request body, for now adding any enctypes which the module claims
464  * to add support for to the list, but in the future perhaps doing more
465  * involved things. */
466 void KRB5_CALLCONV
467 krb5_preauth_prepare_request(krb5_context kcontext,
468                              krb5_gic_opt_ext *opte,
469                              krb5_kdc_req *request)
470 {
471     int i, j;
472
473     if (kcontext->preauth_context == NULL) {
474         return;
475     }
476     /* Add the module-specific enctype list to the request, but only if
477      * it's something we can safely modify. */
478     if (!(opte && (opte->flags & KRB5_GET_INIT_CREDS_OPT_ETYPE_LIST))) {
479         for (i = 0; i < kcontext->preauth_context->n_modules; i++) {
480             if (kcontext->preauth_context->modules[i].enctypes == NULL)
481                 continue;
482             for (j = 0; kcontext->preauth_context->modules[i].enctypes[j] != 0; j++) {
483                 grow_ktypes(&request->ktype, &request->nktypes,
484                             kcontext->preauth_context->modules[i].enctypes[j]);
485             }
486         }
487     }
488 }
489
490 /* Find the first module which provides for the named preauth type which also
491  * hasn't had a chance to run yet (INFO modules don't count, because as a rule
492  * they don't generate preauth data), and run it. */
493 static krb5_error_code
494 run_preauth_plugins(krb5_context kcontext,
495                     int module_required_flags,
496                     krb5_kdc_req *request,
497                     krb5_data *encoded_request_body,
498                     krb5_data *encoded_previous_request,
499                     krb5_pa_data *in_padata,
500                     krb5_prompter_fct prompter,
501                     void *prompter_data,
502                     krb5_clpreauth_get_as_key_fn gak_fct,
503                     krb5_data *salt,
504                     krb5_data *s2kparams,
505                     void *gak_data,
506                     krb5_clpreauth_rock preauth_rock,
507                     krb5_keyblock *as_key,
508                     krb5_pa_data ***out_pa_list,
509                     int *out_pa_list_size,
510                     int *module_ret,
511                     int *module_flags,
512                     krb5_gic_opt_ext *opte)
513 {
514     int i;
515     krb5_pa_data **out_pa_data;
516     krb5_error_code ret;
517     struct krb5_preauth_context_module_st *module;
518
519     if (kcontext->preauth_context == NULL) {
520         return ENOENT;
521     }
522     /* iterate over all loaded modules */
523     for (i = 0; i < kcontext->preauth_context->n_modules; i++) {
524         module = &kcontext->preauth_context->modules[i];
525         /* skip over those which don't match the preauth type */
526         if (module->pa_type != in_padata->pa_type)
527             continue;
528         /* skip over those which don't match the flags (INFO vs REAL, mainly) */
529         if ((module->flags & module_required_flags) == 0)
530             continue;
531         /* if it's a REAL module, try to call it only once per library call */
532         if (module_required_flags & PA_REAL) {
533             if (module->use_count > 0) {
534                 TRACE_PREAUTH_SKIP(kcontext, module->name, module->pa_type);
535                 continue;
536             }
537             module->use_count++;
538         }
539         /* run the module's callback function */
540         out_pa_data = NULL;
541 #ifdef DEBUG
542         fprintf(stderr, "using module \"%s\" (%d), flags = %d\n",
543                 module->name, module->pa_type, module->flags);
544 #endif
545         ret = module->client_process(kcontext, module->moddata,
546                                      *module->modreq_p,
547                                      (krb5_get_init_creds_opt *)opte,
548                                      client_data_proc, preauth_rock,
549                                      request, encoded_request_body,
550                                      encoded_previous_request, in_padata,
551                                      prompter, prompter_data, gak_fct,
552                                      gak_data, salt, s2kparams, as_key,
553                                      &out_pa_data);
554         TRACE_PREAUTH_PROCESS(kcontext, module->name, module->pa_type,
555                               module->flags, ret);
556         /* Make note of the module's flags and status. */
557         *module_flags = module->flags;
558         *module_ret = ret;
559         /* Save the new preauth data item. */
560         if (out_pa_data != NULL) {
561             int j;
562             for (j = 0; out_pa_data[j] != NULL; j++);
563             ret = grow_pa_list(out_pa_list, out_pa_list_size, out_pa_data, j);
564             free(out_pa_data);
565             if (ret != 0)
566                 return ret;
567         }
568         break;
569     }
570     if (i >= kcontext->preauth_context->n_modules) {
571         return ENOENT;
572     }
573     return 0;
574 }
575
576 static inline krb5_data
577 padata2data(krb5_pa_data p)
578 {
579     krb5_data d;
580     d.magic = KV5M_DATA;
581     d.length = p.length;
582     d.data = (char *) p.contents;
583     return d;
584 }
585
586 static krb5_error_code
587 pa_salt(krb5_context context, krb5_kdc_req *request, krb5_pa_data *in_padata,
588         krb5_pa_data **out_padata, krb5_data *salt, krb5_data *s2kparams,
589         krb5_enctype *etype, krb5_keyblock *as_key, krb5_prompter_fct prompter,
590         void *prompter_data, krb5_gic_get_as_key_fct gak_fct, void *gak_data)
591 {
592     krb5_data tmp;
593     krb5_error_code retval;
594
595     tmp = padata2data(*in_padata);
596     krb5_free_data_contents(context, salt);
597     retval = krb5int_copy_data_contents(context, &tmp, salt);
598     if (retval)
599         return retval;
600
601     TRACE_PREAUTH_SALT(context, salt, in_padata->pa_type);
602     if (in_padata->pa_type == KRB5_PADATA_AFS3_SALT)
603         salt->length = SALT_TYPE_AFS_LENGTH;
604
605     return(0);
606 }
607
608 static krb5_error_code
609 pa_fx_cookie(krb5_context context, krb5_kdc_req *request,
610              krb5_pa_data *in_padata, krb5_pa_data **out_padata,
611              krb5_data *salt, krb5_data *s2kparams, krb5_enctype *etype,
612              krb5_keyblock *as_key, krb5_prompter_fct prompter,
613              void *prompter_data, krb5_gic_get_as_key_fct gak_fct,
614              void *gak_data)
615 {
616     krb5_pa_data *pa = calloc(1, sizeof(krb5_pa_data));
617     krb5_octet *contents;
618
619     TRACE_PREAUTH_COOKIE(context, in_padata->length, in_padata->contents);
620     if (pa == NULL)
621         return ENOMEM;
622     contents = malloc(in_padata->length);
623     if (contents == NULL) {
624         free(pa);
625         return ENOMEM;
626     }
627     *pa = *in_padata;
628     pa->contents = contents;
629     memcpy(contents, in_padata->contents, pa->length);
630     *out_padata = pa;
631     return 0;
632 }
633
634 static krb5_error_code
635 pa_enc_timestamp(krb5_context context, krb5_kdc_req *request,
636                  krb5_pa_data *in_padata, krb5_pa_data **out_padata,
637                  krb5_data *salt, krb5_data *s2kparams, krb5_enctype *etype,
638                  krb5_keyblock *as_key, krb5_prompter_fct prompter,
639                  void *prompter_data, krb5_gic_get_as_key_fct gak_fct,
640                  void *gak_data)
641 {
642     krb5_error_code ret;
643     krb5_pa_enc_ts pa_enc;
644     krb5_data *tmp;
645     krb5_enc_data enc_data;
646     krb5_pa_data *pa;
647
648     if (as_key->length == 0) {
649 #ifdef DEBUG
650         fprintf (stderr, "%s:%d: salt len=%d", __FILE__, __LINE__,
651                  salt->length);
652         if ((int) salt->length > 0)
653             fprintf (stderr, " '%.*s'", salt->length, salt->data);
654         fprintf (stderr, "; *etype=%d request->ktype[0]=%d\n",
655                  *etype, request->ktype[0]);
656 #endif
657         if ((ret = ((*gak_fct)(context, request->client,
658                                *etype ? *etype : request->ktype[0],
659                                prompter, prompter_data,
660                                salt, s2kparams, as_key, gak_data))))
661             return(ret);
662         TRACE_PREAUTH_ENC_TS_KEY_GAK(context, as_key);
663     }
664
665     /* now get the time of day, and encrypt it accordingly */
666
667     if ((ret = krb5_us_timeofday(context, &pa_enc.patimestamp, &pa_enc.pausec)))
668         return(ret);
669
670     if ((ret = encode_krb5_pa_enc_ts(&pa_enc, &tmp)))
671         return(ret);
672
673     ret = krb5_encrypt_helper(context, as_key,
674                               KRB5_KEYUSAGE_AS_REQ_PA_ENC_TS,
675                               tmp, &enc_data);
676     TRACE_PREAUTH_ENC_TS(context, pa_enc.patimestamp, pa_enc.pausec,
677                          tmp, &enc_data.ciphertext);
678
679     krb5_free_data(context, tmp);
680
681     if (ret)
682         return(ret);
683
684     ret = encode_krb5_enc_data(&enc_data, &tmp);
685
686     free(enc_data.ciphertext.data);
687
688     if (ret)
689         return(ret);
690
691     if ((pa = (krb5_pa_data *) malloc(sizeof(krb5_pa_data))) == NULL) {
692         krb5_free_data(context, tmp);
693         return(ENOMEM);
694     }
695
696     pa->magic = KV5M_PA_DATA;
697     pa->pa_type = KRB5_PADATA_ENC_TIMESTAMP;
698     pa->length = tmp->length;
699     pa->contents = (krb5_octet *) tmp->data;
700
701     *out_padata = pa;
702
703     free(tmp);
704
705     return(0);
706 }
707
708 #if APPLE_PKINIT
709 /*
710  * PKINIT. One function to generate AS-REQ, one to parse AS-REP
711  */
712 #define  PKINIT_DEBUG    0
713 #if     PKINIT_DEBUG
714 #define kdcPkinitDebug(args...)       printf(args)
715 #else
716 #define kdcPkinitDebug(args...)
717 #endif
718
719 static krb5_error_code
720 pa_pkinit_gen_req(krb5_context context,
721                   krb5_kdc_req *request,
722                   krb5_pa_data *in_padata,
723                   krb5_pa_data **out_padata,
724                   krb5_data *salt,
725                   krb5_data *s2kparams,
726                   krb5_enctype *etype,
727                   krb5_keyblock *as_key,
728                   krb5_prompter_fct prompter,
729                   void *prompter_data,
730                   krb5_gic_get_as_key_fct gak_fct,
731                   void *gak_data)
732 {
733     krb5_error_code             krtn;
734     krb5_data                   out_data = {0, 0, NULL};
735     krb5_timestamp              kctime = 0;
736     krb5_int32                  cusec = 0;
737     krb5_ui_4                   nonce = 0;
738     krb5_checksum               cksum;
739     krb5_pkinit_signing_cert_t  client_cert;
740     krb5_data                   *der_req = NULL;
741     char                        *client_principal = NULL;
742     char                        *server_principal = NULL;
743     unsigned char               nonce_bytes[4];
744     krb5_data                   nonce_data = {0, 4, (char *)nonce_bytes};
745     int                         dex;
746
747     /*
748      * Trusted CA list and specific KC cert optionally obtained via
749      * krb5_pkinit_get_server_certs(). All are DER-encoded certs.
750      */
751     krb5_data *trusted_CAs = NULL;
752     krb5_ui_4 num_trusted_CAs;
753     krb5_data kdc_cert = {0};
754
755     kdcPkinitDebug("pa_pkinit_gen_req\n");
756
757     /* If we don't have a client cert, we're done */
758     if(request->client == NULL) {
759         kdcPkinitDebug("No request->client; aborting PKINIT\n");
760         return KRB5KDC_ERR_PREAUTH_FAILED;
761     }
762     krtn = krb5_unparse_name(context, request->client, &client_principal);
763     if(krtn) {
764         return krtn;
765     }
766     krtn = krb5_pkinit_get_client_cert(client_principal, &client_cert);
767     free(client_principal);
768     if(krtn) {
769         kdcPkinitDebug("No client cert; aborting PKINIT\n");
770         return krtn;
771     }
772
773     /* optional platform-dependent CA list and KDC cert */
774     krtn = krb5_unparse_name(context, request->server, &server_principal);
775     if(krtn) {
776         goto cleanup;
777     }
778     krtn = krb5_pkinit_get_server_certs(client_principal, server_principal,
779                                         &trusted_CAs, &num_trusted_CAs, &kdc_cert);
780     if(krtn) {
781         goto cleanup;
782     }
783
784     /* checksum of the encoded KDC-REQ-BODY */
785     krtn = encode_krb5_kdc_req_body(request, &der_req);
786     if(krtn) {
787         kdcPkinitDebug("encode_krb5_kdc_req_body returned %d\n", (int)krtn);
788         goto cleanup;
789     }
790     krtn = krb5_c_make_checksum(context, CKSUMTYPE_NIST_SHA, NULL, 0, der_req, &cksum);
791     if(krtn) {
792         goto cleanup;
793     }
794
795     krtn = krb5_us_timeofday(context, &kctime, &cusec);
796     if(krtn) {
797         goto cleanup;
798     }
799
800     /* cook up a random 4-byte nonce */
801     krtn = krb5_c_random_make_octets(context, &nonce_data);
802     if(krtn) {
803         goto cleanup;
804     }
805     for(dex=0; dex<4; dex++) {
806         nonce <<= 8;
807         nonce |= nonce_bytes[dex];
808     }
809
810     krtn = krb5int_pkinit_as_req_create(context,
811                                         kctime, cusec, nonce, &cksum,
812                                         client_cert,
813                                         trusted_CAs, num_trusted_CAs,
814                                         (kdc_cert.data ? &kdc_cert : NULL),
815                                         &out_data);
816     if(krtn) {
817         kdcPkinitDebug("error %d on pkinit_as_req_create; aborting PKINIT\n", (int)krtn);
818         goto cleanup;
819     }
820     *out_padata = (krb5_pa_data *)malloc(sizeof(krb5_pa_data));
821     if(*out_padata == NULL) {
822         krtn = ENOMEM;
823         free(out_data.data);
824         goto cleanup;
825     }
826     (*out_padata)->magic = KV5M_PA_DATA;
827     (*out_padata)->pa_type = KRB5_PADATA_PK_AS_REQ;
828     (*out_padata)->length = out_data.length;
829     (*out_padata)->contents = (krb5_octet *)out_data.data;
830     krtn = 0;
831 cleanup:
832     if(client_cert) {
833         krb5_pkinit_release_cert(client_cert);
834     }
835     if(cksum.contents) {
836         free(cksum.contents);
837     }
838     if (der_req) {
839         krb5_free_data(context, der_req);
840     }
841     if(server_principal) {
842         free(server_principal);
843     }
844     /* free data mallocd by krb5_pkinit_get_server_certs() */
845     if(trusted_CAs) {
846         unsigned udex;
847         for(udex=0; udex<num_trusted_CAs; udex++) {
848             free(trusted_CAs[udex].data);
849         }
850         free(trusted_CAs);
851     }
852     if(kdc_cert.data) {
853         free(kdc_cert.data);
854     }
855     return krtn;
856
857 }
858
859 /* If and only if the realm is that of a Local KDC, accept
860  * the KDC certificate as valid if its hash matches the
861  * realm.
862  */
863 static krb5_boolean
864 local_kdc_cert_match(krb5_context context,
865                      krb5_data *signer_cert,
866                      krb5_principal client)
867 {
868     static const char lkdcprefix[] = "LKDC:SHA1.";
869     krb5_boolean match = FALSE;
870     size_t cert_hash_len;
871     char *cert_hash;
872     const char *realm_hash;
873     size_t realm_hash_len;
874
875     if (client->realm.length <= sizeof(lkdcprefix) ||
876         0 != memcmp(lkdcprefix, client->realm.data, sizeof(lkdcprefix)-1))
877         return match;
878     realm_hash = &client->realm.data[sizeof(lkdcprefix)-1];
879     realm_hash_len = client->realm.length - sizeof(lkdcprefix) + 1;
880     kdcPkinitDebug("checking realm versus certificate hash\n");
881     if (NULL != (cert_hash = krb5_pkinit_cert_hash_str(signer_cert))) {
882         kdcPkinitDebug("hash = %s\n", cert_hash);
883         cert_hash_len = strlen(cert_hash);
884         if (cert_hash_len == realm_hash_len &&
885             0 == memcmp(cert_hash, realm_hash, cert_hash_len))
886             match = TRUE;
887         free(cert_hash);
888     }
889     kdcPkinitDebug("result: %s\n", match ? "matches" : "does not match");
890     return match;
891 }
892
893 static krb5_error_code
894 pa_pkinit_parse_rep(krb5_context context,
895                     krb5_kdc_req *request,
896                     krb5_pa_data *in_padata,
897                     krb5_pa_data **out_padata,
898                     krb5_data *salt,
899                     krb5_data *s2kparams,
900                     krb5_enctype *etype,
901                     krb5_keyblock *as_key,
902                     krb5_prompter_fct prompter,
903                     void *prompter_data,
904                     krb5_gic_get_as_key_fct gak_fct,
905                     void *gak_data)
906 {
907     krb5int_cert_sig_status     sig_status = (krb5int_cert_sig_status)-999;
908     krb5_error_code             krtn;
909     krb5_data                   asRep;
910     krb5_keyblock               local_key = {0};
911     krb5_pkinit_signing_cert_t  client_cert;
912     char                        *princ_name = NULL;
913     krb5_checksum               as_req_checksum_rcd = {0};  /* received checksum */
914     krb5_checksum               as_req_checksum_gen = {0};  /* calculated checksum */
915     krb5_data                   *encoded_as_req = NULL;
916     krb5_data                   signer_cert = {0};
917
918     *out_padata = NULL;
919     kdcPkinitDebug("pa_pkinit_parse_rep\n");
920     if((in_padata == NULL) || (in_padata->length== 0)) {
921         kdcPkinitDebug("pa_pkinit_parse_rep: no in_padata\n");
922         return KRB5KDC_ERR_PREAUTH_FAILED;
923     }
924
925     /* If we don't have a client cert, we're done */
926     if(request->client == NULL) {
927         kdcPkinitDebug("No request->client; aborting PKINIT\n");
928         return KRB5KDC_ERR_PREAUTH_FAILED;
929     }
930     krtn = krb5_unparse_name(context, request->client, &princ_name);
931     if(krtn) {
932         return krtn;
933     }
934     krtn = krb5_pkinit_get_client_cert(princ_name, &client_cert);
935     free(princ_name);
936     if(krtn) {
937         kdcPkinitDebug("No client cert; aborting PKINIT\n");
938         return krtn;
939     }
940
941     memset(&local_key, 0, sizeof(local_key));
942     asRep.data = (char *)in_padata->contents;
943     asRep.length = in_padata->length;
944     krtn = krb5int_pkinit_as_rep_parse(context, &asRep, client_cert,
945                                        &local_key, &as_req_checksum_rcd, &sig_status,
946                                        &signer_cert, NULL, NULL);
947     if(krtn) {
948         kdcPkinitDebug("pkinit_as_rep_parse returned %d\n", (int)krtn);
949         return krtn;
950     }
951     switch(sig_status) {
952     case pki_cs_good:
953         break;
954     case pki_cs_unknown_root:
955         if (local_kdc_cert_match(context, &signer_cert, request->client))
956             break;
957         /* FALLTHROUGH */
958     default:
959         kdcPkinitDebug("pa_pkinit_parse_rep: bad cert/sig status %d\n",
960                        (int)sig_status);
961         krtn = KRB5KDC_ERR_PREAUTH_FAILED;
962         goto error_out;
963     }
964
965     /* calculate checksum of incoming AS-REQ using the decryption key
966      * we just got from the ReplyKeyPack */
967     krtn = encode_krb5_as_req(request, &encoded_as_req);
968     if(krtn) {
969         goto error_out;
970     }
971     krtn = krb5_c_make_checksum(context, context->kdc_req_sumtype,
972                                 &local_key, KRB5_KEYUSAGE_TGS_REQ_AUTH_CKSUM,
973                                 encoded_as_req, &as_req_checksum_gen);
974     if(krtn) {
975         goto error_out;
976     }
977     if((as_req_checksum_gen.length != as_req_checksum_rcd.length) ||
978        memcmp(as_req_checksum_gen.contents,
979               as_req_checksum_rcd.contents,
980               as_req_checksum_gen.length)) {
981         kdcPkinitDebug("pa_pkinit_parse_rep: checksum miscompare\n");
982         krtn = KRB5KDC_ERR_PREAUTH_FAILED;
983         goto error_out;
984     }
985
986     /* We have the key; transfer to caller */
987     if (as_key->length) {
988         krb5_free_keyblock_contents(context, as_key);
989     }
990     *as_key = local_key;
991
992 #if PKINIT_DEBUG
993     fprintf(stderr, "pa_pkinit_parse_rep: SUCCESS\n");
994     fprintf(stderr, "enctype %d keylen %d keydata %02x %02x %02x %02x...\n",
995             (int)as_key->enctype, (int)as_key->length,
996             as_key->contents[0], as_key->contents[1],
997             as_key->contents[2], as_key->contents[3]);
998 #endif
999
1000     krtn = 0;
1001
1002 error_out:
1003     if (signer_cert.data) {
1004         free(signer_cert.data);
1005     }
1006     if(as_req_checksum_rcd.contents) {
1007         free(as_req_checksum_rcd.contents);
1008     }
1009     if(as_req_checksum_gen.contents) {
1010         free(as_req_checksum_gen.contents);
1011     }
1012     if(encoded_as_req) {
1013         krb5_free_data(context, encoded_as_req);
1014     }
1015     if(krtn && (local_key.contents != NULL)) {
1016         krb5_free_keyblock_contents(context, &local_key);
1017     }
1018     return krtn;
1019 }
1020 #endif /* APPLE_PKINIT */
1021
1022 /* this macro expands to the int,ptr necessary for "%.*s" in an sprintf */
1023
1024 #define SAMDATA(kdata, str, maxsize)                                    \
1025     (int)((kdata.length)?                                               \
1026           ((((kdata.length)<=(maxsize))?(kdata.length):strlen(str))):   \
1027           strlen(str)),                                                 \
1028         (kdata.length)?                                                 \
1029         ((((kdata.length)<=(maxsize))?(kdata.data):(str))):(str)
1030 static char *
1031 sam_challenge_banner(krb5_int32 sam_type)
1032 {
1033     char *label;
1034
1035     switch (sam_type) {
1036     case PA_SAM_TYPE_ENIGMA:    /* Enigma Logic */
1037         label = _("Challenge for Enigma Logic mechanism");
1038         break;
1039     case PA_SAM_TYPE_DIGI_PATH: /*  Digital Pathways */
1040     case PA_SAM_TYPE_DIGI_PATH_HEX: /*  Digital Pathways */
1041         label = _("Challenge for Digital Pathways mechanism");
1042         break;
1043     case PA_SAM_TYPE_ACTIVCARD_DEC: /*  Digital Pathways */
1044     case PA_SAM_TYPE_ACTIVCARD_HEX: /*  Digital Pathways */
1045         label = _("Challenge for Activcard mechanism");
1046         break;
1047     case PA_SAM_TYPE_SKEY_K0:   /*  S/key where  KDC has key 0 */
1048         label = _("Challenge for Enhanced S/Key mechanism");
1049         break;
1050     case PA_SAM_TYPE_SKEY:      /*  Traditional S/Key */
1051         label = _("Challenge for Traditional S/Key mechanism");
1052         break;
1053     case PA_SAM_TYPE_SECURID:   /*  Security Dynamics */
1054         label = _("Challenge for Security Dynamics mechanism");
1055         break;
1056     case PA_SAM_TYPE_SECURID_PREDICT:   /* predictive Security Dynamics */
1057         label = _("Challenge for Security Dynamics mechanism");
1058         break;
1059     default:
1060         label = _("Challenge from authentication server");
1061         break;
1062     }
1063
1064     return(label);
1065 }
1066
1067 static krb5_error_code
1068 pa_sam_2(krb5_context context, krb5_kdc_req *request, krb5_pa_data *in_padata,
1069          krb5_pa_data **out_padata, krb5_data *salt, krb5_data *s2kparams,
1070          krb5_enctype *etype, krb5_keyblock *as_key,
1071          krb5_prompter_fct prompter, void *prompter_data,
1072          krb5_gic_get_as_key_fct gak_fct, void *gak_data)
1073 {
1074     krb5_error_code retval;
1075     krb5_sam_challenge_2 *sc2 = NULL;
1076     krb5_sam_challenge_2_body *sc2b = NULL;
1077     krb5_data tmp_data;
1078     krb5_data response_data;
1079     char name[100], banner[100], prompt[100], response[100];
1080     krb5_prompt kprompt;
1081     krb5_prompt_type prompt_type;
1082     krb5_data defsalt;
1083     krb5_checksum **cksum;
1084     krb5_data *scratch = NULL;
1085     krb5_boolean valid_cksum = 0;
1086     krb5_enc_sam_response_enc_2 enc_sam_response_enc_2;
1087     krb5_sam_response_2 sr2;
1088     size_t ciph_len;
1089     krb5_pa_data *sam_padata;
1090
1091     if (prompter == NULL)
1092         return KRB5_LIBOS_CANTREADPWD;
1093
1094     tmp_data.length = in_padata->length;
1095     tmp_data.data = (char *)in_padata->contents;
1096
1097     if ((retval = decode_krb5_sam_challenge_2(&tmp_data, &sc2)))
1098         return(retval);
1099
1100     retval = decode_krb5_sam_challenge_2_body(&sc2->sam_challenge_2_body, &sc2b);
1101
1102     if (retval) {
1103         krb5_free_sam_challenge_2(context, sc2);
1104         return(retval);
1105     }
1106
1107     if (!sc2->sam_cksum || ! *sc2->sam_cksum) {
1108         krb5_free_sam_challenge_2(context, sc2);
1109         krb5_free_sam_challenge_2_body(context, sc2b);
1110         return(KRB5_SAM_NO_CHECKSUM);
1111     }
1112
1113     if (sc2b->sam_flags & KRB5_SAM_MUST_PK_ENCRYPT_SAD) {
1114         krb5_free_sam_challenge_2(context, sc2);
1115         krb5_free_sam_challenge_2_body(context, sc2b);
1116         return(KRB5_SAM_UNSUPPORTED);
1117     }
1118
1119     if (!krb5_c_valid_enctype(sc2b->sam_etype)) {
1120         krb5_free_sam_challenge_2(context, sc2);
1121         krb5_free_sam_challenge_2_body(context, sc2b);
1122         return(KRB5_SAM_INVALID_ETYPE);
1123     }
1124
1125     /* All of the above error checks are KDC-specific, that is, they     */
1126     /* assume a failure in the KDC reply.  By returning anything other   */
1127     /* than KRB5_KDC_UNREACH, KRB5_PREAUTH_FAILED,               */
1128     /* KRB5_LIBOS_PWDINTR, or KRB5_REALM_CANT_RESOLVE, the client will   */
1129     /* most likely go on to try the AS_REQ against master KDC            */
1130
1131     if (!(sc2b->sam_flags & KRB5_SAM_USE_SAD_AS_KEY)) {
1132         /* We will need the password to obtain the key used for */
1133         /* the checksum, and encryption of the sam_response.    */
1134         /* Go ahead and get it now, preserving the ordering of  */
1135         /* prompts for the user.                                */
1136
1137         retval = (gak_fct)(context, request->client,
1138                            sc2b->sam_etype, prompter,
1139                            prompter_data, salt, s2kparams, as_key, gak_data);
1140         if (retval) {
1141             krb5_free_sam_challenge_2(context, sc2);
1142             krb5_free_sam_challenge_2_body(context, sc2b);
1143             return(retval);
1144         }
1145     }
1146
1147     snprintf(name, sizeof(name), "%.*s",
1148              SAMDATA(sc2b->sam_type_name, _("SAM Authentication"),
1149                      sizeof(name) - 1));
1150
1151     snprintf(banner, sizeof(banner), "%.*s",
1152              SAMDATA(sc2b->sam_challenge_label,
1153                      sam_challenge_banner(sc2b->sam_type),
1154                      sizeof(banner)-1));
1155
1156     snprintf(prompt, sizeof(prompt), "%s%.*s%s%.*s",
1157              sc2b->sam_challenge.length?"Challenge is [":"",
1158              SAMDATA(sc2b->sam_challenge, "", 20),
1159              sc2b->sam_challenge.length?"], ":"",
1160              SAMDATA(sc2b->sam_response_prompt, "passcode", 55));
1161
1162     response_data.data = response;
1163     response_data.length = sizeof(response);
1164     kprompt.prompt = prompt;
1165     kprompt.hidden = 1;
1166     kprompt.reply = &response_data;
1167
1168     prompt_type = KRB5_PROMPT_TYPE_PREAUTH;
1169     krb5int_set_prompt_types(context, &prompt_type);
1170
1171     if ((retval = ((*prompter)(context, prompter_data, name,
1172                                banner, 1, &kprompt)))) {
1173         krb5_free_sam_challenge_2(context, sc2);
1174         krb5_free_sam_challenge_2_body(context, sc2b);
1175         krb5int_set_prompt_types(context, 0);
1176         return(retval);
1177     }
1178
1179     krb5int_set_prompt_types(context, (krb5_prompt_type *)NULL);
1180
1181     /* Generate salt used by string_to_key() */
1182     if (((int) salt->length == -1) && (salt->data == NULL)) {
1183         if ((retval =
1184              krb5_principal2salt(context, request->client, &defsalt))) {
1185             krb5_free_sam_challenge_2(context, sc2);
1186             krb5_free_sam_challenge_2_body(context, sc2b);
1187             return(retval);
1188         }
1189         salt = &defsalt;
1190     } else {
1191         defsalt.length = 0;
1192     }
1193
1194     /* Get encryption key to be used for checksum and sam_response */
1195     if (!(sc2b->sam_flags & KRB5_SAM_USE_SAD_AS_KEY)) {
1196         /* as_key = string_to_key(password) */
1197
1198         if (as_key->length) {
1199             krb5_free_keyblock_contents(context, as_key);
1200             as_key->length = 0;
1201         }
1202
1203         /* generate a key using the supplied password */
1204         retval = krb5_c_string_to_key(context, sc2b->sam_etype,
1205                                       (krb5_data *)gak_data, salt, as_key);
1206
1207         if (retval) {
1208             krb5_free_sam_challenge_2(context, sc2);
1209             krb5_free_sam_challenge_2_body(context, sc2b);
1210             if (defsalt.length) free(defsalt.data);
1211             return(retval);
1212         }
1213
1214         if (!(sc2b->sam_flags & KRB5_SAM_SEND_ENCRYPTED_SAD)) {
1215             /* as_key = combine_key (as_key, string_to_key(SAD)) */
1216             krb5_keyblock tmp_kb;
1217
1218             retval = krb5_c_string_to_key(context, sc2b->sam_etype,
1219                                           &response_data, salt, &tmp_kb);
1220
1221             if (retval) {
1222                 krb5_free_sam_challenge_2(context, sc2);
1223                 krb5_free_sam_challenge_2_body(context, sc2b);
1224                 if (defsalt.length) free(defsalt.data);
1225                 return(retval);
1226             }
1227
1228             /* This should be a call to the crypto library some day */
1229             /* key types should already match the sam_etype */
1230             retval = krb5int_c_combine_keys(context, as_key, &tmp_kb, as_key);
1231
1232             if (retval) {
1233                 krb5_free_sam_challenge_2(context, sc2);
1234                 krb5_free_sam_challenge_2_body(context, sc2b);
1235                 if (defsalt.length) free(defsalt.data);
1236                 return(retval);
1237             }
1238             krb5_free_keyblock_contents(context, &tmp_kb);
1239         }
1240
1241         if (defsalt.length)
1242             free(defsalt.data);
1243
1244     } else {
1245         /* as_key = string_to_key(SAD) */
1246
1247         if (as_key->length) {
1248             krb5_free_keyblock_contents(context, as_key);
1249             as_key->length = 0;
1250         }
1251
1252         /* generate a key using the supplied password */
1253         retval = krb5_c_string_to_key(context, sc2b->sam_etype,
1254                                       &response_data, salt, as_key);
1255
1256         if (defsalt.length)
1257             free(defsalt.data);
1258
1259         if (retval) {
1260             krb5_free_sam_challenge_2(context, sc2);
1261             krb5_free_sam_challenge_2_body(context, sc2b);
1262             return(retval);
1263         }
1264     }
1265
1266     /* Now we have a key, verify the checksum on the sam_challenge */
1267
1268     cksum = sc2->sam_cksum;
1269
1270     for (; *cksum; cksum++) {
1271         if (!krb5_c_is_keyed_cksum((*cksum)->checksum_type))
1272             continue;
1273         /* Check this cksum */
1274         retval = krb5_c_verify_checksum(context, as_key,
1275                                         KRB5_KEYUSAGE_PA_SAM_CHALLENGE_CKSUM,
1276                                         &sc2->sam_challenge_2_body,
1277                                         *cksum, &valid_cksum);
1278         if (retval) {
1279             krb5_free_data(context, scratch);
1280             krb5_free_sam_challenge_2(context, sc2);
1281             krb5_free_sam_challenge_2_body(context, sc2b);
1282             return(retval);
1283         }
1284         if (valid_cksum)
1285             break;
1286     }
1287
1288     if (!valid_cksum) {
1289         krb5_free_sam_challenge_2(context, sc2);
1290         krb5_free_sam_challenge_2_body(context, sc2b);
1291         /*
1292          * Note: We return AP_ERR_BAD_INTEGRITY so upper-level applications
1293          * can interpret that as "password incorrect", which is probably
1294          * the best error we can return in this situation.
1295          */
1296         return(KRB5KRB_AP_ERR_BAD_INTEGRITY);
1297     }
1298
1299     /* fill in enc_sam_response_enc_2 */
1300     enc_sam_response_enc_2.magic = KV5M_ENC_SAM_RESPONSE_ENC_2;
1301     enc_sam_response_enc_2.sam_nonce = sc2b->sam_nonce;
1302     if (sc2b->sam_flags & KRB5_SAM_SEND_ENCRYPTED_SAD) {
1303         enc_sam_response_enc_2.sam_sad = response_data;
1304     } else {
1305         enc_sam_response_enc_2.sam_sad.data = NULL;
1306         enc_sam_response_enc_2.sam_sad.length = 0;
1307     }
1308
1309     /* encode and encrypt enc_sam_response_enc_2 with as_key */
1310     retval = encode_krb5_enc_sam_response_enc_2(&enc_sam_response_enc_2,
1311                                                 &scratch);
1312     if (retval) {
1313         krb5_free_sam_challenge_2(context, sc2);
1314         krb5_free_sam_challenge_2_body(context, sc2b);
1315         return(retval);
1316     }
1317
1318     /* Fill in sam_response_2 */
1319     memset(&sr2, 0, sizeof(sr2));
1320     sr2.sam_type = sc2b->sam_type;
1321     sr2.sam_flags = sc2b->sam_flags;
1322     sr2.sam_track_id = sc2b->sam_track_id;
1323     sr2.sam_nonce = sc2b->sam_nonce;
1324
1325     /* Now take care of sr2.sam_enc_nonce_or_sad by encrypting encoded   */
1326     /* enc_sam_response_enc_2 from above */
1327
1328     retval = krb5_c_encrypt_length(context, as_key->enctype, scratch->length,
1329                                    &ciph_len);
1330     if (retval) {
1331         krb5_free_sam_challenge_2(context, sc2);
1332         krb5_free_sam_challenge_2_body(context, sc2b);
1333         krb5_free_data(context, scratch);
1334         return(retval);
1335     }
1336     sr2.sam_enc_nonce_or_sad.ciphertext.length = ciph_len;
1337
1338     sr2.sam_enc_nonce_or_sad.ciphertext.data =
1339         (char *)malloc(sr2.sam_enc_nonce_or_sad.ciphertext.length);
1340
1341     if (!sr2.sam_enc_nonce_or_sad.ciphertext.data) {
1342         krb5_free_sam_challenge_2(context, sc2);
1343         krb5_free_sam_challenge_2_body(context, sc2b);
1344         krb5_free_data(context, scratch);
1345         return(ENOMEM);
1346     }
1347
1348     retval = krb5_c_encrypt(context, as_key, KRB5_KEYUSAGE_PA_SAM_RESPONSE,
1349                             NULL, scratch, &sr2.sam_enc_nonce_or_sad);
1350     if (retval) {
1351         krb5_free_sam_challenge_2(context, sc2);
1352         krb5_free_sam_challenge_2_body(context, sc2b);
1353         krb5_free_data(context, scratch);
1354         krb5_free_data_contents(context, &sr2.sam_enc_nonce_or_sad.ciphertext);
1355         return(retval);
1356     }
1357     krb5_free_data(context, scratch);
1358     scratch = NULL;
1359
1360     /* Encode the sam_response_2 */
1361     retval = encode_krb5_sam_response_2(&sr2, &scratch);
1362     krb5_free_sam_challenge_2(context, sc2);
1363     krb5_free_sam_challenge_2_body(context, sc2b);
1364     krb5_free_data_contents(context, &sr2.sam_enc_nonce_or_sad.ciphertext);
1365
1366     if (retval) {
1367         return (retval);
1368     }
1369
1370     /* Almost there, just need to make padata !  */
1371     sam_padata = malloc(sizeof(krb5_pa_data));
1372     if (sam_padata == NULL) {
1373         krb5_free_data(context, scratch);
1374         return(ENOMEM);
1375     }
1376
1377     sam_padata->magic = KV5M_PA_DATA;
1378     sam_padata->pa_type = KRB5_PADATA_SAM_RESPONSE_2;
1379     sam_padata->length = scratch->length;
1380     sam_padata->contents = (krb5_octet *) scratch->data;
1381     free(scratch);
1382
1383     *out_padata = sam_padata;
1384
1385     return(0);
1386 }
1387
1388 static krb5_error_code
1389 pa_s4u_x509_user(krb5_context context, krb5_kdc_req *request,
1390                  krb5_pa_data *in_padata, krb5_pa_data **out_padata,
1391                  krb5_data *salt, krb5_data *s2kparams, krb5_enctype *etype,
1392                  krb5_keyblock *as_key, krb5_prompter_fct prompter,
1393                  void *prompter_data, krb5_gic_get_as_key_fct gak_fct,
1394                  void *gak_data)
1395 {
1396     krb5_s4u_userid *userid = (krb5_s4u_userid *)gak_data; /* XXX private contract */
1397     krb5_pa_data *s4u_padata;
1398     krb5_error_code code;
1399     krb5_principal client;
1400
1401     *out_padata = NULL;
1402
1403     if (userid == NULL)
1404         return EINVAL;
1405
1406     code = krb5_copy_principal(context, request->client, &client);
1407     if (code != 0)
1408         return code;
1409
1410     if (userid->user != NULL)
1411         krb5_free_principal(context, userid->user);
1412     userid->user = client;
1413
1414     if (userid->subject_cert.length != 0) {
1415         s4u_padata = malloc(sizeof(*s4u_padata));
1416         if (s4u_padata == NULL)
1417             return ENOMEM;
1418
1419         s4u_padata->magic = KV5M_PA_DATA;
1420         s4u_padata->pa_type = KRB5_PADATA_S4U_X509_USER;
1421         s4u_padata->contents = malloc(userid->subject_cert.length);
1422         if (s4u_padata->contents == NULL) {
1423             free(s4u_padata);
1424             return ENOMEM;
1425         }
1426         memcpy(s4u_padata->contents, userid->subject_cert.data, userid->subject_cert.length);
1427         s4u_padata->length = userid->subject_cert.length;
1428
1429         *out_padata = s4u_padata;
1430     }
1431
1432     return 0;
1433 }
1434
1435 /* FIXME - order significant? */
1436 static const pa_types_t pa_types[] = {
1437     {
1438         KRB5_PADATA_PW_SALT,
1439         pa_salt,
1440         PA_INFO,
1441     },
1442     {
1443         KRB5_PADATA_AFS3_SALT,
1444         pa_salt,
1445         PA_INFO,
1446     },
1447 #if APPLE_PKINIT
1448     {
1449         KRB5_PADATA_PK_AS_REQ,
1450         pa_pkinit_gen_req,
1451         PA_INFO,
1452     },
1453     {
1454         KRB5_PADATA_PK_AS_REP,
1455         pa_pkinit_parse_rep,
1456         PA_REAL,
1457     },
1458 #endif /* APPLE_PKINIT */
1459     {
1460         KRB5_PADATA_ENC_TIMESTAMP,
1461         pa_enc_timestamp,
1462         PA_REAL,
1463     },
1464     {
1465         KRB5_PADATA_SAM_CHALLENGE_2,
1466         pa_sam_2,
1467         PA_REAL,
1468     },
1469     {
1470         KRB5_PADATA_FX_COOKIE,
1471         pa_fx_cookie,
1472         PA_INFO,
1473     },
1474     {
1475         KRB5_PADATA_S4U_X509_USER,
1476         pa_s4u_x509_user,
1477         PA_INFO,
1478     },
1479     {
1480         -1,
1481         NULL,
1482         0,
1483     },
1484 };
1485
1486 /*
1487  * If one of the modules can adjust its AS_REQ data using the contents of the
1488  * err_reply, return 0.  If it's the sort of correction which requires that we
1489  * ask the user another question, we let the calling application deal with it.
1490  */
1491 krb5_error_code KRB5_CALLCONV
1492 krb5_do_preauth_tryagain(krb5_context kcontext,
1493                          krb5_kdc_req *request,
1494                          krb5_data *encoded_request_body,
1495                          krb5_data *encoded_previous_request,
1496                          krb5_pa_data **padata,
1497                          krb5_pa_data ***return_padata,
1498                          krb5_error *err_reply,
1499                          krb5_data *salt, krb5_data *s2kparams,
1500                          krb5_enctype *etype,
1501                          krb5_keyblock *as_key,
1502                          krb5_prompter_fct prompter, void *prompter_data,
1503                          krb5_gic_get_as_key_fct gak_fct, void *gak_data,
1504                          krb5_clpreauth_rock preauth_rock,
1505                          krb5_gic_opt_ext *opte)
1506 {
1507     krb5_error_code ret;
1508     krb5_pa_data **out_padata;
1509     krb5_preauth_context *context;
1510     struct krb5_preauth_context_module_st *module;
1511     int i, j;
1512     int out_pa_list_size = 0;
1513
1514     ret = KRB5KRB_ERR_GENERIC;
1515     if (kcontext->preauth_context == NULL) {
1516         return KRB5KRB_ERR_GENERIC;
1517     }
1518     context = kcontext->preauth_context;
1519     if (context == NULL) {
1520         return KRB5KRB_ERR_GENERIC;
1521     }
1522
1523     TRACE_PREAUTH_TRYAGAIN_INPUT(kcontext, padata);
1524
1525     for (i = 0; padata[i] != NULL && padata[i]->pa_type != 0; i++) {
1526         out_padata = NULL;
1527         for (j = 0; j < context->n_modules; j++) {
1528             module = &context->modules[j];
1529             if (module->pa_type != padata[i]->pa_type) {
1530                 continue;
1531             }
1532             if (module->client_tryagain == NULL) {
1533                 continue;
1534             }
1535             if ((*module->client_tryagain)(kcontext, module->moddata,
1536                                            *module->modreq_p,
1537                                            (krb5_get_init_creds_opt *)opte,
1538                                            client_data_proc,
1539                                            preauth_rock,
1540                                            request,
1541                                            encoded_request_body,
1542                                            encoded_previous_request,
1543                                            padata[i],
1544                                            err_reply,
1545                                            prompter, prompter_data,
1546                                            gak_fct, gak_data, salt, s2kparams,
1547                                            as_key,
1548                                            &out_padata) == 0) {
1549                 if (out_padata != NULL) {
1550                     int k;
1551                     for (k = 0; out_padata[k] != NULL; k++);
1552                     grow_pa_list(return_padata, &out_pa_list_size,
1553                                  out_padata, k);
1554                     free(out_padata);
1555                     TRACE_PREAUTH_TRYAGAIN_OUTPUT(kcontext, *return_padata);
1556                     return 0;
1557                 }
1558             }
1559         }
1560     }
1561     return ret;
1562 }
1563
1564 krb5_error_code KRB5_CALLCONV
1565 krb5_do_preauth(krb5_context context,
1566                 krb5_kdc_req *request,
1567                 krb5_data *encoded_request_body,
1568                 krb5_data *encoded_previous_request,
1569                 krb5_pa_data **in_padata, krb5_pa_data ***out_padata,
1570                 krb5_data *salt, krb5_data *s2kparams,
1571                 krb5_enctype *etype,
1572                 krb5_keyblock *as_key,
1573                 krb5_prompter_fct prompter, void *prompter_data,
1574                 krb5_gic_get_as_key_fct gak_fct, void *gak_data,
1575                 krb5_clpreauth_rock preauth_rock, krb5_gic_opt_ext *opte)
1576 {
1577     unsigned int h;
1578     int i, j, out_pa_list_size;
1579     int seen_etype_info2 = 0;
1580     krb5_pa_data *out_pa = NULL, **out_pa_list = NULL;
1581     krb5_data scratch;
1582     krb5_etype_info etype_info = NULL;
1583     krb5_error_code ret;
1584     static const int paorder[] = { PA_INFO, PA_REAL };
1585     int realdone;
1586
1587     if (in_padata == NULL) {
1588         *out_padata = NULL;
1589         return(0);
1590     }
1591
1592     TRACE_PREAUTH_INPUT(context, in_padata);
1593
1594     out_pa_list = NULL;
1595     out_pa_list_size = 0;
1596
1597     /* first do all the informational preauths, then the first real one */
1598
1599     for (h=0; h<(sizeof(paorder)/sizeof(paorder[0])); h++) {
1600         realdone = 0;
1601         for (i=0; in_padata[i] && !realdone; i++) {
1602             int k, l, etype_found, valid_etype_found;
1603             /*
1604              * This is really gross, but is necessary to prevent
1605              * lossage when talking to a 1.0.x KDC, which returns an
1606              * erroneous PA-PW-SALT when it returns a KRB-ERROR
1607              * requiring additional preauth.
1608              */
1609             switch (in_padata[i]->pa_type) {
1610             case KRB5_PADATA_ETYPE_INFO:
1611             case KRB5_PADATA_ETYPE_INFO2:
1612             {
1613                 krb5_preauthtype pa_type = in_padata[i]->pa_type;
1614                 if (etype_info) {
1615                     if (seen_etype_info2 || pa_type != KRB5_PADATA_ETYPE_INFO2)
1616                         continue;
1617                     if (pa_type == KRB5_PADATA_ETYPE_INFO2) {
1618                         krb5_free_etype_info( context, etype_info);
1619                         etype_info = NULL;
1620                     }
1621                 }
1622
1623                 scratch.length = in_padata[i]->length;
1624                 scratch.data = (char *) in_padata[i]->contents;
1625                 if (pa_type == KRB5_PADATA_ETYPE_INFO2) {
1626                     seen_etype_info2++;
1627                     ret = decode_krb5_etype_info2(&scratch, &etype_info);
1628                 }
1629                 else ret = decode_krb5_etype_info(&scratch, &etype_info);
1630                 if (ret) {
1631                     ret = 0; /*Ignore error and etype_info element*/
1632                     if (etype_info)
1633                         krb5_free_etype_info( context, etype_info);
1634                     etype_info = NULL;
1635                     continue;
1636                 }
1637                 if (etype_info[0] == NULL) {
1638                     krb5_free_etype_info(context, etype_info);
1639                     etype_info = NULL;
1640                     break;
1641                 }
1642                 /*
1643                  * Select first etype in our request which is also in
1644                  * etype-info (preferring client request ktype order).
1645                  */
1646                 for (etype_found = 0, valid_etype_found = 0, k = 0;
1647                      !etype_found && k < request->nktypes; k++) {
1648                     for (l = 0; etype_info[l]; l++) {
1649                         if (etype_info[l]->etype == request->ktype[k]) {
1650                             etype_found++;
1651                             break;
1652                         }
1653                         /* check if program has support for this etype for more
1654                          * precise error reporting.
1655                          */
1656                         if (krb5_c_valid_enctype(etype_info[l]->etype))
1657                             valid_etype_found++;
1658                     }
1659                 }
1660                 if (!etype_found) {
1661                     if (valid_etype_found) {
1662                         /* supported enctype but not requested */
1663                         ret =  KRB5_CONFIG_ETYPE_NOSUPP;
1664                         goto cleanup;
1665                     }
1666                     else {
1667                         /* unsupported enctype */
1668                         ret =  KRB5_PROG_ETYPE_NOSUPP;
1669                         goto cleanup;
1670                     }
1671
1672                 }
1673                 scratch.data = (char *) etype_info[l]->salt;
1674                 scratch.length = etype_info[l]->length;
1675                 krb5_free_data_contents(context, salt);
1676                 if (scratch.length == KRB5_ETYPE_NO_SALT)
1677                     salt->data = NULL;
1678                 else
1679                     if ((ret = krb5int_copy_data_contents( context, &scratch, salt)) != 0)
1680                         goto cleanup;
1681                 *etype = etype_info[l]->etype;
1682                 krb5_free_data_contents(context, s2kparams);
1683                 if ((ret = krb5int_copy_data_contents(context,
1684                                                       &etype_info[l]->s2kparams,
1685                                                       s2kparams)) != 0)
1686                     goto cleanup;
1687                 TRACE_PREAUTH_ETYPE_INFO(context, *etype, salt, s2kparams);
1688                 break;
1689             }
1690             case KRB5_PADATA_PW_SALT:
1691             case KRB5_PADATA_AFS3_SALT:
1692                 if (etype_info)
1693                     continue;
1694                 break;
1695             default:
1696                 ;
1697             }
1698             /* Try the internally-provided preauth type list. */
1699             if (!realdone) for (j=0; pa_types[j].type >= 0; j++) {
1700                     if ((in_padata[i]->pa_type == pa_types[j].type) &&
1701                         (pa_types[j].flags & paorder[h])) {
1702 #ifdef DEBUG
1703                         fprintf (stderr, "calling internal function for pa_type "
1704                                  "%d, flag %d\n", pa_types[j].type, paorder[h]);
1705 #endif
1706                         out_pa = NULL;
1707
1708                         if ((ret = ((*pa_types[j].fct)(context, request,
1709                                                        in_padata[i], &out_pa,
1710                                                        salt, s2kparams, etype, as_key,
1711                                                        prompter, prompter_data,
1712                                                        gak_fct, gak_data)))) {
1713                             if (paorder[h] == PA_INFO) {
1714                                 TRACE_PREAUTH_INFO_FAIL(context,
1715                                                         in_padata[i]->pa_type,
1716                                                         ret);
1717                                 ret = 0;
1718                                 continue; /* PA_INFO type failed, ignore */
1719                             }
1720
1721                             goto cleanup;
1722                         }
1723
1724                         ret = grow_pa_list(&out_pa_list, &out_pa_list_size,
1725                                            &out_pa, 1);
1726                         if (ret != 0) {
1727                             goto cleanup;
1728                         }
1729                         if (paorder[h] == PA_REAL)
1730                             realdone = 1;
1731                     }
1732                 }
1733
1734             /* Try to use plugins now. */
1735             if (!realdone) {
1736                 krb5_init_preauth_context(context);
1737                 if (context->preauth_context != NULL) {
1738                     int module_ret = 0, module_flags;
1739 #ifdef DEBUG
1740                     fprintf (stderr, "trying modules for pa_type %d, flag %d\n",
1741                              in_padata[i]->pa_type, paorder[h]);
1742 #endif
1743                     ret = run_preauth_plugins(context,
1744                                               paorder[h],
1745                                               request,
1746                                               encoded_request_body,
1747                                               encoded_previous_request,
1748                                               in_padata[i],
1749                                               prompter,
1750                                               prompter_data,
1751                                               gak_fct,
1752                                               salt, s2kparams,
1753                                               gak_data,
1754                                               preauth_rock,
1755                                               as_key,
1756                                               &out_pa_list,
1757                                               &out_pa_list_size,
1758                                               &module_ret,
1759                                               &module_flags,
1760                                               opte);
1761                     if (ret == 0) {
1762                         if (module_ret == 0) {
1763                             if (paorder[h] == PA_REAL) {
1764                                 realdone = 1;
1765                             }
1766                         }
1767                     }
1768                 }
1769             }
1770         }
1771     }
1772
1773     TRACE_PREAUTH_OUTPUT(context, out_pa_list);
1774     *out_padata = out_pa_list;
1775     if (etype_info)
1776         krb5_free_etype_info(context, etype_info);
1777
1778     return(0);
1779 cleanup:
1780     if (out_pa_list) {
1781         out_pa_list[out_pa_list_size++] = NULL;
1782         krb5_free_pa_data(context, out_pa_list);
1783     }
1784     if (etype_info)
1785         krb5_free_etype_info(context, etype_info);
1786     return (ret);
1787 }
1788
1789 /*
1790  * Give all the preauth plugins a look at the preauth option which
1791  * has just been set
1792  */
1793 krb5_error_code
1794 krb5_preauth_supply_preauth_data(krb5_context context, krb5_gic_opt_ext *opte,
1795                                  const char *attr, const char *value)
1796 {
1797     krb5_error_code retval = 0;
1798     int i;
1799     struct krb5_preauth_context_module_st *mod;
1800     const char *emsg = NULL;
1801
1802     if (context->preauth_context == NULL)
1803         krb5_init_preauth_context(context);
1804     if (context->preauth_context == NULL) {
1805         retval = EINVAL;
1806         krb5_set_error_message(context, retval,
1807                                _("Unable to initialize preauth context"));
1808         return retval;
1809     }
1810
1811     /*
1812      * Go down the list of preauth modules, and supply them with the
1813      * attribute/value pair.
1814      */
1815     for (i = 0; i < context->preauth_context->n_modules; i++) {
1816         mod = &context->preauth_context->modules[i];
1817         if (mod->client_supply_gic_opts == NULL)
1818             continue;
1819         retval = mod->client_supply_gic_opts(context, mod->moddata,
1820                                              (krb5_get_init_creds_opt *)opte,
1821                                              attr, value);
1822         if (retval) {
1823             emsg = krb5_get_error_message(context, retval);
1824             krb5_set_error_message(context, retval, _("Preauth plugin %s: %s"),
1825                                    mod->name, emsg);
1826             krb5_free_error_message(context, emsg);
1827             break;
1828         }
1829     }
1830     return retval;
1831 }