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