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