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