Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/crypto/krb5/src/kdc/fast_util.c
34907 views
1
/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2
/* kdc/fast_util.c */
3
/*
4
* Copyright (C) 2009, 2015 by the Massachusetts Institute of Technology.
5
* 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
#include <k5-int.h>
28
29
#include "kdc_util.h"
30
#include "extern.h"
31
32
/* Let cookies be valid for ten minutes. */
33
#define COOKIE_LIFETIME 600
34
35
static krb5_error_code armor_ap_request
36
(struct kdc_request_state *state, krb5_fast_armor *armor)
37
{
38
krb5_error_code retval = 0;
39
krb5_auth_context authcontext = NULL;
40
krb5_ticket *ticket = NULL;
41
krb5_keyblock *subkey = NULL;
42
kdc_realm_t *realm = state->realm_data;
43
krb5_context context = realm->realm_context;
44
45
assert(armor->armor_type == KRB5_FAST_ARMOR_AP_REQUEST);
46
krb5_clear_error_message(context);
47
retval = krb5_auth_con_init(context, &authcontext);
48
/*disable replay cache*/
49
if (retval == 0)
50
retval = krb5_auth_con_setflags(context, authcontext, 0);
51
if (retval == 0)
52
retval = krb5_rd_req(context, &authcontext, &armor->armor_value,
53
NULL /*server*/, realm->realm_keytab,
54
NULL, &ticket);
55
if (retval != 0) {
56
const char * errmsg = krb5_get_error_message(context, retval);
57
k5_setmsg(context, retval, _("%s while handling ap-request armor"),
58
errmsg);
59
krb5_free_error_message(context, errmsg);
60
}
61
if (retval == 0) {
62
if (!krb5_principal_compare_any_realm(context, realm->realm_tgsprinc,
63
ticket->server)) {
64
k5_setmsg(context, KRB5KDC_ERR_SERVER_NOMATCH,
65
_("ap-request armor for something other than the local "
66
"TGS"));
67
retval = KRB5KDC_ERR_SERVER_NOMATCH;
68
}
69
}
70
if (retval == 0) {
71
retval = krb5_auth_con_getrecvsubkey(context, authcontext, &subkey);
72
if (retval != 0 || subkey == NULL) {
73
k5_setmsg(context, KRB5KDC_ERR_POLICY,
74
_("ap-request armor without subkey"));
75
retval = KRB5KDC_ERR_POLICY;
76
}
77
}
78
if (retval == 0)
79
retval = krb5_c_fx_cf2_simple(context,
80
subkey, "subkeyarmor",
81
ticket->enc_part2->session, "ticketarmor",
82
&state->armor_key);
83
if (ticket)
84
krb5_free_ticket(context, ticket);
85
if (subkey)
86
krb5_free_keyblock(context, subkey);
87
if (authcontext)
88
krb5_auth_con_free(context, authcontext);
89
return retval;
90
}
91
92
static krb5_error_code
93
encrypt_fast_reply(struct kdc_request_state *state,
94
const krb5_fast_response *response,
95
krb5_data **fx_fast_reply)
96
{
97
krb5_context context = state->realm_data->realm_context;
98
krb5_error_code retval = 0;
99
krb5_enc_data encrypted_reply;
100
krb5_data *encoded_response = NULL;
101
102
assert(state->armor_key);
103
retval = encode_krb5_fast_response(response, &encoded_response);
104
if (retval== 0)
105
retval = krb5_encrypt_helper(context, state->armor_key,
106
KRB5_KEYUSAGE_FAST_REP,
107
encoded_response, &encrypted_reply);
108
if (encoded_response)
109
krb5_free_data(context, encoded_response);
110
encoded_response = NULL;
111
if (retval == 0) {
112
retval = encode_krb5_pa_fx_fast_reply(&encrypted_reply,
113
fx_fast_reply);
114
krb5_free_data_contents(context, &encrypted_reply.ciphertext);
115
}
116
return retval;
117
}
118
119
120
/*
121
* This function will find the FAST padata and, if FAST is successfully
122
* processed, will free the outer request and update the pointer to point to
123
* the inner request. checksummed_data points to the data that is in the
124
* armored_fast_request checksum; either the pa-tgs-req or the kdc-req-body.
125
*/
126
krb5_error_code
127
kdc_find_fast(krb5_kdc_req **requestptr,
128
krb5_data *checksummed_data,
129
krb5_keyblock *tgs_subkey,
130
krb5_keyblock *tgs_session,
131
struct kdc_request_state *state,
132
krb5_data **inner_body_out)
133
{
134
krb5_context context = state->realm_data->realm_context;
135
krb5_error_code retval = 0;
136
krb5_pa_data *fast_padata;
137
krb5_data scratch, plaintext, *inner_body = NULL;
138
krb5_fast_req * fast_req = NULL;
139
krb5_kdc_req *request = *requestptr;
140
krb5_fast_armored_req *fast_armored_req = NULL;
141
krb5_checksum *cksum;
142
krb5_boolean cksum_valid;
143
krb5_keyblock empty_keyblock;
144
145
if (inner_body_out != NULL)
146
*inner_body_out = NULL;
147
scratch.data = NULL;
148
krb5_clear_error_message(context);
149
memset(&empty_keyblock, 0, sizeof(krb5_keyblock));
150
fast_padata = krb5int_find_pa_data(context, request->padata,
151
KRB5_PADATA_FX_FAST);
152
if (fast_padata != NULL){
153
scratch.length = fast_padata->length;
154
scratch.data = (char *) fast_padata->contents;
155
retval = decode_krb5_pa_fx_fast_request(&scratch, &fast_armored_req);
156
if (retval == 0 &&fast_armored_req->armor) {
157
switch (fast_armored_req->armor->armor_type) {
158
case KRB5_FAST_ARMOR_AP_REQUEST:
159
if (tgs_subkey) {
160
retval = KRB5KDC_ERR_PREAUTH_FAILED;
161
k5_setmsg(context, retval,
162
_("Ap-request armor not permitted with TGS"));
163
break;
164
}
165
retval = armor_ap_request(state, fast_armored_req->armor);
166
break;
167
default:
168
k5_setmsg(context, KRB5KDC_ERR_PREAUTH_FAILED,
169
_("Unknown FAST armor type %d"),
170
fast_armored_req->armor->armor_type);
171
retval = KRB5KDC_ERR_PREAUTH_FAILED;
172
}
173
}
174
if (retval == 0 && !state->armor_key) {
175
if (tgs_subkey)
176
retval = krb5_c_fx_cf2_simple(context,
177
tgs_subkey, "subkeyarmor",
178
tgs_session, "ticketarmor",
179
&state->armor_key);
180
else {
181
retval = KRB5KDC_ERR_PREAUTH_FAILED;
182
k5_setmsg(context, retval,
183
_("No armor key but FAST armored request present"));
184
}
185
}
186
if (retval == 0) {
187
plaintext.length = fast_armored_req->enc_part.ciphertext.length;
188
plaintext.data = k5alloc(plaintext.length, &retval);
189
}
190
if (retval == 0) {
191
retval = krb5_c_decrypt(context, state->armor_key,
192
KRB5_KEYUSAGE_FAST_ENC, NULL,
193
&fast_armored_req->enc_part,
194
&plaintext);
195
if (retval == 0)
196
retval = decode_krb5_fast_req(&plaintext, &fast_req);
197
if (retval == 0 && inner_body_out != NULL) {
198
retval = fetch_asn1_field((unsigned char *)plaintext.data,
199
1, 2, &scratch);
200
if (retval == 0) {
201
retval = krb5_copy_data(context, &scratch, &inner_body);
202
}
203
}
204
if (plaintext.data)
205
free(plaintext.data);
206
}
207
cksum = &fast_armored_req->req_checksum;
208
if (retval == 0)
209
retval = krb5_c_verify_checksum(context, state->armor_key,
210
KRB5_KEYUSAGE_FAST_REQ_CHKSUM,
211
checksummed_data, cksum,
212
&cksum_valid);
213
if (retval == 0 && !cksum_valid) {
214
retval = KRB5KRB_AP_ERR_MODIFIED;
215
k5_setmsg(context, retval,
216
_("FAST req_checksum invalid; request modified"));
217
}
218
if (retval == 0) {
219
if (!krb5_c_is_keyed_cksum(cksum->checksum_type)) {
220
retval = KRB5KDC_ERR_POLICY;
221
k5_setmsg(context, retval,
222
_("Unkeyed checksum used in fast_req"));
223
}
224
}
225
if (retval == 0) {
226
if ((fast_req->fast_options & UNSUPPORTED_CRITICAL_FAST_OPTIONS) != 0)
227
retval = KRB5KDC_ERR_UNKNOWN_CRITICAL_FAST_OPTION;
228
}
229
if (retval == 0) {
230
state->fast_options = fast_req->fast_options;
231
fast_req->req_body->msg_type = request->msg_type;
232
krb5_free_kdc_req(context, request);
233
*requestptr = fast_req->req_body;
234
fast_req->req_body = NULL;
235
}
236
}
237
if (retval == 0 && inner_body_out != NULL) {
238
*inner_body_out = inner_body;
239
inner_body = NULL;
240
}
241
krb5_free_data(context, inner_body);
242
if (fast_req)
243
krb5_free_fast_req(context, fast_req);
244
if (fast_armored_req)
245
krb5_free_fast_armored_req(context, fast_armored_req);
246
return retval;
247
}
248
249
250
krb5_error_code
251
kdc_make_rstate(kdc_realm_t *active_realm, struct kdc_request_state **out)
252
{
253
struct kdc_request_state *state = malloc( sizeof(struct kdc_request_state));
254
if (state == NULL)
255
return ENOMEM;
256
memset( state, 0, sizeof(struct kdc_request_state));
257
state->realm_data = active_realm;
258
*out = state;
259
return 0;
260
}
261
262
void
263
kdc_free_rstate (struct kdc_request_state *s)
264
{
265
if (s == NULL)
266
return;
267
if (s->armor_key)
268
krb5_free_keyblock(s->realm_data->realm_context, s->armor_key);
269
if (s->strengthen_key)
270
krb5_free_keyblock(s->realm_data->realm_context, s->strengthen_key);
271
k5_zapfree_pa_data(s->in_cookie_padata);
272
k5_zapfree_pa_data(s->out_cookie_padata);
273
free(s);
274
}
275
276
krb5_error_code
277
kdc_fast_response_handle_padata(struct kdc_request_state *state,
278
krb5_kdc_req *request,
279
krb5_kdc_rep *rep, krb5_enctype enctype)
280
{
281
krb5_context context = state->realm_data->realm_context;
282
krb5_error_code retval = 0;
283
krb5_fast_finished finish;
284
krb5_fast_response fast_response;
285
krb5_data *encoded_ticket = NULL;
286
krb5_data *encrypted_reply = NULL;
287
krb5_pa_data *pa = NULL, **pa_array = NULL;
288
krb5_cksumtype cksumtype = CKSUMTYPE_RSA_MD5;
289
krb5_pa_data *empty_padata[] = {NULL};
290
krb5_keyblock *strengthen_key = NULL;
291
292
if (!state->armor_key)
293
return 0;
294
memset(&finish, 0, sizeof(finish));
295
retval = krb5_init_keyblock(context, enctype, 0, &strengthen_key);
296
if (retval == 0)
297
retval = krb5_c_make_random_key(context, enctype, strengthen_key);
298
if (retval == 0) {
299
state->strengthen_key = strengthen_key;
300
strengthen_key = NULL;
301
}
302
303
fast_response.padata = rep->padata;
304
if (fast_response.padata == NULL)
305
fast_response.padata = &empty_padata[0];
306
fast_response.strengthen_key = state->strengthen_key;
307
fast_response.nonce = request->nonce;
308
fast_response.finished = &finish;
309
finish.client = rep->client;
310
pa_array = calloc(3, sizeof(*pa_array));
311
if (pa_array == NULL)
312
retval = ENOMEM;
313
pa = calloc(1, sizeof(krb5_pa_data));
314
if (retval == 0 && pa == NULL)
315
retval = ENOMEM;
316
if (retval == 0)
317
retval = krb5_us_timeofday(context, &finish.timestamp, &finish.usec);
318
if (retval == 0)
319
retval = encode_krb5_ticket(rep->ticket, &encoded_ticket);
320
if (retval == 0)
321
retval = krb5int_c_mandatory_cksumtype(context,
322
state->armor_key->enctype,
323
&cksumtype);
324
if (retval == 0)
325
retval = krb5_c_make_checksum(context, cksumtype, state->armor_key,
326
KRB5_KEYUSAGE_FAST_FINISHED,
327
encoded_ticket, &finish.ticket_checksum);
328
if (retval == 0)
329
retval = encrypt_fast_reply(state, &fast_response, &encrypted_reply);
330
if (retval == 0) {
331
pa[0].pa_type = KRB5_PADATA_FX_FAST;
332
pa[0].length = encrypted_reply->length;
333
pa[0].contents = (unsigned char *) encrypted_reply->data;
334
pa_array[0] = &pa[0];
335
krb5_free_pa_data(context, rep->padata);
336
rep->padata = pa_array;
337
pa_array = NULL;
338
free(encrypted_reply);
339
encrypted_reply = NULL;
340
pa = NULL;
341
}
342
if (pa)
343
free(pa);
344
if (pa_array)
345
free(pa_array);
346
if (encrypted_reply)
347
krb5_free_data(context, encrypted_reply);
348
if (encoded_ticket)
349
krb5_free_data(context, encoded_ticket);
350
if (strengthen_key != NULL)
351
krb5_free_keyblock(context, strengthen_key);
352
if (finish.ticket_checksum.contents)
353
krb5_free_checksum_contents(context, &finish.ticket_checksum);
354
return retval;
355
}
356
357
358
/*
359
* We assume the caller is responsible for passing us an in_padata
360
* sufficient to include in a FAST error. In the FAST case we will
361
* set *fast_edata_out to the edata to be included in the error; in
362
* the non-FAST case we will set it to NULL.
363
*/
364
krb5_error_code
365
kdc_fast_handle_error(krb5_context context,
366
struct kdc_request_state *state,
367
krb5_kdc_req *request,
368
krb5_pa_data **in_padata, krb5_error *err,
369
krb5_data **fast_edata_out)
370
{
371
krb5_error_code retval = 0;
372
krb5_fast_response resp;
373
krb5_error fx_error;
374
krb5_data *encoded_fx_error = NULL, *encrypted_reply = NULL;
375
krb5_pa_data pa[1];
376
krb5_pa_data *outer_pa[3];
377
krb5_pa_data **inner_pa = NULL;
378
size_t size = 0;
379
380
*fast_edata_out = NULL;
381
memset(outer_pa, 0, sizeof(outer_pa));
382
if (state->armor_key == NULL)
383
return 0;
384
fx_error = *err;
385
fx_error.e_data.data = NULL;
386
fx_error.e_data.length = 0;
387
for (size = 0; in_padata&&in_padata[size]; size++);
388
inner_pa = calloc(size + 2, sizeof(krb5_pa_data *));
389
if (inner_pa == NULL)
390
retval = ENOMEM;
391
if (retval == 0)
392
for (size=0; in_padata&&in_padata[size]; size++)
393
inner_pa[size] = in_padata[size];
394
if (retval == 0)
395
retval = encode_krb5_error(&fx_error, &encoded_fx_error);
396
if (retval == 0) {
397
pa[0].pa_type = KRB5_PADATA_FX_ERROR;
398
pa[0].length = encoded_fx_error->length;
399
pa[0].contents = (unsigned char *) encoded_fx_error->data;
400
inner_pa[size++] = &pa[0];
401
}
402
if (retval == 0) {
403
resp.padata = inner_pa;
404
resp.nonce = request->nonce;
405
resp.strengthen_key = NULL;
406
resp.finished = NULL;
407
}
408
if (retval == 0)
409
retval = encrypt_fast_reply(state, &resp, &encrypted_reply);
410
if (inner_pa)
411
free(inner_pa); /*contained storage from caller and our stack*/
412
if (retval == 0) {
413
pa[0].pa_type = KRB5_PADATA_FX_FAST;
414
pa[0].length = encrypted_reply->length;
415
pa[0].contents = (unsigned char *) encrypted_reply->data;
416
outer_pa[0] = &pa[0];
417
}
418
retval = encode_krb5_padata_sequence(outer_pa, fast_edata_out);
419
if (encrypted_reply)
420
krb5_free_data(context, encrypted_reply);
421
if (encoded_fx_error)
422
krb5_free_data(context, encoded_fx_error);
423
return retval;
424
}
425
426
krb5_error_code
427
kdc_fast_handle_reply_key(struct kdc_request_state *state,
428
krb5_keyblock *existing_key,
429
krb5_keyblock **out_key)
430
{
431
krb5_context context = state->realm_data->realm_context;
432
krb5_error_code retval = 0;
433
434
if (state->armor_key)
435
retval = krb5_c_fx_cf2_simple(context,
436
state->strengthen_key, "strengthenkey",
437
existing_key, "replykey", out_key);
438
else
439
retval = krb5_copy_keyblock(context, existing_key, out_key);
440
return retval;
441
}
442
443
krb5_boolean
444
kdc_fast_hide_client(struct kdc_request_state *state)
445
{
446
return (state->fast_options & KRB5_FAST_OPTION_HIDE_CLIENT_NAMES) != 0;
447
}
448
449
/* Create a pa-data entry with the specified type and contents. */
450
static krb5_error_code
451
make_padata(krb5_preauthtype pa_type, const void *contents, size_t len,
452
krb5_pa_data **out)
453
{
454
if (k5_alloc_pa_data(pa_type, len, out) != 0)
455
return ENOMEM;
456
memcpy((*out)->contents, contents, len);
457
return 0;
458
}
459
460
/*
461
* Derive the secure cookie encryption key from tgt_key and client_princ. The
462
* cookie key is derived with PRF+ using the concatenation of "COOKIE" and the
463
* unparsed client principal name as input.
464
*/
465
static krb5_error_code
466
derive_cookie_key(krb5_context context, krb5_keyblock *tgt_key,
467
krb5_const_principal client_princ, krb5_keyblock **key_out)
468
{
469
krb5_error_code ret;
470
krb5_data d;
471
char *princstr = NULL, *derive_input = NULL;
472
473
*key_out = NULL;
474
475
/* Construct the input string and derive the cookie key. */
476
ret = krb5_unparse_name(context, client_princ, &princstr);
477
if (ret)
478
goto cleanup;
479
if (asprintf(&derive_input, "COOKIE%s", princstr) < 0) {
480
ret = ENOMEM;
481
goto cleanup;
482
}
483
d = string2data(derive_input);
484
ret = krb5_c_derive_prfplus(context, tgt_key, &d, ENCTYPE_NULL, key_out);
485
486
cleanup:
487
krb5_free_unparsed_name(context, princstr);
488
free(derive_input);
489
return ret;
490
}
491
492
/* Derive the cookie key for the specified kvno in tgt. tgt_key must be the
493
* decrypted first key data entry in tgt. */
494
static krb5_error_code
495
get_cookie_key(krb5_context context, krb5_db_entry *tgt,
496
krb5_keyblock *tgt_key, krb5_kvno kvno,
497
krb5_const_principal client_princ, krb5_keyblock **key_out)
498
{
499
krb5_error_code ret;
500
krb5_keyblock storage, *key;
501
krb5_key_data *kd;
502
503
*key_out = NULL;
504
memset(&storage, 0, sizeof(storage));
505
506
if (kvno == current_kvno(tgt)) {
507
/* Use the already-decrypted first key. */
508
key = tgt_key;
509
} else {
510
/* The cookie used an older TGT key; find and decrypt it. */
511
ret = krb5_dbe_find_enctype(context, tgt, -1, -1, kvno, &kd);
512
if (ret)
513
return ret;
514
ret = krb5_dbe_decrypt_key_data(context, NULL, kd, &storage, NULL);
515
if (ret)
516
return ret;
517
key = &storage;
518
}
519
520
ret = derive_cookie_key(context, key, client_princ, key_out);
521
krb5_free_keyblock_contents(context, &storage);
522
return ret;
523
}
524
525
/* Return true if there is any overlap between padata types in cpadata
526
* (from the cookie) and rpadata (from the request). */
527
static krb5_boolean
528
is_relevant(krb5_pa_data *const *cpadata, krb5_pa_data *const *rpadata)
529
{
530
krb5_pa_data *const *p;
531
532
for (p = cpadata; p != NULL && *p != NULL; p++) {
533
if (krb5int_find_pa_data(NULL, rpadata, (*p)->pa_type) != NULL)
534
return TRUE;
535
}
536
return FALSE;
537
}
538
539
/*
540
* Locate and decode the FAST cookie in req, storing its contents in state for
541
* later access by preauth modules. If the cookie is expired, return
542
* KRB5KDC_ERR_PREAUTH_EXPIRED if its contents are relevant to req, and ignore
543
* it if they aren't.
544
*/
545
krb5_error_code
546
kdc_fast_read_cookie(krb5_context context, struct kdc_request_state *state,
547
krb5_kdc_req *req, krb5_db_entry *local_tgt,
548
krb5_keyblock *local_tgt_key)
549
{
550
krb5_error_code ret;
551
krb5_secure_cookie *cookie = NULL;
552
krb5_timestamp now;
553
krb5_keyblock *key = NULL;
554
krb5_enc_data enc;
555
krb5_pa_data *pa;
556
krb5_kvno kvno;
557
krb5_data plain = empty_data();
558
559
pa = krb5int_find_pa_data(context, req->padata, KRB5_PADATA_FX_COOKIE);
560
if (pa == NULL)
561
return 0;
562
563
/* If it's not an MIT version 1 cookie, ignore it. It may be an empty
564
* "MIT" cookie or a cookie generated by a different KDC implementation. */
565
if (pa->length <= 8 || memcmp(pa->contents, "MIT1", 4) != 0)
566
return 0;
567
568
/* Extract the kvno and generate the corresponding cookie key. */
569
kvno = load_32_be(pa->contents + 4);
570
ret = get_cookie_key(context, local_tgt, local_tgt_key, kvno, req->client,
571
&key);
572
if (ret)
573
goto cleanup;
574
575
/* Decrypt and decode the cookie. */
576
memset(&enc, 0, sizeof(enc));
577
enc.enctype = key->enctype;
578
enc.ciphertext = make_data(pa->contents + 8, pa->length - 8);
579
ret = alloc_data(&plain, pa->length - 8);
580
if (ret)
581
goto cleanup;
582
ret = krb5_c_decrypt(context, key, KRB5_KEYUSAGE_PA_FX_COOKIE, NULL, &enc,
583
&plain);
584
if (ret)
585
goto cleanup;
586
ret = decode_krb5_secure_cookie(&plain, &cookie);
587
if (ret)
588
goto cleanup;
589
590
/* Check if the cookie is expired. */
591
ret = krb5_timeofday(context, &now);
592
if (ret)
593
goto cleanup;
594
if (ts2tt(now) > cookie->time + COOKIE_LIFETIME) {
595
/* Don't accept the cookie contents. Only return an error if the
596
* cookie is relevant to the request. */
597
if (is_relevant(cookie->data, req->padata))
598
ret = KRB5KDC_ERR_PREAUTH_EXPIRED;
599
goto cleanup;
600
}
601
602
/* Steal the pa-data list pointer from the cookie and store it in state. */
603
state->in_cookie_padata = cookie->data;
604
cookie->data = NULL;
605
606
cleanup:
607
zapfree(plain.data, plain.length);
608
krb5_free_keyblock(context, key);
609
k5_free_secure_cookie(context, cookie);
610
return 0;
611
}
612
613
/* If state contains a cookie value for pa_type, set *out to the corresponding
614
* data and return true. Otherwise set *out to empty and return false. */
615
krb5_boolean
616
kdc_fast_search_cookie(struct kdc_request_state *state,
617
krb5_preauthtype pa_type, krb5_data *out)
618
{
619
krb5_pa_data *pa;
620
621
pa = krb5int_find_pa_data(NULL, state->in_cookie_padata, pa_type);
622
if (pa == NULL) {
623
*out = empty_data();
624
return FALSE;
625
} else {
626
*out = make_data(pa->contents, pa->length);
627
return TRUE;
628
}
629
}
630
631
/* Set a cookie value in state for data, to be included in the outgoing
632
* cookie. Duplicate values are ignored. */
633
krb5_error_code
634
kdc_fast_set_cookie(struct kdc_request_state *state, krb5_preauthtype pa_type,
635
const krb5_data *data)
636
{
637
krb5_pa_data **list = state->out_cookie_padata;
638
size_t count;
639
640
for (count = 0; list != NULL && list[count] != NULL; count++) {
641
if (list[count]->pa_type == pa_type)
642
return 0;
643
}
644
645
list = realloc(list, (count + 2) * sizeof(*list));
646
if (list == NULL)
647
return ENOMEM;
648
state->out_cookie_padata = list;
649
list[count] = list[count + 1] = NULL;
650
return make_padata(pa_type, data->data, data->length, &list[count]);
651
}
652
653
/* Construct a cookie pa-data item using the cookie values from state, or a
654
* trivial "MIT" cookie if no values are set. */
655
krb5_error_code
656
kdc_fast_make_cookie(krb5_context context, struct kdc_request_state *state,
657
krb5_db_entry *local_tgt, krb5_keyblock *local_tgt_key,
658
krb5_const_principal client_princ,
659
krb5_pa_data **cookie_out)
660
{
661
krb5_error_code ret;
662
krb5_secure_cookie cookie;
663
krb5_pa_data **contents = state->out_cookie_padata, *pa;
664
krb5_keyblock *key = NULL;
665
krb5_timestamp now;
666
krb5_enc_data enc;
667
krb5_data *der_cookie = NULL;
668
size_t ctlen;
669
670
*cookie_out = NULL;
671
memset(&enc, 0, sizeof(enc));
672
673
/* Make a trivial cookie if there are no contents to marshal or we don't
674
* have a TGT entry to encrypt them. */
675
if (contents == NULL || *contents == NULL || local_tgt_key == NULL)
676
return make_padata(KRB5_PADATA_FX_COOKIE, "MIT", 3, cookie_out);
677
678
ret = derive_cookie_key(context, local_tgt_key, client_princ, &key);
679
if (ret)
680
goto cleanup;
681
682
/* Encode the cookie. */
683
ret = krb5_timeofday(context, &now);
684
if (ret)
685
goto cleanup;
686
cookie.time = ts2tt(now);
687
cookie.data = contents;
688
ret = encode_krb5_secure_cookie(&cookie, &der_cookie);
689
if (ret)
690
goto cleanup;
691
692
/* Encrypt the cookie in key. */
693
ret = krb5_c_encrypt_length(context, key->enctype, der_cookie->length,
694
&ctlen);
695
if (ret)
696
goto cleanup;
697
ret = alloc_data(&enc.ciphertext, ctlen);
698
if (ret)
699
goto cleanup;
700
ret = krb5_c_encrypt(context, key, KRB5_KEYUSAGE_PA_FX_COOKIE, NULL,
701
der_cookie, &enc);
702
if (ret)
703
goto cleanup;
704
705
/* Construct the cookie pa-data entry. */
706
ret = k5_alloc_pa_data(KRB5_PADATA_FX_COOKIE, 8 + enc.ciphertext.length,
707
&pa);
708
memcpy(pa->contents, "MIT1", 4);
709
store_32_be(current_kvno(local_tgt), pa->contents + 4);
710
memcpy(pa->contents + 8, enc.ciphertext.data, enc.ciphertext.length);
711
*cookie_out = pa;
712
713
cleanup:
714
krb5_free_keyblock(context, key);
715
if (der_cookie != NULL) {
716
zapfree(der_cookie->data, der_cookie->length);
717
free(der_cookie);
718
}
719
krb5_free_data_contents(context, &enc.ciphertext);
720
return ret;
721
}
722
723