1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* lib/krb5/krb/fast.c */
4 * Copyright (C) 2009 by the Massachusetts Institute of Technology.
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.
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.
30 * It is possible to support sending a request that includes both a FAST and
31 * normal version. This would complicate the pre-authentication logic
32 * significantly. You would need to maintain two contexts, one for FAST and
33 * one for normal use. In adition, you would need to manage the security
34 * issues surrounding downgrades. However trying FAST at all requires an armor
35 * key. Generally in obtaining the armor key, the client learns enough to know
36 * that FAST is supported. If not, the client can see FAST in the
37 * preauth_required error's padata and retry with FAST. So, this
38 * implementation does not support FAST+normal.
40 * We store the outer version of the request to use. The caller stores the
41 * inner version. We handle the encoding of the request body (and request) and
42 * provide encoded request bodies for the caller to use as these may be used
43 * for checksums. In the AS case we also evaluate whether to continue a
44 * conversation as one of the important questions there is the presence of a
48 #include "int-proto.h"
50 static krb5_error_code
51 fast_armor_ap_request(krb5_context context,
52 struct krb5int_fast_request_state *state,
53 krb5_ccache ccache, krb5_principal target_principal)
55 krb5_error_code retval = 0;
56 krb5_creds creds, *out_creds = NULL;
57 krb5_auth_context authcontext = NULL;
58 krb5_data encoded_authenticator;
59 krb5_fast_armor *armor = NULL;
60 krb5_keyblock *subkey = NULL, *armor_key = NULL;
62 encoded_authenticator.data = NULL;
63 memset(&creds, 0, sizeof(creds));
64 creds.server = target_principal;
65 retval = krb5_cc_get_principal(context, ccache, &creds.client);
67 retval = krb5_get_credentials(context, 0, ccache, &creds, &out_creds);
69 TRACE_FAST_ARMOR_CCACHE_KEY(context, &out_creds->keyblock);
70 retval = krb5_mk_req_extended(context, &authcontext,
71 AP_OPTS_USE_SUBKEY, NULL /*data*/,
72 out_creds, &encoded_authenticator);
75 retval = krb5_auth_con_getsendsubkey(context, authcontext, &subkey);
77 retval = krb5_c_fx_cf2_simple(context, subkey, "subkeyarmor",
78 &out_creds->keyblock, "ticketarmor",
81 TRACE_FAST_ARMOR_KEY(context, armor_key);
82 armor = calloc(1, sizeof(krb5_fast_armor));
87 armor->armor_type = KRB5_FAST_ARMOR_AP_REQUEST;
88 armor->armor_value = encoded_authenticator;
89 encoded_authenticator.data = NULL;
90 encoded_authenticator.length = 0;
93 state->armor_key = armor_key;
96 krb5_free_keyblock(context, armor_key);
97 krb5_free_keyblock(context, subkey);
99 krb5_free_creds(context, out_creds);
100 /* target_principal is owned by caller. */
102 krb5_free_cred_contents(context, &creds);
103 if (encoded_authenticator.data)
104 krb5_free_data_contents(context, &encoded_authenticator);
105 krb5_auth_con_free(context, authcontext);
110 krb5int_fast_prep_req_body(krb5_context context,
111 struct krb5int_fast_request_state *state,
112 krb5_kdc_req *request,
113 krb5_data **encoded_request_body)
115 krb5_error_code retval = 0;
116 krb5_data *local_encoded_request_body = NULL;
118 assert(state != NULL);
119 *encoded_request_body = NULL;
120 if (state->armor_key == NULL)
121 return encode_krb5_kdc_req_body(request, encoded_request_body);
122 state->fast_outer_request = *request;
123 state->fast_outer_request.padata = NULL;
125 retval = encode_krb5_kdc_req_body(&state->fast_outer_request,
126 &local_encoded_request_body);
128 *encoded_request_body = local_encoded_request_body;
129 local_encoded_request_body = NULL;
131 if (local_encoded_request_body != NULL)
132 krb5_free_data(context, local_encoded_request_body);
137 krb5int_fast_as_armor(krb5_context context,
138 struct krb5int_fast_request_state *state,
139 krb5_gic_opt_ext *opte,
140 krb5_kdc_req *request)
142 krb5_error_code retval = 0;
143 krb5_ccache ccache = NULL;
144 krb5_principal target_principal = NULL;
145 krb5_data *target_realm;
147 krb5_clear_error_message(context);
148 target_realm = krb5_princ_realm(context, request->server);
149 if (opte->opt_private->fast_ccache_name) {
150 TRACE_FAST_ARMOR_CCACHE(context, opte->opt_private->fast_ccache_name);
151 state->fast_state_flags |= KRB5INT_FAST_ARMOR_AVAIL;
152 retval = krb5_cc_resolve(context, opte->opt_private->fast_ccache_name,
155 retval = krb5int_tgtname(context, target_realm, target_realm,
159 krb5_data config_data;
160 config_data.data = NULL;
161 retval = krb5_cc_get_config(context, ccache, target_principal,
162 KRB5_CONF_FAST_AVAIL, &config_data);
163 if ((retval == 0) && config_data.data) {
164 TRACE_FAST_CCACHE_CONFIG(context);
165 state->fast_state_flags |= KRB5INT_FAST_DO_FAST;
167 krb5_free_data_contents(context, &config_data);
170 if (opte->opt_private->fast_flags & KRB5_FAST_REQUIRED) {
171 TRACE_FAST_REQUIRED(context);
172 state->fast_state_flags |= KRB5INT_FAST_DO_FAST;
174 if (retval == 0 && (state->fast_state_flags & KRB5INT_FAST_DO_FAST)) {
175 retval = fast_armor_ap_request(context, state, ccache,
180 errmsg = krb5_get_error_message(context, retval);
181 krb5_set_error_message(context, retval,
182 _("%s constructing AP-REQ armor"), errmsg);
183 krb5_free_error_message(context, errmsg);
187 krb5_cc_close(context, ccache);
188 if (target_principal)
189 krb5_free_principal(context, target_principal);
195 krb5int_fast_prep_req(krb5_context context,
196 struct krb5int_fast_request_state *state,
197 krb5_kdc_req *request,
198 const krb5_data *to_be_checksummed,
199 kdc_req_encoder_proc encoder,
200 krb5_data **encoded_request)
202 krb5_error_code retval = 0;
203 krb5_pa_data *pa_array[2];
205 krb5_fast_req fast_req;
206 krb5_fast_armored_req *armored_req = NULL;
207 krb5_data *encoded_fast_req = NULL;
208 krb5_data *encoded_armored_req = NULL;
209 krb5_data *local_encoded_result = NULL;
210 krb5_data random_data;
213 assert(state != NULL);
214 assert(state->fast_outer_request.padata == NULL);
215 memset(pa_array, 0, sizeof pa_array);
216 if (state->armor_key == NULL) {
217 return encoder(request, encoded_request);
220 TRACE_FAST_ENCODE(context);
221 /* Fill in a fresh random nonce for each inner request*/
222 random_data.length = 4;
223 random_data.data = (char *)random_buf;
224 retval = krb5_c_random_make_octets(context, &random_data);
226 request->nonce = 0x7fffffff & load_32_n(random_buf);
227 state->nonce = request->nonce;
229 fast_req.req_body = request;
230 if (fast_req.req_body->padata == NULL) {
231 fast_req.req_body->padata = calloc(1, sizeof(krb5_pa_data *));
232 if (fast_req.req_body->padata == NULL)
235 fast_req.fast_options = state->fast_options;
237 retval = encode_krb5_fast_req(&fast_req, &encoded_fast_req);
239 armored_req = calloc(1, sizeof(krb5_fast_armored_req));
240 if (armored_req == NULL)
244 armored_req->armor = state->armor;
246 retval = krb5_c_make_checksum(context, 0, state->armor_key,
247 KRB5_KEYUSAGE_FAST_REQ_CHKSUM,
249 &armored_req->req_checksum);
251 retval = krb5_encrypt_helper(context, state->armor_key,
252 KRB5_KEYUSAGE_FAST_ENC, encoded_fast_req,
253 &armored_req->enc_part);
255 retval = encode_krb5_pa_fx_fast_request(armored_req,
256 &encoded_armored_req);
258 pa[0].pa_type = KRB5_PADATA_FX_FAST;
259 pa[0].contents = (unsigned char *) encoded_armored_req->data;
260 pa[0].length = encoded_armored_req->length;
261 pa_array[0] = &pa[0];
263 state->fast_outer_request.padata = pa_array;
265 retval = encoder(&state->fast_outer_request, &local_encoded_result);
267 *encoded_request = local_encoded_result;
268 local_encoded_result = NULL;
270 if (encoded_armored_req)
271 krb5_free_data(context, encoded_armored_req);
273 armored_req->armor = NULL; /*owned by state*/
274 krb5_free_fast_armored_req(context, armored_req);
276 if (encoded_fast_req)
277 krb5_free_data(context, encoded_fast_req);
278 if (local_encoded_result)
279 krb5_free_data(context, local_encoded_result);
280 state->fast_outer_request.padata = NULL;
284 static krb5_error_code
285 decrypt_fast_reply(krb5_context context,
286 struct krb5int_fast_request_state *state,
287 krb5_pa_data **in_padata,
288 krb5_fast_response **response)
290 krb5_error_code retval = 0;
292 krb5_enc_data *encrypted_response = NULL;
293 krb5_pa_data *fx_reply = NULL;
294 krb5_fast_response *local_resp = NULL;
296 assert(state != NULL);
297 assert(state->armor_key);
298 fx_reply = krb5int_find_pa_data(context, in_padata, KRB5_PADATA_FX_FAST);
299 if (fx_reply == NULL)
300 retval = KRB5_ERR_FAST_REQUIRED;
301 TRACE_FAST_DECODE(context);
303 scratch.data = (char *) fx_reply->contents;
304 scratch.length = fx_reply->length;
305 retval = decode_krb5_pa_fx_fast_reply(&scratch, &encrypted_response);
309 scratch.data = malloc(encrypted_response->ciphertext.length);
310 if (scratch.data == NULL)
312 scratch.length = encrypted_response->ciphertext.length;
315 retval = krb5_c_decrypt(context, state->armor_key,
316 KRB5_KEYUSAGE_FAST_REP, NULL,
317 encrypted_response, &scratch);
320 errmsg = krb5_get_error_message(context, retval);
321 krb5_set_error_message(context, retval,
322 _("%s while decrypting FAST reply"), errmsg);
323 krb5_free_error_message(context, errmsg);
326 retval = decode_krb5_fast_response(&scratch, &local_resp);
328 if (local_resp->nonce != state->nonce) {
329 retval = KRB5_KDCREP_MODIFIED;
330 krb5_set_error_message(context, retval,
331 _("nonce modified in FAST response: "
332 "KDC response modified"));
336 *response = local_resp;
341 if (encrypted_response)
342 krb5_free_enc_data(context, encrypted_response);
344 krb5_free_fast_response(context, local_resp);
349 * If state contains an armor key and *err_replyptr contains a FAST error,
350 * decode it and set *err_replyptr to the inner error and *out_padata to the
351 * padata in the FAST response. Otherwise, leave *err_replyptr alone and set
352 * *out_padata to the error e_data decoded as pa-data or typed-data, or to NULL
353 * if it doesn't decode as either. In either case, set *retry to indicate
354 * whether the client should try to make a follow-up request.
357 krb5int_fast_process_error(krb5_context context,
358 struct krb5int_fast_request_state *state,
359 krb5_error **err_replyptr,
360 krb5_pa_data ***out_padata,
363 krb5_error_code retval = 0;
364 krb5_error *err_reply = *err_replyptr;
368 if (state->armor_key) {
369 krb5_pa_data *fx_error_pa;
370 krb5_pa_data **result = NULL;
372 krb5_error *fx_error = NULL;
373 krb5_fast_response *fast_response = NULL;
375 retval = decode_krb5_padata_sequence(&err_reply->e_data, &result);
377 retval = decrypt_fast_reply(context, state, result,
381 * This can happen if the KDC does not understand FAST. We don't
382 * expect that, but treating it as the fatal error indicated by the
383 * KDC seems reasonable.
386 krb5_free_pa_data(context, result);
389 krb5_free_pa_data(context, result);
392 fx_error_pa = krb5int_find_pa_data(context, fast_response->padata,
393 KRB5_PADATA_FX_ERROR);
394 if (fx_error_pa == NULL) {
395 krb5_set_error_message(context, KRB5KDC_ERR_PREAUTH_FAILED,
396 _("Expecting FX_ERROR pa-data inside "
398 retval = KRB5KDC_ERR_PREAUTH_FAILED;
402 scratch.data = (char *) fx_error_pa->contents;
403 scratch.length = fx_error_pa->length;
404 retval = decode_krb5_error(&scratch, &fx_error);
407 krb5_free_error(context, err_reply);
408 *err_replyptr = fx_error;
410 *out_padata = fast_response->padata;
411 fast_response->padata = NULL;
413 * If there is more than the fx_error padata, then we want
414 * to retry the error if a cookie is present
416 *retry = (*out_padata)[1] != NULL;
417 if (krb5int_find_pa_data(context, *out_padata,
418 KRB5_PADATA_FX_COOKIE) == NULL)
422 krb5_free_error(context, fx_error);
423 krb5_free_fast_response(context, fast_response);
424 } else { /*not FAST*/
425 /* Possibly retry if there's any e_data to process. */
426 *retry = (err_reply->e_data.length > 0);
427 /* Try to decode e_data as pa-data or typed-data for out_padata. */
428 retval = decode_krb5_padata_sequence(&err_reply->e_data, out_padata);
430 krb5_typed_data **tdata;
431 /* krb5_typed data and krb5_pa_data are compatible structures. */
432 if (decode_krb5_typed_data(&err_reply->e_data, &tdata) == 0)
433 *out_padata = (krb5_pa_data **)tdata;
442 krb5int_fast_process_response(krb5_context context,
443 struct krb5int_fast_request_state *state,
445 krb5_keyblock **strengthen_key)
447 krb5_error_code retval = 0;
448 krb5_fast_response *fast_response = NULL;
449 krb5_data *encoded_ticket = NULL;
450 krb5_boolean cksum_valid;
452 krb5_clear_error_message(context);
453 *strengthen_key = NULL;
454 if (state->armor_key == 0)
456 retval = decrypt_fast_reply(context, state, resp->padata,
459 if (fast_response->finished == 0) {
460 retval = KRB5_KDCREP_MODIFIED;
461 krb5_set_error_message(context, retval,
462 _("FAST response missing finish message "
467 retval = encode_krb5_ticket(resp->ticket, &encoded_ticket);
469 retval = krb5_c_verify_checksum(context, state->armor_key,
470 KRB5_KEYUSAGE_FAST_FINISHED,
472 &fast_response->finished->ticket_checksum,
474 if (retval == 0 && cksum_valid == 0) {
475 retval = KRB5_KDCREP_MODIFIED;
476 krb5_set_error_message(context, retval,
477 _("Ticket modified in KDC reply"));
480 krb5_free_principal(context, resp->client);
481 resp->client = fast_response->finished->client;
482 fast_response->finished->client = NULL;
483 *strengthen_key = fast_response->strengthen_key;
484 fast_response->strengthen_key = NULL;
485 krb5_free_pa_data(context, resp->padata);
486 resp->padata = fast_response->padata;
487 fast_response->padata = NULL;
490 krb5_free_fast_response(context, fast_response);
492 krb5_free_data(context, encoded_ticket);
497 krb5int_fast_reply_key(krb5_context context,
498 krb5_keyblock *strengthen_key,
499 krb5_keyblock *existing_key,
500 krb5_keyblock *out_key)
502 krb5_keyblock *key = NULL;
503 krb5_error_code retval = 0;
504 krb5_free_keyblock_contents(context, out_key);
505 if (strengthen_key) {
506 retval = krb5_c_fx_cf2_simple(context, strengthen_key,
507 "strengthenkey", existing_key,
510 TRACE_FAST_REPLY_KEY(context, key);
515 retval = krb5_copy_keyblock_contents(context, existing_key, out_key);
522 krb5int_fast_make_state(krb5_context context,
523 struct krb5int_fast_request_state **state)
525 struct krb5int_fast_request_state *local_state ;
527 local_state = malloc(sizeof *local_state);
528 if (local_state == NULL)
530 memset(local_state, 0, sizeof(*local_state));
531 *state = local_state;
536 krb5int_fast_free_state(krb5_context context,
537 struct krb5int_fast_request_state *state)
541 /*We are responsible for none of the store in the fast_outer_req*/
542 krb5_free_keyblock(context, state->armor_key);
543 krb5_free_fast_armor(context, state->armor);
548 krb5int_find_pa_data(krb5_context context, krb5_pa_data *const *padata,
549 krb5_preauthtype pa_type)
551 krb5_pa_data * const *tmppa;
556 for (tmppa = padata; *tmppa != NULL; tmppa++) {
557 if ((*tmppa)->pa_type == pa_type)
566 krb5int_fast_verify_nego(krb5_context context,
567 struct krb5int_fast_request_state *state,
568 krb5_kdc_rep *rep, krb5_data *request,
569 krb5_keyblock *decrypting_key,
570 krb5_boolean *fast_avail)
572 krb5_error_code retval = 0;
573 krb5_checksum *checksum = NULL;
579 if (rep->enc_part2->flags& TKT_FLG_ENC_PA_REP) {
580 pa = krb5int_find_pa_data(context, rep->enc_part2->enc_padata,
581 KRB5_ENCPADATA_REQ_ENC_PA_REP);
583 retval = KRB5_KDCREP_MODIFIED;
585 scratch.data = (char *) pa->contents;
586 scratch.length = pa->length;
589 retval = decode_krb5_checksum(&scratch, &checksum);
591 retval = krb5_c_verify_checksum(context, decrypting_key,
592 KRB5_KEYUSAGE_AS_REQ,
593 request, checksum, &valid);
594 if (retval == 0 &&valid == 0)
595 retval = KRB5_KDCREP_MODIFIED;
597 pa = krb5int_find_pa_data(context, rep->enc_part2->enc_padata,
598 KRB5_PADATA_FX_FAST);
599 *fast_avail = (pa != NULL);
602 TRACE_FAST_NEGO(context, *fast_avail);
604 krb5_free_checksum(context, checksum);
609 krb5int_upgrade_to_fast_p(krb5_context context,
610 struct krb5int_fast_request_state *state,
611 krb5_pa_data **padata)
613 if (state->armor_key != NULL)
614 return FALSE; /* Already using FAST. */
615 if (!(state->fast_state_flags & KRB5INT_FAST_ARMOR_AVAIL))
617 if (krb5int_find_pa_data(context, padata, KRB5_PADATA_FX_FAST) != NULL) {
618 TRACE_FAST_PADATA_UPGRADE(context);
619 state->fast_state_flags |= KRB5INT_FAST_DO_FAST;