Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/crypto/krb5/src/kdc/kdc_preauth.c
34878 views
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
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
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
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
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
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
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
347
max_time_skew(krb5_context context, krb5_kdcpreauth_rock rock)
348
{
349
return context->clockskew;
350
}
351
352
static krb5_error_code
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
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 *
398
request_body(krb5_context context, krb5_kdcpreauth_rock rock)
399
{
400
return rock->inner_body;
401
}
402
403
static krb5_keyblock *
404
fast_armor(krb5_context context, krb5_kdcpreauth_rock rock)
405
{
406
return rock->rstate->armor_key;
407
}
408
409
static krb5_error_code
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
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 *
423
client_entry(krb5_context context, krb5_kdcpreauth_rock rock)
424
{
425
return rock->client;
426
}
427
428
static verto_ctx *
429
event_context(krb5_context context, krb5_kdcpreauth_rock rock)
430
{
431
return rock->vctx;
432
}
433
434
static krb5_boolean
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 *
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
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
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
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
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
502
client_name(krb5_context context, krb5_kdcpreauth_rock rock)
503
{
504
return rock->client->princ;
505
}
506
507
static void
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
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
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, &copy) != 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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
1665