1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* lib/krb5/krb/fast.c */
3 /*
4 * Copyright (C) 2009 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 #include "int-proto.h"
29
30 /*
31 * It is possible to support sending a request that includes both a FAST and
32 * normal version. This would complicate the pre-authentication logic
33 * significantly. You would need to maintain two contexts, one for FAST and
34 * one for normal use. In adition, you would need to manage the security
35 * issues surrounding downgrades. However trying FAST at all requires an armor
36 * key. Generally in obtaining the armor key, the client learns enough to know
37 * that FAST is supported. If not, the client can see FAST in the
38 * preauth_required error's padata and retry with FAST. So, this
39 * implementation does not support FAST+normal.
40 *
41 * We store the outer version of the request to use. The caller stores the
42 * inner version. We handle the encoding of the request body (and request) and
43 * provide encoded request bodies for the caller to use as these may be used
44 * for checksums. In the AS case we also evaluate whether to continue a
45 * conversation as one of the important questions there is the presence of a
46 * cookie.
47 */
48 #include "fast.h"
49 #include "int-proto.h"
50
51 static krb5_error_code
fast_armor_ap_request(krb5_context context,struct krb5int_fast_request_state * state,krb5_ccache ccache,krb5_principal target_principal)52 fast_armor_ap_request(krb5_context context,
53 struct krb5int_fast_request_state *state,
54 krb5_ccache ccache, krb5_principal target_principal)
55 {
56 krb5_error_code retval = 0;
57 krb5_creds creds, *out_creds = NULL;
58 krb5_auth_context authcontext = NULL;
59 krb5_data encoded_authenticator;
60 krb5_fast_armor *armor = NULL;
61 krb5_keyblock *subkey = NULL, *armor_key = NULL;
62
63 encoded_authenticator.data = NULL;
64 memset(&creds, 0, sizeof(creds));
65 creds.server = target_principal;
66 retval = krb5_cc_get_principal(context, ccache, &creds.client);
67 if (retval == 0)
68 retval = krb5_get_credentials(context, 0, ccache, &creds, &out_creds);
69 if (retval == 0) {
70 TRACE_FAST_ARMOR_CCACHE_KEY(context, &out_creds->keyblock);
71 retval = krb5_mk_req_extended(context, &authcontext,
72 AP_OPTS_USE_SUBKEY, NULL /*data*/,
73 out_creds, &encoded_authenticator);
74 }
75 if (retval == 0)
76 retval = krb5_auth_con_getsendsubkey(context, authcontext, &subkey);
77 if (retval == 0)
78 retval = krb5_c_fx_cf2_simple(context, subkey, "subkeyarmor",
79 &out_creds->keyblock, "ticketarmor",
80 &armor_key);
81 if (retval == 0) {
82 TRACE_FAST_ARMOR_KEY(context, armor_key);
83 armor = calloc(1, sizeof(krb5_fast_armor));
84 if (armor == NULL)
85 retval = ENOMEM;
86 }
87 if (retval == 0) {
88 armor->armor_type = KRB5_FAST_ARMOR_AP_REQUEST;
89 armor->armor_value = encoded_authenticator;
90 encoded_authenticator.data = NULL;
91 encoded_authenticator.length = 0;
92 state->armor = armor;
93 armor = NULL;
94 state->armor_key = armor_key;
95 armor_key = NULL;
96 }
97 krb5_free_keyblock(context, armor_key);
98 krb5_free_keyblock(context, subkey);
99 if (out_creds)
100 krb5_free_creds(context, out_creds);
101 /* target_principal is owned by caller. */
102 creds.server = NULL;
103 krb5_free_cred_contents(context, &creds);
104 if (encoded_authenticator.data)
105 krb5_free_data_contents(context, &encoded_authenticator);
106 krb5_auth_con_free(context, authcontext);
107 return retval;
108 }
109
110 krb5_error_code
krb5int_fast_tgs_armor(krb5_context context,struct krb5int_fast_request_state * state,krb5_keyblock * subkey,krb5_keyblock * session_key,krb5_ccache ccache,krb5_data * target_realm)111 krb5int_fast_tgs_armor(krb5_context context,
112 struct krb5int_fast_request_state *state,
113 krb5_keyblock *subkey, krb5_keyblock *session_key,
114 krb5_ccache ccache, krb5_data *target_realm)
115 {
116 krb5_principal target_principal = NULL;
117 krb5_keyblock *existing_armor = NULL;
118 krb5_error_code retval = 0;
119
120 if (ccache) {
121 retval = krb5int_tgtname(context, target_realm, target_realm,
122 &target_principal);
123 if (retval == 0)
124 retval = fast_armor_ap_request(context, state, ccache,
125 target_principal);
126 if (retval == 0) {
127 existing_armor = state->armor_key;
128 state->armor_key = NULL;
129 retval = krb5_c_fx_cf2_simple(context, existing_armor,
130 "explicitarmor", subkey,
131 "tgsarmor", &state->armor_key);
132 }
133 } else {
134 retval = krb5_c_fx_cf2_simple(context, subkey, "subkeyarmor",
135 session_key, "ticketarmor",
136 &state->armor_key);
137 }
138 if (target_principal)
139 krb5_free_principal(context, target_principal);
140 krb5_free_keyblock(context, existing_armor);
141 return retval;
142 }
143
144 krb5_error_code
krb5int_fast_prep_req_body(krb5_context context,struct krb5int_fast_request_state * state,krb5_kdc_req * request,krb5_data ** encoded_request_body)145 krb5int_fast_prep_req_body(krb5_context context,
146 struct krb5int_fast_request_state *state,
147 krb5_kdc_req *request,
148 krb5_data **encoded_request_body)
149 {
150 krb5_error_code retval = 0;
151 krb5_data *local_encoded_request_body = NULL;
152
153 assert(state != NULL);
154 *encoded_request_body = NULL;
155 if (state->armor_key == NULL)
156 return encode_krb5_kdc_req_body(request, encoded_request_body);
157 state->fast_outer_request = *request;
158 state->fast_outer_request.padata = NULL;
159 if (retval == 0)
160 retval = encode_krb5_kdc_req_body(&state->fast_outer_request,
161 &local_encoded_request_body);
162 if (retval == 0) {
163 *encoded_request_body = local_encoded_request_body;
164 local_encoded_request_body = NULL;
165 }
166 if (local_encoded_request_body != NULL)
167 krb5_free_data(context, local_encoded_request_body);
168 return retval;
169 }
170
171 krb5_error_code
krb5int_fast_as_armor(krb5_context context,struct krb5int_fast_request_state * state,krb5_get_init_creds_opt * opt,krb5_kdc_req * request)172 krb5int_fast_as_armor(krb5_context context,
173 struct krb5int_fast_request_state *state,
174 krb5_get_init_creds_opt *opt, krb5_kdc_req *request)
175 {
176 krb5_error_code retval = 0;
177 krb5_ccache ccache = NULL;
178 krb5_principal target_principal = NULL;
179 krb5_data *target_realm;
180 const char *ccname = k5_gic_opt_get_fast_ccache_name(opt);
181 krb5_flags fast_flags;
182
183 krb5_clear_error_message(context);
184 target_realm = &request->server->realm;
185 if (ccname != NULL) {
186 TRACE_FAST_ARMOR_CCACHE(context, ccname);
187 state->fast_state_flags |= KRB5INT_FAST_ARMOR_AVAIL;
188 retval = krb5_cc_resolve(context, ccname, &ccache);
189 if (retval == 0) {
190 retval = krb5int_tgtname(context, target_realm, target_realm,
191 &target_principal);
192 }
193 if (retval == 0) {
194 krb5_data config_data;
195 config_data.data = NULL;
196 retval = krb5_cc_get_config(context, ccache, target_principal,
197 KRB5_CC_CONF_FAST_AVAIL, &config_data);
198 if ((retval == 0) && config_data.data) {
199 TRACE_FAST_CCACHE_CONFIG(context);
200 state->fast_state_flags |= KRB5INT_FAST_DO_FAST;
201 }
202 krb5_free_data_contents(context, &config_data);
203 retval = 0;
204 }
205 fast_flags = k5_gic_opt_get_fast_flags(opt);
206 if (fast_flags & KRB5_FAST_REQUIRED) {
207 TRACE_FAST_REQUIRED(context);
208 state->fast_state_flags |= KRB5INT_FAST_DO_FAST;
209 }
210 if (retval == 0 && (state->fast_state_flags & KRB5INT_FAST_DO_FAST)) {
211 retval = fast_armor_ap_request(context, state, ccache,
212 target_principal);
213 }
214 if (retval != 0) {
215 k5_prependmsg(context, retval,
216 _("Error constructing AP-REQ armor"));
217 }
218 }
219 if (ccache)
220 krb5_cc_close(context, ccache);
221 if (target_principal)
222 krb5_free_principal(context, target_principal);
223 return retval;
224 }
225
226 /*
227 * Construct a list of outer request padata for a TGS request. Since we do
228 * FAST TGS even when we don't have reason to believe the KDC supports FAST,
229 * the outer padata has to contain duplicates of the inner padata (such as
230 * S4U2Self padata) as well as the PA-TGS-REQ and PA-FX-FAST padata. The
231 * caller must free *out_padata with free() as it is not a deep copy.
232 */
233 static krb5_error_code
make_tgs_outer_padata(krb5_pa_data * tgs,krb5_pa_data * fast,krb5_pa_data ** other,krb5_pa_data *** out_padata)234 make_tgs_outer_padata(krb5_pa_data *tgs, krb5_pa_data *fast,
235 krb5_pa_data **other, krb5_pa_data ***out_padata)
236 {
237 krb5_pa_data **pa_list;
238 size_t i;
239
240 *out_padata = NULL;
241 for (i = 0; other[i] != NULL; i++);
242 pa_list = calloc(i + 3, sizeof(*pa_list));
243 if (pa_list == NULL)
244 return ENOMEM;
245 pa_list[0] = tgs;
246 pa_list[1] = fast;
247 for (i = 0; other[i] != NULL; i++)
248 pa_list[i + 2] = other[i];
249 *out_padata = pa_list;
250 return 0;
251 }
252
253 krb5_error_code
krb5int_fast_prep_req(krb5_context context,struct krb5int_fast_request_state * state,krb5_kdc_req * request,const krb5_data * to_be_checksummed,kdc_req_encoder_proc encoder,krb5_data ** encoded_request)254 krb5int_fast_prep_req(krb5_context context,
255 struct krb5int_fast_request_state *state,
256 krb5_kdc_req *request,
257 const krb5_data *to_be_checksummed,
258 kdc_req_encoder_proc encoder,
259 krb5_data **encoded_request)
260 {
261 krb5_error_code retval = 0;
262 krb5_pa_data *pa_array[2], **pa_tgs_array = NULL;
263 krb5_pa_data pa[2];
264 krb5_fast_req fast_req;
265 krb5_pa_data *tgs = NULL;
266 krb5_fast_armored_req *armored_req = NULL;
267 krb5_data *encoded_fast_req = NULL;
268 krb5_data *encoded_armored_req = NULL;
269 krb5_data *local_encoded_result = NULL;
270 int i, j;
271
272 assert(state != NULL);
273 assert(state->fast_outer_request.padata == NULL);
274 memset(pa_array, 0, sizeof(pa_array));
275 if (state->armor_key == NULL) {
276 return encoder(request, encoded_request);
277 }
278
279 TRACE_FAST_ENCODE(context);
280 state->nonce = request->nonce;
281 fast_req.req_body = request;
282 if (fast_req.req_body->padata == NULL) {
283 fast_req.req_body->padata = calloc(1, sizeof(krb5_pa_data *));
284 if (fast_req.req_body->padata == NULL)
285 retval = ENOMEM;
286 }
287 fast_req.fast_options = state->fast_options;
288 if (retval == 0
289 && (tgs = krb5int_find_pa_data(context, fast_req.req_body->padata,
290 KRB5_PADATA_AP_REQ)) != NULL) {
291 krb5_pa_data **paptr = &fast_req.req_body->padata[0];
292 for (i = 0, j = 0; paptr[j] != NULL; j++) {
293 if (paptr[j]->pa_type == KRB5_PADATA_AP_REQ)
294 paptr[j] = NULL;
295 else
296 paptr[i++] = paptr[j];
297 }
298 paptr[i] = NULL;
299 }
300 if (retval == 0)
301 retval = encode_krb5_fast_req(&fast_req, &encoded_fast_req);
302 if (retval == 0) {
303 armored_req = calloc(1, sizeof(krb5_fast_armored_req));
304 if (armored_req == NULL)
305 retval = ENOMEM;
306 }
307 if (retval == 0)
308 armored_req->armor = state->armor;
309 if (retval ==0)
310 retval = krb5_c_make_checksum(context, 0, state->armor_key,
311 KRB5_KEYUSAGE_FAST_REQ_CHKSUM,
312 to_be_checksummed,
313 &armored_req->req_checksum);
314 if (retval == 0)
315 retval = krb5_encrypt_helper(context, state->armor_key,
316 KRB5_KEYUSAGE_FAST_ENC, encoded_fast_req,
317 &armored_req->enc_part);
318 if (retval == 0)
319 retval = encode_krb5_pa_fx_fast_request(armored_req,
320 &encoded_armored_req);
321 if (retval == 0) {
322 pa[0].pa_type = KRB5_PADATA_FX_FAST;
323 pa[0].contents = (unsigned char *) encoded_armored_req->data;
324 pa[0].length = encoded_armored_req->length;
325 if (tgs) {
326 retval = make_tgs_outer_padata(tgs, pa, request->padata,
327 &pa_tgs_array);
328 state->fast_outer_request.padata = pa_tgs_array;
329 } else {
330 pa_array[0] = &pa[0];
331 state->fast_outer_request.padata = pa_array;
332 }
333 }
334 if (retval == 0)
335 retval = encoder(&state->fast_outer_request, &local_encoded_result);
336 if (retval == 0) {
337 *encoded_request = local_encoded_result;
338 local_encoded_result = NULL;
339 }
340 if (encoded_armored_req)
341 krb5_free_data(context, encoded_armored_req);
342 if (armored_req) {
343 armored_req->armor = NULL; /*owned by state*/
344 krb5_free_fast_armored_req(context, armored_req);
345 }
346 if (encoded_fast_req)
347 krb5_free_data(context, encoded_fast_req);
348 if (local_encoded_result)
349 krb5_free_data(context, local_encoded_result);
350 if (tgs) {
351 free(tgs->contents);
352 free(tgs);
353 }
354 state->fast_outer_request.padata = NULL;
355 free(pa_tgs_array);
356 return retval;
357 }
358
359 static krb5_error_code
decrypt_fast_reply(krb5_context context,struct krb5int_fast_request_state * state,krb5_pa_data ** in_padata,krb5_fast_response ** response)360 decrypt_fast_reply(krb5_context context,
361 struct krb5int_fast_request_state *state,
362 krb5_pa_data **in_padata,
363 krb5_fast_response **response)
364 {
365 krb5_error_code retval = 0;
366 krb5_data scratch;
367 krb5_enc_data *encrypted_response = NULL;
368 krb5_pa_data *fx_reply = NULL;
369 krb5_fast_response *local_resp = NULL;
370
371 assert(state != NULL);
372 assert(state->armor_key);
373 fx_reply = krb5int_find_pa_data(context, in_padata, KRB5_PADATA_FX_FAST);
374 if (fx_reply == NULL)
375 retval = KRB5_ERR_FAST_REQUIRED;
376 TRACE_FAST_DECODE(context);
377 if (retval == 0) {
378 scratch.data = (char *) fx_reply->contents;
379 scratch.length = fx_reply->length;
380 retval = decode_krb5_pa_fx_fast_reply(&scratch, &encrypted_response);
381 }
382 scratch.data = NULL;
383 if (retval == 0) {
384 scratch.data = malloc(encrypted_response->ciphertext.length);
385 if (scratch.data == NULL)
386 retval = ENOMEM;
387 scratch.length = encrypted_response->ciphertext.length;
388 }
389 if (retval == 0)
390 retval = krb5_c_decrypt(context, state->armor_key,
391 KRB5_KEYUSAGE_FAST_REP, NULL,
392 encrypted_response, &scratch);
393 if (retval != 0)
394 k5_prependmsg(context, retval, _("Failed to decrypt FAST reply"));
395 if (retval == 0)
396 retval = decode_krb5_fast_response(&scratch, &local_resp);
397 if (retval == 0) {
398 if (local_resp->nonce != state->nonce) {
399 retval = KRB5_KDCREP_MODIFIED;
400 k5_setmsg(context, retval, _("nonce modified in FAST response: "
401 "KDC response modified"));
402 }
403 }
404 if (retval == 0) {
405 *response = local_resp;
406 local_resp = NULL;
407 }
408 if (scratch.data)
409 free(scratch.data);
410 if (encrypted_response)
411 krb5_free_enc_data(context, encrypted_response);
412 if (local_resp)
413 krb5_free_fast_response(context, local_resp);
414 return retval;
415 }
416
417 /*
418 * If state contains an armor key and *err_replyptr contains a FAST error,
419 * decode it and set *err_replyptr to the inner error and *out_padata to the
420 * padata in the FAST response. Otherwise, leave *err_replyptr alone and set
421 * *out_padata to the error e_data decoded as pa-data or typed-data, or to NULL
422 * if it doesn't decode as either. In either case, set *retry to indicate
423 * whether the client should try to make a follow-up request.
424 */
425 krb5_error_code
krb5int_fast_process_error(krb5_context context,struct krb5int_fast_request_state * state,krb5_error ** err_replyptr,krb5_pa_data *** out_padata,krb5_boolean * retry)426 krb5int_fast_process_error(krb5_context context,
427 struct krb5int_fast_request_state *state,
428 krb5_error **err_replyptr,
429 krb5_pa_data ***out_padata,
430 krb5_boolean *retry)
431 {
432 krb5_error_code retval = 0;
433 krb5_error *err_reply = *err_replyptr;
434 krb5_pa_data *fx_error_pa;
435 krb5_pa_data **result = NULL;
436 krb5_data scratch = empty_data();
437 krb5_error *fx_error = NULL;
438 krb5_fast_response *fast_response = NULL;
439
440 if (out_padata)
441 *out_padata = NULL;
442 if (retry)
443 *retry = 0;
444
445 if (state->armor_key) {
446 retval = decode_krb5_padata_sequence(&err_reply->e_data, &result);
447 if (retval == 0)
448 retval = decrypt_fast_reply(context, state, result,
449 &fast_response);
450 if (retval) {
451 /*
452 * This can happen if the KDC does not understand FAST. We don't
453 * expect that, but treating it as the fatal error indicated by the
454 * KDC seems reasonable.
455 */
456 if (retry != NULL)
457 *retry = 0;
458 krb5_free_pa_data(context, result);
459 return 0;
460 }
461 if (retval == 0) {
462 fx_error_pa = krb5int_find_pa_data(context, fast_response->padata,
463 KRB5_PADATA_FX_ERROR);
464 if (fx_error_pa == NULL) {
465 k5_setmsg(context, KRB5KDC_ERR_PREAUTH_FAILED,
466 _("Expecting FX_ERROR pa-data inside FAST "
467 "container"));
468 retval = KRB5KDC_ERR_PREAUTH_FAILED;
469 }
470 }
471 if (retval == 0) {
472 scratch = make_data(fx_error_pa->contents, fx_error_pa->length);
473 retval = decode_krb5_error(&scratch, &fx_error);
474 }
475 if (retval == 0) {
476 krb5_free_error(context, err_reply);
477 *err_replyptr = fx_error;
478 fx_error = NULL;
479 if (out_padata) {
480 *out_padata = fast_response->padata;
481 fast_response->padata = NULL;
482 }
483 /*
484 * If there is more than the fx_error padata, then we want
485 * to retry the error if a cookie is present
486 */
487 if (retry != NULL) {
488 *retry = (*out_padata)[1] != NULL;
489 if (krb5int_find_pa_data(context, *out_padata,
490 KRB5_PADATA_FX_COOKIE) == NULL)
491 *retry = 0;
492 }
493 }
494 } else { /*not FAST*/
495 /* Possibly retry if there's any e_data to process. */
496 if (retry)
497 *retry = (err_reply->e_data.length > 0);
498 /* Try to decode e_data as pa-data or typed-data for out_padata. */
499 if (out_padata) {
500 retval = decode_krb5_padata_sequence(&err_reply->e_data,
501 out_padata);
502 if (retval != 0) {
503 (void)decode_krb5_typed_data(&err_reply->e_data, out_padata);
504 retval = 0;
505 }
506 }
507 }
508 krb5_free_pa_data(context, result);
509 krb5_free_fast_response(context, fast_response);
510 if (fx_error)
511 krb5_free_error(context, fx_error);
512 return retval;
513 }
514
515
516 krb5_error_code
krb5int_fast_process_response(krb5_context context,struct krb5int_fast_request_state * state,krb5_kdc_rep * resp,krb5_keyblock ** strengthen_key)517 krb5int_fast_process_response(krb5_context context,
518 struct krb5int_fast_request_state *state,
519 krb5_kdc_rep *resp,
520 krb5_keyblock **strengthen_key)
521 {
522 krb5_error_code retval = 0;
523 krb5_fast_response *fast_response = NULL;
524 krb5_data *encoded_ticket = NULL;
525 krb5_boolean cksum_valid;
526
527 krb5_clear_error_message(context);
528 *strengthen_key = NULL;
529 if (state->armor_key == 0)
530 return 0;
531 retval = decrypt_fast_reply(context, state, resp->padata,
532 &fast_response);
533 if (retval == 0) {
534 if (fast_response->finished == 0) {
535 retval = KRB5_KDCREP_MODIFIED;
536 k5_setmsg(context, retval,
537 _("FAST response missing finish message in KDC reply"));
538 }
539 }
540 if (retval == 0)
541 retval = encode_krb5_ticket(resp->ticket, &encoded_ticket);
542 if (retval == 0)
543 retval = krb5_c_verify_checksum(context, state->armor_key,
544 KRB5_KEYUSAGE_FAST_FINISHED,
545 encoded_ticket,
546 &fast_response->finished->ticket_checksum,
547 &cksum_valid);
548 if (retval == 0 && cksum_valid == 0) {
549 retval = KRB5_KDCREP_MODIFIED;
550 k5_setmsg(context, retval, _("Ticket modified in KDC reply"));
551 }
552 if (retval == 0) {
553 krb5_free_principal(context, resp->client);
554 resp->client = fast_response->finished->client;
555 fast_response->finished->client = NULL;
556 *strengthen_key = fast_response->strengthen_key;
557 fast_response->strengthen_key = NULL;
558 krb5_free_pa_data(context, resp->padata);
559 resp->padata = fast_response->padata;
560 fast_response->padata = NULL;
561 }
562 if (fast_response)
563 krb5_free_fast_response(context, fast_response);
564 if (encoded_ticket)
565 krb5_free_data(context, encoded_ticket);
566 return retval;
567 }
568
569 krb5_error_code
krb5int_fast_reply_key(krb5_context context,const krb5_keyblock * strengthen_key,const krb5_keyblock * existing_key,krb5_keyblock * out_key)570 krb5int_fast_reply_key(krb5_context context,
571 const krb5_keyblock *strengthen_key,
572 const krb5_keyblock *existing_key,
573 krb5_keyblock *out_key)
574 {
575 krb5_keyblock *key = NULL;
576 krb5_error_code retval = 0;
577 krb5_free_keyblock_contents(context, out_key);
578 if (strengthen_key) {
579 retval = krb5_c_fx_cf2_simple(context, (krb5_keyblock *)strengthen_key,
580 "strengthenkey",
581 (krb5_keyblock *)existing_key,
582 "replykey", &key);
583 if (retval == 0) {
584 TRACE_FAST_REPLY_KEY(context, key);
585 *out_key = *key;
586 free(key);
587 }
588 } else {
589 retval = krb5_copy_keyblock_contents(context, existing_key, out_key);
590 }
591 return retval;
592 }
593
594
595 krb5_error_code
krb5int_fast_make_state(krb5_context context,struct krb5int_fast_request_state ** state)596 krb5int_fast_make_state(krb5_context context,
597 struct krb5int_fast_request_state **state)
598 {
599 struct krb5int_fast_request_state *local_state ;
600
601 local_state = malloc(sizeof *local_state);
602 if (local_state == NULL)
603 return ENOMEM;
604 memset(local_state, 0, sizeof(*local_state));
605 *state = local_state;
606 return 0;
607 }
608
609 void
krb5int_fast_free_state(krb5_context context,struct krb5int_fast_request_state * state)610 krb5int_fast_free_state(krb5_context context,
611 struct krb5int_fast_request_state *state)
612 {
613 if (state == NULL)
614 return;
615 /*We are responsible for none of the store in the fast_outer_req*/
616 krb5_free_keyblock(context, state->armor_key);
617 krb5_free_fast_armor(context, state->armor);
618 free(state);
619 }
620
621 /*
622 * Implement FAST negotiation as specified in RFC 6806 section 11. If
623 * the encrypted part of rep sets the enc-pa-rep flag, look for and
624 * verify a PA-REQ-ENC-PA-REP entry in the encrypted padata. If a
625 * PA-FX-FAST entry is also present in the encrypted padata, set
626 * *fast_avail to true. This will result in a fast_avail config entry
627 * being written to the credential cache, if an output ccache was
628 * specified using krb5_get_init_creds_opt_set_out_ccache(). That
629 * entry will be detected in the armor ccache by
630 * krb5int_fast_as_armor(), allowing us to use FAST without a
631 * round-trip for the KDC to indicate support, and without a downgrade
632 * attack.
633 */
634 krb5_error_code
krb5int_fast_verify_nego(krb5_context context,struct krb5int_fast_request_state * state,krb5_kdc_rep * rep,krb5_data * request,krb5_keyblock * decrypting_key,krb5_boolean * fast_avail)635 krb5int_fast_verify_nego(krb5_context context,
636 struct krb5int_fast_request_state *state,
637 krb5_kdc_rep *rep, krb5_data *request,
638 krb5_keyblock *decrypting_key,
639 krb5_boolean *fast_avail)
640 {
641 krb5_error_code retval = 0;
642 krb5_checksum *checksum = NULL;
643 krb5_pa_data *pa;
644 krb5_data scratch;
645 krb5_boolean valid;
646
647 *fast_avail = FALSE;
648 if (rep->enc_part2->flags& TKT_FLG_ENC_PA_REP) {
649 pa = krb5int_find_pa_data(context, rep->enc_part2->enc_padata,
650 KRB5_ENCPADATA_REQ_ENC_PA_REP);
651 if (pa == NULL)
652 retval = KRB5_KDCREP_MODIFIED;
653 else {
654 scratch.data = (char *) pa->contents;
655 scratch.length = pa->length;
656 }
657 if (retval == 0)
658 retval = decode_krb5_checksum(&scratch, &checksum);
659 if (retval == 0)
660 retval = krb5_c_verify_checksum(context, decrypting_key,
661 KRB5_KEYUSAGE_AS_REQ,
662 request, checksum, &valid);
663 if (retval == 0 &&valid == 0)
664 retval = KRB5_KDCREP_MODIFIED;
665 if (retval == 0) {
666 pa = krb5int_find_pa_data(context, rep->enc_part2->enc_padata,
667 KRB5_PADATA_FX_FAST);
668 *fast_avail = (pa != NULL);
669 }
670 }
671 TRACE_FAST_NEGO(context, *fast_avail);
672 if (checksum)
673 krb5_free_checksum(context, checksum);
674 return retval;
675 }
676
677 krb5_boolean
k5_upgrade_to_fast_p(krb5_context context,struct krb5int_fast_request_state * state,krb5_pa_data ** padata)678 k5_upgrade_to_fast_p(krb5_context context,
679 struct krb5int_fast_request_state *state,
680 krb5_pa_data **padata)
681 {
682 if (state->armor_key != NULL)
683 return FALSE; /* Already using FAST. */
684 if (!(state->fast_state_flags & KRB5INT_FAST_ARMOR_AVAIL))
685 return FALSE;
686 if (krb5int_find_pa_data(context, padata, KRB5_PADATA_FX_FAST) != NULL)
687 return TRUE;
688 return FALSE;
689 }
690