1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
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.
34 * This function will find the fast and cookie padata and if fast is
35 * successfully processed, will throw away (and free) the outer
36 * request and update the pointer to point to the inner request. The
37 * checksummed_data points to the data that is in the
38 * armored_fast_request checksum; either the pa-tgs-req or the
42 static krb5_error_code armor_ap_request
43 (struct kdc_request_state *state, krb5_fast_armor *armor)
45 krb5_error_code retval = 0;
46 krb5_auth_context authcontext = NULL;
47 krb5_ticket *ticket = NULL;
48 krb5_keyblock *subkey = NULL;
50 assert(armor->armor_type == KRB5_FAST_ARMOR_AP_REQUEST);
51 krb5_clear_error_message(kdc_context);
52 retval = krb5_auth_con_init(kdc_context, &authcontext);
54 retval = krb5_auth_con_setflags(kdc_context,
55 authcontext, 0); /*disable replay cache*/
56 retval = krb5_rd_req(kdc_context, &authcontext,
57 &armor->armor_value, NULL /*server*/,
58 kdc_active_realm->realm_keytab, NULL, &ticket);
60 const char * errmsg = krb5_get_error_message(kdc_context, retval);
61 krb5_set_error_message(kdc_context, retval,
62 _("%s while handling ap-request armor"),
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 "
72 "than the local TGS"));
73 retval = KRB5KDC_ERR_SERVER_NOMATCH;
77 retval = krb5_auth_con_getrecvsubkey(kdc_context, authcontext, &subkey);
78 if (retval != 0 || subkey == NULL) {
79 krb5_set_error_message(kdc_context, KRB5KDC_ERR_POLICY,
80 _("ap-request armor without subkey"));
81 retval = KRB5KDC_ERR_POLICY;
85 retval = krb5_c_fx_cf2_simple(kdc_context,
86 subkey, "subkeyarmor",
87 ticket->enc_part2->session, "ticketarmor",
90 krb5_free_ticket(kdc_context, ticket);
92 krb5_free_keyblock(kdc_context, subkey);
94 krb5_auth_con_free(kdc_context, authcontext);
98 static krb5_error_code
99 encrypt_fast_reply(struct kdc_request_state *state,
100 const krb5_fast_response *response,
101 krb5_data **fx_fast_reply)
103 krb5_error_code retval = 0;
104 krb5_enc_data encrypted_reply;
105 krb5_data *encoded_response = NULL;
106 assert(state->armor_key);
107 retval = encode_krb5_fast_response(response, &encoded_response);
109 retval = krb5_encrypt_helper(kdc_context, state->armor_key,
110 KRB5_KEYUSAGE_FAST_REP,
111 encoded_response, &encrypted_reply);
112 if (encoded_response)
113 krb5_free_data(kdc_context, encoded_response);
114 encoded_response = NULL;
116 retval = encode_krb5_pa_fx_fast_reply(&encrypted_reply,
118 krb5_free_data_contents(kdc_context, &encrypted_reply.ciphertext);
125 kdc_find_fast(krb5_kdc_req **requestptr,
126 krb5_data *checksummed_data,
127 krb5_keyblock *tgs_subkey,
128 krb5_keyblock *tgs_session,
129 struct kdc_request_state *state,
130 krb5_data **inner_body_out)
132 krb5_error_code retval = 0;
133 krb5_pa_data *fast_padata, *cookie_padata = NULL;
134 krb5_data scratch, *inner_body = NULL;
135 krb5_fast_req * fast_req = NULL;
136 krb5_kdc_req *request = *requestptr;
137 krb5_fast_armored_req *fast_armored_req = NULL;
138 krb5_checksum *cksum;
139 krb5_boolean cksum_valid;
140 krb5_keyblock empty_keyblock;
142 if (inner_body_out != NULL)
143 *inner_body_out = NULL;
145 krb5_clear_error_message(kdc_context);
146 memset(&empty_keyblock, 0, sizeof(krb5_keyblock));
147 fast_padata = find_pa_data(request->padata,
148 KRB5_PADATA_FX_FAST);
149 if (fast_padata != NULL){
150 scratch.length = fast_padata->length;
151 scratch.data = (char *) fast_padata->contents;
152 retval = decode_krb5_pa_fx_fast_request(&scratch, &fast_armored_req);
153 if (retval == 0 &&fast_armored_req->armor) {
154 switch (fast_armored_req->armor->armor_type) {
155 case KRB5_FAST_ARMOR_AP_REQUEST:
157 retval = KRB5KDC_ERR_PREAUTH_FAILED;
158 krb5_set_error_message(kdc_context, retval,
159 _("Ap-request armor not permitted "
163 retval = armor_ap_request(state, fast_armored_req->armor);
166 krb5_set_error_message(kdc_context, KRB5KDC_ERR_PREAUTH_FAILED,
167 _("Unknown FAST armor type %d"),
168 fast_armored_req->armor->armor_type);
169 retval = KRB5KDC_ERR_PREAUTH_FAILED;
172 if (retval == 0 && !state->armor_key) {
174 retval = krb5_c_fx_cf2_simple(kdc_context,
175 tgs_subkey, "subkeyarmor",
176 tgs_session, "ticketarmor",
179 retval = KRB5KDC_ERR_PREAUTH_FAILED;
180 krb5_set_error_message(kdc_context, retval,
181 _("No armor key but FAST armored "
187 plaintext.length = fast_armored_req->enc_part.ciphertext.length;
188 plaintext.data = malloc(plaintext.length);
189 if (plaintext.data == NULL)
191 retval = krb5_c_decrypt(kdc_context,
193 KRB5_KEYUSAGE_FAST_ENC, NULL,
194 &fast_armored_req->enc_part,
197 retval = decode_krb5_fast_req(&plaintext, &fast_req);
198 if (retval == 0 && inner_body_out != NULL) {
199 retval = fetch_asn1_field((unsigned char *)plaintext.data,
202 retval = krb5_copy_data(kdc_context, &scratch,
207 free(plaintext.data);
209 cksum = &fast_armored_req->req_checksum;
211 retval = krb5_c_verify_checksum(kdc_context, state->armor_key,
212 KRB5_KEYUSAGE_FAST_REQ_CHKSUM,
213 checksummed_data, cksum,
215 if (retval == 0 && !cksum_valid) {
216 retval = KRB5KRB_AP_ERR_MODIFIED;
217 krb5_set_error_message(kdc_context, retval,
218 _("FAST req_checksum invalid; request "
222 if (!krb5_c_is_keyed_cksum(cksum->checksum_type)) {
223 retval = KRB5KDC_ERR_POLICY;
224 krb5_set_error_message(kdc_context, retval,
225 _("Unkeyed checksum used in fast_req"));
229 if ((fast_req->fast_options & UNSUPPORTED_CRITICAL_FAST_OPTIONS) != 0)
230 retval = KRB5KDC_ERR_UNKNOWN_CRITICAL_FAST_OPTION;
233 cookie_padata = find_pa_data(fast_req->req_body->padata,
234 KRB5_PADATA_FX_COOKIE);
236 state->fast_options = fast_req->fast_options;
237 krb5_free_kdc_req( kdc_context, request);
238 *requestptr = fast_req->req_body;
239 fast_req->req_body = NULL;
242 else cookie_padata = find_pa_data(request->padata, KRB5_PADATA_FX_COOKIE);
243 if (retval == 0 && cookie_padata != NULL) {
244 krb5_pa_data *new_padata = malloc(sizeof (krb5_pa_data));
245 if (new_padata == NULL) {
248 new_padata->pa_type = KRB5_PADATA_FX_COOKIE;
249 new_padata->length = cookie_padata->length;
250 new_padata->contents = malloc(new_padata->length);
251 if (new_padata->contents == NULL) {
255 memcpy(new_padata->contents, cookie_padata->contents,
257 state->cookie = new_padata;
261 if (retval == 0 && inner_body_out != NULL) {
262 *inner_body_out = inner_body;
265 krb5_free_data(kdc_context, inner_body);
267 krb5_free_fast_req( kdc_context, fast_req);
268 if (fast_armored_req)
269 krb5_free_fast_armored_req(kdc_context, fast_armored_req);
275 kdc_make_rstate(struct kdc_request_state **out)
277 struct kdc_request_state *state = malloc( sizeof(struct kdc_request_state));
280 memset( state, 0, sizeof(struct kdc_request_state));
286 kdc_free_rstate (struct kdc_request_state *s)
291 krb5_free_keyblock(kdc_context, s->armor_key);
292 if (s->strengthen_key)
293 krb5_free_keyblock(kdc_context, s->strengthen_key);
295 free(s->cookie->contents);
302 kdc_fast_response_handle_padata(struct kdc_request_state *state,
303 krb5_kdc_req *request,
304 krb5_kdc_rep *rep, krb5_enctype enctype)
306 krb5_error_code retval = 0;
307 krb5_fast_finished finish;
308 krb5_fast_response fast_response;
309 krb5_data *encoded_ticket = NULL;
310 krb5_data *encrypted_reply = NULL;
311 krb5_pa_data *pa = NULL, **pa_array = NULL;
312 krb5_cksumtype cksumtype = CKSUMTYPE_RSA_MD5;
313 krb5_pa_data *empty_padata[] = {NULL};
314 krb5_keyblock *strengthen_key = NULL;
316 if (!state->armor_key)
318 memset(&finish, 0, sizeof(finish));
319 retval = krb5_init_keyblock(kdc_context, enctype, 0, &strengthen_key);
321 retval = krb5_c_make_random_key(kdc_context, enctype, strengthen_key);
323 state->strengthen_key = strengthen_key;
324 strengthen_key = NULL;
327 fast_response.padata = rep->padata;
328 if (fast_response.padata == NULL)
329 fast_response.padata = &empty_padata[0];
330 fast_response.strengthen_key = state->strengthen_key;
331 fast_response.nonce = request->nonce;
332 fast_response.finished = &finish;
333 finish.client = rep->client;
334 pa_array = calloc(3, sizeof(*pa_array));
335 if (pa_array == NULL)
337 pa = calloc(1, sizeof(krb5_pa_data));
338 if (retval == 0 && pa == NULL)
341 retval = krb5_us_timeofday(kdc_context, &finish.timestamp, &finish.usec);
343 retval = encode_krb5_ticket(rep->ticket, &encoded_ticket);
345 retval = krb5int_c_mandatory_cksumtype(kdc_context,
346 state->armor_key->enctype,
349 retval = krb5_c_make_checksum(kdc_context, cksumtype,
351 KRB5_KEYUSAGE_FAST_FINISHED,
352 encoded_ticket, &finish.ticket_checksum);
354 retval = encrypt_fast_reply(state, &fast_response, &encrypted_reply);
356 pa[0].pa_type = KRB5_PADATA_FX_FAST;
357 pa[0].length = encrypted_reply->length;
358 pa[0].contents = (unsigned char *) encrypted_reply->data;
359 pa_array[0] = &pa[0];
360 krb5_free_pa_data(kdc_context, rep->padata);
361 rep->padata = pa_array;
363 free(encrypted_reply);
364 encrypted_reply = NULL;
372 krb5_free_data(kdc_context, encrypted_reply);
374 krb5_free_data(kdc_context, encoded_ticket);
375 if (strengthen_key != NULL)
376 krb5_free_keyblock(kdc_context, strengthen_key);
377 if (finish.ticket_checksum.contents)
378 krb5_free_checksum_contents(kdc_context, &finish.ticket_checksum);
384 * We assume the caller is responsible for passing us an in_padata
385 * sufficient to include in a FAST error. In the FAST case we will
386 * set *fast_edata_out to the edata to be included in the error; in
387 * the non-FAST case we will set it to NULL.
390 kdc_fast_handle_error(krb5_context context,
391 struct kdc_request_state *state,
392 krb5_kdc_req *request,
393 krb5_pa_data **in_padata, krb5_error *err,
394 krb5_data **fast_edata_out)
396 krb5_error_code retval = 0;
397 krb5_fast_response resp;
399 krb5_data *encoded_fx_error = NULL, *encrypted_reply = NULL;
401 krb5_pa_data *outer_pa[3], *cookie = NULL;
402 krb5_pa_data **inner_pa = NULL;
405 *fast_edata_out = NULL;
406 memset(outer_pa, 0, sizeof(outer_pa));
407 if (!state || !state->armor_key)
410 fx_error.e_data.data = NULL;
411 fx_error.e_data.length = 0;
412 for (size = 0; in_padata&&in_padata[size]; size++);
414 inner_pa = calloc(size, sizeof(krb5_pa_data *));
415 if (inner_pa == NULL)
418 for (size=0; in_padata&&in_padata[size]; size++)
419 inner_pa[size] = in_padata[size];
421 retval = encode_krb5_error(&fx_error, &encoded_fx_error);
423 pa[0].pa_type = KRB5_PADATA_FX_ERROR;
424 pa[0].length = encoded_fx_error->length;
425 pa[0].contents = (unsigned char *) encoded_fx_error->data;
426 inner_pa[size++] = &pa[0];
427 if (find_pa_data(inner_pa, KRB5_PADATA_FX_COOKIE) == NULL)
428 retval = kdc_preauth_get_cookie(state, &cookie);
431 inner_pa[size++] = cookie;
433 resp.padata = inner_pa;
434 resp.nonce = request->nonce;
435 resp.strengthen_key = NULL;
436 resp.finished = NULL;
439 retval = encrypt_fast_reply(state, &resp, &encrypted_reply);
441 free(inner_pa); /*contained storage from caller and our stack*/
443 free(cookie->contents);
448 pa[0].pa_type = KRB5_PADATA_FX_FAST;
449 pa[0].length = encrypted_reply->length;
450 pa[0].contents = (unsigned char *) encrypted_reply->data;
451 outer_pa[0] = &pa[0];
453 retval = encode_krb5_padata_sequence(outer_pa, fast_edata_out);
455 krb5_free_data(kdc_context, encrypted_reply);
456 if (encoded_fx_error)
457 krb5_free_data(kdc_context, encoded_fx_error);
462 kdc_fast_handle_reply_key(struct kdc_request_state *state,
463 krb5_keyblock *existing_key,
464 krb5_keyblock **out_key)
466 krb5_error_code retval = 0;
467 if (state->armor_key)
468 retval = krb5_c_fx_cf2_simple(kdc_context,
469 state->strengthen_key, "strengthenkey",
471 "replykey", out_key);
473 retval = krb5_copy_keyblock(kdc_context, existing_key, out_key);
479 kdc_preauth_get_cookie(struct kdc_request_state *state,
480 krb5_pa_data **cookie)
483 krb5_pa_data *pa = NULL;
484 /* In our current implementation, the only purpose served by
485 * returning a cookie is to indicate that a conversation should
486 * continue on error. Thus, the cookie can have a constant
487 * string. If cookies are used for real, versioning so that KDCs
488 * can be upgraded, keying, expiration and many other issues need
491 contents = strdup("MIT");
492 if (contents == NULL)
494 pa = calloc(1, sizeof(krb5_pa_data));
499 pa->pa_type = KRB5_PADATA_FX_COOKIE;
500 pa->length = strlen(contents);
501 pa->contents = (unsigned char *) contents;