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.
35 * This function will find the fast and cookie padata and if fast is
36 * successfully processed, will throw away (and free) the outer
37 * request and update the pointer to point to the inner request. The
38 * checksummed_data points to the data that is in the
39 * armored_fast_request checksum; either the pa-tgs-req or the
43 static krb5_error_code armor_ap_request
44 (struct kdc_request_state *state, krb5_fast_armor *armor)
46 krb5_error_code retval = 0;
47 krb5_auth_context authcontext = NULL;
48 krb5_ticket *ticket = NULL;
49 krb5_keyblock *subkey = NULL;
51 assert(armor->armor_type == KRB5_FAST_ARMOR_AP_REQUEST);
52 krb5_clear_error_message(kdc_context);
53 retval = krb5_auth_con_init(kdc_context, &authcontext);
55 retval = krb5_auth_con_setflags(kdc_context,
56 authcontext, 0); /*disable replay cache*/
57 retval = krb5_rd_req(kdc_context, &authcontext,
58 &armor->armor_value, NULL /*server*/,
59 kdc_active_realm->realm_keytab, NULL, &ticket);
61 const char * errmsg = krb5_get_error_message(kdc_context, retval);
62 krb5_set_error_message(kdc_context, retval,
63 "%s while handling ap-request armor", errmsg);
64 krb5_free_error_message(kdc_context, errmsg);
67 if (!krb5_principal_compare_any_realm(kdc_context,
70 krb5_set_error_message(kdc_context, KRB5KDC_ERR_SERVER_NOMATCH,
71 "ap-request armor for something other than the local TGS");
72 retval = KRB5KDC_ERR_SERVER_NOMATCH;
76 retval = krb5_auth_con_getrecvsubkey(kdc_context, authcontext, &subkey);
77 if (retval != 0 || subkey == NULL) {
78 krb5_set_error_message(kdc_context, KRB5KDC_ERR_POLICY,
79 "ap-request armor without subkey");
80 retval = KRB5KDC_ERR_POLICY;
84 retval = krb5_c_fx_cf2_simple(kdc_context,
85 subkey, "subkeyarmor",
86 ticket->enc_part2->session, "ticketarmor",
89 krb5_free_ticket(kdc_context, ticket);
91 krb5_free_keyblock(kdc_context, subkey);
93 krb5_auth_con_free(kdc_context, authcontext);
97 static krb5_error_code
98 encrypt_fast_reply(struct kdc_request_state *state,
99 const krb5_fast_response *response,
100 krb5_data **fx_fast_reply)
102 krb5_error_code retval = 0;
103 krb5_enc_data encrypted_reply;
104 krb5_data *encoded_response = NULL;
105 assert(state->armor_key);
106 retval = encode_krb5_fast_response(response, &encoded_response);
108 retval = krb5_encrypt_helper(kdc_context, state->armor_key,
109 KRB5_KEYUSAGE_FAST_REP,
110 encoded_response, &encrypted_reply);
111 if (encoded_response)
112 krb5_free_data(kdc_context, encoded_response);
113 encoded_response = NULL;
115 retval = encode_krb5_pa_fx_fast_reply(&encrypted_reply,
117 krb5_free_data_contents(kdc_context, &encrypted_reply.ciphertext);
124 kdc_find_fast(krb5_kdc_req **requestptr,
125 krb5_data *checksummed_data,
126 krb5_keyblock *tgs_subkey,
127 krb5_keyblock *tgs_session,
128 struct kdc_request_state *state)
130 krb5_error_code retval = 0;
131 krb5_pa_data *fast_padata, *cookie_padata;
133 krb5_fast_req * fast_req = NULL;
134 krb5_kdc_req *request = *requestptr;
135 krb5_fast_armored_req *fast_armored_req = NULL;
136 krb5_boolean cksum_valid;
137 krb5_keyblock empty_keyblock;
140 krb5_clear_error_message(kdc_context);
141 memset(&empty_keyblock, 0, sizeof(krb5_keyblock));
142 fast_padata = find_pa_data(request->padata,
143 KRB5_PADATA_FX_FAST);
144 if (fast_padata != NULL){
145 scratch.length = fast_padata->length;
146 scratch.data = (char *) fast_padata->contents;
147 retval = decode_krb5_pa_fx_fast_request(&scratch, &fast_armored_req);
148 if (retval == 0 &&fast_armored_req->armor) {
149 switch (fast_armored_req->armor->armor_type) {
150 case KRB5_FAST_ARMOR_AP_REQUEST:
152 krb5_set_error_message( kdc_context, KRB5KDC_ERR_PREAUTH_FAILED,
153 "Ap-request armor not permitted with TGS");
154 return KRB5KDC_ERR_PREAUTH_FAILED;
156 retval = armor_ap_request(state, fast_armored_req->armor);
159 krb5_set_error_message(kdc_context, KRB5KDC_ERR_PREAUTH_FAILED,
160 "Unknow FAST armor type %d",
161 fast_armored_req->armor->armor_type);
162 retval = KRB5KDC_ERR_PREAUTH_FAILED;
165 if (retval == 0 && !state->armor_key) {
167 retval = krb5_c_fx_cf2_simple(kdc_context,
168 tgs_subkey, "subkeyarmor",
169 tgs_session, "ticketarmor",
172 krb5_set_error_message(kdc_context, KRB5KDC_ERR_PREAUTH_FAILED,
173 "No armor key but FAST armored request present");
174 retval = KRB5KDC_ERR_PREAUTH_FAILED;
179 plaintext.length = fast_armored_req->enc_part.ciphertext.length;
180 plaintext.data = malloc(plaintext.length);
181 if (plaintext.data == NULL)
183 retval = krb5_c_decrypt(kdc_context,
185 KRB5_KEYUSAGE_FAST_ENC, NULL,
186 &fast_armored_req->enc_part,
189 retval = decode_krb5_fast_req(&plaintext, &fast_req);
191 free(plaintext.data);
194 retval = krb5_c_verify_checksum(kdc_context, state->armor_key,
195 KRB5_KEYUSAGE_FAST_REQ_CHKSUM,
197 &fast_armored_req->req_checksum,
199 if (retval == 0 && !cksum_valid) {
200 retval = KRB5KRB_AP_ERR_MODIFIED;
201 krb5_set_error_message(kdc_context, KRB5KRB_AP_ERR_MODIFIED,
202 "FAST req_checksum invalid; request modified");
207 * We need to confirm that a keyed checksum is used for the
208 * fast_req checksum. In April 2009, the best way to do this is
209 * to try verifying the checksum with a keyblock with an zero
210 * length; if it succeeds, then an unkeyed checksum is used.
212 ret = krb5_c_verify_checksum(kdc_context, &empty_keyblock,
213 KRB5_KEYUSAGE_FAST_REQ_CHKSUM,
215 &fast_armored_req->req_checksum,
218 retval = KRB5KDC_ERR_POLICY;
219 krb5_set_error_message(kdc_context, KRB5KDC_ERR_POLICY,
220 "Unkeyed checksum used in fast_req");
224 if ((fast_req->fast_options & UNSUPPORTED_CRITICAL_FAST_OPTIONS) != 0)
225 retval = KRB5KDC_ERR_UNKNOWN_CRITICAL_FAST_OPTION;
228 cookie_padata = find_pa_data(fast_req->req_body->padata,
229 KRB5_PADATA_FX_COOKIE);
231 state->fast_options = fast_req->fast_options;
232 if (request->kdc_state == state)
233 request->kdc_state = NULL;
234 krb5_free_kdc_req( kdc_context, request);
235 *requestptr = fast_req->req_body;
236 fast_req->req_body = NULL;
239 else cookie_padata = find_pa_data(request->padata, KRB5_PADATA_FX_COOKIE);
240 if (retval == 0 && cookie_padata != NULL) {
241 krb5_pa_data *new_padata = malloc(sizeof (krb5_pa_data));
242 if (new_padata == NULL) {
245 new_padata->pa_type = KRB5_PADATA_FX_COOKIE;
246 new_padata->length = cookie_padata->length;
247 new_padata->contents = malloc(new_padata->length);
248 if (new_padata->contents == NULL) {
252 memcpy(new_padata->contents, cookie_padata->contents,
254 state->cookie = new_padata;
259 krb5_free_fast_req( kdc_context, fast_req);
260 if (fast_armored_req)
261 krb5_free_fast_armored_req(kdc_context, fast_armored_req);
267 kdc_make_rstate(struct kdc_request_state **out)
269 struct kdc_request_state *state = malloc( sizeof(struct kdc_request_state));
272 memset( state, 0, sizeof(struct kdc_request_state));
278 kdc_free_rstate (struct kdc_request_state *s)
283 krb5_free_keyblock(kdc_context, s->armor_key);
284 if (s->strengthen_key)
285 krb5_free_keyblock(kdc_context, s->strengthen_key);
287 free(s->cookie->contents);
294 kdc_fast_response_handle_padata(struct kdc_request_state *state,
295 krb5_kdc_req *request,
296 krb5_kdc_rep *rep, krb5_enctype enctype)
298 krb5_error_code retval = 0;
299 krb5_fast_finished finish;
300 krb5_fast_response fast_response;
301 krb5_data *encoded_ticket = NULL;
302 krb5_data *encrypted_reply = NULL;
303 krb5_pa_data *pa = NULL, **pa_array = NULL;
304 krb5_cksumtype cksumtype = CKSUMTYPE_RSA_MD5;
305 krb5_pa_data *empty_padata[] = {NULL};
306 krb5_keyblock *strengthen_key = NULL;
308 if (!state->armor_key)
310 memset(&finish, 0, sizeof(finish));
311 retval = krb5_init_keyblock(kdc_context, enctype, 0, &strengthen_key);
313 retval = krb5_c_make_random_key(kdc_context, enctype, strengthen_key);
315 state->strengthen_key = strengthen_key;
316 strengthen_key = NULL;
319 fast_response.padata = rep->padata;
320 if (fast_response.padata == NULL)
321 fast_response.padata = &empty_padata[0];
322 fast_response.strengthen_key = state->strengthen_key;
323 fast_response.nonce = request->nonce;
324 fast_response.finished = &finish;
325 finish.client = rep->client;
326 pa_array = calloc(3, sizeof(*pa_array));
327 if (pa_array == NULL)
329 pa = calloc(1, sizeof(krb5_pa_data));
330 if (retval == 0 && pa == NULL)
333 retval = krb5_us_timeofday(kdc_context, &finish.timestamp, &finish.usec);
335 retval = encode_krb5_ticket(rep->ticket, &encoded_ticket);
337 retval = krb5int_c_mandatory_cksumtype(kdc_context,
338 state->armor_key->enctype,
341 retval = krb5_c_make_checksum(kdc_context, cksumtype,
343 KRB5_KEYUSAGE_FAST_FINISHED,
344 encoded_ticket, &finish.ticket_checksum);
346 retval = encrypt_fast_reply(state, &fast_response, &encrypted_reply);
348 pa[0].pa_type = KRB5_PADATA_FX_FAST;
349 pa[0].length = encrypted_reply->length;
350 pa[0].contents = (unsigned char *) encrypted_reply->data;
351 pa_array[0] = &pa[0];
352 rep->padata = pa_array;
354 free(encrypted_reply);
355 encrypted_reply = NULL;
363 krb5_free_data(kdc_context, encrypted_reply);
365 krb5_free_data(kdc_context, encoded_ticket);
366 if (strengthen_key != NULL)
367 krb5_free_keyblock(kdc_context, strengthen_key);
368 if (finish.ticket_checksum.contents)
369 krb5_free_checksum_contents(kdc_context, &finish.ticket_checksum);
375 * We assume the caller is responsible for passing us an in_padata
376 * sufficient to include in a FAST error. In the FAST case we will
377 * throw away the e_data in the error (if any); in the non-FAST case
378 * we will not use the in_padata.
381 kdc_fast_handle_error(krb5_context context,
382 struct kdc_request_state *state,
383 krb5_kdc_req *request,
384 krb5_pa_data **in_padata, krb5_error *err)
386 krb5_error_code retval = 0;
387 krb5_fast_response resp;
389 krb5_data *encoded_fx_error = NULL, *encrypted_reply = NULL;
391 krb5_pa_data *outer_pa[3], *cookie = NULL;
392 krb5_pa_data **inner_pa = NULL;
394 krb5_data *encoded_e_data = NULL;
396 memset(outer_pa, 0, sizeof(outer_pa));
397 if (!state->armor_key)
400 fx_error.e_data.data = NULL;
401 fx_error.e_data.length = 0;
402 for (size = 0; in_padata&&in_padata[size]; size++);
404 inner_pa = calloc(size, sizeof(krb5_pa_data *));
405 if (inner_pa == NULL)
408 for (size=0; in_padata&&in_padata[size]; size++)
409 inner_pa[size] = in_padata[size];
411 retval = encode_krb5_error(&fx_error, &encoded_fx_error);
413 pa[0].pa_type = KRB5_PADATA_FX_ERROR;
414 pa[0].length = encoded_fx_error->length;
415 pa[0].contents = (unsigned char *) encoded_fx_error->data;
416 inner_pa[size++] = &pa[0];
417 if (find_pa_data(inner_pa, KRB5_PADATA_FX_COOKIE) == NULL)
418 retval = kdc_preauth_get_cookie(state, &cookie);
421 inner_pa[size++] = cookie;
423 resp.padata = inner_pa;
424 resp.nonce = request->nonce;
425 resp.strengthen_key = NULL;
426 resp.finished = NULL;
429 retval = encrypt_fast_reply(state, &resp, &encrypted_reply);
431 free(inner_pa); /*contained storage from caller and our stack*/
433 free(cookie->contents);
438 pa[0].pa_type = KRB5_PADATA_FX_FAST;
439 pa[0].length = encrypted_reply->length;
440 pa[0].contents = (unsigned char *) encrypted_reply->data;
441 outer_pa[0] = &pa[0];
443 retval = encode_krb5_padata_sequence(outer_pa, &encoded_e_data);
445 /*process_as holds onto a pointer to the original e_data and frees it*/
446 err->e_data = *encoded_e_data;
447 free(encoded_e_data); /*contents belong to err*/
448 encoded_e_data = NULL;
451 krb5_free_data(kdc_context, encoded_e_data);
453 krb5_free_data(kdc_context, encrypted_reply);
454 if (encoded_fx_error)
455 krb5_free_data(kdc_context, encoded_fx_error);
460 kdc_fast_handle_reply_key(struct kdc_request_state *state,
461 krb5_keyblock *existing_key,
462 krb5_keyblock **out_key)
464 krb5_error_code retval = 0;
465 if (state->armor_key)
466 retval = krb5_c_fx_cf2_simple(kdc_context,
467 state->strengthen_key, "strengthenkey",
469 "replykey", out_key);
471 retval = krb5_copy_keyblock(kdc_context, existing_key, out_key);
477 kdc_preauth_get_cookie(struct kdc_request_state *state,
478 krb5_pa_data **cookie)
481 krb5_pa_data *pa = NULL;
482 /* In our current implementation, the only purpose served by
483 * returning a cookie is to indicate that a conversation should
484 * continue on error. Thus, the cookie can have a constant
485 * string. If cookies are used for real, versioning so that KDCs
486 * can be upgraded, keying, expiration and many other issues need
489 contents = strdup("MIT");
490 if (contents == NULL)
492 pa = calloc(1, sizeof(krb5_pa_data));
497 pa->pa_type = KRB5_PADATA_FX_COOKIE;
498 pa->length = strlen(contents);
499 pa->contents = (unsigned char *) contents;