Imported Upstream version 1.17
[platform/upstream/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, 2012 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 "k5-json.h"
35 #include "osconf.h"
36 #include <krb5/clpreauth_plugin.h>
37 #include "int-proto.h"
38 #include "os-proto.h"
39 #include "fast.h"
40 #include "init_creds_ctx.h"
41
42 #if !defined(_WIN32)
43 #include <unistd.h>
44 #endif
45
46 typedef struct {
47     struct krb5_clpreauth_vtable_st vt;
48     krb5_clpreauth_moddata data;
49 } *clpreauth_handle;
50
51 struct krb5_preauth_context_st {
52     clpreauth_handle *handles;
53 };
54
55 struct krb5_preauth_req_context_st {
56     krb5_context orig_context;
57     krb5_preauthtype *failed;
58     krb5_clpreauth_modreq *modreqs;
59 };
60
61 /* Release the memory used by a list of handles. */
62 static void
63 free_handles(krb5_context context, clpreauth_handle *handles)
64 {
65     clpreauth_handle *hp, h;
66
67     if (handles == NULL)
68         return;
69     for (hp = handles; *hp != NULL; hp++) {
70         h = *hp;
71         if (h->vt.fini != NULL)
72             h->vt.fini(context, h->data);
73         free(h);
74     }
75     free(handles);
76 }
77
78 /* Return an index into handles which can process pa_type, or -1 if none is
79  * found found. */
80 static int
81 search_module_list(clpreauth_handle *handles, krb5_preauthtype pa_type)
82 {
83     clpreauth_handle h;
84     int i, j;
85
86     for (i = 0; handles[i] != NULL; i++) {
87         h = handles[i];
88         for (j = 0; h->vt.pa_type_list[j] != 0; j++) {
89             if (h->vt.pa_type_list[j] == pa_type)
90                 return i;
91         }
92     }
93     return -1;
94 }
95
96 /* Find the handle which can process pa_type, or NULL if none is found.  On
97  * success, set *modreq_out to the corresponding per-request module data. */
98 static clpreauth_handle
99 find_module(krb5_context context, krb5_init_creds_context ctx,
100             krb5_preauthtype pa_type, krb5_clpreauth_modreq *modreq_out)
101 {
102     krb5_preauth_context pctx = context->preauth_context;
103     krb5_preauth_req_context reqctx = ctx->preauth_reqctx;
104     int i;
105
106     *modreq_out = NULL;
107     if (pctx == NULL || reqctx == NULL)
108         return NULL;
109
110     i = search_module_list(pctx->handles, pa_type);
111     if (i == -1)
112         return NULL;
113
114     *modreq_out = reqctx->modreqs[i];
115     return pctx->handles[i];
116 }
117
118 /* Initialize the preauth state for a krb5 context. */
119 void
120 k5_init_preauth_context(krb5_context context)
121 {
122     krb5_plugin_initvt_fn *modules = NULL, *mod;
123     clpreauth_handle *list = NULL, h;
124     int i;
125     size_t count;
126     krb5_preauthtype *tp;
127
128     /* Only do this once for each krb5_context */
129     if (context->preauth_context != NULL)
130         return;
131
132     /* Auto-register built-in modules. */
133     k5_plugin_register_dyn(context, PLUGIN_INTERFACE_CLPREAUTH, "pkinit",
134                            "preauth");
135     k5_plugin_register_dyn(context, PLUGIN_INTERFACE_CLPREAUTH, "spake",
136                            "preauth");
137     k5_plugin_register(context, PLUGIN_INTERFACE_CLPREAUTH,
138                        "encrypted_challenge",
139                        clpreauth_encrypted_challenge_initvt);
140     k5_plugin_register(context, PLUGIN_INTERFACE_CLPREAUTH,
141                        "encrypted_timestamp",
142                        clpreauth_encrypted_timestamp_initvt);
143     k5_plugin_register(context, PLUGIN_INTERFACE_CLPREAUTH, "sam2",
144                        clpreauth_sam2_initvt);
145     k5_plugin_register(context, PLUGIN_INTERFACE_CLPREAUTH, "otp",
146                        clpreauth_otp_initvt);
147
148     /* Get all available clpreauth vtables. */
149     if (k5_plugin_load_all(context, PLUGIN_INTERFACE_CLPREAUTH, &modules))
150         return;
151
152     /* Allocate a large enough list of handles. */
153     for (count = 0; modules[count] != NULL; count++);
154     list = calloc(count + 1, sizeof(*list));
155     if (list == NULL)
156         goto cleanup;
157
158     /* Create a handle for each module we can successfully initialize. */
159     count = 0;
160     for (mod = modules; *mod != NULL; mod++) {
161         h = calloc(1, sizeof(*h));
162         if (h == NULL)
163             goto cleanup;
164
165         /* Initialize the handle vtable. */
166         if ((*mod)(context, 1, 1, (krb5_plugin_vtable)&h->vt) != 0) {
167             free(h);
168             continue;
169         }
170
171         /* Check for a preauth type conflict with an existing module. */
172         for (tp = h->vt.pa_type_list; *tp != 0; tp++) {
173             i = search_module_list(list, *tp);
174             if (i != -1) {
175                 TRACE_PREAUTH_CONFLICT(context, h->vt.name, list[i]->vt.name,
176                                        *tp);
177                 break;
178             }
179         }
180         if (*tp != 0)
181             continue;
182
183         /* Initialize the module data. */
184         h->data = NULL;
185         if (h->vt.init != NULL && h->vt.init(context, &h->data) != 0) {
186             free(h);
187             continue;
188         }
189         list[count++] = h;
190         list[count] = NULL;
191     }
192     list[count] = NULL;
193
194     /* Place the constructed preauth context into the krb5 context. */
195     context->preauth_context = malloc(sizeof(*context->preauth_context));
196     if (context->preauth_context == NULL)
197         goto cleanup;
198     context->preauth_context->handles = list;
199     list = NULL;
200
201 cleanup:
202     k5_plugin_free_modules(context, modules);
203     free_handles(context, list);
204 }
205
206 /* Add pa_type to the list of types which has previously failed. */
207 krb5_error_code
208 k5_preauth_note_failed(krb5_init_creds_context ctx, krb5_preauthtype pa_type)
209 {
210     krb5_preauth_req_context reqctx = ctx->preauth_reqctx;
211     krb5_preauthtype *newptr;
212     size_t i;
213
214     for (i = 0; reqctx->failed != NULL && reqctx->failed[i] != 0; i++);
215     newptr = realloc(reqctx->failed, (i + 2) * sizeof(*newptr));
216     if (newptr == NULL)
217         return ENOMEM;
218     reqctx->failed = newptr;
219     reqctx->failed[i] = pa_type;
220     reqctx->failed[i + 1] = 0;
221     return 0;
222 }
223
224 /* Free the per-krb5_context preauth_context. This means clearing any
225  * plugin-specific context which may have been created, and then
226  * freeing the context itself. */
227 void
228 k5_free_preauth_context(krb5_context context)
229 {
230     krb5_preauth_context pctx = context->preauth_context;
231
232     if (pctx == NULL)
233         return;
234     free_handles(context, pctx->handles);
235     free(pctx);
236     context->preauth_context = NULL;
237 }
238
239 /* Initialize the per-AS-REQ context. This means calling the client_req_init
240  * function to give the plugin a chance to allocate a per-request context. */
241 void
242 k5_preauth_request_context_init(krb5_context context,
243                                 krb5_init_creds_context ctx)
244 {
245     krb5_preauth_context pctx = context->preauth_context;
246     clpreauth_handle h;
247     krb5_preauth_req_context reqctx;
248     size_t count, i;
249
250     if (pctx == NULL) {
251         k5_init_preauth_context(context);
252         pctx = context->preauth_context;
253         if (pctx == NULL)
254             return;
255     }
256
257     reqctx = calloc(1, sizeof(*reqctx));
258     if (reqctx == NULL)
259         return;
260     reqctx->orig_context = context;
261
262     /* Create an array of per-request module data objects corresponding to the
263      * preauth context's array of handles. */
264     for (count = 0; pctx->handles[count] != NULL; count++);
265     reqctx->modreqs = calloc(count, sizeof(*reqctx->modreqs));
266     for (i = 0; i < count; i++) {
267         h = pctx->handles[i];
268         if (h->vt.request_init != NULL)
269             h->vt.request_init(context, h->data, &reqctx->modreqs[i]);
270     }
271     ctx->preauth_reqctx = reqctx;
272 }
273
274 /* Free the per-AS-REQ context. This means clearing any request-specific
275  * context which the plugin may have created. */
276 void
277 k5_preauth_request_context_fini(krb5_context context,
278                                 krb5_init_creds_context ctx)
279 {
280     krb5_preauth_context pctx = context->preauth_context;
281     krb5_preauth_req_context reqctx = ctx->preauth_reqctx;
282     size_t i;
283     clpreauth_handle h;
284
285     if (reqctx == NULL)
286         return;
287     if (reqctx->orig_context == context && pctx != NULL) {
288         for (i = 0; pctx->handles[i] != NULL; i++) {
289             h = pctx->handles[i];
290             if (reqctx->modreqs[i] != NULL && h->vt.request_fini != NULL)
291                 h->vt.request_fini(context, h->data, reqctx->modreqs[i]);
292         }
293     } else {
294         TRACE_PREAUTH_WRONG_CONTEXT(context);
295     }
296     free(reqctx->modreqs);
297     free(reqctx->failed);
298     free(reqctx);
299     ctx->preauth_reqctx = NULL;
300 }
301
302 krb5_error_code
303 k5_preauth_check_context(krb5_context context, krb5_init_creds_context ctx)
304 {
305     krb5_preauth_req_context reqctx = ctx->preauth_reqctx;
306
307     if (reqctx != NULL && reqctx->orig_context != context) {
308         k5_setmsg(context, EINVAL,
309                   _("krb5_init_creds calls must use same library context"));
310         return EINVAL;
311     }
312     return 0;
313 }
314
315 /* Return 1 if pa_type is a real preauthentication mechanism according to the
316  * module h.  Return 0 if it is not. */
317 static int
318 clpreauth_is_real(krb5_context context, clpreauth_handle h,
319                   krb5_preauthtype pa_type)
320 {
321     if (h->vt.flags == NULL)
322         return 1;
323     return (h->vt.flags(context, pa_type) & PA_REAL) != 0;
324 }
325
326 static krb5_error_code
327 clpreauth_prep_questions(krb5_context context, clpreauth_handle h,
328                          krb5_clpreauth_modreq modreq,
329                          krb5_get_init_creds_opt *opt,
330                          krb5_clpreauth_callbacks cb, krb5_clpreauth_rock rock,
331                          krb5_kdc_req *req, krb5_data *req_body,
332                          krb5_data *prev_req, krb5_pa_data *pa_data)
333 {
334     if (h->vt.prep_questions == NULL)
335         return 0;
336     return h->vt.prep_questions(context, h->data, modreq, opt, cb, rock, req,
337                                 req_body, prev_req, pa_data);
338 }
339
340 static krb5_error_code
341 clpreauth_process(krb5_context context, clpreauth_handle h,
342                   krb5_clpreauth_modreq modreq, krb5_get_init_creds_opt *opt,
343                   krb5_clpreauth_callbacks cb, krb5_clpreauth_rock rock,
344                   krb5_kdc_req *req, krb5_data *req_body, krb5_data *prev_req,
345                   krb5_pa_data *pa_data, krb5_prompter_fct prompter,
346                   void *prompter_data, krb5_pa_data ***pa_data_out)
347 {
348     return h->vt.process(context, h->data, modreq, opt, cb, rock, req,
349                          req_body, prev_req, pa_data, prompter, prompter_data,
350                          pa_data_out);
351 }
352
353 static krb5_error_code
354 clpreauth_tryagain(krb5_context context, clpreauth_handle h,
355                    krb5_clpreauth_modreq modreq, krb5_get_init_creds_opt *opt,
356                    krb5_clpreauth_callbacks cb, krb5_clpreauth_rock rock,
357                    krb5_kdc_req *req, krb5_data *req_body, krb5_data *prev_req,
358                    krb5_preauthtype pa_type, krb5_error *error,
359                    krb5_pa_data **error_padata, krb5_prompter_fct prompter,
360                    void *prompter_data, krb5_pa_data ***pa_data_out)
361 {
362     if (h->vt.tryagain == NULL)
363         return 0;
364     return h->vt.tryagain(context, h->data, modreq, opt, cb, rock, req,
365                           req_body, prev_req, pa_type, error, error_padata,
366                           prompter, prompter_data, pa_data_out);
367 }
368
369 static krb5_error_code
370 clpreauth_gic_opts(krb5_context context, clpreauth_handle h,
371                    krb5_get_init_creds_opt *opt, const char *attr,
372                    const char *value)
373 {
374     if (h->vt.gic_opts == NULL)
375         return 0;
376     return h->vt.gic_opts(context, h->data, opt, attr, value);
377 }
378
379 /* Add the named encryption type to the existing list of ktypes. */
380 static void
381 grow_ktypes(krb5_enctype **out_ktypes, int *out_nktypes, krb5_enctype ktype)
382 {
383     int i;
384     krb5_enctype *ktypes;
385
386     for (i = 0; i < *out_nktypes; i++) {
387         if ((*out_ktypes)[i] == ktype)
388             return;
389     }
390     ktypes = realloc(*out_ktypes, (*out_nktypes + 2) * sizeof(ktype));
391     if (ktypes != NULL) {
392         *out_ktypes = ktypes;
393         ktypes[(*out_nktypes)++] = ktype;
394         ktypes[*out_nktypes] = 0;
395     }
396 }
397
398 /* Add a list of new pa_data items to an existing list. */
399 static int
400 grow_pa_list(krb5_pa_data ***out_pa_list, int *out_pa_list_size,
401              krb5_pa_data **addition, int num_addition)
402 {
403     krb5_pa_data **pa_list;
404     int i;
405
406     /* Allocate space for new entries and a null terminator. */
407     pa_list = realloc(*out_pa_list, (*out_pa_list_size + num_addition + 1) *
408                       sizeof(*pa_list));
409     if (pa_list == NULL)
410         return ENOMEM;
411     *out_pa_list = pa_list;
412     for (i = 0; i < num_addition; i++)
413         pa_list[(*out_pa_list_size)++] = addition[i];
414     pa_list[*out_pa_list_size] = NULL;
415     return 0;
416 }
417
418 static krb5_enctype
419 get_etype(krb5_context context, krb5_clpreauth_rock rock)
420 {
421     krb5_init_creds_context ctx = (krb5_init_creds_context)rock;
422
423     if (ctx->reply != NULL)
424         return ctx->reply->enc_part.enctype;
425     return ctx->etype;
426 }
427
428 static krb5_keyblock *
429 fast_armor(krb5_context context, krb5_clpreauth_rock rock)
430 {
431     return ((krb5_init_creds_context)rock)->fast_state->armor_key;
432 }
433
434 static krb5_error_code
435 get_as_key(krb5_context context, krb5_clpreauth_rock rock,
436            krb5_keyblock **keyblock)
437 {
438     krb5_init_creds_context ctx = (krb5_init_creds_context)rock;
439     krb5_error_code ret;
440     krb5_data *salt;
441
442     if (ctx->as_key.length == 0) {
443         salt = ctx->default_salt ? NULL : &ctx->salt;
444         ret = ctx->gak_fct(context, ctx->request->client, ctx->etype,
445                            ctx->prompter, ctx->prompter_data, salt,
446                            &ctx->s2kparams, &ctx->as_key, ctx->gak_data,
447                            ctx->rctx.items);
448         if (ret)
449             return ret;
450     }
451     *keyblock = &ctx->as_key;
452     return 0;
453 }
454
455 static krb5_error_code
456 set_as_key(krb5_context context, krb5_clpreauth_rock rock,
457            const krb5_keyblock *keyblock)
458 {
459     krb5_init_creds_context ctx = (krb5_init_creds_context)rock;
460
461     krb5_free_keyblock_contents(context, &ctx->as_key);
462     return krb5_copy_keyblock_contents(context, keyblock, &ctx->as_key);
463 }
464
465 static krb5_error_code
466 get_preauth_time(krb5_context context, krb5_clpreauth_rock rock,
467                  krb5_boolean allow_unauth_time, krb5_timestamp *time_out,
468                  krb5_int32 *usec_out)
469 {
470     return k5_init_creds_current_time(context, (krb5_init_creds_context)rock,
471                                       allow_unauth_time, time_out, usec_out);
472 }
473
474 static krb5_error_code
475 responder_ask_question(krb5_context context, krb5_clpreauth_rock rock,
476                        const char *question, const char *challenge)
477 {
478     krb5_init_creds_context ctx = (krb5_init_creds_context)rock;
479
480     /* Force plugins to use need_as_key(). */
481     if (strcmp(KRB5_RESPONDER_QUESTION_PASSWORD, question) == 0)
482         return EINVAL;
483     return k5_response_items_ask_question(ctx->rctx.items, question,
484                                           challenge);
485 }
486
487 static const char *
488 responder_get_answer(krb5_context context, krb5_clpreauth_rock rock,
489                      const char *question)
490 {
491     krb5_init_creds_context ctx = (krb5_init_creds_context)rock;
492
493     /* Don't let plugins get the raw password. */
494     if (strcmp(KRB5_RESPONDER_QUESTION_PASSWORD, question) == 0)
495         return NULL;
496     return k5_response_items_get_answer(ctx->rctx.items, question);
497 }
498
499 static void
500 need_as_key(krb5_context context, krb5_clpreauth_rock rock)
501 {
502     krb5_init_creds_context ctx = (krb5_init_creds_context)rock;
503
504     /* Calling gac_fct() with NULL as_key indicates desire for the AS key. */
505     ctx->gak_fct(context, ctx->request->client, ctx->etype, NULL, NULL, NULL,
506                  NULL, NULL, ctx->gak_data, ctx->rctx.items);
507 }
508
509 static const char *
510 get_cc_config(krb5_context context, krb5_clpreauth_rock rock, const char *key)
511 {
512     krb5_init_creds_context ctx = (krb5_init_creds_context)rock;
513     k5_json_value value;
514
515     if (ctx->cc_config_in == NULL)
516         return NULL;
517
518     value = k5_json_object_get(ctx->cc_config_in, key);
519     if (value == NULL)
520         return NULL;
521
522     if (k5_json_get_tid(value) != K5_JSON_TID_STRING)
523         return NULL;
524
525     return k5_json_string_utf8(value);
526 }
527
528 static krb5_error_code
529 set_cc_config(krb5_context context, krb5_clpreauth_rock rock,
530               const char *key, const char *data)
531 {
532     krb5_init_creds_context ctx = (krb5_init_creds_context)rock;
533     krb5_error_code ret;
534     k5_json_string str;
535
536     if (ctx->cc_config_out == NULL)
537         return ENOENT;
538
539     ret = k5_json_string_create(data, &str);
540     if (ret)
541         return ret;
542
543     ret = k5_json_object_set(ctx->cc_config_out, key, str);
544     k5_json_release(str);
545     return ret;
546 }
547
548 static void
549 disable_fallback(krb5_context context, krb5_clpreauth_rock rock)
550 {
551     ((krb5_init_creds_context)rock)->fallback_disabled = TRUE;
552 }
553
554 static struct krb5_clpreauth_callbacks_st callbacks = {
555     3,
556     get_etype,
557     fast_armor,
558     get_as_key,
559     set_as_key,
560     get_preauth_time,
561     responder_ask_question,
562     responder_get_answer,
563     need_as_key,
564     get_cc_config,
565     set_cc_config,
566     disable_fallback
567 };
568
569 /* Tweak the request body, for now adding any enctypes which the module claims
570  * to add support for to the list, but in the future perhaps doing more
571  * involved things. */
572 void
573 k5_preauth_prepare_request(krb5_context context, krb5_get_init_creds_opt *opt,
574                            krb5_kdc_req *req)
575 {
576     krb5_preauth_context pctx = context->preauth_context;
577     clpreauth_handle *hp, h;
578     krb5_enctype *ep;
579
580     if (pctx == NULL)
581         return;
582     /* Don't modify the enctype list if it's specified in the gic opts. */
583     if (opt != NULL && (opt->flags & KRB5_GET_INIT_CREDS_OPT_ETYPE_LIST))
584         return;
585     for (hp = pctx->handles; *hp != NULL; hp++) {
586         h = *hp;
587         if (h->vt.enctype_list == NULL)
588             continue;
589         for (ep = h->vt.enctype_list; *ep != ENCTYPE_NULL; ep++)
590             grow_ktypes(&req->ktype, &req->nktypes, *ep);
591     }
592 }
593
594 const char * const * KRB5_CALLCONV
595 krb5_responder_list_questions(krb5_context ctx, krb5_responder_context rctx)
596 {
597     return k5_response_items_list_questions(rctx->items);
598 }
599
600 const char * KRB5_CALLCONV
601 krb5_responder_get_challenge(krb5_context ctx, krb5_responder_context rctx,
602                              const char *question)
603 {
604     if (rctx == NULL)
605         return NULL;
606
607     return k5_response_items_get_challenge(rctx->items, question);
608 }
609
610 krb5_error_code KRB5_CALLCONV
611 krb5_responder_set_answer(krb5_context ctx, krb5_responder_context rctx,
612                           const char *question, const char *answer)
613 {
614     if (rctx == NULL)
615         return EINVAL;
616
617     return k5_response_items_set_answer(rctx->items, question, answer);
618 }
619
620 /* Return true if pa_type matches the specific preauth type allowed for this
621  * authentication, or if there is no specific allowed type. */
622 static inline krb5_boolean
623 pa_type_allowed(krb5_init_creds_context ctx, krb5_preauthtype pa_type)
624 {
625     return ctx->allowed_preauth_type == KRB5_PADATA_NONE ||
626         pa_type == ctx->allowed_preauth_type;
627 }
628
629 /* Return true if pa_type previously failed during this authentication. */
630 static krb5_boolean
631 previously_failed(krb5_init_creds_context ctx, krb5_preauthtype pa_type)
632 {
633     krb5_preauth_req_context reqctx = ctx->preauth_reqctx;
634     size_t i;
635
636     for (i = 0; reqctx->failed != NULL && reqctx->failed[i] != 0; i++) {
637         if (reqctx->failed[i] == pa_type)
638             return TRUE;
639     }
640     return FALSE;
641 }
642
643 /* Allow clpreauth modules to process in_pa_list and produce output padata. */
644 static krb5_error_code
645 process_pa_data(krb5_context context, krb5_init_creds_context ctx,
646                 krb5_pa_data **in_pa_list, krb5_boolean must_preauth,
647                 krb5_pa_data ***out_pa_list, int *out_pa_list_size,
648                 krb5_preauthtype *out_type)
649 {
650     struct errinfo save = EMPTY_ERRINFO;
651     krb5_pa_data *pa, **pa_ptr, **mod_pa;
652     krb5_error_code ret = 0;
653     krb5_clpreauth_modreq modreq;
654     clpreauth_handle h;
655     int real, i;
656
657     /* Process all informational padata types, then the first real preauth type
658      * we succeed on. */
659     for (real = 0; real <= 1; real++) {
660         for (pa_ptr = in_pa_list; *pa_ptr != NULL; pa_ptr++) {
661             pa = *pa_ptr;
662             /* Restrict real mechanisms to the chosen one if we have one. */
663             if (real && !pa_type_allowed(ctx, pa->pa_type))
664                 continue;
665             h = find_module(context, ctx, pa->pa_type, &modreq);
666             if (h == NULL)
667                 continue;
668             /* Make sure this type is for the current pass. */
669             if (clpreauth_is_real(context, h, pa->pa_type) != real)
670                 continue;
671             /* Don't try a real mechanism again after failure. */
672             if (real && previously_failed(ctx, pa->pa_type))
673                 continue;
674             mod_pa = NULL;
675             ret = clpreauth_process(context, h, modreq, ctx->opt, &callbacks,
676                                     (krb5_clpreauth_rock)ctx, ctx->request,
677                                     ctx->inner_request_body,
678                                     ctx->encoded_previous_request, pa,
679                                     ctx->prompter, ctx->prompter_data,
680                                     &mod_pa);
681             TRACE_PREAUTH_PROCESS(context, h->vt.name, pa->pa_type, real,
682                                   ret);
683             if (mod_pa != NULL) {
684                 for (i = 0; mod_pa[i] != NULL; i++);
685                 ret = grow_pa_list(out_pa_list, out_pa_list_size, mod_pa, i);
686                 if (ret) {
687                     krb5_free_pa_data(context, mod_pa);
688                     goto cleanup;
689                 }
690                 free(mod_pa);
691             }
692             /* Don't continue to try mechanisms after a keyboard interrupt. */
693             if (ret == KRB5_LIBOS_PWDINTR)
694                 goto cleanup;
695             if (ret == 0 && real) {
696                 /* Stop now and record which real padata type we answered. */
697                 *out_type = pa->pa_type;
698                 goto cleanup;
699             } else if (real && save.code == 0) {
700                 /* Save the first error we get from a real preauth type. */
701                 k5_save_ctx_error(context, ret, &save);
702             }
703             if (real && ret) {
704                 /* Don't try this mechanism again for this authentication. */
705                 ret = k5_preauth_note_failed(ctx, pa->pa_type);
706                 if (ret)
707                     goto cleanup;
708             }
709         }
710     }
711
712     if (must_preauth) {
713         /* No real preauth types succeeded and we needed to preauthenticate. */
714         if (save.code != 0) {
715             ret = k5_restore_ctx_error(context, &save);
716             k5_wrapmsg(context, ret, KRB5_PREAUTH_FAILED,
717                        _("Pre-authentication failed"));
718         }
719         ret = KRB5_PREAUTH_FAILED;
720     }
721
722 cleanup:
723     k5_clear_error(&save);
724     return ret;
725 }
726
727 static inline krb5_data
728 padata2data(krb5_pa_data p)
729 {
730     krb5_data d;
731     d.magic = KV5M_DATA;
732     d.length = p.length;
733     d.data = (char *) p.contents;
734     return d;
735 }
736
737 /* Set salt in rock based on pw-salt or afs3-salt elements in padata. */
738 static krb5_error_code
739 get_salt(krb5_context context, krb5_init_creds_context ctx,
740          krb5_pa_data **padata)
741 {
742     krb5_error_code ret;
743     krb5_pa_data *pa;
744     krb5_data d;
745     const char *p;
746
747     /* Look for a pw-salt or afs3-salt element. */
748     pa = krb5int_find_pa_data(context, padata, KRB5_PADATA_PW_SALT);
749     if (pa == NULL)
750         pa = krb5int_find_pa_data(context, padata, KRB5_PADATA_AFS3_SALT);
751     if (pa == NULL)
752         return 0;
753
754     /* Set ctx->salt based on the element we found. */
755     krb5_free_data_contents(context, &ctx->salt);
756     d = padata2data(*pa);
757     ret = krb5int_copy_data_contents(context, &d, &ctx->salt);
758     if (ret)
759         return ret;
760
761     /* Adjust the salt if we got it from an afs3-salt element. */
762     if (pa->pa_type == KRB5_PADATA_AFS3_SALT) {
763         /* Work around a (possible) old Heimdal KDC foible. */
764         p = memchr(ctx->salt.data, '@', ctx->salt.length);
765         if (p != NULL)
766             ctx->salt.length = p - ctx->salt.data;
767         /* Tolerate extra null in MIT KDC afs3-salt value. */
768         if (ctx->salt.length > 0 &&
769             ctx->salt.data[ctx->salt.length - 1] == '\0')
770             ctx->salt.length--;
771         /* Set an s2kparams value to indicate AFS string-to-key. */
772         krb5_free_data_contents(context, &ctx->s2kparams);
773         ret = alloc_data(&ctx->s2kparams, 1);
774         if (ret)
775             return ret;
776         ctx->s2kparams.data[0] = '\1';
777     }
778
779     ctx->default_salt = FALSE;
780     TRACE_PREAUTH_SALT(context, &ctx->salt, pa->pa_type);
781     return 0;
782 }
783
784 /* Set etype info parameters in rock based on padata. */
785 krb5_error_code
786 k5_get_etype_info(krb5_context context, krb5_init_creds_context ctx,
787                   krb5_pa_data **padata)
788 {
789     krb5_error_code ret = 0;
790     krb5_pa_data *pa;
791     krb5_data d;
792     krb5_etype_info etype_info = NULL, e;
793     krb5_etype_info_entry *entry;
794     krb5_boolean valid_found;
795     int i;
796
797     /* Find an etype-info2 or etype-info element in padata. */
798     pa = krb5int_find_pa_data(context, padata, KRB5_PADATA_ETYPE_INFO2);
799     if (pa != NULL) {
800         d = padata2data(*pa);
801         (void)decode_krb5_etype_info2(&d, &etype_info);
802     } else {
803         pa = krb5int_find_pa_data(context, padata, KRB5_PADATA_ETYPE_INFO);
804         if (pa != NULL) {
805             d = padata2data(*pa);
806             (void)decode_krb5_etype_info(&d, &etype_info);
807         }
808     }
809
810     /* Fall back to pw-salt/afs3-salt if no etype-info element is present. */
811     if (etype_info == NULL)
812         return get_salt(context, ctx, padata);
813
814     /* Search entries in order of the request's enctype preference. */
815     entry = NULL;
816     valid_found = FALSE;
817     for (i = 0; i < ctx->request->nktypes && entry == NULL; i++) {
818         for (e = etype_info; *e != NULL && entry == NULL; e++) {
819             if ((*e)->etype == ctx->request->ktype[i])
820                 entry = *e;
821             if (krb5_c_valid_enctype((*e)->etype))
822                 valid_found = TRUE;
823         }
824     }
825     if (entry == NULL) {
826         ret = (valid_found) ? KRB5_CONFIG_ETYPE_NOSUPP :
827             KRB5_PROG_ETYPE_NOSUPP;
828         goto cleanup;
829     }
830
831     /* Set etype/salt/s2kparams fields based on the entry we selected. */
832     ctx->etype = entry->etype;
833     krb5_free_data_contents(context, &ctx->salt);
834     if (entry->length != KRB5_ETYPE_NO_SALT) {
835         ctx->salt = make_data(entry->salt, entry->length);
836         entry->salt = NULL;
837         ctx->default_salt = FALSE;
838     } else {
839         ctx->salt = empty_data();
840         ctx->default_salt = TRUE;
841     }
842     krb5_free_data_contents(context, &ctx->s2kparams);
843     ctx->s2kparams = entry->s2kparams;
844     entry->s2kparams = empty_data();
845     TRACE_PREAUTH_ETYPE_INFO(context, ctx->etype, &ctx->salt, &ctx->s2kparams);
846
847 cleanup:
848     krb5_free_etype_info(context, etype_info);
849     return ret;
850 }
851
852 /* Look for an fx-cookie element in in_padata and add it to out_pa_list. */
853 static krb5_error_code
854 copy_cookie(krb5_context context, krb5_pa_data **in_padata,
855             krb5_pa_data ***out_pa_list, int *out_pa_list_size)
856 {
857     krb5_error_code ret;
858     krb5_pa_data *cookie, *pa = NULL;
859
860     cookie = krb5int_find_pa_data(context, in_padata, KRB5_PADATA_FX_COOKIE);
861     if (cookie == NULL)
862         return 0;
863     TRACE_PREAUTH_COOKIE(context, cookie->length, cookie->contents);
864     pa = k5alloc(sizeof(*pa), &ret);
865     if (pa == NULL)
866         return ret;
867     *pa = *cookie;
868     pa->contents = k5memdup(cookie->contents, cookie->length, &ret);
869     if (pa->contents == NULL)
870         goto error;
871     ret = grow_pa_list(out_pa_list, out_pa_list_size, &pa, 1);
872     if (ret)
873         goto error;
874     return 0;
875
876 error:
877     free(pa->contents);
878     free(pa);
879     return ENOMEM;
880 }
881
882 static krb5_error_code
883 add_s4u_x509_user_padata(krb5_context context, krb5_s4u_userid *userid,
884                          krb5_principal client, krb5_pa_data ***out_pa_list,
885                          int *out_pa_list_size)
886 {
887     krb5_pa_data *s4u_padata;
888     krb5_error_code code;
889     krb5_principal client_copy;
890
891     if (userid == NULL)
892         return EINVAL;
893     code = krb5_copy_principal(context, client, &client_copy);
894     if (code != 0)
895         return code;
896     krb5_free_principal(context, userid->user);
897     userid->user = client_copy;
898
899     if (userid->subject_cert.length != 0) {
900         s4u_padata = malloc(sizeof(*s4u_padata));
901         if (s4u_padata == NULL)
902             return ENOMEM;
903
904         s4u_padata->magic = KV5M_PA_DATA;
905         s4u_padata->pa_type = KRB5_PADATA_S4U_X509_USER;
906         s4u_padata->contents = k5memdup(userid->subject_cert.data,
907                                         userid->subject_cert.length, &code);
908         if (s4u_padata->contents == NULL) {
909             free(s4u_padata);
910             return code;
911         }
912         s4u_padata->length = userid->subject_cert.length;
913
914         code = grow_pa_list(out_pa_list, out_pa_list_size, &s4u_padata, 1);
915         if (code) {
916             free(s4u_padata->contents);
917             free(s4u_padata);
918             return code;
919         }
920     }
921
922     return 0;
923 }
924
925 /*
926  * If the module for pa_type can adjust its AS_REQ data using the contents of
927  * err and err_padata, return 0 with *padata_out set to a padata list for the
928  * next request.  If it's the sort of correction which requires that we ask the
929  * user another question, we let the calling application deal with it.
930  */
931 krb5_error_code
932 k5_preauth_tryagain(krb5_context context, krb5_init_creds_context ctx,
933                     krb5_preauthtype pa_type, krb5_error *err,
934                     krb5_pa_data **err_padata, krb5_pa_data ***padata_out)
935 {
936     krb5_error_code ret;
937     krb5_pa_data **mod_pa;
938     krb5_clpreauth_modreq modreq;
939     clpreauth_handle h;
940     int count;
941
942     *padata_out = NULL;
943
944     TRACE_PREAUTH_TRYAGAIN_INPUT(context, pa_type, err_padata);
945
946     h = find_module(context, ctx, pa_type, &modreq);
947     if (h == NULL)
948         return KRB5KRB_ERR_GENERIC;
949     mod_pa = NULL;
950     ret = clpreauth_tryagain(context, h, modreq, ctx->opt, &callbacks,
951                              (krb5_clpreauth_rock)ctx, ctx->request,
952                              ctx->inner_request_body,
953                              ctx->encoded_previous_request, pa_type, err,
954                              err_padata, ctx->prompter, ctx->prompter_data,
955                              &mod_pa);
956     TRACE_PREAUTH_TRYAGAIN(context, h->vt.name, pa_type, ret);
957     if (!ret && mod_pa == NULL)
958         ret = KRB5KRB_ERR_GENERIC;
959     if (ret) {
960         k5_preauth_note_failed(ctx, pa_type);
961         return ret;
962     }
963
964     for (count = 0; mod_pa[count] != NULL; count++);
965     ret = copy_cookie(context, err_padata, &mod_pa, &count);
966     if (ret) {
967         krb5_free_pa_data(context, mod_pa);
968         return ret;
969     }
970
971     TRACE_PREAUTH_TRYAGAIN_OUTPUT(context, mod_pa);
972     *padata_out = mod_pa;
973     return 0;
974 }
975
976 /* Compile the set of response items for in_padata by invoke each module's
977  * prep_questions method. */
978 static krb5_error_code
979 fill_response_items(krb5_context context, krb5_init_creds_context ctx,
980                     krb5_pa_data **in_padata)
981 {
982     krb5_error_code ret;
983     krb5_pa_data *pa;
984     krb5_clpreauth_modreq modreq;
985     clpreauth_handle h;
986     int i;
987
988     k5_response_items_reset(ctx->rctx.items);
989     for (i = 0; in_padata[i] != NULL; i++) {
990         pa = in_padata[i];
991         if (!pa_type_allowed(ctx, pa->pa_type))
992             continue;
993         h = find_module(context, ctx, pa->pa_type, &modreq);
994         if (h == NULL)
995             continue;
996         ret = clpreauth_prep_questions(context, h, modreq, ctx->opt,
997                                        &callbacks, (krb5_clpreauth_rock)ctx,
998                                        ctx->request, ctx->inner_request_body,
999                                        ctx->encoded_previous_request, pa);
1000         if (ret)
1001             return ret;
1002     }
1003     return 0;
1004 }
1005
1006 krb5_error_code
1007 k5_preauth(krb5_context context, krb5_init_creds_context ctx,
1008            krb5_pa_data **in_padata, krb5_boolean must_preauth,
1009            krb5_pa_data ***padata_out, krb5_preauthtype *pa_type_out)
1010 {
1011     int out_pa_list_size = 0;
1012     krb5_pa_data **out_pa_list = NULL;
1013     krb5_error_code ret;
1014     krb5_responder_fn responder;
1015     void *responder_data;
1016
1017     *padata_out = NULL;
1018     *pa_type_out = KRB5_PADATA_NONE;
1019
1020     if (in_padata == NULL)
1021         return 0;
1022
1023     TRACE_PREAUTH_INPUT(context, in_padata);
1024
1025     /* Scan the padata list and process etype-info or salt elements. */
1026     ret = k5_get_etype_info(context, ctx, in_padata);
1027     if (ret)
1028         return ret;
1029
1030     /* Copy the cookie if there is one. */
1031     ret = copy_cookie(context, in_padata, &out_pa_list, &out_pa_list_size);
1032     if (ret)
1033         goto error;
1034
1035     if (krb5int_find_pa_data(context, in_padata,
1036                              KRB5_PADATA_S4U_X509_USER) != NULL) {
1037         /* Fulfill a private contract with krb5_get_credentials_for_user. */
1038         ret = add_s4u_x509_user_padata(context, ctx->gak_data,
1039                                        ctx->request->client,
1040                                        &out_pa_list, &out_pa_list_size);
1041         if (ret)
1042             goto error;
1043     }
1044
1045     /* If we can't initialize the preauth context, stop with what we have. */
1046     k5_init_preauth_context(context);
1047     if (context->preauth_context == NULL) {
1048         *padata_out = out_pa_list;
1049         out_pa_list = NULL;
1050         goto error;
1051     }
1052
1053     /* Get a list of response items for in_padata from the preauth modules. */
1054     ret = fill_response_items(context, ctx, in_padata);
1055     if (ret)
1056         goto error;
1057
1058     /* Call the responder to answer response items. */
1059     k5_gic_opt_get_responder(ctx->opt, &responder, &responder_data);
1060     if (responder != NULL && !k5_response_items_empty(ctx->rctx.items)) {
1061         ret = (*responder)(context, responder_data, &ctx->rctx);
1062         if (ret)
1063             goto error;
1064     }
1065
1066     ret = process_pa_data(context, ctx, in_padata, must_preauth,
1067                           &out_pa_list, &out_pa_list_size, pa_type_out);
1068     if (ret)
1069         goto error;
1070
1071     TRACE_PREAUTH_OUTPUT(context, out_pa_list);
1072     *padata_out = out_pa_list;
1073     return 0;
1074
1075 error:
1076     krb5_free_pa_data(context, out_pa_list);
1077     return ret;
1078 }
1079
1080 /*
1081  * Give all the preauth plugins a look at the preauth option which
1082  * has just been set
1083  */
1084 krb5_error_code
1085 krb5_preauth_supply_preauth_data(krb5_context context,
1086                                  krb5_get_init_creds_opt *opt,
1087                                  const char *attr, const char *value)
1088 {
1089     krb5_preauth_context pctx = context->preauth_context;
1090     clpreauth_handle *hp, h;
1091     krb5_error_code ret;
1092
1093     if (pctx == NULL) {
1094         k5_init_preauth_context(context);
1095         pctx = context->preauth_context;
1096         if (pctx == NULL) {
1097             k5_setmsg(context, EINVAL,
1098                       _("Unable to initialize preauth context"));
1099             return EINVAL;
1100         }
1101     }
1102
1103     /*
1104      * Go down the list of preauth modules, and supply them with the
1105      * attribute/value pair.
1106      */
1107     for (hp = pctx->handles; *hp != NULL; hp++) {
1108         h = *hp;
1109         ret = clpreauth_gic_opts(context, h, opt, attr, value);
1110         if (ret) {
1111             k5_prependmsg(context, ret, _("Preauth module %s"), h->vt.name);
1112             return ret;
1113         }
1114     }
1115     return 0;
1116 }