1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
3 * Copyright 1995, 2003, 2008, 2012 by the Massachusetts Institute of Technology. All
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.
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.
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.
36 #include <krb5/clpreauth_plugin.h>
37 #include "int-proto.h"
40 #include "init_creds_ctx.h"
47 struct krb5_clpreauth_vtable_st vt;
48 krb5_clpreauth_moddata data;
51 struct krb5_preauth_context_st {
52 clpreauth_handle *handles;
55 struct krb5_preauth_req_context_st {
56 krb5_context orig_context;
57 krb5_preauthtype *failed;
58 krb5_clpreauth_modreq *modreqs;
61 /* Release the memory used by a list of handles. */
63 free_handles(krb5_context context, clpreauth_handle *handles)
65 clpreauth_handle *hp, h;
69 for (hp = handles; *hp != NULL; hp++) {
71 if (h->vt.fini != NULL)
72 h->vt.fini(context, h->data);
78 /* Return an index into handles which can process pa_type, or -1 if none is
81 search_module_list(clpreauth_handle *handles, krb5_preauthtype pa_type)
86 for (i = 0; handles[i] != NULL; i++) {
88 for (j = 0; h->vt.pa_type_list[j] != 0; j++) {
89 if (h->vt.pa_type_list[j] == pa_type)
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)
102 krb5_preauth_context pctx = context->preauth_context;
103 krb5_preauth_req_context reqctx = ctx->preauth_reqctx;
107 if (pctx == NULL || reqctx == NULL)
110 i = search_module_list(pctx->handles, pa_type);
114 *modreq_out = reqctx->modreqs[i];
115 return pctx->handles[i];
118 /* Initialize the preauth state for a krb5 context. */
120 k5_init_preauth_context(krb5_context context)
122 krb5_plugin_initvt_fn *modules = NULL, *mod;
123 clpreauth_handle *list = NULL, h;
126 krb5_preauthtype *tp;
128 /* Only do this once for each krb5_context */
129 if (context->preauth_context != NULL)
132 /* Auto-register built-in modules. */
133 k5_plugin_register_dyn(context, PLUGIN_INTERFACE_CLPREAUTH, "pkinit",
135 k5_plugin_register_dyn(context, PLUGIN_INTERFACE_CLPREAUTH, "spake",
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);
148 /* Get all available clpreauth vtables. */
149 if (k5_plugin_load_all(context, PLUGIN_INTERFACE_CLPREAUTH, &modules))
152 /* Allocate a large enough list of handles. */
153 for (count = 0; modules[count] != NULL; count++);
154 list = calloc(count + 1, sizeof(*list));
158 /* Create a handle for each module we can successfully initialize. */
160 for (mod = modules; *mod != NULL; mod++) {
161 h = calloc(1, sizeof(*h));
165 /* Initialize the handle vtable. */
166 if ((*mod)(context, 1, 1, (krb5_plugin_vtable)&h->vt) != 0) {
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);
175 TRACE_PREAUTH_CONFLICT(context, h->vt.name, list[i]->vt.name,
183 /* Initialize the module data. */
185 if (h->vt.init != NULL && h->vt.init(context, &h->data) != 0) {
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)
198 context->preauth_context->handles = list;
202 k5_plugin_free_modules(context, modules);
203 free_handles(context, list);
206 /* Add pa_type to the list of types which has previously failed. */
208 k5_preauth_note_failed(krb5_init_creds_context ctx, krb5_preauthtype pa_type)
210 krb5_preauth_req_context reqctx = ctx->preauth_reqctx;
211 krb5_preauthtype *newptr;
214 for (i = 0; reqctx->failed != NULL && reqctx->failed[i] != 0; i++);
215 newptr = realloc(reqctx->failed, (i + 2) * sizeof(*newptr));
218 reqctx->failed = newptr;
219 reqctx->failed[i] = pa_type;
220 reqctx->failed[i + 1] = 0;
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. */
228 k5_free_preauth_context(krb5_context context)
230 krb5_preauth_context pctx = context->preauth_context;
234 free_handles(context, pctx->handles);
236 context->preauth_context = NULL;
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. */
242 k5_preauth_request_context_init(krb5_context context,
243 krb5_init_creds_context ctx)
245 krb5_preauth_context pctx = context->preauth_context;
247 krb5_preauth_req_context reqctx;
251 k5_init_preauth_context(context);
252 pctx = context->preauth_context;
257 reqctx = calloc(1, sizeof(*reqctx));
260 reqctx->orig_context = context;
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]);
271 ctx->preauth_reqctx = reqctx;
274 /* Free the per-AS-REQ context. This means clearing any request-specific
275 * context which the plugin may have created. */
277 k5_preauth_request_context_fini(krb5_context context,
278 krb5_init_creds_context ctx)
280 krb5_preauth_context pctx = context->preauth_context;
281 krb5_preauth_req_context reqctx = ctx->preauth_reqctx;
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]);
294 TRACE_PREAUTH_WRONG_CONTEXT(context);
296 free(reqctx->modreqs);
297 free(reqctx->failed);
299 ctx->preauth_reqctx = NULL;
303 k5_preauth_check_context(krb5_context context, krb5_init_creds_context ctx)
305 krb5_preauth_req_context reqctx = ctx->preauth_reqctx;
307 if (reqctx != NULL && reqctx->orig_context != context) {
308 k5_setmsg(context, EINVAL,
309 _("krb5_init_creds calls must use same library context"));
315 /* Return 1 if pa_type is a real preauthentication mechanism according to the
316 * module h. Return 0 if it is not. */
318 clpreauth_is_real(krb5_context context, clpreauth_handle h,
319 krb5_preauthtype pa_type)
321 if (h->vt.flags == NULL)
323 return (h->vt.flags(context, pa_type) & PA_REAL) != 0;
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)
334 if (h->vt.prep_questions == NULL)
336 return h->vt.prep_questions(context, h->data, modreq, opt, cb, rock, req,
337 req_body, prev_req, pa_data);
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)
348 return h->vt.process(context, h->data, modreq, opt, cb, rock, req,
349 req_body, prev_req, pa_data, prompter, prompter_data,
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)
362 if (h->vt.tryagain == NULL)
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);
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,
374 if (h->vt.gic_opts == NULL)
376 return h->vt.gic_opts(context, h->data, opt, attr, value);
379 /* Add the named encryption type to the existing list of ktypes. */
381 grow_ktypes(krb5_enctype **out_ktypes, int *out_nktypes, krb5_enctype ktype)
384 krb5_enctype *ktypes;
386 for (i = 0; i < *out_nktypes; i++) {
387 if ((*out_ktypes)[i] == ktype)
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;
398 /* Add a list of new pa_data items to an existing list. */
400 grow_pa_list(krb5_pa_data ***out_pa_list, int *out_pa_list_size,
401 krb5_pa_data **addition, int num_addition)
403 krb5_pa_data **pa_list;
406 /* Allocate space for new entries and a null terminator. */
407 pa_list = realloc(*out_pa_list, (*out_pa_list_size + num_addition + 1) *
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;
419 get_etype(krb5_context context, krb5_clpreauth_rock rock)
421 krb5_init_creds_context ctx = (krb5_init_creds_context)rock;
423 if (ctx->reply != NULL)
424 return ctx->reply->enc_part.enctype;
428 static krb5_keyblock *
429 fast_armor(krb5_context context, krb5_clpreauth_rock rock)
431 return ((krb5_init_creds_context)rock)->fast_state->armor_key;
434 static krb5_error_code
435 get_as_key(krb5_context context, krb5_clpreauth_rock rock,
436 krb5_keyblock **keyblock)
438 krb5_init_creds_context ctx = (krb5_init_creds_context)rock;
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,
451 *keyblock = &ctx->as_key;
455 static krb5_error_code
456 set_as_key(krb5_context context, krb5_clpreauth_rock rock,
457 const krb5_keyblock *keyblock)
459 krb5_init_creds_context ctx = (krb5_init_creds_context)rock;
461 krb5_free_keyblock_contents(context, &ctx->as_key);
462 return krb5_copy_keyblock_contents(context, keyblock, &ctx->as_key);
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)
470 return k5_init_creds_current_time(context, (krb5_init_creds_context)rock,
471 allow_unauth_time, time_out, usec_out);
474 static krb5_error_code
475 responder_ask_question(krb5_context context, krb5_clpreauth_rock rock,
476 const char *question, const char *challenge)
478 krb5_init_creds_context ctx = (krb5_init_creds_context)rock;
480 /* Force plugins to use need_as_key(). */
481 if (strcmp(KRB5_RESPONDER_QUESTION_PASSWORD, question) == 0)
483 return k5_response_items_ask_question(ctx->rctx.items, question,
488 responder_get_answer(krb5_context context, krb5_clpreauth_rock rock,
489 const char *question)
491 krb5_init_creds_context ctx = (krb5_init_creds_context)rock;
493 /* Don't let plugins get the raw password. */
494 if (strcmp(KRB5_RESPONDER_QUESTION_PASSWORD, question) == 0)
496 return k5_response_items_get_answer(ctx->rctx.items, question);
500 need_as_key(krb5_context context, krb5_clpreauth_rock rock)
502 krb5_init_creds_context ctx = (krb5_init_creds_context)rock;
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);
510 get_cc_config(krb5_context context, krb5_clpreauth_rock rock, const char *key)
512 krb5_init_creds_context ctx = (krb5_init_creds_context)rock;
515 if (ctx->cc_config_in == NULL)
518 value = k5_json_object_get(ctx->cc_config_in, key);
522 if (k5_json_get_tid(value) != K5_JSON_TID_STRING)
525 return k5_json_string_utf8(value);
528 static krb5_error_code
529 set_cc_config(krb5_context context, krb5_clpreauth_rock rock,
530 const char *key, const char *data)
532 krb5_init_creds_context ctx = (krb5_init_creds_context)rock;
536 if (ctx->cc_config_out == NULL)
539 ret = k5_json_string_create(data, &str);
543 ret = k5_json_object_set(ctx->cc_config_out, key, str);
544 k5_json_release(str);
549 disable_fallback(krb5_context context, krb5_clpreauth_rock rock)
551 ((krb5_init_creds_context)rock)->fallback_disabled = TRUE;
554 static struct krb5_clpreauth_callbacks_st callbacks = {
561 responder_ask_question,
562 responder_get_answer,
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. */
573 k5_preauth_prepare_request(krb5_context context, krb5_get_init_creds_opt *opt,
576 krb5_preauth_context pctx = context->preauth_context;
577 clpreauth_handle *hp, h;
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))
585 for (hp = pctx->handles; *hp != NULL; hp++) {
587 if (h->vt.enctype_list == NULL)
589 for (ep = h->vt.enctype_list; *ep != ENCTYPE_NULL; ep++)
590 grow_ktypes(&req->ktype, &req->nktypes, *ep);
594 const char * const * KRB5_CALLCONV
595 krb5_responder_list_questions(krb5_context ctx, krb5_responder_context rctx)
597 return k5_response_items_list_questions(rctx->items);
600 const char * KRB5_CALLCONV
601 krb5_responder_get_challenge(krb5_context ctx, krb5_responder_context rctx,
602 const char *question)
607 return k5_response_items_get_challenge(rctx->items, question);
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)
617 return k5_response_items_set_answer(rctx->items, question, answer);
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)
625 return ctx->allowed_preauth_type == KRB5_PADATA_NONE ||
626 pa_type == ctx->allowed_preauth_type;
629 /* Return true if pa_type previously failed during this authentication. */
631 previously_failed(krb5_init_creds_context ctx, krb5_preauthtype pa_type)
633 krb5_preauth_req_context reqctx = ctx->preauth_reqctx;
636 for (i = 0; reqctx->failed != NULL && reqctx->failed[i] != 0; i++) {
637 if (reqctx->failed[i] == pa_type)
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)
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;
657 /* Process all informational padata types, then the first real preauth type
659 for (real = 0; real <= 1; real++) {
660 for (pa_ptr = in_pa_list; *pa_ptr != NULL; pa_ptr++) {
662 /* Restrict real mechanisms to the chosen one if we have one. */
663 if (real && !pa_type_allowed(ctx, pa->pa_type))
665 h = find_module(context, ctx, pa->pa_type, &modreq);
668 /* Make sure this type is for the current pass. */
669 if (clpreauth_is_real(context, h, pa->pa_type) != real)
671 /* Don't try a real mechanism again after failure. */
672 if (real && previously_failed(ctx, pa->pa_type))
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,
681 TRACE_PREAUTH_PROCESS(context, h->vt.name, pa->pa_type, real,
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);
687 krb5_free_pa_data(context, mod_pa);
692 /* Don't continue to try mechanisms after a keyboard interrupt. */
693 if (ret == KRB5_LIBOS_PWDINTR)
695 if (ret == 0 && real) {
696 /* Stop now and record which real padata type we answered. */
697 *out_type = pa->pa_type;
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);
704 /* Don't try this mechanism again for this authentication. */
705 ret = k5_preauth_note_failed(ctx, pa->pa_type);
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"));
719 ret = KRB5_PREAUTH_FAILED;
723 k5_clear_error(&save);
727 static inline krb5_data
728 padata2data(krb5_pa_data p)
733 d.data = (char *) p.contents;
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)
747 /* Look for a pw-salt or afs3-salt element. */
748 pa = krb5int_find_pa_data(context, padata, KRB5_PADATA_PW_SALT);
750 pa = krb5int_find_pa_data(context, padata, KRB5_PADATA_AFS3_SALT);
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);
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);
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')
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);
776 ctx->s2kparams.data[0] = '\1';
779 ctx->default_salt = FALSE;
780 TRACE_PREAUTH_SALT(context, &ctx->salt, pa->pa_type);
784 /* Set etype info parameters in rock based on padata. */
786 k5_get_etype_info(krb5_context context, krb5_init_creds_context ctx,
787 krb5_pa_data **padata)
789 krb5_error_code ret = 0;
792 krb5_etype_info etype_info = NULL, e;
793 krb5_etype_info_entry *entry;
794 krb5_boolean valid_found;
797 /* Find an etype-info2 or etype-info element in padata. */
798 pa = krb5int_find_pa_data(context, padata, KRB5_PADATA_ETYPE_INFO2);
800 d = padata2data(*pa);
801 (void)decode_krb5_etype_info2(&d, &etype_info);
803 pa = krb5int_find_pa_data(context, padata, KRB5_PADATA_ETYPE_INFO);
805 d = padata2data(*pa);
806 (void)decode_krb5_etype_info(&d, &etype_info);
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);
814 /* Search entries in order of the request's enctype preference. */
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])
821 if (krb5_c_valid_enctype((*e)->etype))
826 ret = (valid_found) ? KRB5_CONFIG_ETYPE_NOSUPP :
827 KRB5_PROG_ETYPE_NOSUPP;
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);
837 ctx->default_salt = FALSE;
839 ctx->salt = empty_data();
840 ctx->default_salt = TRUE;
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);
848 krb5_free_etype_info(context, etype_info);
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)
858 krb5_pa_data *cookie, *pa = NULL;
860 cookie = krb5int_find_pa_data(context, in_padata, KRB5_PADATA_FX_COOKIE);
863 TRACE_PREAUTH_COOKIE(context, cookie->length, cookie->contents);
864 pa = k5alloc(sizeof(*pa), &ret);
868 pa->contents = k5memdup(cookie->contents, cookie->length, &ret);
869 if (pa->contents == NULL)
871 ret = grow_pa_list(out_pa_list, out_pa_list_size, &pa, 1);
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)
887 krb5_pa_data *s4u_padata;
888 krb5_error_code code;
889 krb5_principal client_copy;
893 code = krb5_copy_principal(context, client, &client_copy);
896 krb5_free_principal(context, userid->user);
897 userid->user = client_copy;
899 if (userid->subject_cert.length != 0) {
900 s4u_padata = malloc(sizeof(*s4u_padata));
901 if (s4u_padata == NULL)
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) {
912 s4u_padata->length = userid->subject_cert.length;
914 code = grow_pa_list(out_pa_list, out_pa_list_size, &s4u_padata, 1);
916 free(s4u_padata->contents);
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.
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)
937 krb5_pa_data **mod_pa;
938 krb5_clpreauth_modreq modreq;
944 TRACE_PREAUTH_TRYAGAIN_INPUT(context, pa_type, err_padata);
946 h = find_module(context, ctx, pa_type, &modreq);
948 return KRB5KRB_ERR_GENERIC;
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,
956 TRACE_PREAUTH_TRYAGAIN(context, h->vt.name, pa_type, ret);
957 if (!ret && mod_pa == NULL)
958 ret = KRB5KRB_ERR_GENERIC;
960 k5_preauth_note_failed(ctx, pa_type);
964 for (count = 0; mod_pa[count] != NULL; count++);
965 ret = copy_cookie(context, err_padata, &mod_pa, &count);
967 krb5_free_pa_data(context, mod_pa);
971 TRACE_PREAUTH_TRYAGAIN_OUTPUT(context, mod_pa);
972 *padata_out = mod_pa;
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)
984 krb5_clpreauth_modreq modreq;
988 k5_response_items_reset(ctx->rctx.items);
989 for (i = 0; in_padata[i] != NULL; i++) {
991 if (!pa_type_allowed(ctx, pa->pa_type))
993 h = find_module(context, ctx, pa->pa_type, &modreq);
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);
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)
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;
1018 *pa_type_out = KRB5_PADATA_NONE;
1020 if (in_padata == NULL)
1023 TRACE_PREAUTH_INPUT(context, in_padata);
1025 /* Scan the padata list and process etype-info or salt elements. */
1026 ret = k5_get_etype_info(context, ctx, in_padata);
1030 /* Copy the cookie if there is one. */
1031 ret = copy_cookie(context, in_padata, &out_pa_list, &out_pa_list_size);
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);
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;
1053 /* Get a list of response items for in_padata from the preauth modules. */
1054 ret = fill_response_items(context, ctx, in_padata);
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);
1066 ret = process_pa_data(context, ctx, in_padata, must_preauth,
1067 &out_pa_list, &out_pa_list_size, pa_type_out);
1071 TRACE_PREAUTH_OUTPUT(context, out_pa_list);
1072 *padata_out = out_pa_list;
1076 krb5_free_pa_data(context, out_pa_list);
1081 * Give all the preauth plugins a look at the preauth option which
1085 krb5_preauth_supply_preauth_data(krb5_context context,
1086 krb5_get_init_creds_opt *opt,
1087 const char *attr, const char *value)
1089 krb5_preauth_context pctx = context->preauth_context;
1090 clpreauth_handle *hp, h;
1091 krb5_error_code ret;
1094 k5_init_preauth_context(context);
1095 pctx = context->preauth_context;
1097 k5_setmsg(context, EINVAL,
1098 _("Unable to initialize preauth context"));
1104 * Go down the list of preauth modules, and supply them with the
1105 * attribute/value pair.
1107 for (hp = pctx->handles; *hp != NULL; hp++) {
1109 ret = clpreauth_gic_opts(context, h, opt, attr, value);
1111 k5_prependmsg(context, ret, _("Preauth module %s"), h->vt.name);