9242705927ad4f5561dec82273fb9c9152e43372
[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 #include "osconf.h"
35 #include <krb5/preauth_plugin.h>
36 #include "int-proto.h"
37 #include "fast.h"
38
39 #if !defined(_WIN32)
40 #include <unistd.h>
41 #endif
42
43 /* This structure lets us keep track of all of the modules which are loaded,
44  * turning the list of modules and their lists of implemented preauth types
45  * into a single list which we can walk easily. */
46 struct krb5_preauth_context_st {
47     int n_modules;
48     struct krb5_preauth_context_module_st {
49         /* Which of the possibly more than one preauth types which the
50          * module supports we're using at this point in the list. */
51         krb5_preauthtype pa_type;
52         /* Encryption types which the client claims to support -- we
53          * copy them directly into the krb5_kdc_req structure during
54          * krb5_preauth_prepare_request(). */
55         krb5_enctype *enctypes;
56         /* The plugin's module data and a function to clear it. */
57         krb5_clpreauth_moddata moddata;
58         krb5_clpreauth_fini_fn client_fini;
59         /* The module's table, and some of its members, copied here for
60          * convenience when we populated the list. */
61         const char *name;
62         int flags, use_count;
63         krb5_clpreauth_process_fn client_process;
64         krb5_clpreauth_tryagain_fn client_tryagain;
65         krb5_clpreauth_supply_gic_opts_fn client_supply_gic_opts;
66         krb5_clpreauth_request_init_fn client_req_init;
67         krb5_clpreauth_request_fini_fn client_req_fini;
68         /* The per-request context which the client_req_init() function
69          * might allocate, which we'll need to clean up later by
70          * calling the client_req_fini() function. */
71         krb5_clpreauth_modreq modreq;
72         /* A pointer to the request_context pointer.  All modules within
73          * a plugin will point at the request_context of the first
74          * module within the plugin. */
75         krb5_clpreauth_modreq *modreq_p;
76     } *modules;
77 };
78
79 typedef krb5_error_code (*pa_function)(krb5_context,
80                                        krb5_kdc_req *request,
81                                        krb5_pa_data *in_padata,
82                                        krb5_pa_data **out_padata,
83                                        krb5_data *salt, krb5_data *s2kparams,
84                                        krb5_enctype *etype,
85                                        krb5_keyblock *as_key,
86                                        krb5_prompter_fct prompter_fct,
87                                        void *prompter_data,
88                                        krb5_gic_get_as_key_fct gak_fct,
89                                        void *gak_data);
90
91 typedef struct _pa_types_t {
92     krb5_preauthtype type;
93     pa_function fct;
94     int flags;
95 } pa_types_t;
96
97 /* Create the per-krb5_context context. This means loading the modules
98  * if we haven't done that yet (applications which never obtain initial
99  * credentials should never hit this routine), breaking up the module's
100  * list of support pa_types so that we can iterate over the modules more
101  * easily, and copying over the relevant parts of the module's table. */
102 void KRB5_CALLCONV
103 krb5_init_preauth_context(krb5_context kcontext)
104 {
105     int n_tables, n_modules, i, count;
106     krb5_plugin_initvt_fn *plugins = NULL, *pl;
107     struct krb5_clpreauth_vtable_st *vtables = NULL, *vt;
108     struct krb5_preauth_context_module_st *mod;
109     krb5_preauth_context *context = NULL;
110     krb5_clpreauth_moddata moddata;
111     krb5_preauthtype pa_type, *pat;
112     krb5_boolean first;
113     krb5_clpreauth_modreq *rcpp;
114
115     /* Only do this once for each krb5_context */
116     if (kcontext->preauth_context != NULL)
117         return;
118
119     /* Auto-register built-in modules. */
120     k5_plugin_register_dyn(kcontext, PLUGIN_INTERFACE_CLPREAUTH, "pkinit",
121                            "preauth");
122     k5_plugin_register(kcontext, PLUGIN_INTERFACE_CLPREAUTH,
123                        "encrypted_challenge",
124                        clpreauth_encrypted_challenge_initvt);
125     k5_plugin_register(kcontext, PLUGIN_INTERFACE_CLPREAUTH,
126                        "encrypted_timestamp",
127                        clpreauth_encrypted_timestamp_initvt);
128     k5_plugin_register(kcontext, PLUGIN_INTERFACE_CLPREAUTH, "sam2",
129                        clpreauth_sam2_initvt);
130
131     /* Get all available clpreauth vtables. */
132     if (k5_plugin_load_all(kcontext, PLUGIN_INTERFACE_CLPREAUTH, &plugins))
133         return;
134     for (count = 0; plugins[count] != NULL; count++);
135     vtables = calloc(count, sizeof(*vtables));
136     if (vtables == NULL)
137         goto cleanup;
138     for (pl = plugins, n_tables = 0; *pl != NULL; pl++) {
139         if ((*pl)(kcontext, 1, 1, (krb5_plugin_vtable)&vtables[n_tables]) == 0)
140             n_tables++;
141     }
142
143     /* Count how many modules we ended up loading, and how many preauth
144      * types we may claim to support as a result. */
145     n_modules = 0;
146     for (i = 0; i < n_tables; i++) {
147         for (count = 0; vtables[i].pa_type_list[count] > 0; count++);
148         n_modules += count;
149     }
150
151     /* Allocate the space we need. */
152     context = malloc(sizeof(*context));
153     if (context == NULL)
154         goto cleanup;
155     context->modules = calloc(n_modules, sizeof(*context->modules));
156     if (context->modules == NULL)
157         goto cleanup;
158
159     /* fill in the structure */
160     n_modules = 0;
161     for (i = 0; i < n_tables; i++) {
162         vt = &vtables[i];
163         if ((vt->pa_type_list != NULL) && (vt->process != NULL)) {
164             moddata = NULL;
165             if (vt->init != NULL && vt->init(kcontext, &moddata) != 0) {
166 #ifdef DEBUG
167                 fprintf(stderr, "init err, skipping module \"%s\"\n",
168                         vt->name);
169 #endif
170                 continue;
171             }
172
173             rcpp = NULL;
174             for (pat = vt->pa_type_list, first = TRUE; *pat > 0;
175                  pat++, first = FALSE) {
176                 pa_type = *pat;
177                 mod = &context->modules[n_modules];
178                 mod->pa_type = pa_type;
179                 mod->enctypes = vt->enctype_list;
180                 mod->moddata = moddata;
181                 /* Only call client_fini once per plugin */
182                 if (first)
183                     mod->client_fini = vt->fini;
184                 else
185                     mod->client_fini = NULL;
186                 mod->name = vt->name;
187                 mod->flags = (*vt->flags)(kcontext, pa_type);
188                 mod->use_count = 0;
189                 mod->client_process = vt->process;
190                 mod->client_tryagain = vt->tryagain;
191                 mod->client_supply_gic_opts = first ? vt->gic_opts : NULL;
192                 mod->modreq = NULL;
193                 /*
194                  * Only call request_init and request_fini once per plugin.
195                  * Only the first module within each plugin will ever
196                  * have request_context filled in.  Every module within
197                  * the plugin will have its request_context_pp pointing
198                  * to that entry's request_context.  That way all the
199                  * modules within the plugin share the same request_context
200                  */
201                 if (first) {
202                     mod->client_req_init = vt->request_init;
203                     mod->client_req_fini = vt->request_fini;
204                     rcpp = &mod->modreq;
205                 } else {
206                     mod->client_req_init = NULL;
207                     mod->client_req_fini = NULL;
208                 }
209                 mod->modreq_p = rcpp;
210 #ifdef DEBUG
211                 fprintf(stderr, "init module \"%s\", pa_type %d, flag %d\n",
212                         mod->name, mod->pa_type, mod->flags);
213 #endif
214                 n_modules++;
215             }
216         }
217     }
218     context->n_modules = n_modules;
219
220     /* Place the constructed preauth context into the krb5 context. */
221     kcontext->preauth_context = context;
222     context = NULL;
223
224 cleanup:
225     if (context)
226         free(context->modules);
227     free(context);
228     k5_plugin_free_modules(kcontext, plugins);
229     free(vtables);
230 }
231
232 /* Zero the use counts for the modules herein.  Usually used before we
233  * start processing any data from the server, at which point every module
234  * will again be able to take a crack at whatever the server sent. */
235 void KRB5_CALLCONV
236 krb5_clear_preauth_context_use_counts(krb5_context context)
237 {
238     int i;
239     if (context->preauth_context != NULL) {
240         for (i = 0; i < context->preauth_context->n_modules; i++) {
241             context->preauth_context->modules[i].use_count = 0;
242         }
243     }
244 }
245
246
247 /* Free the per-krb5_context preauth_context. This means clearing any
248  * plugin-specific context which may have been created, and then
249  * freeing the context itself. */
250 void KRB5_CALLCONV
251 krb5_free_preauth_context(krb5_context context)
252 {
253     int i;
254     struct krb5_preauth_context_module_st *mod;
255
256     if (context == NULL || context->preauth_context == NULL)
257         return;
258     for (i = 0; i < context->preauth_context->n_modules; i++) {
259         mod = &context->preauth_context->modules[i];
260         if (mod->client_fini != NULL)
261             mod->client_fini(context, mod->moddata);
262         zap(mod, sizeof(*mod));
263     }
264     free(context->preauth_context->modules);
265     free(context->preauth_context);
266     context->preauth_context = NULL;
267 }
268
269 /* Initialize the per-AS-REQ context. This means calling the client_req_init
270  * function to give the plugin a chance to allocate a per-request context. */
271 void KRB5_CALLCONV
272 krb5_preauth_request_context_init(krb5_context context)
273 {
274     int i;
275     struct krb5_preauth_context_module_st *mod;
276
277     if (context->preauth_context == NULL)
278         krb5_init_preauth_context(context);
279     if (context->preauth_context == NULL)
280         return;
281     for (i = 0; i < context->preauth_context->n_modules; i++) {
282         context->preauth_context->modules[i].use_count = 0;
283         mod = &context->preauth_context->modules[i];
284         if (mod->client_req_init != NULL)
285             mod->client_req_init(context, mod->moddata, mod->modreq_p);
286     }
287 }
288
289 /* Free the per-AS-REQ context. This means clearing any request-specific
290  * context which the plugin may have created. */
291 void KRB5_CALLCONV
292 krb5_preauth_request_context_fini(krb5_context context)
293 {
294     int i;
295     struct krb5_preauth_context_module_st *mod;
296
297     if (context->preauth_context == NULL)
298         return;
299     for (i = 0; i < context->preauth_context->n_modules; i++) {
300         mod = &context->preauth_context->modules[i];
301         if (mod->modreq != NULL) {
302             if (mod->client_req_fini != NULL)
303                 mod->client_req_fini(context, mod->moddata, mod->modreq);
304             mod->modreq = NULL;
305         }
306     }
307 }
308
309 /* Add the named encryption type to the existing list of ktypes. */
310 static void
311 grow_ktypes(krb5_enctype **out_ktypes, int *out_nktypes, krb5_enctype ktype)
312 {
313     int i;
314     krb5_enctype *ktypes;
315     for (i = 0; i < *out_nktypes; i++) {
316         if ((*out_ktypes)[i] == ktype)
317             return;
318     }
319     ktypes = malloc((*out_nktypes + 2) * sizeof(ktype));
320     if (ktypes) {
321         for (i = 0; i < *out_nktypes; i++)
322             ktypes[i] = (*out_ktypes)[i];
323         ktypes[i++] = ktype;
324         ktypes[i] = 0;
325         free(*out_ktypes);
326         *out_ktypes = ktypes;
327         *out_nktypes = i;
328     }
329 }
330
331 /*
332  * Add the given list of pa_data items to the existing list of items.
333  * Factored out here to make reading the do_preauth logic easier to read.
334  */
335 static int
336 grow_pa_list(krb5_pa_data ***out_pa_list, int *out_pa_list_size,
337              krb5_pa_data **addition, int num_addition)
338 {
339     krb5_pa_data **pa_list;
340     int i, j;
341
342     if (out_pa_list == NULL || addition == NULL) {
343         return EINVAL;
344     }
345
346     if (*out_pa_list == NULL) {
347         /* Allocate room for the new additions and a NULL terminator. */
348         pa_list = malloc((num_addition + 1) * sizeof(krb5_pa_data *));
349         if (pa_list == NULL)
350             return ENOMEM;
351         for (i = 0; i < num_addition; i++)
352             pa_list[i] = addition[i];
353         pa_list[i] = NULL;
354         *out_pa_list = pa_list;
355         *out_pa_list_size = num_addition;
356     } else {
357         /*
358          * Allocate room for the existing entries plus
359          * the new additions and a NULL terminator.
360          */
361         pa_list = malloc((*out_pa_list_size + num_addition + 1)
362                          * sizeof(krb5_pa_data *));
363         if (pa_list == NULL)
364             return ENOMEM;
365         for (i = 0; i < *out_pa_list_size; i++)
366             pa_list[i] = (*out_pa_list)[i];
367         for (j = 0; j < num_addition;)
368             pa_list[i++] = addition[j++];
369         pa_list[i] = NULL;
370         free(*out_pa_list);
371         *out_pa_list = pa_list;
372         *out_pa_list_size = i;
373     }
374     return 0;
375 }
376
377 static krb5_enctype
378 get_etype(krb5_context context, krb5_clpreauth_rock rock)
379 {
380     return *rock->etype;
381 }
382
383 static krb5_keyblock *
384 fast_armor(krb5_context context, krb5_clpreauth_rock rock)
385 {
386     return rock->fast_state->armor_key;
387 }
388
389 static krb5_error_code
390 get_as_key(krb5_context context, krb5_clpreauth_rock rock,
391            krb5_keyblock **keyblock)
392 {
393     krb5_error_code ret;
394
395     if (rock->as_key->length == 0) {
396         ret = (*rock->gak_fct)(context, rock->client, *rock->etype,
397                                rock->prompter, rock->prompter_data, rock->salt,
398                                rock->s2kparams, rock->as_key, *rock->gak_data);
399         if (ret)
400             return ret;
401     }
402     *keyblock = rock->as_key;
403     return 0;
404 }
405
406 static krb5_error_code
407 set_as_key(krb5_context context, krb5_clpreauth_rock rock,
408            const krb5_keyblock *keyblock)
409 {
410     krb5_free_keyblock_contents(context, rock->as_key);
411     return krb5_copy_keyblock_contents(context, keyblock, rock->as_key);
412 }
413
414 static krb5_error_code
415 get_preauth_time(krb5_context context, krb5_clpreauth_rock rock,
416                  krb5_boolean allow_unauth_time, krb5_timestamp *time_out,
417                  krb5_int32 *usec_out)
418 {
419     if (rock->pa_offset_state != NO_OFFSET &&
420         (allow_unauth_time || rock->pa_offset_state == AUTH_OFFSET) &&
421         (context->library_options & KRB5_LIBOPT_SYNC_KDCTIME)) {
422         /* Use the offset we got from the preauth-required error. */
423         return k5_time_with_offset(rock->pa_offset, rock->pa_offset_usec,
424                                    time_out, usec_out);
425
426     } else {
427         /* Use the time offset from the context, or no offset. */
428         return krb5_us_timeofday(context, time_out, usec_out);
429     }
430 }
431
432 static struct krb5_clpreauth_callbacks_st callbacks = {
433     2,
434     get_etype,
435     fast_armor,
436     get_as_key,
437     set_as_key,
438     get_preauth_time
439 };
440
441 /* Tweak the request body, for now adding any enctypes which the module claims
442  * to add support for to the list, but in the future perhaps doing more
443  * involved things. */
444 void KRB5_CALLCONV
445 krb5_preauth_prepare_request(krb5_context kcontext,
446                              krb5_gic_opt_ext *opte,
447                              krb5_kdc_req *request)
448 {
449     int i, j;
450
451     if (kcontext->preauth_context == NULL) {
452         return;
453     }
454     /* Add the module-specific enctype list to the request, but only if
455      * it's something we can safely modify. */
456     if (!(opte && (opte->flags & KRB5_GET_INIT_CREDS_OPT_ETYPE_LIST))) {
457         for (i = 0; i < kcontext->preauth_context->n_modules; i++) {
458             if (kcontext->preauth_context->modules[i].enctypes == NULL)
459                 continue;
460             for (j = 0; kcontext->preauth_context->modules[i].enctypes[j] != 0; j++) {
461                 grow_ktypes(&request->ktype, &request->nktypes,
462                             kcontext->preauth_context->modules[i].enctypes[j]);
463             }
464         }
465     }
466 }
467
468 /* Find the first module which provides for the named preauth type which also
469  * hasn't had a chance to run yet (INFO modules don't count, because as a rule
470  * they don't generate preauth data), and run it. */
471 static krb5_error_code
472 run_preauth_plugins(krb5_context kcontext,
473                     int module_required_flags,
474                     krb5_kdc_req *request,
475                     krb5_data *encoded_request_body,
476                     krb5_data *encoded_previous_request,
477                     krb5_pa_data *in_padata,
478                     krb5_prompter_fct prompter,
479                     void *prompter_data,
480                     krb5_clpreauth_rock preauth_rock,
481                     krb5_pa_data ***out_pa_list,
482                     int *out_pa_list_size,
483                     int *module_ret,
484                     int *module_flags,
485                     krb5_gic_opt_ext *opte)
486 {
487     int i;
488     krb5_pa_data **out_pa_data;
489     krb5_error_code ret;
490     struct krb5_preauth_context_module_st *module;
491
492     if (kcontext->preauth_context == NULL) {
493         return ENOENT;
494     }
495     /* iterate over all loaded modules */
496     for (i = 0; i < kcontext->preauth_context->n_modules; i++) {
497         module = &kcontext->preauth_context->modules[i];
498         /* skip over those which don't match the preauth type */
499         if (module->pa_type != in_padata->pa_type)
500             continue;
501         /* skip over those which don't match the flags (INFO vs REAL, mainly) */
502         if ((module->flags & module_required_flags) == 0)
503             continue;
504         /* if it's a REAL module, try to call it only once per library call */
505         if (module_required_flags & PA_REAL) {
506             if (module->use_count > 0) {
507                 TRACE_PREAUTH_SKIP(kcontext, module->name, module->pa_type);
508                 continue;
509             }
510             module->use_count++;
511         }
512         /* run the module's callback function */
513         out_pa_data = NULL;
514 #ifdef DEBUG
515         fprintf(stderr, "using module \"%s\" (%d), flags = %d\n",
516                 module->name, module->pa_type, module->flags);
517 #endif
518         ret = module->client_process(kcontext, module->moddata,
519                                      *module->modreq_p,
520                                      (krb5_get_init_creds_opt *)opte,
521                                      &callbacks, preauth_rock,
522                                      request, encoded_request_body,
523                                      encoded_previous_request, in_padata,
524                                      prompter, prompter_data, &out_pa_data);
525         TRACE_PREAUTH_PROCESS(kcontext, module->name, module->pa_type,
526                               module->flags, ret);
527         /* Make note of the module's flags and status. */
528         *module_flags = module->flags;
529         *module_ret = ret;
530         /* Save the new preauth data item. */
531         if (out_pa_data != NULL) {
532             int j;
533             for (j = 0; out_pa_data[j] != NULL; j++);
534             ret = grow_pa_list(out_pa_list, out_pa_list_size, out_pa_data, j);
535             free(out_pa_data);
536             if (ret != 0)
537                 return ret;
538         }
539         break;
540     }
541     if (i >= kcontext->preauth_context->n_modules) {
542         return ENOENT;
543     }
544     return 0;
545 }
546
547 static inline krb5_data
548 padata2data(krb5_pa_data p)
549 {
550     krb5_data d;
551     d.magic = KV5M_DATA;
552     d.length = p.length;
553     d.data = (char *) p.contents;
554     return d;
555 }
556
557 static krb5_error_code
558 pa_salt(krb5_context context, krb5_kdc_req *request, krb5_pa_data *in_padata,
559         krb5_pa_data **out_padata, krb5_data *salt, krb5_data *s2kparams,
560         krb5_enctype *etype, krb5_keyblock *as_key, krb5_prompter_fct prompter,
561         void *prompter_data, krb5_gic_get_as_key_fct gak_fct, void *gak_data)
562 {
563     krb5_data tmp;
564     krb5_error_code retval;
565
566     tmp = padata2data(*in_padata);
567     krb5_free_data_contents(context, salt);
568     retval = krb5int_copy_data_contents_add0(context, &tmp, salt);
569     if (retval)
570         return retval;
571
572     TRACE_PREAUTH_SALT(context, salt, in_padata->pa_type);
573     if (in_padata->pa_type == KRB5_PADATA_AFS3_SALT)
574         salt->length = SALT_TYPE_AFS_LENGTH;
575
576     return(0);
577 }
578
579 static krb5_error_code
580 pa_fx_cookie(krb5_context context, krb5_kdc_req *request,
581              krb5_pa_data *in_padata, krb5_pa_data **out_padata,
582              krb5_data *salt, krb5_data *s2kparams, krb5_enctype *etype,
583              krb5_keyblock *as_key, krb5_prompter_fct prompter,
584              void *prompter_data, krb5_gic_get_as_key_fct gak_fct,
585              void *gak_data)
586 {
587     krb5_pa_data *pa = calloc(1, sizeof(krb5_pa_data));
588     krb5_octet *contents;
589
590     TRACE_PREAUTH_COOKIE(context, in_padata->length, in_padata->contents);
591     if (pa == NULL)
592         return ENOMEM;
593     contents = malloc(in_padata->length);
594     if (contents == NULL) {
595         free(pa);
596         return ENOMEM;
597     }
598     *pa = *in_padata;
599     pa->contents = contents;
600     memcpy(contents, in_padata->contents, pa->length);
601     *out_padata = pa;
602     return 0;
603 }
604
605 static krb5_error_code
606 pa_s4u_x509_user(krb5_context context, krb5_kdc_req *request,
607                  krb5_pa_data *in_padata, krb5_pa_data **out_padata,
608                  krb5_data *salt, krb5_data *s2kparams, krb5_enctype *etype,
609                  krb5_keyblock *as_key, krb5_prompter_fct prompter,
610                  void *prompter_data, krb5_gic_get_as_key_fct gak_fct,
611                  void *gak_data)
612 {
613     krb5_s4u_userid *userid = (krb5_s4u_userid *)gak_data; /* XXX private contract */
614     krb5_pa_data *s4u_padata;
615     krb5_error_code code;
616     krb5_principal client;
617
618     *out_padata = NULL;
619
620     if (userid == NULL)
621         return EINVAL;
622
623     code = krb5_copy_principal(context, request->client, &client);
624     if (code != 0)
625         return code;
626
627     if (userid->user != NULL)
628         krb5_free_principal(context, userid->user);
629     userid->user = client;
630
631     if (userid->subject_cert.length != 0) {
632         s4u_padata = malloc(sizeof(*s4u_padata));
633         if (s4u_padata == NULL)
634             return ENOMEM;
635
636         s4u_padata->magic = KV5M_PA_DATA;
637         s4u_padata->pa_type = KRB5_PADATA_S4U_X509_USER;
638         s4u_padata->contents = malloc(userid->subject_cert.length);
639         if (s4u_padata->contents == NULL) {
640             free(s4u_padata);
641             return ENOMEM;
642         }
643         memcpy(s4u_padata->contents, userid->subject_cert.data, userid->subject_cert.length);
644         s4u_padata->length = userid->subject_cert.length;
645
646         *out_padata = s4u_padata;
647     }
648
649     return 0;
650 }
651
652 /* FIXME - order significant? */
653 static const pa_types_t pa_types[] = {
654     {
655         KRB5_PADATA_PW_SALT,
656         pa_salt,
657         PA_INFO,
658     },
659     {
660         KRB5_PADATA_AFS3_SALT,
661         pa_salt,
662         PA_INFO,
663     },
664     {
665         KRB5_PADATA_FX_COOKIE,
666         pa_fx_cookie,
667         PA_INFO,
668     },
669     {
670         KRB5_PADATA_S4U_X509_USER,
671         pa_s4u_x509_user,
672         PA_INFO,
673     },
674     {
675         -1,
676         NULL,
677         0,
678     },
679 };
680
681 /*
682  * If one of the modules can adjust its AS_REQ data using the contents of the
683  * err_reply, return 0.  If it's the sort of correction which requires that we
684  * ask the user another question, we let the calling application deal with it.
685  */
686 krb5_error_code KRB5_CALLCONV
687 krb5_do_preauth_tryagain(krb5_context kcontext,
688                          krb5_kdc_req *request,
689                          krb5_data *encoded_request_body,
690                          krb5_data *encoded_previous_request,
691                          krb5_pa_data **padata,
692                          krb5_pa_data ***return_padata,
693                          krb5_error *err_reply,
694                          krb5_pa_data **err_padata,
695                          krb5_prompter_fct prompter, void *prompter_data,
696                          krb5_clpreauth_rock preauth_rock,
697                          krb5_gic_opt_ext *opte)
698 {
699     krb5_error_code ret;
700     krb5_pa_data **out_padata;
701     krb5_preauth_context *context;
702     struct krb5_preauth_context_module_st *module;
703     int i, j;
704     int out_pa_list_size = 0;
705
706     ret = KRB5KRB_ERR_GENERIC;
707     if (kcontext->preauth_context == NULL) {
708         return KRB5KRB_ERR_GENERIC;
709     }
710     context = kcontext->preauth_context;
711     if (context == NULL) {
712         return KRB5KRB_ERR_GENERIC;
713     }
714
715     TRACE_PREAUTH_TRYAGAIN_INPUT(kcontext, padata);
716
717     for (i = 0; padata[i] != NULL && padata[i]->pa_type != 0; i++) {
718         out_padata = NULL;
719         for (j = 0; j < context->n_modules; j++) {
720             module = &context->modules[j];
721             if (module->pa_type != padata[i]->pa_type) {
722                 continue;
723             }
724             if (module->client_tryagain == NULL) {
725                 continue;
726             }
727             if ((*module->client_tryagain)(kcontext, module->moddata,
728                                            *module->modreq_p,
729                                            (krb5_get_init_creds_opt *)opte,
730                                            &callbacks, preauth_rock,
731                                            request,
732                                            encoded_request_body,
733                                            encoded_previous_request,
734                                            padata[i]->pa_type,
735                                            err_reply, err_padata,
736                                            prompter, prompter_data,
737                                            &out_padata) == 0) {
738                 if (out_padata != NULL) {
739                     int k;
740                     for (k = 0; out_padata[k] != NULL; k++);
741                     grow_pa_list(return_padata, &out_pa_list_size,
742                                  out_padata, k);
743                     free(out_padata);
744                     TRACE_PREAUTH_TRYAGAIN_OUTPUT(kcontext, *return_padata);
745                     return 0;
746                 }
747             }
748         }
749     }
750     return ret;
751 }
752
753 krb5_error_code KRB5_CALLCONV
754 krb5_do_preauth(krb5_context context, krb5_kdc_req *request,
755                 krb5_data *encoded_request_body,
756                 krb5_data *encoded_previous_request,
757                 krb5_pa_data **in_padata, krb5_pa_data ***out_padata,
758                 krb5_prompter_fct prompter, void *prompter_data,
759                 krb5_clpreauth_rock rock, krb5_gic_opt_ext *opte,
760                 krb5_boolean *got_real_out)
761 {
762     unsigned int h;
763     int i, j, out_pa_list_size;
764     int seen_etype_info2 = 0;
765     krb5_pa_data *out_pa = NULL, **out_pa_list = NULL;
766     krb5_data scratch;
767     krb5_etype_info etype_info = NULL;
768     krb5_error_code ret;
769     static const int paorder[] = { PA_INFO, PA_REAL };
770     int realdone;
771
772     *got_real_out = FALSE;
773
774     if (in_padata == NULL) {
775         *out_padata = NULL;
776         return(0);
777     }
778
779     TRACE_PREAUTH_INPUT(context, in_padata);
780
781     out_pa_list = NULL;
782     out_pa_list_size = 0;
783
784     /* first do all the informational preauths, then the first real one */
785
786     for (h=0; h<(sizeof(paorder)/sizeof(paorder[0])); h++) {
787         realdone = 0;
788         for (i=0; in_padata[i] && !realdone; i++) {
789             int k, l, etype_found, valid_etype_found;
790             /*
791              * This is really gross, but is necessary to prevent
792              * lossage when talking to a 1.0.x KDC, which returns an
793              * erroneous PA-PW-SALT when it returns a KRB-ERROR
794              * requiring additional preauth.
795              */
796             switch (in_padata[i]->pa_type) {
797             case KRB5_PADATA_ETYPE_INFO:
798             case KRB5_PADATA_ETYPE_INFO2:
799             {
800                 krb5_preauthtype pa_type = in_padata[i]->pa_type;
801                 if (etype_info) {
802                     if (seen_etype_info2 || pa_type != KRB5_PADATA_ETYPE_INFO2)
803                         continue;
804                     if (pa_type == KRB5_PADATA_ETYPE_INFO2) {
805                         krb5_free_etype_info( context, etype_info);
806                         etype_info = NULL;
807                     }
808                 }
809
810                 scratch.length = in_padata[i]->length;
811                 scratch.data = (char *) in_padata[i]->contents;
812                 if (pa_type == KRB5_PADATA_ETYPE_INFO2) {
813                     seen_etype_info2++;
814                     ret = decode_krb5_etype_info2(&scratch, &etype_info);
815                 }
816                 else ret = decode_krb5_etype_info(&scratch, &etype_info);
817                 if (ret) {
818                     ret = 0; /*Ignore error and etype_info element*/
819                     if (etype_info)
820                         krb5_free_etype_info( context, etype_info);
821                     etype_info = NULL;
822                     continue;
823                 }
824                 if (etype_info[0] == NULL) {
825                     krb5_free_etype_info(context, etype_info);
826                     etype_info = NULL;
827                     break;
828                 }
829                 /*
830                  * Select first etype in our request which is also in
831                  * etype-info (preferring client request ktype order).
832                  */
833                 for (etype_found = 0, valid_etype_found = 0, k = 0;
834                      !etype_found && k < request->nktypes; k++) {
835                     for (l = 0; etype_info[l]; l++) {
836                         if (etype_info[l]->etype == request->ktype[k]) {
837                             etype_found++;
838                             break;
839                         }
840                         /* check if program has support for this etype for more
841                          * precise error reporting.
842                          */
843                         if (krb5_c_valid_enctype(etype_info[l]->etype))
844                             valid_etype_found++;
845                     }
846                 }
847                 if (!etype_found) {
848                     if (valid_etype_found) {
849                         /* supported enctype but not requested */
850                         ret =  KRB5_CONFIG_ETYPE_NOSUPP;
851                         goto cleanup;
852                     }
853                     else {
854                         /* unsupported enctype */
855                         ret =  KRB5_PROG_ETYPE_NOSUPP;
856                         goto cleanup;
857                     }
858
859                 }
860                 scratch.data = (char *) etype_info[l]->salt;
861                 scratch.length = etype_info[l]->length;
862                 krb5_free_data_contents(context, rock->salt);
863                 if (scratch.length == KRB5_ETYPE_NO_SALT)
864                     rock->salt->data = NULL;
865                 else {
866                     ret = krb5int_copy_data_contents(context, &scratch,
867                                                      rock->salt);
868                     if (ret)
869                         goto cleanup;
870                 }
871                 *rock->etype = etype_info[l]->etype;
872                 krb5_free_data_contents(context, rock->s2kparams);
873                 ret = krb5int_copy_data_contents(context,
874                                                  &etype_info[l]->s2kparams,
875                                                  rock->s2kparams);
876                 if (ret)
877                     goto cleanup;
878                 TRACE_PREAUTH_ETYPE_INFO(context, *rock->etype, rock->salt,
879                                          rock->s2kparams);
880                 break;
881             }
882             case KRB5_PADATA_PW_SALT:
883             case KRB5_PADATA_AFS3_SALT:
884                 if (etype_info)
885                     continue;
886                 break;
887             default:
888                 ;
889             }
890             /* Try the internally-provided preauth type list. */
891             if (!realdone) for (j=0; pa_types[j].type >= 0; j++) {
892                     if ((in_padata[i]->pa_type == pa_types[j].type) &&
893                         (pa_types[j].flags & paorder[h])) {
894 #ifdef DEBUG
895                         fprintf (stderr, "calling internal function for pa_type "
896                                  "%d, flag %d\n", pa_types[j].type, paorder[h]);
897 #endif
898                         out_pa = NULL;
899
900                         ret = pa_types[j].fct(context, request, in_padata[i],
901                                               &out_pa, rock->salt,
902                                               rock->s2kparams, rock->etype,
903                                               rock->as_key, prompter,
904                                               prompter_data, *rock->gak_fct,
905                                               *rock->gak_data);
906                         if (ret) {
907                             if (paorder[h] == PA_INFO) {
908                                 TRACE_PREAUTH_INFO_FAIL(context,
909                                                         in_padata[i]->pa_type,
910                                                         ret);
911                                 ret = 0;
912                                 continue; /* PA_INFO type failed, ignore */
913                             }
914
915                             goto cleanup;
916                         }
917
918                         ret = grow_pa_list(&out_pa_list, &out_pa_list_size,
919                                            &out_pa, 1);
920                         if (ret != 0) {
921                             goto cleanup;
922                         }
923                         if (paorder[h] == PA_REAL)
924                             realdone = 1;
925                     }
926                 }
927
928             /* Try to use plugins now. */
929             if (!realdone) {
930                 krb5_init_preauth_context(context);
931                 if (context->preauth_context != NULL) {
932                     int module_ret = 0, module_flags;
933 #ifdef DEBUG
934                     fprintf (stderr, "trying modules for pa_type %d, flag %d\n",
935                              in_padata[i]->pa_type, paorder[h]);
936 #endif
937                     ret = run_preauth_plugins(context,
938                                               paorder[h],
939                                               request,
940                                               encoded_request_body,
941                                               encoded_previous_request,
942                                               in_padata[i],
943                                               prompter,
944                                               prompter_data,
945                                               rock,
946                                               &out_pa_list,
947                                               &out_pa_list_size,
948                                               &module_ret,
949                                               &module_flags,
950                                               opte);
951                     if (ret == 0) {
952                         if (module_ret == 0) {
953                             if (paorder[h] == PA_REAL) {
954                                 realdone = 1;
955                             }
956                         }
957                     }
958                 }
959             }
960         }
961     }
962
963     TRACE_PREAUTH_OUTPUT(context, out_pa_list);
964     *out_padata = out_pa_list;
965     if (etype_info)
966         krb5_free_etype_info(context, etype_info);
967
968     *got_real_out = realdone;
969     return(0);
970 cleanup:
971     if (out_pa_list) {
972         out_pa_list[out_pa_list_size++] = NULL;
973         krb5_free_pa_data(context, out_pa_list);
974     }
975     if (etype_info)
976         krb5_free_etype_info(context, etype_info);
977     return (ret);
978 }
979
980 /*
981  * Give all the preauth plugins a look at the preauth option which
982  * has just been set
983  */
984 krb5_error_code
985 krb5_preauth_supply_preauth_data(krb5_context context, krb5_gic_opt_ext *opte,
986                                  const char *attr, const char *value)
987 {
988     krb5_error_code retval = 0;
989     int i;
990     struct krb5_preauth_context_module_st *mod;
991     const char *emsg = NULL;
992
993     if (context->preauth_context == NULL)
994         krb5_init_preauth_context(context);
995     if (context->preauth_context == NULL) {
996         retval = EINVAL;
997         krb5_set_error_message(context, retval,
998                                _("Unable to initialize preauth context"));
999         return retval;
1000     }
1001
1002     /*
1003      * Go down the list of preauth modules, and supply them with the
1004      * attribute/value pair.
1005      */
1006     for (i = 0; i < context->preauth_context->n_modules; i++) {
1007         mod = &context->preauth_context->modules[i];
1008         if (mod->client_supply_gic_opts == NULL)
1009             continue;
1010         retval = mod->client_supply_gic_opts(context, mod->moddata,
1011                                              (krb5_get_init_creds_opt *)opte,
1012                                              attr, value);
1013         if (retval) {
1014             emsg = krb5_get_error_message(context, retval);
1015             krb5_set_error_message(context, retval, _("Preauth plugin %s: %s"),
1016                                    mod->name, emsg);
1017             krb5_free_error_message(context, emsg);
1018             break;
1019         }
1020     }
1021     return retval;
1022 }