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
35 * FAST and normal version. This would complicate the
36 * pre-authentication logic significantly. You would need to maintain
37 * two contexts, one for FAST and one for normal use. In adition, you
38 * would need to manage the security issues surrounding downgrades.
39 * However trying FAST at all requires an armor key. Generally in
40 * obtaining the armor key, the client learns enough to know that FAST
41 * is supported. If not, the client can see FAST in the
42 * preauth_required error's padata and retry with FAST. So, this
43 * implementation does not support FAST+normal.
45 * We store the outer version of the request to use . The caller
46 * stores the inner version. We handle the encoding of the request
47 * body (and request) and provide encoded request bodies for the
48 * caller to use as these may be used for checksums. In the AS case
49 * we also evaluate whether to continue a conversation as one of the
50 * important questions there is the presence of a cookie.
53 #include "int-proto.h"
56 static krb5_error_code fast_armor_ap_request
57 (krb5_context context, struct krb5int_fast_request_state *state,
58 krb5_ccache ccache, krb5_data *target_realm)
60 krb5_error_code retval = 0;
61 krb5_creds creds, *out_creds = NULL;
62 krb5_auth_context authcontext = NULL;
63 krb5_data encoded_authenticator;
64 krb5_fast_armor *armor = NULL;
65 krb5_keyblock *subkey = NULL, *armor_key = NULL;
66 encoded_authenticator.data = NULL;
67 memset(&creds, 0, sizeof(creds));
68 retval = krb5_tgtname(context, target_realm, target_realm, &creds.server);
70 retval = krb5_cc_get_principal(context, ccache, &creds.client);
72 retval = krb5_get_credentials(context, 0, ccache, &creds, &out_creds);
74 retval = krb5_mk_req_extended(context, &authcontext, AP_OPTS_USE_SUBKEY, NULL /*data*/,
75 out_creds, &encoded_authenticator);
77 retval = krb5_auth_con_getsendsubkey(context, authcontext, &subkey);
79 retval = krb5_c_fx_cf2_simple(context, subkey, "subkeyarmor",
80 &out_creds->keyblock, "ticketarmor", &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 krb5_free_cred_contents(context, &creds);
101 if (encoded_authenticator.data)
102 krb5_free_data_contents(context, &encoded_authenticator);
103 krb5_auth_con_free(context, authcontext);
108 krb5int_fast_prep_req_body(krb5_context context, struct krb5int_fast_request_state *state,
109 krb5_kdc_req *request, krb5_data **encoded_request_body)
111 krb5_error_code retval = 0;
112 krb5_data *local_encoded_request_body = NULL;
113 assert(state != NULL);
114 *encoded_request_body = NULL;
115 if (state->armor_key == NULL) {
116 return encode_krb5_kdc_req_body(request, encoded_request_body);
118 state->fast_outer_request = *request;
119 state->fast_outer_request.padata = NULL;
121 retval = encode_krb5_kdc_req_body(&state->fast_outer_request,
122 &local_encoded_request_body);
124 *encoded_request_body = local_encoded_request_body;
125 local_encoded_request_body = NULL;
127 if (local_encoded_request_body != NULL)
128 krb5_free_data(context, local_encoded_request_body);
132 krb5_error_code krb5int_fast_as_armor
133 (krb5_context context, struct krb5int_fast_request_state *state,
134 krb5_gic_opt_ext *opte,
135 krb5_kdc_req *request)
137 krb5_error_code retval = 0;
138 krb5_ccache ccache = NULL;
139 krb5_clear_error_message(context);
140 if (opte->opt_private->fast_ccache_name) {
141 retval = krb5_cc_resolve(context, opte->opt_private->fast_ccache_name,
144 retval = fast_armor_ap_request(context, state, ccache,
145 krb5_princ_realm(context, request->server));
148 errmsg = krb5_get_error_message(context, retval);
150 krb5_set_error_message(context, retval, "%s constructing AP-REQ armor", errmsg);
151 krb5_free_error_message(context, errmsg);
156 krb5_cc_close(context, ccache);
162 krb5int_fast_prep_req (krb5_context context, struct krb5int_fast_request_state *state,
163 krb5_kdc_req *request,
164 const krb5_data *to_be_checksummed, kdc_req_encoder_proc encoder,
165 krb5_data **encoded_request)
167 krb5_error_code retval = 0;
168 krb5_pa_data *pa_array[2];
170 krb5_fast_req fast_req;
171 krb5_fast_armored_req *armored_req = NULL;
172 krb5_data *encoded_fast_req = NULL;
173 krb5_data *encoded_armored_req = NULL;
174 krb5_data *local_encoded_result = NULL;
175 krb5_cksumtype cksumtype;
176 krb5_data random_data;
180 assert(state != NULL);
181 assert(state->fast_outer_request.padata == NULL);
182 memset(pa_array, 0, sizeof pa_array);
183 if (state->armor_key == NULL) {
184 return encoder(request, encoded_request);
186 /* Fill in a fresh random nonce for each inner request*/
187 random_data.length = 4;
188 random_data.data = (char *)random_buf;
189 retval = krb5_c_random_make_octets(context, &random_data);
191 request->nonce = 0x7fffffff & load_32_n(random_buf);
192 state->nonce = request->nonce;
194 fast_req.req_body = request;
195 if (fast_req.req_body->padata == NULL) {
196 fast_req.req_body->padata = calloc(1, sizeof(krb5_pa_data *));
197 if (fast_req.req_body->padata == NULL)
200 fast_req.fast_options = state->fast_options;
202 retval = encode_krb5_fast_req(&fast_req, &encoded_fast_req);
204 armored_req = calloc(1, sizeof(krb5_fast_armored_req));
205 if (armored_req == NULL)
209 armored_req->armor = state->armor;
211 retval = krb5int_c_mandatory_cksumtype(context, state->armor_key->enctype,
214 retval = krb5_c_make_checksum(context, cksumtype, state->armor_key,
215 KRB5_KEYUSAGE_FAST_REQ_CHKSUM, to_be_checksummed,
216 &armored_req->req_checksum);
218 retval = krb5_encrypt_helper(context, state->armor_key,
219 KRB5_KEYUSAGE_FAST_ENC, encoded_fast_req,
220 &armored_req->enc_part);
222 retval = encode_krb5_pa_fx_fast_request(armored_req, &encoded_armored_req);
224 pa[0].pa_type = KRB5_PADATA_FX_FAST;
225 pa[0].contents = (unsigned char *) encoded_armored_req->data;
226 pa[0].length = encoded_armored_req->length;
227 pa_array[0] = &pa[0];
229 state->fast_outer_request.padata = pa_array;
231 retval = encoder(&state->fast_outer_request, &local_encoded_result);
233 *encoded_request = local_encoded_result;
234 local_encoded_result = NULL;
236 if (encoded_armored_req)
237 krb5_free_data(context, encoded_armored_req);
239 armored_req->armor = NULL; /*owned by state*/
240 krb5_free_fast_armored_req(context, armored_req);
242 if (encoded_fast_req)
243 krb5_free_data(context, encoded_fast_req);
244 if (local_encoded_result)
245 krb5_free_data(context, local_encoded_result);
246 state->fast_outer_request.padata = NULL;
250 static krb5_error_code decrypt_fast_reply
251 (krb5_context context, struct krb5int_fast_request_state *state,
252 krb5_pa_data **in_padata,
253 krb5_fast_response **response)
255 krb5_error_code retval = 0;
257 krb5_enc_data *encrypted_response = NULL;
258 krb5_pa_data *fx_reply = NULL;
259 krb5_fast_response *local_resp = NULL;
260 assert(state != NULL);
261 assert(state->armor_key);
262 fx_reply = krb5int_find_pa_data(context, in_padata, KRB5_PADATA_FX_FAST);
263 if (fx_reply == NULL)
264 retval = KRB5_ERR_FAST_REQUIRED;
266 scratch.data = (char *) fx_reply->contents;
267 scratch.length = fx_reply->length;
268 retval = decode_krb5_pa_fx_fast_reply(&scratch, &encrypted_response);
272 scratch.data = malloc(encrypted_response->ciphertext.length);
273 if (scratch.data == NULL)
275 scratch.length = encrypted_response->ciphertext.length;
278 retval = krb5_c_decrypt(context, state->armor_key,
279 KRB5_KEYUSAGE_FAST_REP, NULL,
280 encrypted_response, &scratch);
283 errmsg = krb5_get_error_message(context, retval);
284 krb5_set_error_message(context, retval, "%s while decrypting FAST reply", errmsg);
285 krb5_free_error_message(context, errmsg);
288 retval = decode_krb5_fast_response(&scratch, &local_resp);
290 if (local_resp->nonce != state->nonce) {
291 retval = KRB5_KDCREP_MODIFIED;
292 krb5_set_error_message(context, retval, "nonce modified in FAST response: KDC response modified");
296 *response = local_resp;
301 if (encrypted_response)
302 krb5_free_enc_data(context, encrypted_response);
304 krb5_free_fast_response(context, local_resp);
309 * FAST separates two concepts: the set of padata we're using to
310 * decide what pre-auth mechanisms to use and the set of padata we're
311 * making available to mechanisms in order for them to respond to an
312 * error. The plugin interface in March 2009 does not permit
313 * separating these concepts for the plugins. This function makes
314 * both available for future revisions to the plugin interface. It
315 * also re-encodes the padata from the current error as a encoded
316 * typed-data and puts that in the e_data field. That will allow
317 * existing plugins with the old interface to find the error data.
318 * The output parameter out_padata contains the padata from the error
319 * whenever padata is available (all the time with fast).
322 krb5int_fast_process_error(krb5_context context, struct krb5int_fast_request_state *state,
323 krb5_error **err_replyptr , krb5_pa_data ***out_padata,
326 krb5_error_code retval = 0;
327 krb5_error *err_reply = *err_replyptr;
330 if (state->armor_key) {
331 krb5_pa_data *fx_error_pa;
332 krb5_pa_data **result = NULL;
333 krb5_data scratch, *encoded_td = NULL;
334 krb5_error *fx_error = NULL;
335 krb5_fast_response *fast_response = NULL;
336 retval = decode_krb5_padata_sequence(&err_reply->e_data, &result);
338 retval = decrypt_fast_reply(context, state, result, &fast_response);
340 /*This can happen if the KDC does not understand FAST. We
341 * don't expect that, but treating it as the fatal error
342 * indicated by the KDC seems reasonable.
345 krb5_free_pa_data(context, result);
348 krb5_free_pa_data(context, result);
351 fx_error_pa = krb5int_find_pa_data(context, fast_response->padata, KRB5_PADATA_FX_ERROR);
352 if (fx_error_pa == NULL) {
353 krb5_set_error_message(context, KRB5KDC_ERR_PREAUTH_FAILED, "Expecting FX_ERROR pa-data inside FAST container");
354 retval = KRB5KDC_ERR_PREAUTH_FAILED;
358 scratch.data = (char *) fx_error_pa->contents;
359 scratch.length = fx_error_pa->length;
360 retval = decode_krb5_error(&scratch, &fx_error);
363 * krb5_pa_data and krb5_typed_data are safe to cast between:
364 * they have the same type fields in the same order.
365 * (krb5_preauthtype is a krb5_int32). If krb5_typed_data is
366 * ever changed then this will need to be a copy not a cast.
369 retval = encode_krb5_typed_data( (krb5_typed_data **) fast_response->padata,
372 fx_error->e_data = *encoded_td;
373 free(encoded_td); /*contents owned by fx_error*/
375 krb5_free_error(context, err_reply);
376 *err_replyptr = fx_error;
378 *out_padata = fast_response->padata;
379 fast_response->padata = NULL;
381 * If there is more than the fx_error padata, then we want
382 * to retry the error if a cookie is present
384 *retry = (*out_padata)[1] != NULL;
385 if (krb5int_find_pa_data(context, *out_padata, KRB5_PADATA_FX_COOKIE) == NULL)
389 krb5_free_error(context, fx_error);
390 krb5_free_fast_response(context, fast_response);
391 } else { /*not FAST*/
392 *retry = (err_reply->e_data.length > 0);
393 if ((err_reply->error == KDC_ERR_PREAUTH_REQUIRED
394 ||err_reply->error == KDC_ERR_PREAUTH_FAILED) && err_reply->e_data.length) {
395 krb5_pa_data **result = NULL;
396 retval = decode_krb5_padata_sequence(&err_reply->e_data, &result);
399 *out_padata = result;
403 krb5_free_pa_data(context, result);
404 krb5_set_error_message(context, retval,
405 "Error decoding padata in error reply");
413 krb5_error_code krb5int_fast_process_response
414 (krb5_context context, struct krb5int_fast_request_state *state,
416 krb5_keyblock **strengthen_key)
418 krb5_error_code retval = 0;
419 krb5_fast_response *fast_response = NULL;
420 krb5_data *encoded_ticket = NULL;
421 krb5_boolean cksum_valid;
422 krb5_clear_error_message(context);
423 *strengthen_key = NULL;
424 if (state->armor_key == 0)
426 retval = decrypt_fast_reply(context, state, resp->padata,
429 if (fast_response->finished == 0) {
430 retval = KRB5_KDCREP_MODIFIED;
431 krb5_set_error_message(context, retval, "FAST response missing finish message in KDC reply");
435 retval = encode_krb5_ticket(resp->ticket, &encoded_ticket);
437 retval = krb5_c_verify_checksum(context, state->armor_key,
438 KRB5_KEYUSAGE_FAST_FINISHED,
440 &fast_response->finished->ticket_checksum,
442 if (retval == 0 && cksum_valid == 0) {
443 retval = KRB5_KDCREP_MODIFIED;
444 krb5_set_error_message(context, retval, "ticket modified in KDC reply");
447 krb5_free_principal(context, resp->client);
448 resp->client = fast_response->finished->client;
449 fast_response->finished->client = NULL;
450 *strengthen_key = fast_response->strengthen_key;
451 fast_response->strengthen_key = NULL;
452 krb5_free_pa_data(context, resp->padata);
453 resp->padata = fast_response->padata;
454 fast_response->padata = NULL;
457 krb5_free_fast_response(context, fast_response);
459 krb5_free_data(context, encoded_ticket);
463 krb5_error_code krb5int_fast_reply_key(krb5_context context,
464 krb5_keyblock *strengthen_key,
465 krb5_keyblock *existing_key,
466 krb5_keyblock *out_key)
468 krb5_keyblock *key = NULL;
469 krb5_error_code retval = 0;
470 krb5_free_keyblock_contents(context, out_key);
471 if (strengthen_key) {
472 retval = krb5_c_fx_cf2_simple(context, strengthen_key,
473 "strengthenkey", existing_key, "replykey", &key);
479 retval = krb5_copy_keyblock_contents(context, existing_key, out_key);
486 krb5int_fast_make_state( krb5_context context, struct krb5int_fast_request_state **state)
488 struct krb5int_fast_request_state *local_state ;
489 local_state = malloc(sizeof *local_state);
490 if (local_state == NULL)
492 memset(local_state, 0, sizeof(*local_state));
493 *state = local_state;
498 krb5int_fast_free_state( krb5_context context, struct krb5int_fast_request_state *state)
500 /*We are responsible for none of the store in the fast_outer_req*/
501 krb5_free_keyblock(context, state->armor_key);
502 krb5_free_fast_armor(context, state->armor);
506 krb5_pa_data * krb5int_find_pa_data
507 (krb5_context context, krb5_pa_data *const *padata, krb5_preauthtype pa_type)
509 krb5_pa_data * const *tmppa;
514 for (tmppa = padata; *tmppa != NULL; tmppa++) {
515 if ((*tmppa)->pa_type == pa_type)