1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
5 * Copyright (C) 2009 by the Massachusetts Institute of Technology.
8 * Export of this software from the United States of America may
9 * require a specific license from the United States Government.
10 * It is the responsibility of any person or organization contemplating
11 * export to obtain such a license before exporting.
13 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
14 * distribute this software and its documentation for any purpose and
15 * without fee is hereby granted, provided that the above copyright
16 * notice appear in all copies and that both that copyright notice and
17 * this permission notice appear in supporting documentation, and that
18 * the name of M.I.T. not be used in advertising or publicity pertaining
19 * to distribution of the software without specific, written prior
20 * permission. Furthermore if you modify this software you must label
21 * your software as modified software and not distribute it in such a
22 * fashion that it might be confused with the original M.I.T. software.
23 * M.I.T. makes no representations about the suitability of
24 * this software for any purpose. It is provided "as is" without express
25 * or implied warranty.
34 * It is possible to support sending a request that includes both a FAST and
35 * normal version. This would complicate the pre-authentication logic
36 * significantly. You would need to maintain two contexts, one for FAST and
37 * one for normal use. In adition, you would need to manage the security
38 * issues surrounding downgrades. However trying FAST at all requires an armor
39 * key. Generally in obtaining the armor key, the client learns enough to know
40 * that FAST is supported. If not, the client can see FAST in the
41 * preauth_required error's padata and retry with FAST. So, this
42 * implementation does not support FAST+normal.
44 * We store the outer version of the request to use. The caller stores the
45 * inner version. We handle the encoding of the request body (and request) and
46 * provide encoded request bodies for the caller to use as these may be used
47 * for checksums. In the AS case we also evaluate whether to continue a
48 * conversation as one of the important questions there is the presence of a
52 #include "int-proto.h"
54 static krb5_error_code
55 fast_armor_ap_request(krb5_context context,
56 struct krb5int_fast_request_state *state,
57 krb5_ccache ccache, krb5_principal target_principal)
59 krb5_error_code retval = 0;
60 krb5_creds creds, *out_creds = NULL;
61 krb5_auth_context authcontext = NULL;
62 krb5_data encoded_authenticator;
63 krb5_fast_armor *armor = NULL;
64 krb5_keyblock *subkey = NULL, *armor_key = NULL;
66 encoded_authenticator.data = NULL;
67 memset(&creds, 0, sizeof(creds));
68 creds.server = target_principal;
69 retval = krb5_cc_get_principal(context, ccache, &creds.client);
71 retval = krb5_get_credentials(context, 0, ccache, &creds, &out_creds);
73 TRACE_FAST_ARMOR_CCACHE_KEY(context, &out_creds->keyblock);
74 retval = krb5_mk_req_extended(context, &authcontext,
75 AP_OPTS_USE_SUBKEY, NULL /*data*/,
76 out_creds, &encoded_authenticator);
79 retval = krb5_auth_con_getsendsubkey(context, authcontext, &subkey);
81 retval = krb5_c_fx_cf2_simple(context, subkey, "subkeyarmor",
82 &out_creds->keyblock, "ticketarmor",
85 TRACE_FAST_ARMOR_KEY(context, armor_key);
86 armor = calloc(1, sizeof(krb5_fast_armor));
91 armor->armor_type = KRB5_FAST_ARMOR_AP_REQUEST;
92 armor->armor_value = encoded_authenticator;
93 encoded_authenticator.data = NULL;
94 encoded_authenticator.length = 0;
97 state->armor_key = armor_key;
100 krb5_free_keyblock(context, armor_key);
101 krb5_free_keyblock(context, subkey);
103 krb5_free_creds(context, out_creds);
104 /* target_principal is owned by caller. */
106 krb5_free_cred_contents(context, &creds);
107 if (encoded_authenticator.data)
108 krb5_free_data_contents(context, &encoded_authenticator);
109 krb5_auth_con_free(context, authcontext);
114 krb5int_fast_prep_req_body(krb5_context context,
115 struct krb5int_fast_request_state *state,
116 krb5_kdc_req *request,
117 krb5_data **encoded_request_body)
119 krb5_error_code retval = 0;
120 krb5_data *local_encoded_request_body = NULL;
122 assert(state != NULL);
123 *encoded_request_body = NULL;
124 if (state->armor_key == NULL)
125 return encode_krb5_kdc_req_body(request, encoded_request_body);
126 state->fast_outer_request = *request;
127 state->fast_outer_request.padata = NULL;
129 retval = encode_krb5_kdc_req_body(&state->fast_outer_request,
130 &local_encoded_request_body);
132 *encoded_request_body = local_encoded_request_body;
133 local_encoded_request_body = NULL;
135 if (local_encoded_request_body != NULL)
136 krb5_free_data(context, local_encoded_request_body);
141 krb5int_fast_as_armor(krb5_context context,
142 struct krb5int_fast_request_state *state,
143 krb5_gic_opt_ext *opte,
144 krb5_kdc_req *request)
146 krb5_error_code retval = 0;
147 krb5_ccache ccache = NULL;
148 krb5_principal target_principal = NULL;
149 krb5_data *target_realm;
151 krb5_clear_error_message(context);
152 target_realm = krb5_princ_realm(context, request->server);
153 if (opte->opt_private->fast_ccache_name) {
154 TRACE_FAST_ARMOR_CCACHE(context, opte->opt_private->fast_ccache_name);
155 state->fast_state_flags |= KRB5INT_FAST_ARMOR_AVAIL;
156 retval = krb5_cc_resolve(context, opte->opt_private->fast_ccache_name,
159 retval = krb5int_tgtname(context, target_realm, target_realm,
163 krb5_data config_data;
164 config_data.data = NULL;
165 retval = krb5_cc_get_config(context, ccache, target_principal,
166 KRB5_CONF_FAST_AVAIL, &config_data);
167 if ((retval == 0) && config_data.data) {
168 TRACE_FAST_CCACHE_CONFIG(context);
169 state->fast_state_flags |= KRB5INT_FAST_DO_FAST;
171 krb5_free_data_contents(context, &config_data);
174 if (opte->opt_private->fast_flags & KRB5_FAST_REQUIRED) {
175 TRACE_FAST_REQUIRED(context);
176 state->fast_state_flags |= KRB5INT_FAST_DO_FAST;
178 if (retval == 0 && (state->fast_state_flags & KRB5INT_FAST_DO_FAST)) {
179 retval = fast_armor_ap_request(context, state, ccache,
184 errmsg = krb5_get_error_message(context, retval);
186 krb5_set_error_message(context, retval,
187 "%s constructing AP-REQ armor", errmsg);
188 krb5_free_error_message(context, errmsg);
193 krb5_cc_close(context, ccache);
194 if (target_principal)
195 krb5_free_principal(context, target_principal);
201 krb5int_fast_prep_req(krb5_context context,
202 struct krb5int_fast_request_state *state,
203 krb5_kdc_req *request,
204 const krb5_data *to_be_checksummed,
205 kdc_req_encoder_proc encoder,
206 krb5_data **encoded_request)
208 krb5_error_code retval = 0;
209 krb5_pa_data *pa_array[2];
211 krb5_fast_req fast_req;
212 krb5_fast_armored_req *armored_req = NULL;
213 krb5_data *encoded_fast_req = NULL;
214 krb5_data *encoded_armored_req = NULL;
215 krb5_data *local_encoded_result = NULL;
216 krb5_cksumtype cksumtype;
217 krb5_data random_data;
220 assert(state != NULL);
221 assert(state->fast_outer_request.padata == NULL);
222 memset(pa_array, 0, sizeof pa_array);
223 if (state->armor_key == NULL) {
224 return encoder(request, encoded_request);
227 TRACE_FAST_ENCODE(context);
228 /* Fill in a fresh random nonce for each inner request*/
229 random_data.length = 4;
230 random_data.data = (char *)random_buf;
231 retval = krb5_c_random_make_octets(context, &random_data);
233 request->nonce = 0x7fffffff & load_32_n(random_buf);
234 state->nonce = request->nonce;
236 fast_req.req_body = request;
237 if (fast_req.req_body->padata == NULL) {
238 fast_req.req_body->padata = calloc(1, sizeof(krb5_pa_data *));
239 if (fast_req.req_body->padata == NULL)
242 fast_req.fast_options = state->fast_options;
244 retval = encode_krb5_fast_req(&fast_req, &encoded_fast_req);
246 armored_req = calloc(1, sizeof(krb5_fast_armored_req));
247 if (armored_req == NULL)
251 armored_req->armor = state->armor;
253 retval = krb5int_c_mandatory_cksumtype(context,
254 state->armor_key->enctype,
256 /* DES enctypes have unkeyed mandatory checksums; need a keyed one. */
257 if (retval == 0 && !krb5_c_is_keyed_cksum(cksumtype))
258 cksumtype = CKSUMTYPE_RSA_MD5_DES;
260 retval = krb5_c_make_checksum(context, cksumtype, state->armor_key,
261 KRB5_KEYUSAGE_FAST_REQ_CHKSUM,
263 &armored_req->req_checksum);
265 retval = krb5_encrypt_helper(context, state->armor_key,
266 KRB5_KEYUSAGE_FAST_ENC, encoded_fast_req,
267 &armored_req->enc_part);
269 retval = encode_krb5_pa_fx_fast_request(armored_req,
270 &encoded_armored_req);
272 pa[0].pa_type = KRB5_PADATA_FX_FAST;
273 pa[0].contents = (unsigned char *) encoded_armored_req->data;
274 pa[0].length = encoded_armored_req->length;
275 pa_array[0] = &pa[0];
277 state->fast_outer_request.padata = pa_array;
279 retval = encoder(&state->fast_outer_request, &local_encoded_result);
281 *encoded_request = local_encoded_result;
282 local_encoded_result = NULL;
284 if (encoded_armored_req)
285 krb5_free_data(context, encoded_armored_req);
287 armored_req->armor = NULL; /*owned by state*/
288 krb5_free_fast_armored_req(context, armored_req);
290 if (encoded_fast_req)
291 krb5_free_data(context, encoded_fast_req);
292 if (local_encoded_result)
293 krb5_free_data(context, local_encoded_result);
294 state->fast_outer_request.padata = NULL;
298 static krb5_error_code
299 decrypt_fast_reply(krb5_context context,
300 struct krb5int_fast_request_state *state,
301 krb5_pa_data **in_padata,
302 krb5_fast_response **response)
304 krb5_error_code retval = 0;
306 krb5_enc_data *encrypted_response = NULL;
307 krb5_pa_data *fx_reply = NULL;
308 krb5_fast_response *local_resp = NULL;
310 assert(state != NULL);
311 assert(state->armor_key);
312 fx_reply = krb5int_find_pa_data(context, in_padata, KRB5_PADATA_FX_FAST);
313 if (fx_reply == NULL)
314 retval = KRB5_ERR_FAST_REQUIRED;
315 TRACE_FAST_DECODE(context);
317 scratch.data = (char *) fx_reply->contents;
318 scratch.length = fx_reply->length;
319 retval = decode_krb5_pa_fx_fast_reply(&scratch, &encrypted_response);
323 scratch.data = malloc(encrypted_response->ciphertext.length);
324 if (scratch.data == NULL)
326 scratch.length = encrypted_response->ciphertext.length;
329 retval = krb5_c_decrypt(context, state->armor_key,
330 KRB5_KEYUSAGE_FAST_REP, NULL,
331 encrypted_response, &scratch);
334 errmsg = krb5_get_error_message(context, retval);
335 krb5_set_error_message(context, retval,
336 "%s while decrypting FAST reply", errmsg);
337 krb5_free_error_message(context, errmsg);
340 retval = decode_krb5_fast_response(&scratch, &local_resp);
342 if (local_resp->nonce != state->nonce) {
343 retval = KRB5_KDCREP_MODIFIED;
344 krb5_set_error_message(context, retval, "nonce modified in FAST "
345 "response: KDC response modified");
349 *response = local_resp;
354 if (encrypted_response)
355 krb5_free_enc_data(context, encrypted_response);
357 krb5_free_fast_response(context, local_resp);
362 * FAST separates two concepts: the set of padata we're using to
363 * decide what pre-auth mechanisms to use and the set of padata we're
364 * making available to mechanisms in order for them to respond to an
365 * error. The plugin interface in March 2009 does not permit
366 * separating these concepts for the plugins. This function makes
367 * both available for future revisions to the plugin interface. It
368 * also re-encodes the padata from the current error as a encoded
369 * typed-data and puts that in the e_data field. That will allow
370 * existing plugins with the old interface to find the error data.
371 * The output parameter out_padata contains the padata from the error
372 * whenever padata is available (all the time with fast).
375 krb5int_fast_process_error(krb5_context context,
376 struct krb5int_fast_request_state *state,
377 krb5_error **err_replyptr,
378 krb5_pa_data ***out_padata,
381 krb5_error_code retval = 0;
382 krb5_error *err_reply = *err_replyptr;
386 if (state->armor_key) {
387 krb5_pa_data *fx_error_pa;
388 krb5_pa_data **result = NULL;
389 krb5_data scratch, *encoded_td = NULL;
390 krb5_error *fx_error = NULL;
391 krb5_fast_response *fast_response = NULL;
393 retval = decode_krb5_padata_sequence(&err_reply->e_data, &result);
395 retval = decrypt_fast_reply(context, state, result,
399 * This can happen if the KDC does not understand FAST. We don't
400 * expect that, but treating it as the fatal error indicated by the
401 * KDC seems reasonable.
404 krb5_free_pa_data(context, result);
407 krb5_free_pa_data(context, result);
410 fx_error_pa = krb5int_find_pa_data(context, fast_response->padata,
411 KRB5_PADATA_FX_ERROR);
412 if (fx_error_pa == NULL) {
413 krb5_set_error_message(context, KRB5KDC_ERR_PREAUTH_FAILED,
414 "Expecting FX_ERROR pa-data inside "
416 retval = KRB5KDC_ERR_PREAUTH_FAILED;
420 scratch.data = (char *) fx_error_pa->contents;
421 scratch.length = fx_error_pa->length;
422 retval = decode_krb5_error(&scratch, &fx_error);
425 * krb5_pa_data and krb5_typed_data are safe to cast between:
426 * they have the same type fields in the same order.
427 * (krb5_preauthtype is a krb5_int32). If krb5_typed_data is
428 * ever changed then this will need to be a copy not a cast.
431 retval = encode_krb5_typed_data((const krb5_typed_data **)
432 fast_response->padata,
435 fx_error->e_data = *encoded_td;
436 free(encoded_td); /*contents owned by fx_error*/
438 krb5_free_error(context, err_reply);
439 *err_replyptr = fx_error;
441 *out_padata = fast_response->padata;
442 fast_response->padata = NULL;
444 * If there is more than the fx_error padata, then we want
445 * to retry the error if a cookie is present
447 *retry = (*out_padata)[1] != NULL;
448 if (krb5int_find_pa_data(context, *out_padata,
449 KRB5_PADATA_FX_COOKIE) == NULL)
453 krb5_free_error(context, fx_error);
454 krb5_free_fast_response(context, fast_response);
455 } else { /*not FAST*/
456 *retry = (err_reply->e_data.length > 0);
457 if ((err_reply->error == KDC_ERR_PREAUTH_REQUIRED ||
458 err_reply->error == KDC_ERR_PREAUTH_FAILED) &&
459 err_reply->e_data.length) {
460 krb5_pa_data **result = NULL;
461 retval = decode_krb5_padata_sequence(&err_reply->e_data, &result);
463 *out_padata = result;
466 krb5_free_pa_data(context, result);
467 krb5_set_error_message(context, retval,
468 "Error decoding padata in error reply");
477 krb5int_fast_process_response(krb5_context context,
478 struct krb5int_fast_request_state *state,
480 krb5_keyblock **strengthen_key)
482 krb5_error_code retval = 0;
483 krb5_fast_response *fast_response = NULL;
484 krb5_data *encoded_ticket = NULL;
485 krb5_boolean cksum_valid;
487 krb5_clear_error_message(context);
488 *strengthen_key = NULL;
489 if (state->armor_key == 0)
491 retval = decrypt_fast_reply(context, state, resp->padata,
494 if (fast_response->finished == 0) {
495 retval = KRB5_KDCREP_MODIFIED;
496 krb5_set_error_message(context, retval, "FAST response missing "
497 "finish message in KDC reply");
501 retval = encode_krb5_ticket(resp->ticket, &encoded_ticket);
503 retval = krb5_c_verify_checksum(context, state->armor_key,
504 KRB5_KEYUSAGE_FAST_FINISHED,
506 &fast_response->finished->ticket_checksum,
508 if (retval == 0 && cksum_valid == 0) {
509 retval = KRB5_KDCREP_MODIFIED;
510 krb5_set_error_message(context, retval,
511 "ticket modified in KDC reply");
514 krb5_free_principal(context, resp->client);
515 resp->client = fast_response->finished->client;
516 fast_response->finished->client = NULL;
517 *strengthen_key = fast_response->strengthen_key;
518 fast_response->strengthen_key = NULL;
519 krb5_free_pa_data(context, resp->padata);
520 resp->padata = fast_response->padata;
521 fast_response->padata = NULL;
524 krb5_free_fast_response(context, fast_response);
526 krb5_free_data(context, encoded_ticket);
531 krb5int_fast_reply_key(krb5_context context,
532 krb5_keyblock *strengthen_key,
533 krb5_keyblock *existing_key,
534 krb5_keyblock *out_key)
536 krb5_keyblock *key = NULL;
537 krb5_error_code retval = 0;
538 krb5_free_keyblock_contents(context, out_key);
539 if (strengthen_key) {
540 retval = krb5_c_fx_cf2_simple(context, strengthen_key,
541 "strengthenkey", existing_key,
544 TRACE_FAST_REPLY_KEY(context, key);
549 retval = krb5_copy_keyblock_contents(context, existing_key, out_key);
556 krb5int_fast_make_state(krb5_context context,
557 struct krb5int_fast_request_state **state)
559 struct krb5int_fast_request_state *local_state ;
561 local_state = malloc(sizeof *local_state);
562 if (local_state == NULL)
564 memset(local_state, 0, sizeof(*local_state));
565 *state = local_state;
570 krb5int_fast_free_state(krb5_context context,
571 struct krb5int_fast_request_state *state)
575 /*We are responsible for none of the store in the fast_outer_req*/
576 krb5_free_keyblock(context, state->armor_key);
577 krb5_free_fast_armor(context, state->armor);
582 krb5int_find_pa_data(krb5_context context, krb5_pa_data *const *padata,
583 krb5_preauthtype pa_type)
585 krb5_pa_data * const *tmppa;
590 for (tmppa = padata; *tmppa != NULL; tmppa++) {
591 if ((*tmppa)->pa_type == pa_type)
600 krb5int_fast_verify_nego(krb5_context context,
601 struct krb5int_fast_request_state *state,
602 krb5_kdc_rep *rep, krb5_data *request,
603 krb5_keyblock *decrypting_key,
604 krb5_boolean *fast_avail)
606 krb5_error_code retval = 0;
607 krb5_checksum *checksum = NULL;
613 if (rep->enc_part2->flags& TKT_FLG_ENC_PA_REP) {
614 pa = krb5int_find_pa_data(context, rep->enc_part2->enc_padata,
615 KRB5_ENCPADATA_REQ_ENC_PA_REP);
617 retval = KRB5_KDCREP_MODIFIED;
619 scratch.data = (char *) pa->contents;
620 scratch.length = pa->length;
623 retval = decode_krb5_checksum(&scratch, &checksum);
625 retval = krb5_c_verify_checksum(context, decrypting_key,
626 KRB5_KEYUSAGE_AS_REQ,
627 request, checksum, &valid);
628 if (retval == 0 &&valid == 0)
629 retval = KRB5_KDCREP_MODIFIED;
631 pa = krb5int_find_pa_data(context, rep->enc_part2->enc_padata,
632 KRB5_PADATA_FX_FAST);
633 *fast_avail = (pa != NULL);
636 TRACE_FAST_NEGO(context, *fast_avail);
638 krb5_free_checksum(context, checksum);
643 krb5int_upgrade_to_fast_p(krb5_context context,
644 struct krb5int_fast_request_state *state,
645 krb5_pa_data **padata)
647 if (state->armor_key != NULL)
648 return FALSE; /* Already using FAST. */
649 if (!(state->fast_state_flags & KRB5INT_FAST_ARMOR_AVAIL))
651 if (krb5int_find_pa_data(context, padata, KRB5_PADATA_FX_FAST) != NULL) {
652 TRACE_FAST_PADATA_UPGRADE(context);
653 state->fast_state_flags |= KRB5INT_FAST_DO_FAST;