1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* kdc/kdc_preauth.c - Preauthentication routines for the KDC */
3 /*
4 * Copyright 1995, 2003, 2007, 2009 by the Massachusetts Institute of
5 * Technology. All Rights Reserved.
6 *
7 * Export of this software from the United States of America may
8 * require a specific license from the United States Government.
9 * It is the responsibility of any person or organization contemplating
10 * export to obtain such a license before exporting.
11 *
12 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
13 * distribute this software and its documentation for any purpose and
14 * without fee is hereby granted, provided that the above copyright
15 * notice appear in all copies and that both that copyright notice and
16 * this permission notice appear in supporting documentation, and that
17 * the name of M.I.T. not be used in advertising or publicity pertaining
18 * to distribution of the software without specific, written prior
19 * permission. Furthermore if you modify this software you must label
20 * your software as modified software and not distribute it in such a
21 * fashion that it might be confused with the original M.I.T. software.
22 * M.I.T. makes no representations about the suitability of
23 * this software for any purpose. It is provided "as is" without express
24 * or implied warranty.
25 */
26 /*
27 * Copyright (C) 1998 by the FundsXpress, INC.
28 *
29 * All rights reserved.
30 *
31 * Export of this software from the United States of America may require
32 * a specific license from the United States Government. It is the
33 * responsibility of any person or organization contemplating export to
34 * obtain such a license before exporting.
35 *
36 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
37 * distribute this software and its documentation for any purpose and
38 * without fee is hereby granted, provided that the above copyright
39 * notice appear in all copies and that both that copyright notice and
40 * this permission notice appear in supporting documentation, and that
41 * the name of FundsXpress. not be used in advertising or publicity pertaining
42 * to distribution of the software without specific, written prior
43 * permission. FundsXpress makes no representations about the suitability of
44 * this software for any purpose. It is provided "as is" without express
45 * or implied warranty.
46 *
47 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
48 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
49 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
50 */
51 /*
52 * Copyright (c) 2006-2008, Novell, Inc.
53 * All rights reserved.
54 *
55 * Redistribution and use in source and binary forms, with or without
56 * modification, are permitted provided that the following conditions are met:
57 *
58 * * Redistributions of source code must retain the above copyright notice,
59 * this list of conditions and the following disclaimer.
60 * * Redistributions in binary form must reproduce the above copyright
61 * notice, this list of conditions and the following disclaimer in the
62 * documentation and/or other materials provided with the distribution.
63 * * The copyright holder's name is not used to endorse or promote products
64 * derived from this software without specific prior written permission.
65 *
66 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
67 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
68 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
69 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
70 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
71 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
72 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
73 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
74 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
75 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
76 * POSSIBILITY OF SUCH DAMAGE.
77 */
78
79 #include "k5-int.h"
80 #include "kdc_util.h"
81 #include "extern.h"
82 #include <stdio.h>
83 #include "adm_proto.h"
84
85 #include <syslog.h>
86
87 #include <assert.h>
88 #include <krb5/kdcpreauth_plugin.h>
89
90 /* Let freshness tokens be valid for ten minutes. */
91 #define FRESHNESS_LIFETIME 600
92
93 typedef struct preauth_system_st {
94 const char *name;
95 int type;
96 int flags;
97 krb5_kdcpreauth_moddata moddata;
98 krb5_kdcpreauth_init_fn init;
99 krb5_kdcpreauth_fini_fn fini;
100 krb5_kdcpreauth_edata_fn get_edata;
101 krb5_kdcpreauth_verify_fn verify_padata;
102 krb5_kdcpreauth_return_fn return_padata;
103 krb5_kdcpreauth_free_modreq_fn free_modreq;
104 krb5_kdcpreauth_loop_fn loop;
105 } preauth_system;
106
107 static preauth_system *preauth_systems;
108 static size_t n_preauth_systems;
109
110 static krb5_error_code
111 make_etype_info(krb5_context context, krb5_boolean etype_info2,
112 krb5_principal client, krb5_key_data *client_key,
113 krb5_enctype enctype, krb5_data **der_out);
114
115 /* Get all available kdcpreauth vtables and a count of preauth types they
116 * support. Return an empty list on failure. */
117 static void
get_plugin_vtables(krb5_context context,struct krb5_kdcpreauth_vtable_st ** vtables_out,size_t * n_tables_out,size_t * n_systems_out)118 get_plugin_vtables(krb5_context context,
119 struct krb5_kdcpreauth_vtable_st **vtables_out,
120 size_t *n_tables_out, size_t *n_systems_out)
121 {
122 krb5_plugin_initvt_fn *plugins = NULL, *pl;
123 struct krb5_kdcpreauth_vtable_st *vtables;
124 size_t count, n_tables, n_systems, i;
125
126 *vtables_out = NULL;
127 *n_tables_out = *n_systems_out = 0;
128
129 /* Auto-register encrypted challenge and (if possible) pkinit. */
130 k5_plugin_register_dyn(context, PLUGIN_INTERFACE_KDCPREAUTH, "pkinit",
131 "preauth");
132 k5_plugin_register_dyn(context, PLUGIN_INTERFACE_KDCPREAUTH, "otp",
133 "preauth");
134 k5_plugin_register_dyn(context, PLUGIN_INTERFACE_KDCPREAUTH, "spake",
135 "preauth");
136 k5_plugin_register(context, PLUGIN_INTERFACE_KDCPREAUTH,
137 "encrypted_challenge",
138 kdcpreauth_encrypted_challenge_initvt);
139 k5_plugin_register(context, PLUGIN_INTERFACE_KDCPREAUTH,
140 "encrypted_timestamp",
141 kdcpreauth_encrypted_timestamp_initvt);
142
143 if (k5_plugin_load_all(context, PLUGIN_INTERFACE_KDCPREAUTH, &plugins))
144 return;
145 for (count = 0; plugins[count]; count++);
146 vtables = calloc(count + 1, sizeof(*vtables));
147 if (vtables == NULL)
148 goto cleanup;
149 for (pl = plugins, n_tables = 0; *pl != NULL; pl++) {
150 if ((*pl)(context, 1, 2, (krb5_plugin_vtable)&vtables[n_tables]) == 0)
151 n_tables++;
152 }
153 for (i = 0, n_systems = 0; i < n_tables; i++) {
154 for (count = 0; vtables[i].pa_type_list[count] != 0; count++);
155 n_systems += count;
156 }
157 *vtables_out = vtables;
158 *n_tables_out = n_tables;
159 *n_systems_out = n_systems;
160
161 cleanup:
162 k5_plugin_free_modules(context, plugins);
163 }
164
165 /* Make a list of realm names. The caller should free the list container but
166 * not the list elements (which are aliases into kdc_realmlist). */
167 static krb5_error_code
get_realm_names(struct server_handle * handle,const char *** list_out)168 get_realm_names(struct server_handle *handle, const char ***list_out)
169 {
170 const char **list;
171 int i;
172
173 list = calloc(handle->kdc_numrealms + 1, sizeof(*list));
174 if (list == NULL)
175 return ENOMEM;
176 for (i = 0; i < handle->kdc_numrealms; i++)
177 list[i] = handle->kdc_realmlist[i]->realm_name;
178 list[i] = NULL;
179 *list_out = list;
180 return 0;
181 }
182
183 void
load_preauth_plugins(struct server_handle * handle,krb5_context context,verto_ctx * ctx)184 load_preauth_plugins(struct server_handle *handle, krb5_context context,
185 verto_ctx *ctx)
186 {
187 krb5_error_code ret;
188 struct krb5_kdcpreauth_vtable_st *vtables = NULL, *vt;
189 size_t n_systems, n_tables, i, j;
190 krb5_kdcpreauth_moddata moddata;
191 const char **realm_names = NULL, *emsg;
192 preauth_system *sys;
193
194 /* Get all available kdcpreauth vtables. */
195 get_plugin_vtables(context, &vtables, &n_tables, &n_systems);
196
197 /* Allocate the list of static and plugin preauth systems. */
198 preauth_systems = calloc(n_systems + 1, sizeof(preauth_system));
199 if (preauth_systems == NULL)
200 goto cleanup;
201
202 if (get_realm_names(handle, &realm_names))
203 goto cleanup;
204
205 /* Add the dynamically-loaded mechanisms to the list. */
206 n_systems = 0;
207 for (i = 0; i < n_tables; i++) {
208 /* Try to initialize this module. */
209 vt = &vtables[i];
210 moddata = NULL;
211 if (vt->init) {
212 ret = vt->init(context, &moddata, realm_names);
213 if (ret) {
214 emsg = krb5_get_error_message(context, ret);
215 krb5_klog_syslog(LOG_ERR, _("preauth %s failed to "
216 "initialize: %s"), vt->name, emsg);
217 krb5_free_error_message(context, emsg);
218 continue;
219 }
220 }
221
222 if (vt->loop) {
223 ret = vt->loop(context, moddata, ctx);
224 if (ret) {
225 emsg = krb5_get_error_message(context, ret);
226 krb5_klog_syslog(LOG_ERR, _("preauth %s failed to setup "
227 "loop: %s"), vt->name, emsg);
228 krb5_free_error_message(context, emsg);
229 if (vt->fini)
230 vt->fini(context, moddata);
231 continue;
232 }
233 }
234
235 /* Add this module to the systems list once for each pa type. */
236 for (j = 0; vt->pa_type_list[j] != 0; j++) {
237 sys = &preauth_systems[n_systems];
238 sys->name = vt->name;
239 sys->type = vt->pa_type_list[j];
240 sys->flags = (vt->flags) ? vt->flags(context, sys->type) : 0;
241 sys->moddata = moddata;
242 sys->init = vt->init;
243 /* Only call fini once for each plugin. */
244 sys->fini = (j == 0) ? vt->fini : NULL;
245 sys->get_edata = vt->edata;
246 sys->verify_padata = vt->verify;
247 sys->return_padata = vt->return_padata;
248 sys->free_modreq = vt->free_modreq;
249 sys->loop = vt->loop;
250 n_systems++;
251 }
252 }
253 n_preauth_systems = n_systems;
254 /* Add the end-of-list marker. */
255 preauth_systems[n_systems].name = "[end]";
256 preauth_systems[n_systems].type = -1;
257
258 cleanup:
259 free(vtables);
260 free(realm_names);
261 }
262
263 void
unload_preauth_plugins(krb5_context context)264 unload_preauth_plugins(krb5_context context)
265 {
266 size_t i;
267
268 for (i = 0; i < n_preauth_systems; i++) {
269 if (preauth_systems[i].fini)
270 preauth_systems[i].fini(context, preauth_systems[i].moddata);
271 }
272 free(preauth_systems);
273 preauth_systems = NULL;
274 n_preauth_systems = 0;
275 }
276
277 /*
278 * The make_padata_context() function creates a space for storing any
279 * request-specific module data which will be needed by return_padata() later.
280 * Each preauth type gets a storage location of its own.
281 */
282 struct request_pa_context {
283 int n_contexts;
284 struct {
285 preauth_system *pa_system;
286 krb5_kdcpreauth_modreq modreq;
287 } *contexts;
288 };
289
290 static krb5_error_code
make_padata_context(krb5_context context,void ** padata_context)291 make_padata_context(krb5_context context, void **padata_context)
292 {
293 int i;
294 struct request_pa_context *ret;
295
296 ret = malloc(sizeof(*ret));
297 if (ret == NULL) {
298 return ENOMEM;
299 }
300
301 ret->n_contexts = n_preauth_systems;
302 ret->contexts = malloc(sizeof(ret->contexts[0]) * ret->n_contexts);
303 if (ret->contexts == NULL) {
304 free(ret);
305 return ENOMEM;
306 }
307
308 memset(ret->contexts, 0, sizeof(ret->contexts[0]) * ret->n_contexts);
309
310 for (i = 0; i < ret->n_contexts; i++) {
311 ret->contexts[i].pa_system = &preauth_systems[i];
312 ret->contexts[i].modreq = NULL;
313 }
314
315 *padata_context = ret;
316
317 return 0;
318 }
319
320 /*
321 * The free_padata_context function frees any context information pointers
322 * which the check_padata() function created but which weren't already cleaned
323 * up by return_padata().
324 */
325 void
free_padata_context(krb5_context kcontext,void * padata_context)326 free_padata_context(krb5_context kcontext, void *padata_context)
327 {
328 struct request_pa_context *context = padata_context;
329 preauth_system *sys;
330 int i;
331
332 if (context == NULL)
333 return;
334 for (i = 0; i < context->n_contexts; i++) {
335 sys = context->contexts[i].pa_system;
336 if (!sys->free_modreq || !context->contexts[i].modreq)
337 continue;
338 sys->free_modreq(kcontext, sys->moddata, context->contexts[i].modreq);
339 context->contexts[i].modreq = NULL;
340 }
341
342 free(context->contexts);
343 free(context);
344 }
345
346 static krb5_deltat
max_time_skew(krb5_context context,krb5_kdcpreauth_rock rock)347 max_time_skew(krb5_context context, krb5_kdcpreauth_rock rock)
348 {
349 return context->clockskew;
350 }
351
352 static krb5_error_code
client_keys(krb5_context context,krb5_kdcpreauth_rock rock,krb5_keyblock ** keys_out)353 client_keys(krb5_context context, krb5_kdcpreauth_rock rock,
354 krb5_keyblock **keys_out)
355 {
356 krb5_kdc_req *request = rock->request;
357 krb5_db_entry *client = rock->client;
358 krb5_keyblock *keys, key;
359 krb5_key_data *entry_key;
360 int i, k;
361
362 keys = calloc(request->nktypes + 1, sizeof(krb5_keyblock));
363 if (keys == NULL)
364 return ENOMEM;
365
366 k = 0;
367 for (i = 0; i < request->nktypes; i++) {
368 entry_key = NULL;
369 if (krb5_dbe_find_enctype(context, client, request->ktype[i],
370 -1, 0, &entry_key) != 0)
371 continue;
372 if (krb5_dbe_decrypt_key_data(context, NULL, entry_key,
373 &key, NULL) != 0)
374 continue;
375 keys[k++] = key;
376 }
377 if (k == 0) {
378 free(keys);
379 return ENOENT;
380 }
381 *keys_out = keys;
382 return 0;
383 }
384
free_keys(krb5_context context,krb5_kdcpreauth_rock rock,krb5_keyblock * keys)385 static void free_keys(krb5_context context, krb5_kdcpreauth_rock rock,
386 krb5_keyblock *keys)
387 {
388 krb5_keyblock *k;
389
390 if (keys == NULL)
391 return;
392 for (k = keys; k->enctype != 0; k++)
393 krb5_free_keyblock_contents(context, k);
394 free(keys);
395 }
396
397 static krb5_data *
request_body(krb5_context context,krb5_kdcpreauth_rock rock)398 request_body(krb5_context context, krb5_kdcpreauth_rock rock)
399 {
400 return rock->inner_body;
401 }
402
403 static krb5_keyblock *
fast_armor(krb5_context context,krb5_kdcpreauth_rock rock)404 fast_armor(krb5_context context, krb5_kdcpreauth_rock rock)
405 {
406 return rock->rstate->armor_key;
407 }
408
409 static krb5_error_code
get_string(krb5_context context,krb5_kdcpreauth_rock rock,const char * key,char ** value_out)410 get_string(krb5_context context, krb5_kdcpreauth_rock rock, const char *key,
411 char **value_out)
412 {
413 return krb5_dbe_get_string(context, rock->client, key, value_out);
414 }
415
416 static void
free_string(krb5_context context,krb5_kdcpreauth_rock rock,char * string)417 free_string(krb5_context context, krb5_kdcpreauth_rock rock, char *string)
418 {
419 krb5_dbe_free_string(context, string);
420 }
421
422 static void *
client_entry(krb5_context context,krb5_kdcpreauth_rock rock)423 client_entry(krb5_context context, krb5_kdcpreauth_rock rock)
424 {
425 return rock->client;
426 }
427
428 static verto_ctx *
event_context(krb5_context context,krb5_kdcpreauth_rock rock)429 event_context(krb5_context context, krb5_kdcpreauth_rock rock)
430 {
431 return rock->vctx;
432 }
433
434 static krb5_boolean
have_client_keys(krb5_context context,krb5_kdcpreauth_rock rock)435 have_client_keys(krb5_context context, krb5_kdcpreauth_rock rock)
436 {
437 krb5_kdc_req *request = rock->request;
438 krb5_key_data *kd;
439 int i;
440
441 for (i = 0; i < request->nktypes; i++) {
442 if (krb5_dbe_find_enctype(context, rock->client, request->ktype[i],
443 -1, 0, &kd) == 0)
444 return TRUE;
445 }
446 return FALSE;
447 }
448
449 static const krb5_keyblock *
client_keyblock(krb5_context context,krb5_kdcpreauth_rock rock)450 client_keyblock(krb5_context context, krb5_kdcpreauth_rock rock)
451 {
452 if (rock->client_keyblock->enctype == ENCTYPE_NULL)
453 return NULL;
454 return rock->client_keyblock;
455 }
456
457 static krb5_error_code
add_auth_indicator(krb5_context context,krb5_kdcpreauth_rock rock,const char * indicator)458 add_auth_indicator(krb5_context context, krb5_kdcpreauth_rock rock,
459 const char *indicator)
460 {
461 return authind_add(context, indicator, rock->auth_indicators);
462 }
463
464 static krb5_boolean
get_cookie(krb5_context context,krb5_kdcpreauth_rock rock,krb5_preauthtype pa_type,krb5_data * out)465 get_cookie(krb5_context context, krb5_kdcpreauth_rock rock,
466 krb5_preauthtype pa_type, krb5_data *out)
467 {
468 return kdc_fast_search_cookie(rock->rstate, pa_type, out);
469 }
470
471 static krb5_error_code
set_cookie(krb5_context context,krb5_kdcpreauth_rock rock,krb5_preauthtype pa_type,const krb5_data * data)472 set_cookie(krb5_context context, krb5_kdcpreauth_rock rock,
473 krb5_preauthtype pa_type, const krb5_data *data)
474 {
475 return kdc_fast_set_cookie(rock->rstate, pa_type, data);
476 }
477
478 static krb5_boolean
match_client(krb5_context context,krb5_kdcpreauth_rock rock,krb5_principal princ)479 match_client(krb5_context context, krb5_kdcpreauth_rock rock,
480 krb5_principal princ)
481 {
482 krb5_db_entry *ent;
483 krb5_boolean match = FALSE;
484 krb5_principal req_client = rock->request->client;
485 krb5_principal client = rock->client->princ;
486
487 /* Check for a direct match against the request principal or
488 * the post-canon client principal. */
489 if (krb5_principal_compare_flags(context, princ, req_client,
490 KRB5_PRINCIPAL_COMPARE_ENTERPRISE) ||
491 krb5_principal_compare(context, princ, client))
492 return TRUE;
493
494 if (krb5_db_get_principal(context, princ, KRB5_KDB_FLAG_CLIENT, &ent))
495 return FALSE;
496 match = krb5_principal_compare(context, ent->princ, client);
497 krb5_db_free_principal(context, ent);
498 return match;
499 }
500
501 static krb5_principal
client_name(krb5_context context,krb5_kdcpreauth_rock rock)502 client_name(krb5_context context, krb5_kdcpreauth_rock rock)
503 {
504 return rock->client->princ;
505 }
506
507 static void
send_freshness_token(krb5_context context,krb5_kdcpreauth_rock rock)508 send_freshness_token(krb5_context context, krb5_kdcpreauth_rock rock)
509 {
510 rock->send_freshness_token = TRUE;
511 }
512
513 static krb5_error_code
check_freshness_token(krb5_context context,krb5_kdcpreauth_rock rock,const krb5_data * token)514 check_freshness_token(krb5_context context, krb5_kdcpreauth_rock rock,
515 const krb5_data *token)
516 {
517 krb5_timestamp token_ts, now;
518 krb5_key_data *kd;
519 krb5_keyblock kb;
520 krb5_kvno token_kvno;
521 krb5_checksum cksum;
522 krb5_data d;
523 uint8_t *token_cksum;
524 size_t token_cksum_len;
525 krb5_boolean valid = FALSE;
526 char ckbuf[4];
527
528 memset(&kb, 0, sizeof(kb));
529
530 if (krb5_timeofday(context, &now) != 0)
531 goto cleanup;
532
533 if (token->length <= 8)
534 goto cleanup;
535 token_ts = load_32_be(token->data);
536 token_kvno = load_32_be(token->data + 4);
537 token_cksum = (uint8_t *)token->data + 8;
538 token_cksum_len = token->length - 8;
539
540 /* Check if the token timestamp is too old. */
541 if (ts_after(now, ts_incr(token_ts, FRESHNESS_LIFETIME)))
542 goto cleanup;
543
544 /* Fetch and decrypt the local krbtgt key of the token's kvno. */
545 if (krb5_dbe_find_enctype(context, rock->local_tgt, -1, -1, token_kvno,
546 &kd) != 0)
547 goto cleanup;
548 if (krb5_dbe_decrypt_key_data(context, NULL, kd, &kb, NULL) != 0)
549 goto cleanup;
550
551 /* Verify the token checksum against the current KDC time. The checksum
552 * must use the mandatory checksum type of the krbtgt key's enctype. */
553 store_32_be(token_ts, ckbuf);
554 d = make_data(ckbuf, sizeof(ckbuf));
555 cksum.magic = KV5M_CHECKSUM;
556 cksum.checksum_type = 0;
557 cksum.length = token_cksum_len;
558 cksum.contents = token_cksum;
559 (void)krb5_c_verify_checksum(context, &kb, KRB5_KEYUSAGE_PA_AS_FRESHNESS,
560 &d, &cksum, &valid);
561
562 cleanup:
563 krb5_free_keyblock_contents(context, &kb);
564 return valid ? 0 : KRB5KDC_ERR_PREAUTH_EXPIRED;
565 }
566
567 static krb5_error_code
replace_reply_key(krb5_context context,krb5_kdcpreauth_rock rock,const krb5_keyblock * key,krb5_boolean is_strengthen)568 replace_reply_key(krb5_context context, krb5_kdcpreauth_rock rock,
569 const krb5_keyblock *key, krb5_boolean is_strengthen)
570 {
571 krb5_keyblock copy;
572
573 if (krb5_copy_keyblock_contents(context, key, ©) != 0)
574 return ENOMEM;
575 krb5_free_keyblock_contents(context, rock->client_keyblock);
576 *rock->client_keyblock = copy;
577 if (!is_strengthen)
578 rock->replaced_reply_key = TRUE;
579 return 0;
580 }
581
582 static struct krb5_kdcpreauth_callbacks_st callbacks = {
583 6,
584 max_time_skew,
585 client_keys,
586 free_keys,
587 request_body,
588 fast_armor,
589 get_string,
590 free_string,
591 client_entry,
592 event_context,
593 have_client_keys,
594 client_keyblock,
595 add_auth_indicator,
596 get_cookie,
597 set_cookie,
598 match_client,
599 client_name,
600 send_freshness_token,
601 check_freshness_token,
602 replace_reply_key
603 };
604
605 static krb5_error_code
find_pa_system(int type,preauth_system ** preauth)606 find_pa_system(int type, preauth_system **preauth)
607 {
608 preauth_system *ap;
609
610 if (preauth_systems == NULL)
611 return KRB5_PREAUTH_BAD_TYPE;
612 ap = preauth_systems;
613 while ((ap->type != -1) && (ap->type != type))
614 ap++;
615 if (ap->type == -1)
616 return(KRB5_PREAUTH_BAD_TYPE);
617 *preauth = ap;
618 return 0;
619 }
620
621 /* Find a pointer to the request-specific module data for pa_sys. */
622 static krb5_error_code
find_modreq(preauth_system * pa_sys,struct request_pa_context * context,krb5_kdcpreauth_modreq ** modreq_out)623 find_modreq(preauth_system *pa_sys, struct request_pa_context *context,
624 krb5_kdcpreauth_modreq **modreq_out)
625 {
626 int i;
627
628 *modreq_out = NULL;
629 if (context == NULL)
630 return KRB5KRB_ERR_GENERIC;
631
632 for (i = 0; i < context->n_contexts; i++) {
633 if (context->contexts[i].pa_system == pa_sys) {
634 *modreq_out = &context->contexts[i].modreq;
635 return 0;
636 }
637 }
638
639 return KRB5KRB_ERR_GENERIC;
640 }
641
642 /*
643 * Create a list of indices into the preauth_systems array, sorted by order of
644 * preference.
645 */
646 static krb5_boolean
pa_list_includes(krb5_pa_data ** pa_data,krb5_preauthtype pa_type)647 pa_list_includes(krb5_pa_data **pa_data, krb5_preauthtype pa_type)
648 {
649 while (*pa_data != NULL) {
650 if ((*pa_data)->pa_type == pa_type)
651 return TRUE;
652 pa_data++;
653 }
654 return FALSE;
655 }
656 static void
sort_pa_order(krb5_context context,krb5_kdc_req * request,int * pa_order)657 sort_pa_order(krb5_context context, krb5_kdc_req *request, int *pa_order)
658 {
659 size_t i, j, k, n_repliers, n_key_replacers;
660
661 /* First, set up the default order. */
662 i = 0;
663 for (j = 0; j < n_preauth_systems; j++) {
664 if (preauth_systems[j].return_padata != NULL)
665 pa_order[i++] = j;
666 }
667 n_repliers = i;
668 pa_order[n_repliers] = -1;
669
670 /* Reorder so that PA_REPLACES_KEY modules are listed first. */
671 for (i = 0; i < n_repliers; i++) {
672 /* If this module replaces the key, then it's okay to leave it where it
673 * is in the order. */
674 if (preauth_systems[pa_order[i]].flags & PA_REPLACES_KEY)
675 continue;
676 /* If not, search for a module which does, and swap in the first one we
677 * find. */
678 for (j = i + 1; j < n_repliers; j++) {
679 if (preauth_systems[pa_order[j]].flags & PA_REPLACES_KEY) {
680 k = pa_order[j];
681 pa_order[j] = pa_order[i];
682 pa_order[i] = k;
683 break;
684 }
685 }
686 /* If we didn't find one, we have moved all of the key-replacing
687 * modules, and i is the count of those modules. */
688 if (j == n_repliers)
689 break;
690 }
691 n_key_replacers = i;
692
693 if (request->padata != NULL) {
694 /* Now reorder the subset of modules which replace the key,
695 * bubbling those which handle pa_data types provided by the
696 * client ahead of the others.
697 */
698 for (i = 0; i < n_key_replacers; i++) {
699 if (pa_list_includes(request->padata,
700 preauth_systems[pa_order[i]].type))
701 continue;
702 for (j = i + 1; j < n_key_replacers; j++) {
703 if (pa_list_includes(request->padata,
704 preauth_systems[pa_order[j]].type)) {
705 k = pa_order[j];
706 pa_order[j] = pa_order[i];
707 pa_order[i] = k;
708 break;
709 }
710 }
711 }
712 }
713 #ifdef DEBUG
714 krb5_klog_syslog(LOG_DEBUG, "original preauth mechanism list:");
715 for (i = 0; i < n_preauth_systems; i++) {
716 if (preauth_systems[i].return_padata != NULL)
717 krb5_klog_syslog(LOG_DEBUG, "... %s(%d)", preauth_systems[i].name,
718 preauth_systems[i].type);
719 }
720 krb5_klog_syslog(LOG_DEBUG, "sorted preauth mechanism list:");
721 for (i = 0; pa_order[i] != -1; i++) {
722 krb5_klog_syslog(LOG_DEBUG, "... %s(%d)",
723 preauth_systems[pa_order[i]].name,
724 preauth_systems[pa_order[i]].type);
725 }
726 #endif
727 }
728
missing_required_preauth(krb5_db_entry * client,krb5_db_entry * server,krb5_enc_tkt_part * enc_tkt_reply)729 const char *missing_required_preauth(krb5_db_entry *client,
730 krb5_db_entry *server,
731 krb5_enc_tkt_part *enc_tkt_reply)
732 {
733 #ifdef DEBUG
734 krb5_klog_syslog (
735 LOG_DEBUG,
736 "client needs %spreauth, %shw preauth; request has %spreauth, %shw preauth",
737 isflagset (client->attributes, KRB5_KDB_REQUIRES_PRE_AUTH) ? "" : "no ",
738 isflagset (client->attributes, KRB5_KDB_REQUIRES_HW_AUTH) ? "" : "no ",
739 isflagset (enc_tkt_reply->flags, TKT_FLG_PRE_AUTH) ? "" : "no ",
740 isflagset (enc_tkt_reply->flags, TKT_FLG_HW_AUTH) ? "" : "no ");
741 #endif
742
743 if (isflagset(client->attributes, KRB5_KDB_REQUIRES_PRE_AUTH) &&
744 !isflagset(enc_tkt_reply->flags, TKT_FLG_PRE_AUTH))
745 return "NEEDED_PREAUTH";
746
747 if (isflagset(client->attributes, KRB5_KDB_REQUIRES_HW_AUTH) &&
748 !isflagset(enc_tkt_reply->flags, TKT_FLG_HW_AUTH))
749 return "NEEDED_HW_PREAUTH";
750
751 return 0;
752 }
753
754 /* Return true if request's enctypes indicate support for etype-info2. */
755 static krb5_boolean
requires_info2(const krb5_kdc_req * request)756 requires_info2(const krb5_kdc_req *request)
757 {
758 int i;
759
760 for (i = 0; i < request->nktypes; i++) {
761 if (enctype_requires_etype_info_2(request->ktype[i]))
762 return TRUE;
763 }
764 return FALSE;
765 }
766
767 /* Add PA-ETYPE-INFO2 and possibly PA-ETYPE-INFO entries to pa_list as
768 * appropriate for the request and client principal. */
769 static krb5_error_code
add_etype_info(krb5_context context,krb5_kdcpreauth_rock rock,krb5_pa_data *** pa_list)770 add_etype_info(krb5_context context, krb5_kdcpreauth_rock rock,
771 krb5_pa_data ***pa_list)
772 {
773 krb5_error_code ret;
774 krb5_data *der;
775
776 if (rock->client_key == NULL)
777 return 0;
778
779 if (!requires_info2(rock->request)) {
780 /* Include PA-ETYPE-INFO only for old clients. */
781 ret = make_etype_info(context, FALSE, rock->client->princ,
782 rock->client_key, rock->client_keyblock->enctype,
783 &der);
784 if (ret)
785 return ret;
786 ret = k5_add_pa_data_from_data(pa_list, KRB5_PADATA_ETYPE_INFO, der);
787 krb5_free_data(context, der);
788 if (ret)
789 return ret;
790 }
791
792 /* Always include PA-ETYPE-INFO2. */
793 ret = make_etype_info(context, TRUE, rock->client->princ, rock->client_key,
794 rock->client_keyblock->enctype, &der);
795 if (ret)
796 return ret;
797 ret = k5_add_pa_data_from_data(pa_list, KRB5_PADATA_ETYPE_INFO2, der);
798 krb5_free_data(context, der);
799 return ret;
800 }
801
802 /* Add PW-SALT entries to pa_list as appropriate for the request and client
803 * principal. */
804 static krb5_error_code
add_pw_salt(krb5_context context,krb5_kdcpreauth_rock rock,krb5_pa_data *** pa_list)805 add_pw_salt(krb5_context context, krb5_kdcpreauth_rock rock,
806 krb5_pa_data ***pa_list)
807 {
808 krb5_error_code ret;
809 krb5_data *salt = NULL;
810 krb5_int16 salttype;
811
812 /* Only include this pa-data for old clients. */
813 if (rock->client_key == NULL || requires_info2(rock->request))
814 return 0;
815
816 ret = krb5_dbe_compute_salt(context, rock->client_key,
817 rock->request->client, &salttype, &salt);
818 if (ret)
819 return 0;
820
821 ret = k5_add_pa_data_from_data(pa_list, KRB5_PADATA_PW_SALT, salt);
822 krb5_free_data(context, salt);
823 return ret;
824 }
825
826 static krb5_error_code
add_freshness_token(krb5_context context,krb5_kdcpreauth_rock rock,krb5_pa_data *** pa_list)827 add_freshness_token(krb5_context context, krb5_kdcpreauth_rock rock,
828 krb5_pa_data ***pa_list)
829 {
830 krb5_error_code ret;
831 krb5_timestamp now;
832 krb5_keyblock kb;
833 krb5_checksum cksum;
834 krb5_data d;
835 krb5_pa_data *pa = NULL;
836 char ckbuf[4];
837
838 memset(&cksum, 0, sizeof(cksum));
839 memset(&kb, 0, sizeof(kb));
840
841 if (!rock->send_freshness_token)
842 return 0;
843 if (krb5int_find_pa_data(context, rock->request->padata,
844 KRB5_PADATA_AS_FRESHNESS) == NULL)
845 return 0;
846
847 /* Compute a checksum over the current KDC time. */
848 ret = krb5_timeofday(context, &now);
849 if (ret)
850 goto cleanup;
851 store_32_be(now, ckbuf);
852 d = make_data(ckbuf, sizeof(ckbuf));
853 ret = krb5_c_make_checksum(context, 0, rock->local_tgt_key,
854 KRB5_KEYUSAGE_PA_AS_FRESHNESS, &d, &cksum);
855
856 /* Compose a freshness token from the time, krbtgt kvno, and checksum. */
857 ret = k5_alloc_pa_data(KRB5_PADATA_AS_FRESHNESS, 8 + cksum.length, &pa);
858 if (ret)
859 goto cleanup;
860 store_32_be(now, pa->contents);
861 store_32_be(current_kvno(rock->local_tgt), pa->contents + 4);
862 memcpy(pa->contents + 8, cksum.contents, cksum.length);
863
864 ret = k5_add_pa_data_element(pa_list, &pa);
865
866 cleanup:
867 krb5_free_keyblock_contents(context, &kb);
868 krb5_free_checksum_contents(context, &cksum);
869 k5_free_pa_data_element(pa);
870 return ret;
871 }
872
873 struct hint_state {
874 kdc_hint_respond_fn respond;
875 void *arg;
876 krb5_context context;
877
878 krb5_kdcpreauth_rock rock;
879 krb5_kdc_req *request;
880 krb5_pa_data ***e_data_out;
881
882 int hw_only;
883 preauth_system *ap;
884 krb5_pa_data **pa_data;
885 krb5_preauthtype pa_type;
886 };
887
888 static void
hint_list_finish(struct hint_state * state,krb5_error_code code)889 hint_list_finish(struct hint_state *state, krb5_error_code code)
890 {
891 krb5_context context = state->context;
892 kdc_hint_respond_fn oldrespond = state->respond;
893 void *oldarg = state->arg;
894
895 /* Add a freshness token if a preauth module requested it and the client
896 * request indicates support for it. */
897 if (!code)
898 code = add_freshness_token(context, state->rock, &state->pa_data);
899
900 if (!code) {
901 if (state->pa_data == NULL) {
902 krb5_klog_syslog(LOG_INFO,
903 _("%spreauth required but hint list is empty"),
904 state->hw_only ? "hw" : "");
905 }
906
907 *state->e_data_out = state->pa_data;
908 state->pa_data = NULL;
909 }
910
911 krb5_free_pa_data(context, state->pa_data);
912 free(state);
913 (*oldrespond)(oldarg);
914 }
915
916 static void
917 hint_list_next(struct hint_state *arg);
918
919 static void
finish_get_edata(void * arg,krb5_error_code code,krb5_pa_data * pa)920 finish_get_edata(void *arg, krb5_error_code code, krb5_pa_data *pa)
921 {
922 krb5_error_code ret;
923 struct hint_state *state = arg;
924
925 if (code == 0) {
926 if (pa == NULL) {
927 ret = k5_alloc_pa_data(state->pa_type, 0, &pa);
928 if (ret)
929 goto error;
930 }
931 ret = k5_add_pa_data_element(&state->pa_data, &pa);
932 k5_free_pa_data_element(pa);
933 if (ret)
934 goto error;
935 }
936
937 state->ap++;
938 hint_list_next(state);
939 return;
940
941 error:
942 hint_list_finish(state, ret);
943 }
944
945 static void
hint_list_next(struct hint_state * state)946 hint_list_next(struct hint_state *state)
947 {
948 krb5_context context = state->context;
949 preauth_system *ap = state->ap;
950
951 if (ap->type == -1) {
952 hint_list_finish(state, 0);
953 return;
954 }
955
956 if (state->hw_only && !(ap->flags & PA_HARDWARE))
957 goto next;
958 if (ap->flags & PA_PSEUDO)
959 goto next;
960
961 state->pa_type = ap->type;
962 if (ap->get_edata) {
963 ap->get_edata(context, state->request, &callbacks, state->rock,
964 ap->moddata, ap->type, finish_get_edata, state);
965 } else
966 finish_get_edata(state, 0, NULL);
967 return;
968
969 next:
970 state->ap++;
971 hint_list_next(state);
972 }
973
974 void
get_preauth_hint_list(krb5_kdc_req * request,krb5_kdcpreauth_rock rock,krb5_pa_data *** e_data_out,kdc_hint_respond_fn respond,void * arg)975 get_preauth_hint_list(krb5_kdc_req *request, krb5_kdcpreauth_rock rock,
976 krb5_pa_data ***e_data_out, kdc_hint_respond_fn respond,
977 void *arg)
978 {
979 krb5_context context = rock->rstate->realm_data->realm_context;
980 struct hint_state *state;
981
982 *e_data_out = NULL;
983
984 /* Allocate our state. */
985 state = calloc(1, sizeof(*state));
986 if (state == NULL)
987 goto error;
988 state->hw_only = isflagset(rock->client->attributes,
989 KRB5_KDB_REQUIRES_HW_AUTH);
990 state->respond = respond;
991 state->arg = arg;
992 state->request = request;
993 state->rock = rock;
994 state->context = context;
995 state->e_data_out = e_data_out;
996 state->pa_data = NULL;
997 state->ap = preauth_systems;
998
999 /* Add an empty PA-FX-FAST element to advertise FAST support. */
1000 if (k5_add_empty_pa_data(&state->pa_data, KRB5_PADATA_FX_FAST) != 0)
1001 goto error;
1002
1003 if (add_etype_info(context, rock, &state->pa_data) != 0)
1004 goto error;
1005
1006 hint_list_next(state);
1007 return;
1008
1009 error:
1010 if (state != NULL)
1011 krb5_free_pa_data(context, state->pa_data);
1012 free(state);
1013 (*respond)(arg);
1014 }
1015
1016 /*
1017 * Add authorization data returned from preauth modules to the ticket
1018 * It is assumed that ad is a "null-terminated" array of krb5_authdata ptrs
1019 */
1020 static krb5_error_code
add_authorization_data(krb5_enc_tkt_part * enc_tkt_part,krb5_authdata ** ad)1021 add_authorization_data(krb5_enc_tkt_part *enc_tkt_part, krb5_authdata **ad)
1022 {
1023 krb5_authdata **newad;
1024 int oldones, newones;
1025 int i;
1026
1027 if (enc_tkt_part == NULL || ad == NULL)
1028 return EINVAL;
1029
1030 for (newones = 0; ad[newones] != NULL; newones++);
1031 if (newones == 0)
1032 return 0; /* nothing to add */
1033
1034 if (enc_tkt_part->authorization_data == NULL)
1035 oldones = 0;
1036 else
1037 for (oldones = 0;
1038 enc_tkt_part->authorization_data[oldones] != NULL; oldones++);
1039
1040 newad = malloc((oldones + newones + 1) * sizeof(krb5_authdata *));
1041 if (newad == NULL)
1042 return ENOMEM;
1043
1044 /* Copy any existing pointers */
1045 for (i = 0; i < oldones; i++)
1046 newad[i] = enc_tkt_part->authorization_data[i];
1047
1048 /* Add the new ones */
1049 for (i = 0; i < newones; i++)
1050 newad[oldones+i] = ad[i];
1051
1052 /* Terminate the new list */
1053 newad[oldones+i] = NULL;
1054
1055 /* Free any existing list */
1056 if (enc_tkt_part->authorization_data != NULL)
1057 free(enc_tkt_part->authorization_data);
1058
1059 /* Install our new list */
1060 enc_tkt_part->authorization_data = newad;
1061
1062 return 0;
1063 }
1064
1065 struct padata_state {
1066 kdc_preauth_respond_fn respond;
1067 void *arg;
1068 kdc_realm_t *realm;
1069
1070 krb5_kdcpreauth_modreq *modreq_ptr;
1071 krb5_pa_data **padata;
1072 int pa_found;
1073 krb5_context context;
1074 krb5_kdcpreauth_rock rock;
1075 krb5_data *req_pkt;
1076 krb5_kdc_req *request;
1077 krb5_enc_tkt_part *enc_tkt_reply;
1078 void **padata_context;
1079
1080 preauth_system *pa_sys;
1081 krb5_pa_data **pa_e_data;
1082 krb5_boolean typed_e_data_flag;
1083 int pa_ok;
1084 krb5_error_code saved_code;
1085
1086 krb5_pa_data ***e_data_out;
1087 krb5_boolean *typed_e_data_out;
1088 };
1089
1090 /* Return code if it is 0 or one of the codes we pass through to the client.
1091 * Otherwise return KRB5KDC_ERR_PREAUTH_FAILED. */
1092 static krb5_error_code
filter_preauth_error(krb5_error_code code)1093 filter_preauth_error(krb5_error_code code)
1094 {
1095 /* The following switch statement allows us
1096 * to return some preauth system errors back to the client.
1097 */
1098 switch(code) {
1099 case 0:
1100 case KRB5KRB_AP_ERR_BAD_INTEGRITY:
1101 case KRB5KRB_AP_ERR_SKEW:
1102 case KRB5KDC_ERR_PREAUTH_REQUIRED:
1103 case KRB5KDC_ERR_ETYPE_NOSUPP:
1104 /* rfc 4556 */
1105 case KRB5KDC_ERR_CLIENT_NOT_TRUSTED:
1106 case KRB5KDC_ERR_INVALID_SIG:
1107 case KRB5KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED:
1108 case KRB5KDC_ERR_CANT_VERIFY_CERTIFICATE:
1109 case KRB5KDC_ERR_INVALID_CERTIFICATE:
1110 case KRB5KDC_ERR_REVOKED_CERTIFICATE:
1111 case KRB5KDC_ERR_REVOCATION_STATUS_UNKNOWN:
1112 case KRB5KDC_ERR_CLIENT_NAME_MISMATCH:
1113 case KRB5KDC_ERR_INCONSISTENT_KEY_PURPOSE:
1114 case KRB5KDC_ERR_DIGEST_IN_CERT_NOT_ACCEPTED:
1115 case KRB5KDC_ERR_PA_CHECKSUM_MUST_BE_INCLUDED:
1116 case KRB5KDC_ERR_DIGEST_IN_SIGNED_DATA_NOT_ACCEPTED:
1117 case KRB5KDC_ERR_PUBLIC_KEY_ENCRYPTION_NOT_SUPPORTED:
1118 /* earlier drafts of what became rfc 4556 */
1119 case KRB5KDC_ERR_CERTIFICATE_MISMATCH:
1120 case KRB5KDC_ERR_KDC_NOT_TRUSTED:
1121 case KRB5KDC_ERR_REVOCATION_STATUS_UNAVAILABLE:
1122 /* This value is shared with
1123 * KRB5KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED. */
1124 /* case KRB5KDC_ERR_KEY_TOO_WEAK: */
1125 case KRB5KDC_ERR_DISCARD:
1126 /* pkinit alg-agility */
1127 case KRB5KDC_ERR_NO_ACCEPTABLE_KDF:
1128 /* rfc 6113 */
1129 case KRB5KDC_ERR_MORE_PREAUTH_DATA_REQUIRED:
1130 return code;
1131 default:
1132 return KRB5KDC_ERR_PREAUTH_FAILED;
1133 }
1134 }
1135
1136 /*
1137 * If the client performed optimistic pre-authentication for a multi-round-trip
1138 * mechanism, it may need key information to complete the exchange, so send it
1139 * a PA-ETYPE-INFO2 element in addition to the pa-data from the module.
1140 */
1141 static krb5_error_code
maybe_add_etype_info2(struct padata_state * state,krb5_error_code code)1142 maybe_add_etype_info2(struct padata_state *state, krb5_error_code code)
1143 {
1144 krb5_error_code ret;
1145 krb5_context context = state->context;
1146 krb5_kdcpreauth_rock rock = state->rock;
1147 krb5_data *der;
1148
1149 /* Only add key information when requesting another preauth round trip. */
1150 if (code != KRB5KDC_ERR_MORE_PREAUTH_DATA_REQUIRED)
1151 return 0;
1152
1153 /* Don't try to add key information when there is no key. */
1154 if (rock->client_key == NULL)
1155 return 0;
1156
1157 /* If the client sent a cookie, it has already seen a KDC response with key
1158 * information. */
1159 if (krb5int_find_pa_data(context, state->request->padata,
1160 KRB5_PADATA_FX_COOKIE) != NULL)
1161 return 0;
1162
1163 ret = make_etype_info(context, TRUE, rock->client->princ, rock->client_key,
1164 rock->client_keyblock->enctype, &der);
1165 if (ret)
1166 return ret;
1167 ret = k5_add_pa_data_from_data(&state->pa_e_data, KRB5_PADATA_ETYPE_INFO2,
1168 der);
1169 krb5_free_data(context, der);
1170 return ret;
1171 }
1172
1173 /* Release state and respond to the AS-REQ processing code with the result of
1174 * checking pre-authentication data. */
1175 static void
finish_check_padata(struct padata_state * state,krb5_error_code code)1176 finish_check_padata(struct padata_state *state, krb5_error_code code)
1177 {
1178 kdc_preauth_respond_fn respond;
1179 void *arg;
1180
1181 if (state->pa_ok || !state->pa_found) {
1182 /* Return successfully. If we didn't match a preauth system, we may
1183 * return PREAUTH_REQUIRED later, but we didn't fail to verify. */
1184 code = 0;
1185 goto cleanup;
1186 }
1187
1188 /* Add key information to the saved error pa-data if required. */
1189 if (maybe_add_etype_info2(state, code) != 0) {
1190 code = KRB5KDC_ERR_PREAUTH_FAILED;
1191 goto cleanup;
1192 }
1193
1194 /* Return any saved error pa-data, stealing the pointer from state. */
1195 *state->e_data_out = state->pa_e_data;
1196 *state->typed_e_data_out = state->typed_e_data_flag;
1197 state->pa_e_data = NULL;
1198
1199 cleanup:
1200 /* Discard saved error pa-data if we aren't returning it, free state, and
1201 * respond to the AS-REQ processing code. */
1202 respond = state->respond;
1203 arg = state->arg;
1204 krb5_free_pa_data(state->context, state->pa_e_data);
1205 free(state);
1206 (*respond)(arg, filter_preauth_error(code));
1207 }
1208
1209 static void
1210 next_padata(struct padata_state *state);
1211
1212 static void
finish_verify_padata(void * arg,krb5_error_code code,krb5_kdcpreauth_modreq modreq,krb5_pa_data ** e_data,krb5_authdata ** authz_data)1213 finish_verify_padata(void *arg, krb5_error_code code,
1214 krb5_kdcpreauth_modreq modreq, krb5_pa_data **e_data,
1215 krb5_authdata **authz_data)
1216 {
1217 struct padata_state *state = arg;
1218 const char *emsg;
1219 krb5_boolean typed_e_data_flag;
1220
1221 assert(state);
1222 *state->modreq_ptr = modreq;
1223
1224 if (code) {
1225 emsg = krb5_get_error_message(state->context, code);
1226 krb5_klog_syslog(LOG_INFO, "preauth (%s) verify failure: %s",
1227 state->pa_sys->name, emsg);
1228 krb5_free_error_message(state->context, emsg);
1229
1230 /* Ignore authorization data returned from modules that fail */
1231 if (authz_data != NULL) {
1232 krb5_free_authdata(state->context, authz_data);
1233 authz_data = NULL;
1234 }
1235
1236 typed_e_data_flag = ((state->pa_sys->flags & PA_TYPED_E_DATA) != 0);
1237
1238 /*
1239 * We'll return edata from either the first PA_REQUIRED module
1240 * that fails, or the first non-PA_REQUIRED module that fails.
1241 * Hang on to edata from the first non-PA_REQUIRED module.
1242 * If we've already got one saved, simply discard this one.
1243 */
1244 if (state->pa_sys->flags & PA_REQUIRED) {
1245 /* free up any previous edata we might have been saving */
1246 if (state->pa_e_data != NULL)
1247 krb5_free_pa_data(state->context, state->pa_e_data);
1248 state->pa_e_data = e_data;
1249 state->typed_e_data_flag = typed_e_data_flag;
1250
1251 /* Make sure we use the current retval */
1252 state->pa_ok = 0;
1253 finish_check_padata(state, code);
1254 return;
1255 } else if (state->pa_e_data == NULL) {
1256 /* save the first error code and e-data */
1257 state->pa_e_data = e_data;
1258 state->typed_e_data_flag = typed_e_data_flag;
1259 state->saved_code = code;
1260 } else if (e_data != NULL) {
1261 /* discard this extra e-data from non-PA_REQUIRED module */
1262 krb5_free_pa_data(state->context, e_data);
1263 }
1264 } else {
1265 #ifdef DEBUG
1266 krb5_klog_syslog (LOG_DEBUG, ".. .. ok");
1267 #endif
1268
1269 /* Ignore any edata returned on success */
1270 if (e_data != NULL)
1271 krb5_free_pa_data(state->context, e_data);
1272
1273 /* Add any authorization data to the ticket */
1274 if (authz_data != NULL) {
1275 add_authorization_data(state->enc_tkt_reply, authz_data);
1276 free(authz_data);
1277 }
1278
1279 state->pa_ok = 1;
1280 if (state->pa_sys->flags & PA_SUFFICIENT) {
1281 finish_check_padata(state, state->saved_code);
1282 return;
1283 }
1284 }
1285
1286 next_padata(state);
1287 }
1288
1289 static void
next_padata(struct padata_state * state)1290 next_padata(struct padata_state *state)
1291 {
1292 assert(state);
1293 if (!state->padata)
1294 state->padata = state->request->padata;
1295 else
1296 state->padata++;
1297
1298 if (!*state->padata) {
1299 finish_check_padata(state, state->saved_code);
1300 return;
1301 }
1302
1303 #ifdef DEBUG
1304 krb5_klog_syslog (LOG_DEBUG, ".. pa_type 0x%x", (*state->padata)->pa_type);
1305 #endif
1306 if (find_pa_system((*state->padata)->pa_type, &state->pa_sys))
1307 goto next;
1308 if (find_modreq(state->pa_sys, *state->padata_context, &state->modreq_ptr))
1309 goto next;
1310 #ifdef DEBUG
1311 krb5_klog_syslog (LOG_DEBUG, ".. pa_type %s", state->pa_sys->name);
1312 #endif
1313 if (state->pa_sys->verify_padata == 0)
1314 goto next;
1315
1316 state->pa_found++;
1317 state->pa_sys->verify_padata(state->context, state->req_pkt,
1318 state->request, state->enc_tkt_reply,
1319 *state->padata, &callbacks, state->rock,
1320 state->pa_sys->moddata, finish_verify_padata,
1321 state);
1322 return;
1323
1324 next:
1325 next_padata(state);
1326 }
1327
1328 /*
1329 * This routine is called to verify the preauthentication information
1330 * for a V5 request.
1331 *
1332 * Returns 0 if the pre-authentication is valid, non-zero to indicate
1333 * an error code of some sort.
1334 */
1335
1336 void
check_padata(krb5_context context,krb5_kdcpreauth_rock rock,krb5_data * req_pkt,krb5_kdc_req * request,krb5_enc_tkt_part * enc_tkt_reply,void ** padata_context,krb5_pa_data *** e_data,krb5_boolean * typed_e_data,kdc_preauth_respond_fn respond,void * arg)1337 check_padata(krb5_context context, krb5_kdcpreauth_rock rock,
1338 krb5_data *req_pkt, krb5_kdc_req *request,
1339 krb5_enc_tkt_part *enc_tkt_reply, void **padata_context,
1340 krb5_pa_data ***e_data, krb5_boolean *typed_e_data,
1341 kdc_preauth_respond_fn respond, void *arg)
1342 {
1343 struct padata_state *state;
1344
1345 if (request->padata == 0) {
1346 (*respond)(arg, 0);
1347 return;
1348 }
1349
1350 if (make_padata_context(context, padata_context) != 0) {
1351 (*respond)(arg, KRB5KRB_ERR_GENERIC);
1352 return;
1353 }
1354
1355 state = calloc(1, sizeof(*state));
1356 if (state == NULL) {
1357 (*respond)(arg, ENOMEM);
1358 return;
1359 }
1360 state->respond = respond;
1361 state->arg = arg;
1362 state->context = context;
1363 state->rock = rock;
1364 state->req_pkt = req_pkt;
1365 state->request = request;
1366 state->enc_tkt_reply = enc_tkt_reply;
1367 state->padata_context = padata_context;
1368 state->e_data_out = e_data;
1369 state->typed_e_data_out = typed_e_data;
1370 state->realm = rock->rstate->realm_data;
1371
1372 #ifdef DEBUG
1373 krb5_klog_syslog (LOG_DEBUG, "checking padata");
1374 #endif
1375
1376 next_padata(state);
1377 }
1378
1379 /* Return true if k1 and k2 have the same type and contents. */
1380 static krb5_boolean
keyblock_equal(const krb5_keyblock * k1,const krb5_keyblock * k2)1381 keyblock_equal(const krb5_keyblock *k1, const krb5_keyblock *k2)
1382 {
1383 if (k1->enctype != k2->enctype)
1384 return FALSE;
1385 if (k1->length != k2->length)
1386 return FALSE;
1387 return memcmp(k1->contents, k2->contents, k1->length) == 0;
1388 }
1389
1390 /*
1391 * return_padata creates any necessary preauthentication
1392 * structures which should be returned by the KDC to the client
1393 */
1394 krb5_error_code
return_padata(krb5_context context,krb5_kdcpreauth_rock rock,krb5_data * req_pkt,krb5_kdc_req * request,krb5_kdc_rep * reply,krb5_keyblock * encrypting_key,void ** padata_context)1395 return_padata(krb5_context context, krb5_kdcpreauth_rock rock,
1396 krb5_data *req_pkt, krb5_kdc_req *request, krb5_kdc_rep *reply,
1397 krb5_keyblock *encrypting_key, void **padata_context)
1398 {
1399 krb5_error_code retval;
1400 krb5_pa_data ** padata;
1401 krb5_pa_data ** send_pa_list = NULL;
1402 krb5_pa_data * send_pa;
1403 krb5_pa_data * pa = 0;
1404 krb5_pa_data null_item;
1405 preauth_system * ap;
1406 int * pa_order = NULL;
1407 int * pa_type;
1408 int size = 0;
1409 krb5_kdcpreauth_modreq *modreq_ptr;
1410 krb5_boolean key_modified;
1411 krb5_keyblock original_key;
1412
1413 memset(&original_key, 0, sizeof(original_key));
1414
1415 if ((!*padata_context) &&
1416 (make_padata_context(context, padata_context) != 0)) {
1417 return KRB5KRB_ERR_GENERIC;
1418 }
1419
1420 for (ap = preauth_systems; ap->type != -1; ap++) {
1421 if (ap->return_padata)
1422 size++;
1423 }
1424
1425 pa_order = k5calloc(size + 1, sizeof(int), &retval);
1426 if (pa_order == NULL)
1427 goto cleanup;
1428 sort_pa_order(context, request, pa_order);
1429
1430 retval = krb5_copy_keyblock_contents(context, encrypting_key,
1431 &original_key);
1432 if (retval)
1433 goto cleanup;
1434 key_modified = FALSE;
1435 null_item.contents = NULL;
1436 null_item.length = 0;
1437
1438 for (pa_type = pa_order; *pa_type != -1; pa_type++) {
1439 ap = &preauth_systems[*pa_type];
1440 if (key_modified && (ap->flags & PA_REPLACES_KEY))
1441 continue;
1442 if (ap->return_padata == 0)
1443 continue;
1444 if (find_modreq(ap, *padata_context, &modreq_ptr))
1445 continue;
1446 pa = &null_item;
1447 null_item.pa_type = ap->type;
1448 if (request->padata) {
1449 for (padata = request->padata; *padata; padata++) {
1450 if ((*padata)->pa_type == ap->type) {
1451 pa = *padata;
1452 break;
1453 }
1454 }
1455 }
1456 send_pa = NULL;
1457 retval = ap->return_padata(context, pa, req_pkt, request, reply,
1458 encrypting_key, &send_pa, &callbacks, rock,
1459 ap->moddata, *modreq_ptr);
1460 if (retval)
1461 goto cleanup;
1462
1463 if (send_pa != NULL) {
1464 retval = k5_add_pa_data_element(&send_pa_list, &send_pa);
1465 k5_free_pa_data_element(send_pa);
1466 if (retval)
1467 goto cleanup;
1468 }
1469
1470 if (!key_modified && !keyblock_equal(&original_key, encrypting_key))
1471 key_modified = TRUE;
1472 }
1473
1474 /*
1475 * Add etype-info and pw-salt pa-data as needed. If we replaced the reply
1476 * key, we can't send consistent etype-info; the salt from the client key
1477 * data doesn't correspond to the replaced reply key, and RFC 4120 section
1478 * 5.2.7.5 forbids us from sending etype-info describing the initial reply
1479 * key in an AS-REP if it doesn't have the same enctype as the replaced
1480 * reply key. For all current and foreseeable preauth mechs, we can assume
1481 * the client received etype-info2 in an earlier step and already computed
1482 * the initial reply key if it needed it. The client can determine the
1483 * enctype of the replaced reply key from the etype field of the enc-part
1484 * field of the AS-REP.
1485 */
1486 if (!key_modified) {
1487 retval = add_etype_info(context, rock, &send_pa_list);
1488 if (retval)
1489 goto cleanup;
1490 retval = add_pw_salt(context, rock, &send_pa_list);
1491 if (retval)
1492 goto cleanup;
1493 }
1494
1495 if (send_pa_list != NULL) {
1496 reply->padata = send_pa_list;
1497 send_pa_list = 0;
1498 }
1499
1500 cleanup:
1501 krb5_free_keyblock_contents(context, &original_key);
1502 free(pa_order);
1503 krb5_free_pa_data(context, send_pa_list);
1504
1505 return (retval);
1506 }
1507
1508 static krb5_error_code
_make_etype_info_entry(krb5_context context,krb5_principal client_princ,krb5_key_data * client_key,krb5_enctype etype,krb5_etype_info_entry ** entry_out,int etype_info2)1509 _make_etype_info_entry(krb5_context context,
1510 krb5_principal client_princ, krb5_key_data *client_key,
1511 krb5_enctype etype, krb5_etype_info_entry **entry_out,
1512 int etype_info2)
1513 {
1514 krb5_error_code retval;
1515 krb5_int16 salttype;
1516 krb5_data *salt = NULL;
1517 krb5_etype_info_entry *entry = NULL;
1518
1519 *entry_out = NULL;
1520 entry = malloc(sizeof(*entry));
1521 if (entry == NULL)
1522 return ENOMEM;
1523
1524 entry->magic = KV5M_ETYPE_INFO_ENTRY;
1525 entry->etype = etype;
1526 entry->length = KRB5_ETYPE_NO_SALT;
1527 entry->salt = NULL;
1528 entry->s2kparams = empty_data();
1529 retval = krb5_dbe_compute_salt(context, client_key, client_princ,
1530 &salttype, &salt);
1531 if (retval)
1532 goto cleanup;
1533
1534 entry->length = salt->length;
1535 entry->salt = (unsigned char *)salt->data;
1536 salt->data = NULL;
1537 *entry_out = entry;
1538 entry = NULL;
1539
1540 cleanup:
1541 if (entry != NULL)
1542 krb5_free_data_contents(context, &entry->s2kparams);
1543 free(entry);
1544 krb5_free_data(context, salt);
1545 return retval;
1546 }
1547
1548 /* Encode an etype-info or etype-info2 message for client_key with the given
1549 * enctype, using client to compute the salt if necessary. */
1550 static krb5_error_code
make_etype_info(krb5_context context,krb5_boolean etype_info2,krb5_principal client,krb5_key_data * client_key,krb5_enctype enctype,krb5_data ** der_out)1551 make_etype_info(krb5_context context, krb5_boolean etype_info2,
1552 krb5_principal client, krb5_key_data *client_key,
1553 krb5_enctype enctype, krb5_data **der_out)
1554 {
1555 krb5_error_code retval;
1556 krb5_etype_info_entry **entry = NULL;
1557
1558 *der_out = NULL;
1559
1560 entry = k5calloc(2, sizeof(*entry), &retval);
1561 if (entry == NULL)
1562 goto cleanup;
1563 retval = _make_etype_info_entry(context, client, client_key, enctype,
1564 &entry[0], etype_info2);
1565 if (retval != 0)
1566 goto cleanup;
1567
1568 if (etype_info2)
1569 retval = encode_krb5_etype_info2(entry, der_out);
1570 else
1571 retval = encode_krb5_etype_info(entry, der_out);
1572
1573 cleanup:
1574 krb5_free_etype_info(context, entry);
1575 return retval;
1576 }
1577
1578 /*
1579 * Returns TRUE if the PAC should be included
1580 */
1581 krb5_boolean
include_pac_p(krb5_context context,krb5_kdc_req * request)1582 include_pac_p(krb5_context context, krb5_kdc_req *request)
1583 {
1584 krb5_error_code code;
1585 krb5_pa_data **padata;
1586 krb5_boolean retval = TRUE; /* default is to return PAC */
1587 krb5_data data;
1588 krb5_pa_pac_req *req = NULL;
1589
1590 if (request->padata == NULL) {
1591 return retval;
1592 }
1593
1594 for (padata = request->padata; *padata != NULL; padata++) {
1595 if ((*padata)->pa_type == KRB5_PADATA_PAC_REQUEST) {
1596 data.data = (char *)(*padata)->contents;
1597 data.length = (*padata)->length;
1598
1599 code = decode_krb5_pa_pac_req(&data, &req);
1600 if (code == 0) {
1601 retval = req->include_pac;
1602 krb5_free_pa_pac_req(context, req);
1603 req = NULL;
1604 }
1605 break;
1606 }
1607 }
1608
1609 return retval;
1610 }
1611
1612 static krb5_error_code
return_referral_enc_padata(krb5_context context,krb5_enc_kdc_rep_part * reply,krb5_db_entry * server)1613 return_referral_enc_padata( krb5_context context,
1614 krb5_enc_kdc_rep_part *reply,
1615 krb5_db_entry *server)
1616 {
1617 krb5_error_code code;
1618 krb5_tl_data tl_data;
1619 krb5_pa_data *pa;
1620
1621 tl_data.tl_data_type = KRB5_TL_SVR_REFERRAL_DATA;
1622 code = krb5_dbe_lookup_tl_data(context, server, &tl_data);
1623 if (code || tl_data.tl_data_length == 0)
1624 return 0;
1625
1626 code = k5_alloc_pa_data(KRB5_PADATA_SVR_REFERRAL_INFO,
1627 tl_data.tl_data_length, &pa);
1628 if (code)
1629 return code;
1630 memcpy(pa->contents, tl_data.tl_data_contents, tl_data.tl_data_length);
1631 code = k5_add_pa_data_element(&reply->enc_padata, &pa);
1632 k5_free_pa_data_element(pa);
1633 return code;
1634 }
1635
1636 krb5_error_code
return_enc_padata(krb5_context context,krb5_data * req_pkt,krb5_kdc_req * request,krb5_keyblock * reply_key,krb5_db_entry * server,krb5_enc_kdc_rep_part * reply_encpart,krb5_boolean is_referral)1637 return_enc_padata(krb5_context context, krb5_data *req_pkt,
1638 krb5_kdc_req *request, krb5_keyblock *reply_key,
1639 krb5_db_entry *server, krb5_enc_kdc_rep_part *reply_encpart,
1640 krb5_boolean is_referral)
1641 {
1642 krb5_error_code code = 0;
1643 /* This should be initialized and only used for Win2K compat and other
1644 * specific standardized uses such as FAST negotiation. */
1645 if (is_referral) {
1646 code = return_referral_enc_padata(context, reply_encpart, server);
1647 if (code)
1648 return code;
1649 }
1650 code = kdc_handle_protected_negotiation(context, req_pkt, request, reply_key,
1651 &reply_encpart->enc_padata);
1652 if (code)
1653 goto cleanup;
1654
1655 code = kdc_add_pa_pac_options(context, request,
1656 &reply_encpart->enc_padata);
1657 if (code)
1658 goto cleanup;
1659
1660 /*Add potentially other enc_padata providers*/
1661 cleanup:
1662 return code;
1663 }
1664