2 * Copyright (c) 1997-2011 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
6 * Portions Copyright (c) 2010 - 2011 Apple Inc. All rights reserved.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
19 * 3. Neither the name of the Institute nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
23 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
38 static krb5_error_code
39 salt_fastuser_crypto(astgs_request_t r,
40 krb5_const_principal salt_principal,
42 krb5_crypto fast_crypto,
43 krb5_crypto *salted_crypto)
46 krb5_principal client_princ = NULL;
51 *salted_crypto = NULL;
53 krb5_data_zero(&salt);
54 krb5_keyblock_zero(&dkey);
56 if (salt_principal == NULL) {
57 if (r->req.req_body.cname == NULL) {
58 ret = KRB5KRB_ERR_GENERIC;
62 ret = _krb5_principalname2krb5_principal(r->context, &client_princ,
63 *(r->req.req_body.cname),
64 r->req.req_body.realm);
68 salt_principal = client_princ;
71 ret = krb5_unparse_name(r->context, salt_principal, (char **)&salt.data);
75 salt.length = strlen(salt.data);
77 kdc_log(r->context, r->config, 10,
78 "salt_fastuser_crypto: salt principal is %s (%d)",
79 (char *)salt.data, enctype);
81 ret = krb5_enctype_keysize(r->context, enctype, &size);
85 ret = krb5_crypto_prfplus(r->context, fast_crypto, &salt,
86 size, &dkey.keyvalue);
90 dkey.keytype = enctype;
92 ret = krb5_crypto_init(r->context, &dkey, ENCTYPE_NULL, salted_crypto);
97 krb5_free_keyblock_contents(r->context, &dkey);
98 krb5_data_free(&salt);
99 krb5_free_principal(r->context, client_princ);
104 static krb5_error_code
105 get_fastuser_crypto(astgs_request_t r,
106 krb5_const_principal ticket_client,
107 krb5_enctype enctype,
110 krb5_principal fast_princ;
111 hdb_entry_ex *fast_user = NULL;
112 Key *cookie_key = NULL;
113 krb5_crypto fast_crypto = NULL;
118 ret = krb5_make_principal(r->context, &fast_princ,
119 KRB5_WELLKNOWN_ORG_H5L_REALM,
120 KRB5_WELLKNOWN_NAME, "org.h5l.fast-cookie", NULL);
124 ret = _kdc_db_fetch(r->context, r->config, fast_princ,
125 HDB_F_GET_FAST_COOKIE, NULL, NULL, &fast_user);
129 if (enctype == KRB5_ENCTYPE_NULL)
130 ret = _kdc_get_preferred_key(r->context, r->config, fast_user,
131 "fast-cookie", &enctype, &cookie_key);
133 ret = hdb_enctype2key(r->context, &fast_user->entry, NULL,
134 enctype, &cookie_key);
138 ret = krb5_crypto_init(r->context, &cookie_key->key,
139 ENCTYPE_NULL, &fast_crypto);
143 ret = salt_fastuser_crypto(r, ticket_client,
144 cookie_key->key.keytype,
145 fast_crypto, crypto);
151 _kdc_free_ent(r->context, fast_user);
153 krb5_crypto_destroy(r->context, fast_crypto);
154 krb5_free_principal(r->context, fast_princ);
160 static krb5_error_code
161 fast_parse_cookie(astgs_request_t r,
162 krb5_const_principal ticket_client,
165 krb5_crypto crypto = NULL;
171 ret = decode_KDCFastCookie(pa->padata_value.data,
172 pa->padata_value.length,
177 if (len != pa->padata_value.length || strcmp("H5L1", data.version) != 0) {
178 free_KDCFastCookie(&data);
179 return KRB5KDC_ERR_POLICY;
182 ret = get_fastuser_crypto(r, ticket_client, data.cookie.etype, &crypto);
186 ret = krb5_decrypt_EncryptedData(r->context, crypto,
189 krb5_crypto_destroy(r->context, crypto);
193 ret = decode_KDCFastState(d1.data, d1.length, &r->fast, &len);
198 if (r->fast.expiration < kdc_time) {
199 kdc_log(r->context, r->config, 2, "FAST cookie expired");
200 ret = KRB5KDC_ERR_POLICY;
205 free_KDCFastCookie(&data);
210 static krb5_error_code
211 fast_add_cookie(astgs_request_t r,
212 krb5_const_principal ticket_client,
213 METHOD_DATA *method_data)
215 krb5_crypto crypto = NULL;
221 memset(&shell, 0, sizeof(shell));
223 r->fast.expiration = kdc_time + FAST_EXPIRATION_TIME;
225 ASN1_MALLOC_ENCODE(KDCFastState, data.data, data.length,
226 &r->fast, &size, ret);
229 heim_assert(size == data.length, "internal asn.1 encoder error");
231 ret = get_fastuser_crypto(r, ticket_client, KRB5_ENCTYPE_NULL, &crypto);
233 kdc_log(r->context, r->config, 0,
234 "Failed to find FAST principal for cookie encryption: %d", ret);
238 ret = krb5_encrypt_EncryptedData(r->context, crypto,
240 data.data, data.length, 0,
242 krb5_crypto_destroy(r->context, crypto);
246 krb5_data_free(&data);
248 shell.version = "H5L1";
250 ASN1_MALLOC_ENCODE(KDCFastCookie, data.data, data.length,
252 free_EncryptedData(&shell.cookie);
255 heim_assert(size == data.length, "internal asn.1 encoder error");
257 ret = krb5_padata_add(r->context, method_data,
258 KRB5_PADATA_FX_COOKIE,
259 data.data, data.length);
261 krb5_data_zero(&data);
264 krb5_data_free(&data);
269 _kdc_fast_mk_response(krb5_context context,
270 krb5_crypto armor_crypto,
271 METHOD_DATA *pa_data,
272 krb5_keyblock *strengthen_key,
273 KrbFastFinished *finished,
277 PA_FX_FAST_REPLY fxfastrep;
278 KrbFastResponse fastrep;
283 memset(&fxfastrep, 0, sizeof(fxfastrep));
284 memset(&fastrep, 0, sizeof(fastrep));
285 krb5_data_zero(data);
288 fastrep.padata.val = pa_data->val;
289 fastrep.padata.len = pa_data->len;
291 fastrep.strengthen_key = strengthen_key;
292 fastrep.finished = finished;
293 fastrep.nonce = nonce;
295 ASN1_MALLOC_ENCODE(KrbFastResponse, buf.data, buf.length,
296 &fastrep, &size, ret);
299 heim_assert(size == buf.length, "internal asn.1 encoder error");
301 fxfastrep.element = choice_PA_FX_FAST_REPLY_armored_data;
303 ret = krb5_encrypt_EncryptedData(context,
309 &fxfastrep.u.armored_data.enc_fast_rep);
310 krb5_data_free(&buf);
314 ASN1_MALLOC_ENCODE(PA_FX_FAST_REPLY, data->data, data->length,
315 &fxfastrep, &size, ret);
316 free_PA_FX_FAST_REPLY(&fxfastrep);
319 heim_assert(size == data->length, "internal asn.1 encoder error");
326 _kdc_fast_mk_error(astgs_request_t r,
327 METHOD_DATA *error_method,
328 krb5_crypto armor_crypto,
329 const KDC_REQ_BODY *req_body,
330 krb5_error_code outer_error,
331 krb5_principal error_client,
332 krb5_principal error_server,
333 time_t *csec, int *cusec,
334 krb5_data *error_msg)
340 krb5_data_zero(&e_data);
342 heim_assert(r != NULL, "invalid request in _kdc_fast_mk_error");
345 * FX-COOKIE can be used outside of FAST, e.g. SRP or GSS.
347 if (armor_crypto || r->fast.fast_state.len) {
348 kdc_log(r->context, r->config, 5, "Adding FAST cookie for KRB-ERROR");
349 ret = fast_add_cookie(r, error_client, error_method);
351 kdc_log(r->context, r->config, 1,
352 "Failed to add FAST cookie: %d", ret);
353 free_METHOD_DATA(error_method);
359 PA_FX_FAST_REPLY fxfastrep;
360 KrbFastResponse fastrep;
362 memset(&fxfastrep, 0, sizeof(fxfastrep));
363 memset(&fastrep, 0, sizeof(fastrep));
365 kdc_log(r->context, r->config, 5, "Making FAST inner KRB-ERROR");
367 /* first add the KRB-ERROR to the fast errors */
369 ret = krb5_mk_error(r->context,
379 kdc_log(r->context, r->config, 1,
380 "Failed to make inner KRB-ERROR: %d", ret);
384 ret = krb5_padata_add(r->context, error_method,
385 KRB5_PADATA_FX_ERROR,
386 e_data.data, e_data.length);
388 kdc_log(r->context, r->config, 1,
389 "Failed to make add FAST PADATA to inner KRB-ERROR: %d", ret);
390 krb5_data_free(&e_data);
395 if (r->fast.flags.requested_hidden_names) {
402 ret = _kdc_fast_mk_response(r->context, armor_crypto,
403 error_method, NULL, NULL,
404 req_body->nonce, &e_data);
405 free_METHOD_DATA(error_method);
407 kdc_log(r->context, r->config, 1,
408 "Failed to make outer KRB-ERROR: %d", ret);
412 ret = krb5_padata_add(r->context, error_method,
414 e_data.data, e_data.length);
416 kdc_log(r->context, r->config, 1,
417 "Failed to make add FAST PADATA to outer KRB-ERROR: %d", ret);
421 kdc_log(r->context, r->config, 5, "Making non-FAST KRB-ERROR");
423 if (error_method && error_method->len) {
424 ASN1_MALLOC_ENCODE(METHOD_DATA, e_data.data, e_data.length,
425 error_method, &size, ret);
427 kdc_log(r->context, r->config, 1,
428 "Failed to make encode METHOD-DATA: %d", ret);
431 heim_assert(size == e_data.length, "internal asn.1 encoder error");
434 ret = krb5_mk_error(r->context,
437 (e_data.length ? &e_data : NULL),
443 krb5_data_free(&e_data);
446 kdc_log(r->context, r->config, 1,
447 "Failed to make encode KRB-ERROR: %d", ret);
452 static krb5_error_code
453 fast_unwrap_request(astgs_request_t r,
454 krb5_ticket *tgs_ticket,
455 krb5_auth_context tgs_ac)
457 krb5_principal armor_server_principal = NULL;
458 char *armor_client_principal_name = NULL;
459 char *armor_server_principal_name = NULL;
460 PA_FX_FAST_REQUEST fxreq = {0};
461 krb5_auth_context ac = NULL;
462 krb5_ticket *ticket = NULL;
463 krb5_flags ap_req_options;
464 krb5_keyblock armorkey;
465 krb5_keyblock explicit_armorkey;
466 krb5_boolean explicit_armor;
469 KrbFastReq fastreq = {0};
475 pa = _kdc_find_padata(&r->req, &i, KRB5_PADATA_FX_FAST);
477 if (tgs_ac && r->fast_asserted) {
478 kdc_log(r->context, r->config, 1,
479 "Client asserted FAST but did not include FX-FAST pa-data");
480 ret = KRB5KRB_AP_ERR_MODIFIED;
484 kdc_log(r->context, r->config, 10, "Not a FAST request");
488 ret = decode_PA_FX_FAST_REQUEST(pa->padata_value.data,
489 pa->padata_value.length,
493 kdc_log(r->context, r->config, 4,
494 "Failed to decode PA-FX-FAST-REQUEST: %d", ret);
498 if (fxreq.element != choice_PA_FX_FAST_REQUEST_armored_data) {
499 kdc_log(r->context, r->config, 4,
500 "PA-FX-FAST-REQUEST contains unknown type: %d",
502 ret = KRB5KDC_ERR_PREAUTH_FAILED;
507 * If check for armor data or it's not a TGS-REQ with implicit
510 if (fxreq.u.armored_data.armor == NULL && tgs_ac == NULL) {
511 kdc_log(r->context, r->config, 4,
512 "AS-REQ armor missing");
513 ret = KRB5KDC_ERR_PREAUTH_FAILED;
517 explicit_armor = fxreq.u.armored_data.armor != NULL && tgs_ac != NULL;
522 if (fxreq.u.armored_data.armor != NULL) {
523 if (fxreq.u.armored_data.armor->armor_type != 1) {
524 kdc_log(r->context, r->config, 4,
525 "Incorrect AS-REQ armor type");
526 ret = KRB5KDC_ERR_PREAUTH_FAILED;
530 ret = krb5_decode_ap_req(r->context,
531 &fxreq.u.armored_data.armor->armor_value,
534 kdc_log(r->context, r->config, 4, "Failed to decode AP-REQ");
538 /* Save that principal that was in the request */
539 ret = _krb5_principalname2krb5_principal(r->context,
540 &armor_server_principal,
542 ap_req.ticket.realm);
544 free_AP_REQ(&ap_req);
548 ret = _kdc_db_fetch(r->context, r->config, armor_server_principal,
549 HDB_F_GET_KRBTGT | HDB_F_DELAY_NEW_KEYS,
550 (krb5uint32 *)ap_req.ticket.enc_part.kvno,
551 NULL, &r->armor_server);
552 if(ret == HDB_ERR_NOT_FOUND_HERE) {
553 free_AP_REQ(&ap_req);
554 kdc_log(r->context, r->config, 5,
555 "Armor key does not have secrets at this KDC, "
559 free_AP_REQ(&ap_req);
560 ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
564 ret = hdb_enctype2key(r->context, &r->armor_server->entry, NULL,
565 ap_req.ticket.enc_part.etype,
568 free_AP_REQ(&ap_req);
572 ret = krb5_verify_ap_req2(r->context, &ac,
574 armor_server_principal,
579 KRB5_KU_AP_REQ_AUTH);
580 free_AP_REQ(&ap_req);
584 ret = krb5_unparse_name(r->context, armor_server_principal,
585 &armor_server_principal_name);
589 /* FIXME krb5_verify_ap_req2() also checks this */
590 ret = _kdc_verify_flags(r->context, r->config,
591 &r->armor_ticket->ticket,
592 armor_server_principal_name);
594 _kdc_audit_addreason((kdc_request_t)r,
595 "Armor TGT expired or invalid");
598 ticket = r->armor_ticket;
600 heim_assert(tgs_ticket != NULL, "TGS authentication context without ticket");
605 krb5_unparse_name(r->context, ticket->client, &armor_client_principal_name);
606 _kdc_audit_addkv((kdc_request_t)r, 0, "armor_client_name", "%s",
607 armor_client_principal_name ? armor_client_principal_name : "<unknown>");
609 if (ac->remote_subkey == NULL) {
610 krb5_auth_con_free(r->context, ac);
611 kdc_log(r->context, r->config, 2,
612 "FAST AP-REQ remote subkey missing");
613 ret = KRB5KDC_ERR_PREAUTH_FAILED;
617 r->fast.flags.kdc_verified =
618 !_kdc_is_anonymous_pkinit(r->context, ticket->client);
620 ret = _krb5_fast_armor_key(r->context,
624 explicit_armor ? NULL : &r->armor_crypto);
628 if (explicit_armor) {
629 ret = _krb5_fast_explicit_armor_key(r->context,
631 tgs_ac->remote_subkey,
637 krb5_free_keyblock_contents(r->context, &explicit_armorkey);
640 krb5_free_keyblock_contents(r->context, &armorkey);
642 ret = krb5_decrypt_EncryptedData(r->context, r->armor_crypto,
644 &fxreq.u.armored_data.enc_fast_req,
647 kdc_log(r->context, r->config, 2,
648 "Failed to decrypt FAST request");
652 ret = decode_KrbFastReq(data.data, data.length, &fastreq, NULL);
653 krb5_data_free(&data);
658 * verify req-checksum of the outer body
662 * -- For TGS, contains the checksum performed over the type
663 * -- AP-REQ in the PA-TGS-REQ padata.
666 pa = _kdc_find_padata(&r->req, &i, KRB5_PADATA_TGS_REQ);
668 kdc_log(r->context, r->config, 4,
669 "FAST TGS request missing TGS-REQ padata");
670 ret = KRB5KRB_ERR_GENERIC;
674 ret = _kdc_verify_checksum(r->context, r->armor_crypto,
675 KRB5_KU_FAST_REQ_CHKSUM,
677 &fxreq.u.armored_data.req_checksum);
679 kdc_log(r->context, r->config, 2,
680 "Bad checksum in FAST TGS request");
685 * -- For AS, contains the checksum performed over the type
686 * -- KDC-REQ-BODY for the req-body field of the KDC-REQ
689 ret = _kdc_verify_checksum(r->context, r->armor_crypto,
690 KRB5_KU_FAST_REQ_CHKSUM,
691 &r->req.req_body._save,
692 &fxreq.u.armored_data.req_checksum);
694 kdc_log(r->context, r->config, 2,
695 "Bad checksum in FAST AS request");
701 * check for unsupported mandatory options
703 if (FastOptions2int(fastreq.fast_options) & 0xfffc) {
704 kdc_log(r->context, r->config, 2,
705 "FAST unsupported mandatory option set");
706 ret = KRB5_KDC_ERR_UNKNOWN_CRITICAL_FAST_OPTIONS;
710 r->fast.flags.requested_hidden_names = fastreq.fast_options.hide_client_names;
712 /* KDC MUST ignore outer pa data preauth-14 - 6.5.5 */
714 free_METHOD_DATA(r->req.padata);
716 ALLOC(r->req.padata);
718 ret = copy_METHOD_DATA(&fastreq.padata, r->req.padata);
722 free_KDC_REQ_BODY(&r->req.req_body);
723 ret = copy_KDC_REQ_BODY(&fastreq.req_body, &r->req.req_body);
727 kdc_log(r->context, r->config, 5, "Client selected FAST");
730 if (ac && ac != tgs_ac)
731 krb5_auth_con_free(r->context, ac);
733 krb5_free_principal(r->context, armor_server_principal);
734 krb5_xfree(armor_client_principal_name);
735 krb5_xfree(armor_server_principal_name);
737 free_KrbFastReq(&fastreq);
738 free_PA_FX_FAST_REQUEST(&fxreq);
747 _kdc_fast_unwrap_request(astgs_request_t r,
748 krb5_ticket *tgs_ticket,
749 krb5_auth_context tgs_ac)
755 ret = fast_unwrap_request(r, tgs_ticket, tgs_ac);
760 * FX-COOKIE can be used outside of FAST, e.g. SRP or GSS.
762 pa = _kdc_find_padata(&r->req, &i, KRB5_PADATA_FX_COOKIE);
764 krb5_const_principal ticket_client = NULL;
767 ticket_client = tgs_ticket->client;
769 ret = fast_parse_cookie(r, ticket_client, pa);
776 * Strengthen reply key by mixing with a random key that is
780 _kdc_fast_strengthen_reply_key(astgs_request_t r)
782 if (r->armor_crypto) {
783 krb5_keyblock new_reply_key;
786 kdc_log(r->context, r->config, 5,
787 "FAST strengthen reply key with strengthen-key");
789 heim_assert(r->reply_key.keytype != KRB5_ENCTYPE_NULL, "NULL reply key enctype");
791 ret = krb5_generate_random_keyblock(r->context, r->reply_key.keytype,
794 krb5_abortx(r->context, "random generator fail");
796 ret = _krb5_fast_cf2(r->context,
797 &r->strengthen_key, "strengthenkey",
798 &r->reply_key, "replykey",
799 &new_reply_key, NULL);
803 krb5_free_keyblock_contents(r->context, &r->reply_key);
804 r->reply_key = new_reply_key;
811 * Zero and free KDCFastState
814 _kdc_free_fast_state(KDCFastState *state)
818 for (i = 0; i < state->fast_state.len; i++) {
819 PA_DATA *pa = &state->fast_state.val[i];
821 if (pa->padata_value.data)
822 memset_s(pa->padata_value.data, 0,
823 pa->padata_value.length, pa->padata_value.length);
825 free_KDCFastState(state);
829 _kdc_fast_check_armor_pac(astgs_request_t r)
833 krb5_boolean ad_kdc_issued = FALSE;
834 krb5_pac mspac = NULL;
835 krb5_principal armor_client_principal = NULL;
836 hdb_entry_ex *armor_client = NULL;
837 char *armor_client_principal_name = NULL;
839 flags = HDB_F_FOR_TGS_REQ;
840 if (_kdc_synthetic_princ_used_p(r->context, r->armor_ticket))
841 flags |= HDB_F_SYNTHETIC_OK;
842 if (r->req.req_body.kdc_options.canonicalize)
843 flags |= HDB_F_CANON;
845 ret = _krb5_principalname2krb5_principal(r->context,
846 &armor_client_principal,
847 r->armor_ticket->ticket.cname,
848 r->armor_ticket->ticket.crealm);
852 ret = krb5_unparse_name(r->context, armor_client_principal,
853 &armor_client_principal_name);
857 ret = _kdc_db_fetch_client(r->context, r->config, flags,
858 armor_client_principal, armor_client_principal_name,
859 r->req.req_body.realm, NULL, &armor_client);
863 ret = kdc_check_flags(r, FALSE, armor_client, NULL);
867 ret = _kdc_check_pac(r->context, r->config, armor_client_principal, NULL,
868 armor_client, r->armor_server,
869 r->armor_server, r->armor_server,
870 &r->armor_key->key, &r->armor_key->key,
871 &r->armor_ticket->ticket, &ad_kdc_issued, &mspac, NULL, NULL);
873 const char *msg = krb5_get_error_message(r->context, ret);
875 kdc_log(r->context, r->config, 4,
876 "Verify armor PAC (%s) failed for %s (%s) from %s with %s (%s)",
877 armor_client_principal_name, r->cname, r->sname,
878 r->from, msg, mspac ? "Ticket unsigned" : "No PAC");
880 krb5_free_error_message(r->context, msg);
886 krb5_xfree(armor_client_principal_name);
888 _kdc_free_ent(r->context, armor_client);
889 krb5_free_principal(r->context, armor_client_principal);
890 krb5_pac_free(r->context, mspac);