1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
4 * Copyright (C) 2009, 2015 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.
32 /* Let cookies be valid for ten minutes. */
33 #define COOKIE_LIFETIME 600
35 static krb5_error_code armor_ap_request
36 (struct kdc_request_state *state, krb5_fast_armor *armor)
38 krb5_error_code retval = 0;
39 krb5_auth_context authcontext = NULL;
40 krb5_ticket *ticket = NULL;
41 krb5_keyblock *subkey = NULL;
42 kdc_realm_t *kdc_active_realm = state->realm_data;
44 assert(armor->armor_type == KRB5_FAST_ARMOR_AP_REQUEST);
45 krb5_clear_error_message(kdc_context);
46 retval = krb5_auth_con_init(kdc_context, &authcontext);
48 retval = krb5_auth_con_setflags(kdc_context,
49 authcontext, 0); /*disable replay cache*/
50 retval = krb5_rd_req(kdc_context, &authcontext,
51 &armor->armor_value, NULL /*server*/,
52 kdc_active_realm->realm_keytab, NULL, &ticket);
54 const char * errmsg = krb5_get_error_message(kdc_context, retval);
55 k5_setmsg(kdc_context, retval, _("%s while handling ap-request armor"),
57 krb5_free_error_message(kdc_context, errmsg);
60 if (!krb5_principal_compare_any_realm(kdc_context,
63 k5_setmsg(kdc_context, KRB5KDC_ERR_SERVER_NOMATCH,
64 _("ap-request armor for something other than the local "
66 retval = KRB5KDC_ERR_SERVER_NOMATCH;
70 retval = krb5_auth_con_getrecvsubkey(kdc_context, authcontext, &subkey);
71 if (retval != 0 || subkey == NULL) {
72 k5_setmsg(kdc_context, KRB5KDC_ERR_POLICY,
73 _("ap-request armor without subkey"));
74 retval = KRB5KDC_ERR_POLICY;
78 retval = krb5_c_fx_cf2_simple(kdc_context,
79 subkey, "subkeyarmor",
80 ticket->enc_part2->session, "ticketarmor",
83 krb5_free_ticket(kdc_context, ticket);
85 krb5_free_keyblock(kdc_context, subkey);
87 krb5_auth_con_free(kdc_context, authcontext);
91 static krb5_error_code
92 encrypt_fast_reply(struct kdc_request_state *state,
93 const krb5_fast_response *response,
94 krb5_data **fx_fast_reply)
96 krb5_error_code retval = 0;
97 krb5_enc_data encrypted_reply;
98 krb5_data *encoded_response = NULL;
99 kdc_realm_t *kdc_active_realm = state->realm_data;
101 assert(state->armor_key);
102 retval = encode_krb5_fast_response(response, &encoded_response);
104 retval = krb5_encrypt_helper(kdc_context, state->armor_key,
105 KRB5_KEYUSAGE_FAST_REP,
106 encoded_response, &encrypted_reply);
107 if (encoded_response)
108 krb5_free_data(kdc_context, encoded_response);
109 encoded_response = NULL;
111 retval = encode_krb5_pa_fx_fast_reply(&encrypted_reply,
113 krb5_free_data_contents(kdc_context, &encrypted_reply.ciphertext);
120 * This function will find the FAST padata and, if FAST is successfully
121 * processed, will free the outer request and update the pointer to point to
122 * the inner request. checksummed_data points to the data that is in the
123 * armored_fast_request checksum; either the pa-tgs-req or the kdc-req-body.
126 kdc_find_fast(krb5_kdc_req **requestptr,
127 krb5_data *checksummed_data,
128 krb5_keyblock *tgs_subkey,
129 krb5_keyblock *tgs_session,
130 struct kdc_request_state *state,
131 krb5_data **inner_body_out)
133 krb5_error_code retval = 0;
134 krb5_pa_data *fast_padata;
135 krb5_data scratch, *inner_body = NULL;
136 krb5_fast_req * fast_req = NULL;
137 krb5_kdc_req *request = *requestptr;
138 krb5_fast_armored_req *fast_armored_req = NULL;
139 krb5_checksum *cksum;
140 krb5_boolean cksum_valid;
141 krb5_keyblock empty_keyblock;
142 kdc_realm_t *kdc_active_realm = state->realm_data;
144 if (inner_body_out != NULL)
145 *inner_body_out = NULL;
147 krb5_clear_error_message(kdc_context);
148 memset(&empty_keyblock, 0, sizeof(krb5_keyblock));
149 fast_padata = krb5int_find_pa_data(kdc_context,
150 request->padata, KRB5_PADATA_FX_FAST);
151 if (fast_padata != NULL){
152 scratch.length = fast_padata->length;
153 scratch.data = (char *) fast_padata->contents;
154 retval = decode_krb5_pa_fx_fast_request(&scratch, &fast_armored_req);
155 if (retval == 0 &&fast_armored_req->armor) {
156 switch (fast_armored_req->armor->armor_type) {
157 case KRB5_FAST_ARMOR_AP_REQUEST:
159 retval = KRB5KDC_ERR_PREAUTH_FAILED;
160 k5_setmsg(kdc_context, retval,
161 _("Ap-request armor not permitted with TGS"));
164 retval = armor_ap_request(state, fast_armored_req->armor);
167 k5_setmsg(kdc_context, KRB5KDC_ERR_PREAUTH_FAILED,
168 _("Unknown FAST armor type %d"),
169 fast_armored_req->armor->armor_type);
170 retval = KRB5KDC_ERR_PREAUTH_FAILED;
173 if (retval == 0 && !state->armor_key) {
175 retval = krb5_c_fx_cf2_simple(kdc_context,
176 tgs_subkey, "subkeyarmor",
177 tgs_session, "ticketarmor",
180 retval = KRB5KDC_ERR_PREAUTH_FAILED;
181 k5_setmsg(kdc_context, retval,
182 _("No armor key but FAST armored request present"));
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 k5_setmsg(kdc_context, retval,
218 _("FAST req_checksum invalid; request modified"));
221 if (!krb5_c_is_keyed_cksum(cksum->checksum_type)) {
222 retval = KRB5KDC_ERR_POLICY;
223 k5_setmsg(kdc_context, retval,
224 _("Unkeyed checksum used in fast_req"));
228 if ((fast_req->fast_options & UNSUPPORTED_CRITICAL_FAST_OPTIONS) != 0)
229 retval = KRB5KDC_ERR_UNKNOWN_CRITICAL_FAST_OPTION;
232 state->fast_options = fast_req->fast_options;
233 fast_req->req_body->msg_type = request->msg_type;
234 krb5_free_kdc_req( kdc_context, request);
235 *requestptr = fast_req->req_body;
236 fast_req->req_body = NULL;
239 if (retval == 0 && inner_body_out != NULL) {
240 *inner_body_out = inner_body;
243 krb5_free_data(kdc_context, inner_body);
245 krb5_free_fast_req( kdc_context, fast_req);
246 if (fast_armored_req)
247 krb5_free_fast_armored_req(kdc_context, fast_armored_req);
253 kdc_make_rstate(kdc_realm_t *active_realm, struct kdc_request_state **out)
255 struct kdc_request_state *state = malloc( sizeof(struct kdc_request_state));
258 memset( state, 0, sizeof(struct kdc_request_state));
259 state->realm_data = active_realm;
265 kdc_free_rstate (struct kdc_request_state *s)
267 kdc_realm_t *kdc_active_realm = s->realm_data;
270 krb5_free_keyblock(kdc_context, s->armor_key);
271 if (s->strengthen_key)
272 krb5_free_keyblock(kdc_context, s->strengthen_key);
273 k5_zapfree_pa_data(s->in_cookie_padata);
274 k5_zapfree_pa_data(s->out_cookie_padata);
279 kdc_fast_response_handle_padata(struct kdc_request_state *state,
280 krb5_kdc_req *request,
281 krb5_kdc_rep *rep, krb5_enctype enctype)
283 krb5_error_code retval = 0;
284 krb5_fast_finished finish;
285 krb5_fast_response fast_response;
286 krb5_data *encoded_ticket = NULL;
287 krb5_data *encrypted_reply = NULL;
288 krb5_pa_data *pa = NULL, **pa_array = NULL;
289 krb5_cksumtype cksumtype = CKSUMTYPE_RSA_MD5;
290 krb5_pa_data *empty_padata[] = {NULL};
291 krb5_keyblock *strengthen_key = NULL;
292 kdc_realm_t *kdc_active_realm = state->realm_data;
294 if (!state->armor_key)
296 memset(&finish, 0, sizeof(finish));
297 retval = krb5_init_keyblock(kdc_context, enctype, 0, &strengthen_key);
299 retval = krb5_c_make_random_key(kdc_context, enctype, strengthen_key);
301 state->strengthen_key = strengthen_key;
302 strengthen_key = NULL;
305 fast_response.padata = rep->padata;
306 if (fast_response.padata == NULL)
307 fast_response.padata = &empty_padata[0];
308 fast_response.strengthen_key = state->strengthen_key;
309 fast_response.nonce = request->nonce;
310 fast_response.finished = &finish;
311 finish.client = rep->client;
312 pa_array = calloc(3, sizeof(*pa_array));
313 if (pa_array == NULL)
315 pa = calloc(1, sizeof(krb5_pa_data));
316 if (retval == 0 && pa == NULL)
319 retval = krb5_us_timeofday(kdc_context, &finish.timestamp, &finish.usec);
321 retval = encode_krb5_ticket(rep->ticket, &encoded_ticket);
323 retval = krb5int_c_mandatory_cksumtype(kdc_context,
324 state->armor_key->enctype,
327 retval = krb5_c_make_checksum(kdc_context, cksumtype,
329 KRB5_KEYUSAGE_FAST_FINISHED,
330 encoded_ticket, &finish.ticket_checksum);
332 retval = encrypt_fast_reply(state, &fast_response, &encrypted_reply);
334 pa[0].pa_type = KRB5_PADATA_FX_FAST;
335 pa[0].length = encrypted_reply->length;
336 pa[0].contents = (unsigned char *) encrypted_reply->data;
337 pa_array[0] = &pa[0];
338 krb5_free_pa_data(kdc_context, rep->padata);
339 rep->padata = pa_array;
341 free(encrypted_reply);
342 encrypted_reply = NULL;
350 krb5_free_data(kdc_context, encrypted_reply);
352 krb5_free_data(kdc_context, encoded_ticket);
353 if (strengthen_key != NULL)
354 krb5_free_keyblock(kdc_context, strengthen_key);
355 if (finish.ticket_checksum.contents)
356 krb5_free_checksum_contents(kdc_context, &finish.ticket_checksum);
362 * We assume the caller is responsible for passing us an in_padata
363 * sufficient to include in a FAST error. In the FAST case we will
364 * set *fast_edata_out to the edata to be included in the error; in
365 * the non-FAST case we will set it to NULL.
368 kdc_fast_handle_error(krb5_context context,
369 struct kdc_request_state *state,
370 krb5_kdc_req *request,
371 krb5_pa_data **in_padata, krb5_error *err,
372 krb5_data **fast_edata_out)
374 krb5_error_code retval = 0;
375 krb5_fast_response resp;
377 krb5_data *encoded_fx_error = NULL, *encrypted_reply = NULL;
379 krb5_pa_data *outer_pa[3];
380 krb5_pa_data **inner_pa = NULL;
382 kdc_realm_t *kdc_active_realm = state->realm_data;
384 *fast_edata_out = NULL;
385 memset(outer_pa, 0, sizeof(outer_pa));
386 if (state->armor_key == NULL)
389 fx_error.e_data.data = NULL;
390 fx_error.e_data.length = 0;
391 for (size = 0; in_padata&&in_padata[size]; size++);
392 inner_pa = calloc(size + 2, sizeof(krb5_pa_data *));
393 if (inner_pa == NULL)
396 for (size=0; in_padata&&in_padata[size]; size++)
397 inner_pa[size] = in_padata[size];
399 retval = encode_krb5_error(&fx_error, &encoded_fx_error);
401 pa[0].pa_type = KRB5_PADATA_FX_ERROR;
402 pa[0].length = encoded_fx_error->length;
403 pa[0].contents = (unsigned char *) encoded_fx_error->data;
404 inner_pa[size++] = &pa[0];
407 resp.padata = inner_pa;
408 resp.nonce = request->nonce;
409 resp.strengthen_key = NULL;
410 resp.finished = NULL;
413 retval = encrypt_fast_reply(state, &resp, &encrypted_reply);
415 free(inner_pa); /*contained storage from caller and our stack*/
417 pa[0].pa_type = KRB5_PADATA_FX_FAST;
418 pa[0].length = encrypted_reply->length;
419 pa[0].contents = (unsigned char *) encrypted_reply->data;
420 outer_pa[0] = &pa[0];
422 retval = encode_krb5_padata_sequence(outer_pa, fast_edata_out);
424 krb5_free_data(kdc_context, encrypted_reply);
425 if (encoded_fx_error)
426 krb5_free_data(kdc_context, encoded_fx_error);
431 kdc_fast_handle_reply_key(struct kdc_request_state *state,
432 krb5_keyblock *existing_key,
433 krb5_keyblock **out_key)
435 krb5_error_code retval = 0;
436 kdc_realm_t *kdc_active_realm = state->realm_data;
438 if (state->armor_key)
439 retval = krb5_c_fx_cf2_simple(kdc_context,
440 state->strengthen_key, "strengthenkey",
442 "replykey", out_key);
444 retval = krb5_copy_keyblock(kdc_context, existing_key, out_key);
449 kdc_fast_hide_client(struct kdc_request_state *state)
451 return (state->fast_options & KRB5_FAST_OPTION_HIDE_CLIENT_NAMES) != 0;
454 /* Create a pa-data entry with the specified type and contents. */
455 static krb5_error_code
456 make_padata(krb5_preauthtype pa_type, const void *contents, size_t len,
459 if (alloc_pa_data(pa_type, len, out) != 0)
461 memcpy((*out)->contents, contents, len);
466 * Construct the secure cookie encryption key for the given local-realm TGT
467 * entry, kvno, and client principal. The cookie key is derived from the first
468 * TGT key for the given kvno, using the concatenation of "COOKIE" and the
469 * unparsed client principal name as input. If kvno is 0, the highest current
470 * kvno of the TGT is used. If kvno_out is not null, *kvno_out is set to the
473 static krb5_error_code
474 get_cookie_key(krb5_context context, krb5_db_entry *tgt, krb5_kvno kvno,
475 krb5_const_principal client_princ, krb5_keyblock **key_out,
482 krb5_int32 start = 0;
483 char *princstr = NULL, *derive_input = NULL;
486 memset(&kb, 0, sizeof(kb));
488 /* Find the first krbtgt key with the specified kvno. */
489 ret = krb5_dbe_search_enctype(context, tgt, &start, -1, -1, kvno, &kd);
493 /* Decrypt the key. */
494 ret = krb5_dbe_decrypt_key_data(context, NULL, kd, &kb, NULL);
498 /* Construct the input string and derive the cookie key. */
499 ret = krb5_unparse_name(context, client_princ, &princstr);
502 if (asprintf(&derive_input, "COOKIE%s", princstr) < 0) {
506 d = string2data(derive_input);
507 ret = krb5_c_derive_prfplus(context, &kb, &d, ENCTYPE_NULL, key_out);
509 if (kvno_out != NULL)
510 *kvno_out = kd->key_data_kvno;
513 krb5_free_keyblock_contents(context, &kb);
514 krb5_free_unparsed_name(context, princstr);
519 /* Return true if there is any overlap between padata types in cpadata
520 * (from the cookie) and rpadata (from the request). */
522 is_relevant(krb5_pa_data *const *cpadata, krb5_pa_data *const *rpadata)
524 krb5_pa_data *const *p;
526 for (p = cpadata; p != NULL && *p != NULL; p++) {
527 if (krb5int_find_pa_data(NULL, rpadata, (*p)->pa_type) != NULL)
534 * Locate and decode the FAST cookie in req, storing its contents in state for
535 * later access by preauth modules. If the cookie is expired, return
536 * KRB5KDC_ERR_PREAUTH_EXPIRED if its contents are relevant to req, and ignore
540 kdc_fast_read_cookie(krb5_context context, struct kdc_request_state *state,
541 krb5_kdc_req *req, krb5_db_entry *local_tgt)
544 krb5_secure_cookie *cookie = NULL;
546 krb5_keyblock *key = NULL;
550 krb5_data plain = empty_data();
552 pa = krb5int_find_pa_data(context, req->padata, KRB5_PADATA_FX_COOKIE);
556 /* If it's not an MIT version 1 cookie, ignore it. It may be an empty
557 * "MIT" cookie or a cookie generated by a different KDC implementation. */
558 if (pa->length <= 8 || memcmp(pa->contents, "MIT1", 4) != 0)
561 /* Extract the kvno and generate the corresponding cookie key. */
562 kvno = load_32_be(pa->contents + 4);
563 ret = get_cookie_key(context, local_tgt, kvno, req->client, &key, NULL);
567 /* Decrypt and decode the cookie. */
568 memset(&enc, 0, sizeof(enc));
569 enc.enctype = key->enctype;
570 enc.ciphertext = make_data(pa->contents + 8, pa->length - 8);
571 ret = alloc_data(&plain, pa->length - 8);
574 ret = krb5_c_decrypt(context, key, KRB5_KEYUSAGE_PA_FX_COOKIE, NULL, &enc,
578 ret = decode_krb5_secure_cookie(&plain, &cookie);
582 /* Check if the cookie is expired. */
583 ret = krb5_timeofday(context, &now);
586 if (ts2tt(now) > cookie->time + COOKIE_LIFETIME) {
587 /* Don't accept the cookie contents. Only return an error if the
588 * cookie is relevant to the request. */
589 if (is_relevant(cookie->data, req->padata))
590 ret = KRB5KDC_ERR_PREAUTH_EXPIRED;
594 /* Steal the pa-data list pointer from the cookie and store it in state. */
595 state->in_cookie_padata = cookie->data;
599 zapfree(plain.data, plain.length);
600 krb5_free_keyblock(context, key);
601 k5_free_secure_cookie(context, cookie);
605 /* If state contains a cookie value for pa_type, set *out to the corresponding
606 * data and return true. Otherwise set *out to empty and return false. */
608 kdc_fast_search_cookie(struct kdc_request_state *state,
609 krb5_preauthtype pa_type, krb5_data *out)
613 pa = krb5int_find_pa_data(NULL, state->in_cookie_padata, pa_type);
618 *out = make_data(pa->contents, pa->length);
623 /* Set a cookie value in state for data, to be included in the outgoing
624 * cookie. Duplicate values are ignored. */
626 kdc_fast_set_cookie(struct kdc_request_state *state, krb5_preauthtype pa_type,
627 const krb5_data *data)
629 krb5_pa_data **list = state->out_cookie_padata;
632 for (count = 0; list != NULL && list[count] != NULL; count++) {
633 if (list[count]->pa_type == pa_type)
637 list = realloc(list, (count + 2) * sizeof(*list));
640 state->out_cookie_padata = list;
641 list[count] = list[count + 1] = NULL;
642 return make_padata(pa_type, data->data, data->length, &list[count]);
645 /* Construct a cookie pa-data item using the cookie values from state, or a
646 * trivial "MIT" cookie if no values are set. */
648 kdc_fast_make_cookie(krb5_context context, struct kdc_request_state *state,
649 krb5_db_entry *local_tgt,
650 krb5_const_principal client_princ,
651 krb5_pa_data **cookie_out)
654 krb5_secure_cookie cookie;
655 krb5_pa_data **contents = state->out_cookie_padata, *pa;
656 krb5_keyblock *key = NULL;
659 krb5_data *der_cookie = NULL;
664 memset(&enc, 0, sizeof(enc));
666 /* Make a trivial cookie if there are no contents to marshal or we don't
667 * have a TGT entry to encrypt them. */
668 if (contents == NULL || *contents == NULL || local_tgt == NULL)
669 return make_padata(KRB5_PADATA_FX_COOKIE, "MIT", 3, cookie_out);
671 ret = get_cookie_key(context, local_tgt, 0, client_princ, &key, &kvno);
675 /* Encode the cookie. */
676 ret = krb5_timeofday(context, &now);
679 cookie.time = ts2tt(now);
680 cookie.data = contents;
681 ret = encode_krb5_secure_cookie(&cookie, &der_cookie);
685 /* Encrypt the cookie in key. */
686 ret = krb5_c_encrypt_length(context, key->enctype, der_cookie->length,
690 ret = alloc_data(&enc.ciphertext, ctlen);
693 ret = krb5_c_encrypt(context, key, KRB5_KEYUSAGE_PA_FX_COOKIE, NULL,
698 /* Construct the cookie pa-data entry. */
699 ret = alloc_pa_data(KRB5_PADATA_FX_COOKIE, 8 + enc.ciphertext.length, &pa);
700 memcpy(pa->contents, "MIT1", 4);
701 store_32_be(kvno, pa->contents + 4);
702 memcpy(pa->contents + 8, enc.ciphertext.data, enc.ciphertext.length);
706 krb5_free_keyblock(context, key);
707 if (der_cookie != NULL) {
708 zapfree(der_cookie->data, der_cookie->length);
711 krb5_free_data_contents(context, &enc.ciphertext);