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
free_handles(krb5_context context,clpreauth_handle * handles)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
search_module_list(clpreauth_handle * handles,krb5_preauthtype pa_type)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
find_module(krb5_context context,krb5_init_creds_context ctx,krb5_preauthtype pa_type,krb5_clpreauth_modreq * modreq_out)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
k5_init_preauth_context(krb5_context context)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
k5_preauth_note_failed(krb5_init_creds_context ctx,krb5_preauthtype pa_type)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
k5_free_preauth_context(krb5_context context)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
k5_preauth_request_context_init(krb5_context context,krb5_init_creds_context ctx)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 if (reqctx->modreqs == NULL) {
267 free(reqctx);
268 return;
269 }
270 for (i = 0; i < count; i++) {
271 h = pctx->handles[i];
272 if (h->vt.request_init != NULL)
273 h->vt.request_init(context, h->data, &reqctx->modreqs[i]);
274 }
275 ctx->preauth_reqctx = reqctx;
276 }
277
278 /* Free the per-AS-REQ context. This means clearing any request-specific
279 * context which the plugin may have created. */
280 void
k5_preauth_request_context_fini(krb5_context context,krb5_init_creds_context ctx)281 k5_preauth_request_context_fini(krb5_context context,
282 krb5_init_creds_context ctx)
283 {
284 krb5_preauth_context pctx = context->preauth_context;
285 krb5_preauth_req_context reqctx = ctx->preauth_reqctx;
286 size_t i;
287 clpreauth_handle h;
288
289 if (reqctx == NULL)
290 return;
291 if (reqctx->orig_context == context && pctx != NULL) {
292 for (i = 0; pctx->handles[i] != NULL; i++) {
293 h = pctx->handles[i];
294 if (reqctx->modreqs[i] != NULL && h->vt.request_fini != NULL)
295 h->vt.request_fini(context, h->data, reqctx->modreqs[i]);
296 }
297 } else {
298 TRACE_PREAUTH_WRONG_CONTEXT(context);
299 }
300 free(reqctx->modreqs);
301 free(reqctx->failed);
302 free(reqctx);
303 ctx->preauth_reqctx = NULL;
304 }
305
306 krb5_error_code
k5_preauth_check_context(krb5_context context,krb5_init_creds_context ctx)307 k5_preauth_check_context(krb5_context context, krb5_init_creds_context ctx)
308 {
309 krb5_preauth_req_context reqctx = ctx->preauth_reqctx;
310
311 if (reqctx != NULL && reqctx->orig_context != context) {
312 k5_setmsg(context, EINVAL,
313 _("krb5_init_creds calls must use same library context"));
314 return EINVAL;
315 }
316 return 0;
317 }
318
319 /* Return 1 if pa_type is a real preauthentication mechanism according to the
320 * module h. Return 0 if it is not. */
321 static int
clpreauth_is_real(krb5_context context,clpreauth_handle h,krb5_preauthtype pa_type)322 clpreauth_is_real(krb5_context context, clpreauth_handle h,
323 krb5_preauthtype pa_type)
324 {
325 if (h->vt.flags == NULL)
326 return 1;
327 return (h->vt.flags(context, pa_type) & PA_REAL) != 0;
328 }
329
330 static krb5_error_code
clpreauth_prep_questions(krb5_context context,clpreauth_handle h,krb5_clpreauth_modreq modreq,krb5_get_init_creds_opt * opt,krb5_clpreauth_callbacks cb,krb5_clpreauth_rock rock,krb5_kdc_req * req,krb5_data * req_body,krb5_data * prev_req,krb5_pa_data * pa_data)331 clpreauth_prep_questions(krb5_context context, clpreauth_handle h,
332 krb5_clpreauth_modreq modreq,
333 krb5_get_init_creds_opt *opt,
334 krb5_clpreauth_callbacks cb, krb5_clpreauth_rock rock,
335 krb5_kdc_req *req, krb5_data *req_body,
336 krb5_data *prev_req, krb5_pa_data *pa_data)
337 {
338 if (h->vt.prep_questions == NULL)
339 return 0;
340 return h->vt.prep_questions(context, h->data, modreq, opt, cb, rock, req,
341 req_body, prev_req, pa_data);
342 }
343
344 static krb5_error_code
clpreauth_process(krb5_context context,clpreauth_handle h,krb5_clpreauth_modreq modreq,krb5_get_init_creds_opt * opt,krb5_clpreauth_callbacks cb,krb5_clpreauth_rock rock,krb5_kdc_req * req,krb5_data * req_body,krb5_data * prev_req,krb5_pa_data * pa_data,krb5_prompter_fct prompter,void * prompter_data,krb5_pa_data *** pa_data_out)345 clpreauth_process(krb5_context context, clpreauth_handle h,
346 krb5_clpreauth_modreq modreq, krb5_get_init_creds_opt *opt,
347 krb5_clpreauth_callbacks cb, krb5_clpreauth_rock rock,
348 krb5_kdc_req *req, krb5_data *req_body, krb5_data *prev_req,
349 krb5_pa_data *pa_data, krb5_prompter_fct prompter,
350 void *prompter_data, krb5_pa_data ***pa_data_out)
351 {
352 return h->vt.process(context, h->data, modreq, opt, cb, rock, req,
353 req_body, prev_req, pa_data, prompter, prompter_data,
354 pa_data_out);
355 }
356
357 static krb5_error_code
clpreauth_tryagain(krb5_context context,clpreauth_handle h,krb5_clpreauth_modreq modreq,krb5_get_init_creds_opt * opt,krb5_clpreauth_callbacks cb,krb5_clpreauth_rock rock,krb5_kdc_req * req,krb5_data * req_body,krb5_data * prev_req,krb5_preauthtype pa_type,krb5_error * error,krb5_pa_data ** error_padata,krb5_prompter_fct prompter,void * prompter_data,krb5_pa_data *** pa_data_out)358 clpreauth_tryagain(krb5_context context, clpreauth_handle h,
359 krb5_clpreauth_modreq modreq, krb5_get_init_creds_opt *opt,
360 krb5_clpreauth_callbacks cb, krb5_clpreauth_rock rock,
361 krb5_kdc_req *req, krb5_data *req_body, krb5_data *prev_req,
362 krb5_preauthtype pa_type, krb5_error *error,
363 krb5_pa_data **error_padata, krb5_prompter_fct prompter,
364 void *prompter_data, krb5_pa_data ***pa_data_out)
365 {
366 if (h->vt.tryagain == NULL)
367 return 0;
368 return h->vt.tryagain(context, h->data, modreq, opt, cb, rock, req,
369 req_body, prev_req, pa_type, error, error_padata,
370 prompter, prompter_data, pa_data_out);
371 }
372
373 static krb5_error_code
clpreauth_gic_opts(krb5_context context,clpreauth_handle h,krb5_get_init_creds_opt * opt,const char * attr,const char * value)374 clpreauth_gic_opts(krb5_context context, clpreauth_handle h,
375 krb5_get_init_creds_opt *opt, const char *attr,
376 const char *value)
377 {
378 if (h->vt.gic_opts == NULL)
379 return 0;
380 return h->vt.gic_opts(context, h->data, opt, attr, value);
381 }
382
383 /* Add the named encryption type to the existing list of ktypes. */
384 static void
grow_ktypes(krb5_enctype ** out_ktypes,int * out_nktypes,krb5_enctype ktype)385 grow_ktypes(krb5_enctype **out_ktypes, int *out_nktypes, krb5_enctype ktype)
386 {
387 int i;
388 krb5_enctype *ktypes;
389
390 for (i = 0; i < *out_nktypes; i++) {
391 if ((*out_ktypes)[i] == ktype)
392 return;
393 }
394 ktypes = realloc(*out_ktypes, (*out_nktypes + 2) * sizeof(ktype));
395 if (ktypes != NULL) {
396 *out_ktypes = ktypes;
397 ktypes[(*out_nktypes)++] = ktype;
398 ktypes[*out_nktypes] = 0;
399 }
400 }
401
402 /* Add a list of new pa_data items to an existing list. */
403 static int
grow_pa_list(krb5_pa_data *** out_pa_list,int * out_pa_list_size,krb5_pa_data ** addition,int num_addition)404 grow_pa_list(krb5_pa_data ***out_pa_list, int *out_pa_list_size,
405 krb5_pa_data **addition, int num_addition)
406 {
407 krb5_pa_data **pa_list;
408 int i;
409
410 /* Allocate space for new entries and a null terminator. */
411 pa_list = realloc(*out_pa_list, (*out_pa_list_size + num_addition + 1) *
412 sizeof(*pa_list));
413 if (pa_list == NULL)
414 return ENOMEM;
415 *out_pa_list = pa_list;
416 for (i = 0; i < num_addition; i++)
417 pa_list[(*out_pa_list_size)++] = addition[i];
418 pa_list[*out_pa_list_size] = NULL;
419 return 0;
420 }
421
422 static krb5_enctype
get_etype(krb5_context context,krb5_clpreauth_rock rock)423 get_etype(krb5_context context, krb5_clpreauth_rock rock)
424 {
425 krb5_init_creds_context ctx = (krb5_init_creds_context)rock;
426
427 if (ctx->reply != NULL)
428 return ctx->reply->enc_part.enctype;
429 return ctx->etype;
430 }
431
432 static krb5_keyblock *
fast_armor(krb5_context context,krb5_clpreauth_rock rock)433 fast_armor(krb5_context context, krb5_clpreauth_rock rock)
434 {
435 return ((krb5_init_creds_context)rock)->fast_state->armor_key;
436 }
437
438 static krb5_error_code
get_as_key(krb5_context context,krb5_clpreauth_rock rock,krb5_keyblock ** keyblock)439 get_as_key(krb5_context context, krb5_clpreauth_rock rock,
440 krb5_keyblock **keyblock)
441 {
442 krb5_init_creds_context ctx = (krb5_init_creds_context)rock;
443 krb5_error_code ret;
444 krb5_data *salt;
445
446 if (ctx->as_key.length == 0) {
447 salt = ctx->default_salt ? NULL : &ctx->salt;
448 ret = ctx->gak_fct(context, ctx->request->client, ctx->etype,
449 ctx->prompter, ctx->prompter_data, salt,
450 &ctx->s2kparams, &ctx->as_key, ctx->gak_data,
451 ctx->rctx.items);
452 if (ret)
453 return ret;
454 }
455 *keyblock = &ctx->as_key;
456 return 0;
457 }
458
459 static krb5_error_code
set_as_key(krb5_context context,krb5_clpreauth_rock rock,const krb5_keyblock * keyblock)460 set_as_key(krb5_context context, krb5_clpreauth_rock rock,
461 const krb5_keyblock *keyblock)
462 {
463 krb5_init_creds_context ctx = (krb5_init_creds_context)rock;
464
465 krb5_free_keyblock_contents(context, &ctx->as_key);
466 return krb5_copy_keyblock_contents(context, keyblock, &ctx->as_key);
467 }
468
469 static krb5_error_code
get_preauth_time(krb5_context context,krb5_clpreauth_rock rock,krb5_boolean allow_unauth_time,krb5_timestamp * time_out,krb5_int32 * usec_out)470 get_preauth_time(krb5_context context, krb5_clpreauth_rock rock,
471 krb5_boolean allow_unauth_time, krb5_timestamp *time_out,
472 krb5_int32 *usec_out)
473 {
474 return k5_init_creds_current_time(context, (krb5_init_creds_context)rock,
475 allow_unauth_time, time_out, usec_out);
476 }
477
478 static krb5_error_code
responder_ask_question(krb5_context context,krb5_clpreauth_rock rock,const char * question,const char * challenge)479 responder_ask_question(krb5_context context, krb5_clpreauth_rock rock,
480 const char *question, const char *challenge)
481 {
482 krb5_init_creds_context ctx = (krb5_init_creds_context)rock;
483
484 /* Force plugins to use need_as_key(). */
485 if (strcmp(KRB5_RESPONDER_QUESTION_PASSWORD, question) == 0)
486 return EINVAL;
487 return k5_response_items_ask_question(ctx->rctx.items, question,
488 challenge);
489 }
490
491 static const char *
responder_get_answer(krb5_context context,krb5_clpreauth_rock rock,const char * question)492 responder_get_answer(krb5_context context, krb5_clpreauth_rock rock,
493 const char *question)
494 {
495 krb5_init_creds_context ctx = (krb5_init_creds_context)rock;
496
497 /* Don't let plugins get the raw password. */
498 if (strcmp(KRB5_RESPONDER_QUESTION_PASSWORD, question) == 0)
499 return NULL;
500 return k5_response_items_get_answer(ctx->rctx.items, question);
501 }
502
503 static void
need_as_key(krb5_context context,krb5_clpreauth_rock rock)504 need_as_key(krb5_context context, krb5_clpreauth_rock rock)
505 {
506 krb5_init_creds_context ctx = (krb5_init_creds_context)rock;
507
508 /* Calling gac_fct() with NULL as_key indicates desire for the AS key. */
509 ctx->gak_fct(context, ctx->request->client, ctx->etype, NULL, NULL, NULL,
510 NULL, NULL, ctx->gak_data, ctx->rctx.items);
511 }
512
513 static const char *
get_cc_config(krb5_context context,krb5_clpreauth_rock rock,const char * key)514 get_cc_config(krb5_context context, krb5_clpreauth_rock rock, const char *key)
515 {
516 krb5_init_creds_context ctx = (krb5_init_creds_context)rock;
517 k5_json_value value;
518
519 if (ctx->cc_config_in == NULL)
520 return NULL;
521
522 value = k5_json_object_get(ctx->cc_config_in, key);
523 if (value == NULL)
524 return NULL;
525
526 if (k5_json_get_tid(value) != K5_JSON_TID_STRING)
527 return NULL;
528
529 return k5_json_string_utf8(value);
530 }
531
532 static krb5_error_code
set_cc_config(krb5_context context,krb5_clpreauth_rock rock,const char * key,const char * data)533 set_cc_config(krb5_context context, krb5_clpreauth_rock rock,
534 const char *key, const char *data)
535 {
536 krb5_init_creds_context ctx = (krb5_init_creds_context)rock;
537 krb5_error_code ret;
538 k5_json_string str;
539
540 if (ctx->cc_config_out == NULL)
541 return ENOENT;
542
543 ret = k5_json_string_create(data, &str);
544 if (ret)
545 return ret;
546
547 ret = k5_json_object_set(ctx->cc_config_out, key, str);
548 k5_json_release(str);
549 return ret;
550 }
551
552 static void
disable_fallback(krb5_context context,krb5_clpreauth_rock rock)553 disable_fallback(krb5_context context, krb5_clpreauth_rock rock)
554 {
555 ((krb5_init_creds_context)rock)->fallback_disabled = TRUE;
556 }
557
558 static struct krb5_clpreauth_callbacks_st callbacks = {
559 3,
560 get_etype,
561 fast_armor,
562 get_as_key,
563 set_as_key,
564 get_preauth_time,
565 responder_ask_question,
566 responder_get_answer,
567 need_as_key,
568 get_cc_config,
569 set_cc_config,
570 disable_fallback
571 };
572
573 /* Tweak the request body, for now adding any enctypes which the module claims
574 * to add support for to the list, but in the future perhaps doing more
575 * involved things. */
576 void
k5_preauth_prepare_request(krb5_context context,krb5_get_init_creds_opt * opt,krb5_kdc_req * req)577 k5_preauth_prepare_request(krb5_context context, krb5_get_init_creds_opt *opt,
578 krb5_kdc_req *req)
579 {
580 krb5_preauth_context pctx = context->preauth_context;
581 clpreauth_handle *hp, h;
582 krb5_enctype *ep;
583
584 if (pctx == NULL)
585 return;
586 /* Don't modify the enctype list if it's specified in the gic opts. */
587 if (opt != NULL && (opt->flags & KRB5_GET_INIT_CREDS_OPT_ETYPE_LIST))
588 return;
589 for (hp = pctx->handles; *hp != NULL; hp++) {
590 h = *hp;
591 if (h->vt.enctype_list == NULL)
592 continue;
593 for (ep = h->vt.enctype_list; *ep != ENCTYPE_NULL; ep++)
594 grow_ktypes(&req->ktype, &req->nktypes, *ep);
595 }
596 }
597
598 const char * const * KRB5_CALLCONV
krb5_responder_list_questions(krb5_context ctx,krb5_responder_context rctx)599 krb5_responder_list_questions(krb5_context ctx, krb5_responder_context rctx)
600 {
601 return k5_response_items_list_questions(rctx->items);
602 }
603
604 const char * KRB5_CALLCONV
krb5_responder_get_challenge(krb5_context ctx,krb5_responder_context rctx,const char * question)605 krb5_responder_get_challenge(krb5_context ctx, krb5_responder_context rctx,
606 const char *question)
607 {
608 if (rctx == NULL)
609 return NULL;
610
611 return k5_response_items_get_challenge(rctx->items, question);
612 }
613
614 krb5_error_code KRB5_CALLCONV
krb5_responder_set_answer(krb5_context ctx,krb5_responder_context rctx,const char * question,const char * answer)615 krb5_responder_set_answer(krb5_context ctx, krb5_responder_context rctx,
616 const char *question, const char *answer)
617 {
618 if (rctx == NULL)
619 return EINVAL;
620
621 return k5_response_items_set_answer(rctx->items, question, answer);
622 }
623
624 /* Return true if pa_type matches the specific preauth type allowed for this
625 * authentication, or if there is no specific allowed type. */
626 static inline krb5_boolean
pa_type_allowed(krb5_init_creds_context ctx,krb5_preauthtype pa_type)627 pa_type_allowed(krb5_init_creds_context ctx, krb5_preauthtype pa_type)
628 {
629 return ctx->allowed_preauth_type == KRB5_PADATA_NONE ||
630 pa_type == ctx->allowed_preauth_type;
631 }
632
633 /* Return true if pa_type previously failed during this authentication. */
634 static krb5_boolean
previously_failed(krb5_init_creds_context ctx,krb5_preauthtype pa_type)635 previously_failed(krb5_init_creds_context ctx, krb5_preauthtype pa_type)
636 {
637 krb5_preauth_req_context reqctx = ctx->preauth_reqctx;
638 size_t i;
639
640 for (i = 0; reqctx->failed != NULL && reqctx->failed[i] != 0; i++) {
641 if (reqctx->failed[i] == pa_type)
642 return TRUE;
643 }
644 return FALSE;
645 }
646
647 /* Allow clpreauth modules to process in_pa_list and produce output padata. */
648 static krb5_error_code
process_pa_data(krb5_context context,krb5_init_creds_context ctx,krb5_pa_data ** in_pa_list,krb5_boolean must_preauth,krb5_pa_data *** out_pa_list,int * out_pa_list_size,krb5_preauthtype * out_type)649 process_pa_data(krb5_context context, krb5_init_creds_context ctx,
650 krb5_pa_data **in_pa_list, krb5_boolean must_preauth,
651 krb5_pa_data ***out_pa_list, int *out_pa_list_size,
652 krb5_preauthtype *out_type)
653 {
654 struct errinfo save = EMPTY_ERRINFO;
655 krb5_pa_data *pa, **pa_ptr, **mod_pa;
656 krb5_error_code ret = 0;
657 krb5_clpreauth_modreq modreq;
658 clpreauth_handle h;
659 int real, i;
660
661 /* Process all informational padata types, then the first real preauth type
662 * we succeed on. */
663 for (real = 0; real <= 1; real++) {
664 for (pa_ptr = in_pa_list; *pa_ptr != NULL; pa_ptr++) {
665 pa = *pa_ptr;
666 /* Restrict real mechanisms to the chosen one if we have one. */
667 if (real && !pa_type_allowed(ctx, pa->pa_type))
668 continue;
669 h = find_module(context, ctx, pa->pa_type, &modreq);
670 if (h == NULL)
671 continue;
672 /* Make sure this type is for the current pass. */
673 if (clpreauth_is_real(context, h, pa->pa_type) != real)
674 continue;
675 /* Don't try a real mechanism again after failure. */
676 if (real && previously_failed(ctx, pa->pa_type))
677 continue;
678 mod_pa = NULL;
679 ret = clpreauth_process(context, h, modreq, ctx->opt, &callbacks,
680 (krb5_clpreauth_rock)ctx, ctx->request,
681 ctx->inner_request_body,
682 ctx->encoded_previous_request, pa,
683 ctx->prompter, ctx->prompter_data,
684 &mod_pa);
685 TRACE_PREAUTH_PROCESS(context, h->vt.name, pa->pa_type, real,
686 ret);
687 if (mod_pa != NULL) {
688 for (i = 0; mod_pa[i] != NULL; i++);
689 ret = grow_pa_list(out_pa_list, out_pa_list_size, mod_pa, i);
690 if (ret) {
691 krb5_free_pa_data(context, mod_pa);
692 goto cleanup;
693 }
694 free(mod_pa);
695 }
696 /* Don't continue to try mechanisms after a keyboard interrupt. */
697 if (ret == KRB5_LIBOS_PWDINTR)
698 goto cleanup;
699 if (ret == 0 && real) {
700 /* Stop now and record which real padata type we answered. */
701 *out_type = pa->pa_type;
702 goto cleanup;
703 } else if (real && save.code == 0) {
704 /* Save the first error we get from a real preauth type. */
705 k5_save_ctx_error(context, ret, &save);
706 }
707 if (real && ret) {
708 /* Don't try this mechanism again for this authentication. */
709 ret = k5_preauth_note_failed(ctx, pa->pa_type);
710 if (ret)
711 goto cleanup;
712 }
713 }
714 }
715
716 if (must_preauth) {
717 /* No real preauth types succeeded and we needed to preauthenticate. */
718 if (save.code != 0) {
719 ret = k5_restore_ctx_error(context, &save);
720 k5_wrapmsg(context, ret, KRB5_PREAUTH_FAILED,
721 _("Pre-authentication failed"));
722 }
723 ret = KRB5_PREAUTH_FAILED;
724 }
725
726 cleanup:
727 k5_clear_error(&save);
728 return ret;
729 }
730
731 static inline krb5_data
padata2data(krb5_pa_data p)732 padata2data(krb5_pa_data p)
733 {
734 krb5_data d;
735 d.magic = KV5M_DATA;
736 d.length = p.length;
737 d.data = (char *) p.contents;
738 return d;
739 }
740
741 /* Set salt in rock based on pw-salt or afs3-salt elements in padata. */
742 static krb5_error_code
get_salt(krb5_context context,krb5_init_creds_context ctx,krb5_pa_data ** padata)743 get_salt(krb5_context context, krb5_init_creds_context ctx,
744 krb5_pa_data **padata)
745 {
746 krb5_error_code ret;
747 krb5_pa_data *pa;
748 krb5_data d;
749 const char *p;
750
751 /* Look for a pw-salt or afs3-salt element. */
752 pa = krb5int_find_pa_data(context, padata, KRB5_PADATA_PW_SALT);
753 if (pa == NULL)
754 pa = krb5int_find_pa_data(context, padata, KRB5_PADATA_AFS3_SALT);
755 if (pa == NULL)
756 return 0;
757
758 /* Set ctx->salt based on the element we found. */
759 krb5_free_data_contents(context, &ctx->salt);
760 d = padata2data(*pa);
761 ret = krb5int_copy_data_contents(context, &d, &ctx->salt);
762 if (ret)
763 return ret;
764
765 /* Adjust the salt if we got it from an afs3-salt element. */
766 if (pa->pa_type == KRB5_PADATA_AFS3_SALT) {
767 /* Work around a (possible) old Heimdal KDC foible. */
768 p = memchr(ctx->salt.data, '@', ctx->salt.length);
769 if (p != NULL)
770 ctx->salt.length = p - ctx->salt.data;
771 /* Tolerate extra null in MIT KDC afs3-salt value. */
772 if (ctx->salt.length > 0 &&
773 ctx->salt.data[ctx->salt.length - 1] == '\0')
774 ctx->salt.length--;
775 /* Set an s2kparams value to indicate AFS string-to-key. */
776 krb5_free_data_contents(context, &ctx->s2kparams);
777 ret = alloc_data(&ctx->s2kparams, 1);
778 if (ret)
779 return ret;
780 ctx->s2kparams.data[0] = '\1';
781 }
782
783 ctx->default_salt = FALSE;
784 TRACE_PREAUTH_SALT(context, &ctx->salt, pa->pa_type);
785 return 0;
786 }
787
788 /* Set etype info parameters in rock based on padata. */
789 krb5_error_code
k5_get_etype_info(krb5_context context,krb5_init_creds_context ctx,krb5_pa_data ** padata)790 k5_get_etype_info(krb5_context context, krb5_init_creds_context ctx,
791 krb5_pa_data **padata)
792 {
793 krb5_error_code ret = 0;
794 krb5_pa_data *pa;
795 krb5_data d;
796 krb5_etype_info etype_info = NULL, e;
797 krb5_etype_info_entry *entry;
798 krb5_boolean valid_found;
799 int i;
800
801 /* Find an etype-info2 or etype-info element in padata. */
802 pa = krb5int_find_pa_data(context, padata, KRB5_PADATA_ETYPE_INFO2);
803 if (pa != NULL) {
804 d = padata2data(*pa);
805 (void)decode_krb5_etype_info2(&d, &etype_info);
806 } else {
807 pa = krb5int_find_pa_data(context, padata, KRB5_PADATA_ETYPE_INFO);
808 if (pa != NULL) {
809 d = padata2data(*pa);
810 (void)decode_krb5_etype_info(&d, &etype_info);
811 }
812 }
813
814 /* Fall back to pw-salt/afs3-salt if no etype-info element is present. */
815 if (etype_info == NULL)
816 return get_salt(context, ctx, padata);
817
818 /* Search entries in order of the request's enctype preference. */
819 entry = NULL;
820 valid_found = FALSE;
821 for (i = 0; i < ctx->request->nktypes && entry == NULL; i++) {
822 for (e = etype_info; *e != NULL && entry == NULL; e++) {
823 if ((*e)->etype == ctx->request->ktype[i])
824 entry = *e;
825 if (krb5_c_valid_enctype((*e)->etype))
826 valid_found = TRUE;
827 }
828 }
829 if (entry == NULL) {
830 ret = (valid_found) ? KRB5_CONFIG_ETYPE_NOSUPP :
831 KRB5_PROG_ETYPE_NOSUPP;
832 goto cleanup;
833 }
834
835 /* Set etype/salt/s2kparams fields based on the entry we selected. */
836 ctx->etype = entry->etype;
837 krb5_free_data_contents(context, &ctx->salt);
838 if (entry->length != KRB5_ETYPE_NO_SALT) {
839 ctx->salt = make_data(entry->salt, entry->length);
840 entry->salt = NULL;
841 ctx->default_salt = FALSE;
842 } else {
843 ctx->salt = empty_data();
844 ctx->default_salt = TRUE;
845 }
846 krb5_free_data_contents(context, &ctx->s2kparams);
847 ctx->s2kparams = entry->s2kparams;
848 entry->s2kparams = empty_data();
849 TRACE_PREAUTH_ETYPE_INFO(context, ctx->etype, &ctx->salt, &ctx->s2kparams);
850
851 cleanup:
852 krb5_free_etype_info(context, etype_info);
853 return ret;
854 }
855
856 /* Look for an fx-cookie element in in_padata and add it to out_pa_list. */
857 static krb5_error_code
copy_cookie(krb5_context context,krb5_pa_data ** in_padata,krb5_pa_data *** out_pa_list,int * out_pa_list_size)858 copy_cookie(krb5_context context, krb5_pa_data **in_padata,
859 krb5_pa_data ***out_pa_list, int *out_pa_list_size)
860 {
861 krb5_error_code ret;
862 krb5_pa_data *cookie, *pa = NULL;
863
864 cookie = krb5int_find_pa_data(context, in_padata, KRB5_PADATA_FX_COOKIE);
865 if (cookie == NULL)
866 return 0;
867 TRACE_PREAUTH_COOKIE(context, cookie->length, cookie->contents);
868 pa = k5alloc(sizeof(*pa), &ret);
869 if (pa == NULL)
870 return ret;
871 *pa = *cookie;
872 pa->contents = k5memdup(cookie->contents, cookie->length, &ret);
873 if (pa->contents == NULL)
874 goto error;
875 ret = grow_pa_list(out_pa_list, out_pa_list_size, &pa, 1);
876 if (ret)
877 goto error;
878 return 0;
879
880 error:
881 free(pa->contents);
882 free(pa);
883 return ENOMEM;
884 }
885
886 /*
887 * If the module for pa_type can adjust its AS_REQ data using the contents of
888 * err and err_padata, return 0 with *padata_out set to a padata list for the
889 * next request. If it's the sort of correction which requires that we ask the
890 * user another question, we let the calling application deal with it.
891 */
892 krb5_error_code
k5_preauth_tryagain(krb5_context context,krb5_init_creds_context ctx,krb5_preauthtype pa_type,krb5_error * err,krb5_pa_data ** err_padata,krb5_pa_data *** padata_out)893 k5_preauth_tryagain(krb5_context context, krb5_init_creds_context ctx,
894 krb5_preauthtype pa_type, krb5_error *err,
895 krb5_pa_data **err_padata, krb5_pa_data ***padata_out)
896 {
897 krb5_error_code ret;
898 krb5_pa_data **mod_pa;
899 krb5_clpreauth_modreq modreq;
900 clpreauth_handle h;
901 int count;
902
903 *padata_out = NULL;
904
905 TRACE_PREAUTH_TRYAGAIN_INPUT(context, pa_type, err_padata);
906
907 h = find_module(context, ctx, pa_type, &modreq);
908 if (h == NULL)
909 return KRB5KRB_ERR_GENERIC;
910 mod_pa = NULL;
911 ret = clpreauth_tryagain(context, h, modreq, ctx->opt, &callbacks,
912 (krb5_clpreauth_rock)ctx, ctx->request,
913 ctx->inner_request_body,
914 ctx->encoded_previous_request, pa_type, err,
915 err_padata, ctx->prompter, ctx->prompter_data,
916 &mod_pa);
917 TRACE_PREAUTH_TRYAGAIN(context, h->vt.name, pa_type, ret);
918 if (!ret && mod_pa == NULL)
919 ret = KRB5KRB_ERR_GENERIC;
920 if (ret) {
921 k5_preauth_note_failed(ctx, pa_type);
922 return ret;
923 }
924
925 for (count = 0; mod_pa[count] != NULL; count++);
926 ret = copy_cookie(context, err_padata, &mod_pa, &count);
927 if (ret) {
928 krb5_free_pa_data(context, mod_pa);
929 return ret;
930 }
931
932 TRACE_PREAUTH_TRYAGAIN_OUTPUT(context, mod_pa);
933 *padata_out = mod_pa;
934 return 0;
935 }
936
937 /* Compile the set of response items for in_padata by invoke each module's
938 * prep_questions method. */
939 static krb5_error_code
fill_response_items(krb5_context context,krb5_init_creds_context ctx,krb5_pa_data ** in_padata)940 fill_response_items(krb5_context context, krb5_init_creds_context ctx,
941 krb5_pa_data **in_padata)
942 {
943 krb5_error_code ret;
944 krb5_pa_data *pa;
945 krb5_clpreauth_modreq modreq;
946 clpreauth_handle h;
947 int i;
948
949 k5_response_items_reset(ctx->rctx.items);
950 for (i = 0; in_padata[i] != NULL; i++) {
951 pa = in_padata[i];
952 if (!pa_type_allowed(ctx, pa->pa_type))
953 continue;
954 h = find_module(context, ctx, pa->pa_type, &modreq);
955 if (h == NULL)
956 continue;
957 ret = clpreauth_prep_questions(context, h, modreq, ctx->opt,
958 &callbacks, (krb5_clpreauth_rock)ctx,
959 ctx->request, ctx->inner_request_body,
960 ctx->encoded_previous_request, pa);
961 if (ret)
962 return ret;
963 }
964 return 0;
965 }
966
967 krb5_error_code
k5_preauth(krb5_context context,krb5_init_creds_context ctx,krb5_pa_data ** in_padata,krb5_boolean must_preauth,krb5_pa_data *** padata_out,krb5_preauthtype * pa_type_out)968 k5_preauth(krb5_context context, krb5_init_creds_context ctx,
969 krb5_pa_data **in_padata, krb5_boolean must_preauth,
970 krb5_pa_data ***padata_out, krb5_preauthtype *pa_type_out)
971 {
972 int out_pa_list_size = 0;
973 krb5_pa_data **out_pa_list = NULL;
974 krb5_error_code ret;
975 krb5_responder_fn responder;
976 void *responder_data;
977
978 *padata_out = NULL;
979 *pa_type_out = KRB5_PADATA_NONE;
980
981 /* We should never invoke preauth modules when identifying the realm. */
982 if (in_padata == NULL || ctx->identify_realm)
983 return 0;
984
985 TRACE_PREAUTH_INPUT(context, in_padata);
986
987 /* Scan the padata list and process etype-info or salt elements. */
988 ret = k5_get_etype_info(context, ctx, in_padata);
989 if (ret)
990 return ret;
991
992 /* Copy the cookie if there is one. */
993 ret = copy_cookie(context, in_padata, &out_pa_list, &out_pa_list_size);
994 if (ret)
995 goto error;
996
997 /* If we can't initialize the preauth context, stop with what we have. */
998 k5_init_preauth_context(context);
999 if (context->preauth_context == NULL) {
1000 *padata_out = out_pa_list;
1001 out_pa_list = NULL;
1002 goto error;
1003 }
1004
1005 /* Get a list of response items for in_padata from the preauth modules. */
1006 ret = fill_response_items(context, ctx, in_padata);
1007 if (ret)
1008 goto error;
1009
1010 /* Call the responder to answer response items. */
1011 k5_gic_opt_get_responder(ctx->opt, &responder, &responder_data);
1012 if (responder != NULL && !k5_response_items_empty(ctx->rctx.items)) {
1013 ret = (*responder)(context, responder_data, &ctx->rctx);
1014 if (ret)
1015 goto error;
1016 }
1017
1018 ret = process_pa_data(context, ctx, in_padata, must_preauth,
1019 &out_pa_list, &out_pa_list_size, pa_type_out);
1020 if (ret)
1021 goto error;
1022
1023 TRACE_PREAUTH_OUTPUT(context, out_pa_list);
1024 *padata_out = out_pa_list;
1025 return 0;
1026
1027 error:
1028 krb5_free_pa_data(context, out_pa_list);
1029 return ret;
1030 }
1031
1032 /*
1033 * Give all the preauth plugins a look at the preauth option which
1034 * has just been set
1035 */
1036 krb5_error_code
krb5_preauth_supply_preauth_data(krb5_context context,krb5_get_init_creds_opt * opt,const char * attr,const char * value)1037 krb5_preauth_supply_preauth_data(krb5_context context,
1038 krb5_get_init_creds_opt *opt,
1039 const char *attr, const char *value)
1040 {
1041 krb5_preauth_context pctx = context->preauth_context;
1042 clpreauth_handle *hp, h;
1043 krb5_error_code ret;
1044
1045 if (pctx == NULL) {
1046 k5_init_preauth_context(context);
1047 pctx = context->preauth_context;
1048 if (pctx == NULL) {
1049 k5_setmsg(context, EINVAL,
1050 _("Unable to initialize preauth context"));
1051 return EINVAL;
1052 }
1053 }
1054
1055 /*
1056 * Go down the list of preauth modules, and supply them with the
1057 * attribute/value pair.
1058 */
1059 for (hp = pctx->handles; *hp != NULL; hp++) {
1060 h = *hp;
1061 ret = clpreauth_gic_opts(context, h, opt, attr, value);
1062 if (ret) {
1063 k5_prependmsg(context, ret, _("Preauth module %s"), h->vt.name);
1064 return ret;
1065 }
1066 }
1067 return 0;
1068 }
1069