2 * Copyright (c) 2003 - 2016 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
6 * Portions Copyright (c) 2009 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
40 #include <heim_asn1.h>
41 #include <rfc2459_asn1.h>
43 #include <pkinit_asn1.h>
46 #include "crypto-headers.h"
48 struct pk_client_params
{
49 enum krb5_pk_type type
;
50 enum keyex_enum keyex
;
62 krb5_timestamp endtime
;
63 krb5_timestamp max_life
;
65 EncryptionKey reply_key
;
68 hx509_certs client_anchors
;
69 hx509_verify_ctx verify_ctx
;
70 heim_octet_string
*freshness_token
;
73 struct pk_principal_mapping
{
75 struct pk_allowed_princ
{
76 krb5_principal principal
;
81 static struct krb5_pk_identity
*kdc_identity
;
82 static struct pk_principal_mapping principal_mappings
;
83 static struct krb5_dh_moduli
**moduli
;
95 static krb5_error_code
96 pk_check_pkauthenticator_win2k(krb5_context context
,
97 PKAuthenticator_Win2k
*a
,
102 krb5_timeofday (context
, &now
);
105 if (a
->ctime
== 0 || labs(a
->ctime
- now
) > context
->max_skew
) {
106 krb5_clear_error_message(context
);
107 return KRB5KRB_AP_ERR_SKEW
;
112 static krb5_error_code
113 pk_check_pkauthenticator(krb5_context context
,
114 const PKAuthenticator
*a
,
121 krb5_timeofday (context
, &now
);
124 if (a
->ctime
== 0 || labs(a
->ctime
- now
) > context
->max_skew
) {
125 krb5_clear_error_message(context
);
126 return KRB5KRB_AP_ERR_SKEW
;
129 ret
= krb5_create_checksum(context
,
133 req
->req_body
._save
.data
,
134 req
->req_body
._save
.length
,
137 krb5_clear_error_message(context
);
141 if (a
->paChecksum
== NULL
) {
142 krb5_clear_error_message(context
);
143 ret
= KRB5_KDC_ERR_PA_CHECKSUM_MUST_BE_INCLUDED
;
147 if (der_heim_octet_string_cmp(a
->paChecksum
, &checksum
.checksum
) != 0) {
148 krb5_clear_error_message(context
);
149 ret
= KRB5KRB_ERR_GENERIC
;
153 free_Checksum(&checksum
);
159 _kdc_pk_free_client_param(krb5_context context
, pk_client_params
*cp
)
164 hx509_cert_free(cp
->cert
);
166 hx509_verify_destroy_ctx(cp
->verify_ctx
);
167 if (cp
->keyex
== USE_DH
) {
169 DH_free(cp
->u
.dh
.key
);
170 if (cp
->u
.dh
.public_key
)
171 BN_free(cp
->u
.dh
.public_key
);
173 if (cp
->keyex
== USE_ECDH
)
174 _kdc_pk_free_client_ec_param(context
, cp
->u
.ecdh
.key
,
175 cp
->u
.ecdh
.public_key
);
176 krb5_free_keyblock_contents(context
, &cp
->reply_key
);
177 if (cp
->dh_group_name
)
178 free(cp
->dh_group_name
);
180 hx509_peer_info_free(cp
->peer
);
181 if (cp
->client_anchors
)
182 hx509_certs_free(&cp
->client_anchors
);
183 if (cp
->freshness_token
)
184 der_free_octet_string(cp
->freshness_token
);
185 free(cp
->freshness_token
);
186 memset(cp
, 0, sizeof(*cp
));
190 static krb5_error_code
191 generate_dh_keyblock(krb5_context context
,
192 pk_client_params
*client_params
,
193 krb5_enctype enctype
)
195 unsigned char *dh_gen_key
= NULL
;
198 size_t dh_gen_keylen
, size
;
200 memset(&key
, 0, sizeof(key
));
202 if (client_params
->keyex
== USE_DH
) {
204 if (client_params
->u
.dh
.public_key
== NULL
) {
205 ret
= KRB5KRB_ERR_GENERIC
;
206 krb5_set_error_message(context
, ret
, "missing DH public_key");
210 if (!DH_generate_key(client_params
->u
.dh
.key
)) {
211 ret
= KRB5KRB_ERR_GENERIC
;
212 krb5_set_error_message(context
, ret
,
213 "Can't generate Diffie-Hellman keys");
217 size
= DH_size(client_params
->u
.dh
.key
);
219 dh_gen_key
= malloc(size
);
220 if (dh_gen_key
== NULL
) {
222 krb5_set_error_message(context
, ret
, "malloc: out of memory");
226 dh_gen_keylen
= DH_compute_key(dh_gen_key
,client_params
->u
.dh
.public_key
, client_params
->u
.dh
.key
);
227 if (dh_gen_keylen
== (size_t)-1) {
228 ret
= KRB5KRB_ERR_GENERIC
;
229 krb5_set_error_message(context
, ret
,
230 "Can't compute Diffie-Hellman key");
233 if (dh_gen_keylen
< size
) {
234 size
-= dh_gen_keylen
;
235 memmove(dh_gen_key
+ size
, dh_gen_key
, dh_gen_keylen
);
236 memset(dh_gen_key
, 0, size
);
237 dh_gen_keylen
+= size
;
239 } else if (client_params
->keyex
== USE_ECDH
) {
240 if (client_params
->u
.ecdh
.public_key
== NULL
) {
241 ret
= KRB5KRB_ERR_GENERIC
;
242 krb5_set_error_message(context
, ret
, "missing ECDH public_key");
245 ret
= _kdc_generate_ecdh_keyblock(context
,
246 client_params
->u
.ecdh
.public_key
,
247 &client_params
->u
.ecdh
.key
,
248 &dh_gen_key
, &dh_gen_keylen
);
252 ret
= KRB5KRB_ERR_GENERIC
;
253 krb5_set_error_message(context
, ret
,
254 "Diffie-Hellman not selected keys");
258 ret
= _krb5_pk_octetstring2key(context
,
260 dh_gen_key
, dh_gen_keylen
,
262 &client_params
->reply_key
);
267 if (key
.keyvalue
.data
)
268 krb5_free_keyblock_contents(context
, &key
);
274 integer_to_BN(krb5_context context
, const char *field
, heim_integer
*f
)
278 bn
= BN_bin2bn((const unsigned char *)f
->data
, f
->length
, NULL
);
280 krb5_set_error_message(context
, KRB5_BADMSGTYPE
,
281 "PKINIT: parsing BN failed %s", field
);
284 BN_set_negative(bn
, f
->negative
);
288 static krb5_error_code
289 get_dh_param(krb5_context context
,
290 krb5_kdc_configuration
*config
,
291 SubjectPublicKeyInfo
*dh_key_info
,
292 pk_client_params
*client_params
)
294 DomainParameters dhparam
;
298 memset(&dhparam
, 0, sizeof(dhparam
));
300 if ((dh_key_info
->subjectPublicKey
.length
% 8) != 0) {
301 ret
= KRB5_BADMSGTYPE
;
302 krb5_set_error_message(context
, ret
,
303 "PKINIT: subjectPublicKey not aligned "
304 "to 8 bit boundary");
308 if (dh_key_info
->algorithm
.parameters
== NULL
) {
309 krb5_set_error_message(context
, KRB5_BADMSGTYPE
,
310 "PKINIT missing algorithm parameter "
311 "in clientPublicValue");
312 return KRB5_BADMSGTYPE
;
315 ret
= decode_DomainParameters(dh_key_info
->algorithm
.parameters
->data
,
316 dh_key_info
->algorithm
.parameters
->length
,
320 krb5_set_error_message(context
, ret
, "Can't decode algorithm "
321 "parameters in clientPublicValue");
325 ret
= _krb5_dh_group_ok(context
, config
->pkinit_dh_min_bits
,
326 &dhparam
.p
, &dhparam
.g
, dhparam
.q
, moduli
,
327 &client_params
->dh_group_name
);
329 /* XXX send back proposal of better group */
336 krb5_set_error_message(context
, ret
, "Cannot create DH structure");
339 ret
= KRB5_BADMSGTYPE
;
340 dh
->p
= integer_to_BN(context
, "DH prime", &dhparam
.p
);
343 dh
->g
= integer_to_BN(context
, "DH base", &dhparam
.g
);
348 dh
->q
= integer_to_BN(context
, "DH p-1 factor", dhparam
.q
);
357 ret
= decode_DHPublicKey(dh_key_info
->subjectPublicKey
.data
,
358 dh_key_info
->subjectPublicKey
.length
/ 8,
362 krb5_clear_error_message(context
);
366 client_params
->u
.dh
.public_key
= integer_to_BN(context
,
369 der_free_heim_integer(&glue
);
370 if (client_params
->u
.dh
.public_key
== NULL
) {
371 ret
= KRB5_BADMSGTYPE
;
376 client_params
->u
.dh
.key
= dh
;
383 free_DomainParameters(&dhparam
);
388 _kdc_pk_rd_padata(astgs_request_t priv
,
390 pk_client_params
**ret_params
)
392 /* XXXrcd: we use priv vs r due to a conflict */
393 krb5_context context
= priv
->context
;
394 krb5_kdc_configuration
*config
= priv
->config
;
395 const KDC_REQ
*req
= &priv
->req
;
396 hdb_entry
*client
= priv
->client
;
397 pk_client_params
*cp
;
399 heim_oid eContentType
= { 0, NULL
}, contentInfoOid
= { 0, NULL
};
400 krb5_data eContent
= { 0, NULL
};
401 krb5_data signed_content
= { 0, NULL
};
402 const char *type
= "unknown type";
403 hx509_certs trust_anchors
;
405 const HDB_Ext_PKINIT_cert
*pc
;
409 if (!config
->enable_pkinit
) {
410 kdc_log(context
, config
, 0, "PKINIT request but PKINIT not enabled");
411 krb5_clear_error_message(context
);
415 cp
= calloc(1, sizeof(*cp
));
417 krb5_clear_error_message(context
);
422 ret
= hx509_certs_init(context
->hx509ctx
,
423 "MEMORY:trust-anchors",
424 0, NULL
, &trust_anchors
);
426 krb5_set_error_message(context
, ret
, "failed to create trust anchors");
430 ret
= hx509_certs_merge(context
->hx509ctx
, trust_anchors
,
431 kdc_identity
->anchors
);
433 hx509_certs_free(&trust_anchors
);
434 krb5_set_error_message(context
, ret
, "failed to create verify context");
438 /* Add any registered certificates for this client as trust anchors */
439 ret
= hdb_entry_get_pkinit_cert(client
, &pc
);
440 if (ret
== 0 && pc
!= NULL
) {
444 for (i
= 0; i
< pc
->len
; i
++) {
445 cert
= hx509_cert_init_data(context
->hx509ctx
,
446 pc
->val
[i
].cert
.data
,
447 pc
->val
[i
].cert
.length
,
451 hx509_certs_add(context
->hx509ctx
, trust_anchors
, cert
);
452 hx509_cert_free(cert
);
456 ret
= hx509_verify_init_ctx(context
->hx509ctx
, &cp
->verify_ctx
);
458 hx509_certs_free(&trust_anchors
);
459 krb5_set_error_message(context
, ret
, "failed to create verify context");
463 hx509_verify_set_time(cp
->verify_ctx
, kdc_time
);
464 hx509_verify_attach_anchors(cp
->verify_ctx
, trust_anchors
);
465 hx509_certs_free(&trust_anchors
);
467 hx509_verify_attach_revoke(cp
->verify_ctx
, kdc_identity
->revokectx
);
469 if (config
->pkinit_allow_proxy_certs
)
470 hx509_verify_set_proxy_certificate(cp
->verify_ctx
, 1);
472 if (pa
->padata_type
== KRB5_PADATA_PK_AS_REQ_WIN
) {
473 PA_PK_AS_REQ_Win2k r
;
475 type
= "PK-INIT-Win2k";
477 if (_kdc_is_anonymous(context
, client
->principal
)) {
478 ret
= KRB5_KDC_ERR_PUBLIC_KEY_ENCRYPTION_NOT_SUPPORTED
;
479 krb5_set_error_message(context
, ret
,
480 "Anonymous client not supported in RSA mode");
484 ret
= decode_PA_PK_AS_REQ_Win2k(pa
->padata_value
.data
,
485 pa
->padata_value
.length
,
489 krb5_set_error_message(context
, ret
, "Can't decode "
490 "PK-AS-REQ-Win2k: %d", ret
);
494 ret
= hx509_cms_unwrap_ContentInfo(&r
.signed_auth_pack
,
498 free_PA_PK_AS_REQ_Win2k(&r
);
500 krb5_set_error_message(context
, ret
,
501 "Can't unwrap ContentInfo(win): %d", ret
);
505 } else if (pa
->padata_type
== KRB5_PADATA_PK_AS_REQ
) {
508 type
= "PK-INIT-IETF";
510 ret
= decode_PA_PK_AS_REQ(pa
->padata_value
.data
,
511 pa
->padata_value
.length
,
515 krb5_set_error_message(context
, ret
,
516 "Can't decode PK-AS-REQ: %d", ret
);
520 /* XXX look at r.kdcPkId */
521 if (r
.trustedCertifiers
) {
522 ExternalPrincipalIdentifiers
*edi
= r
.trustedCertifiers
;
523 unsigned int i
, maxedi
;
525 ret
= hx509_certs_init(context
->hx509ctx
,
526 "MEMORY:client-anchors",
528 &cp
->client_anchors
);
530 krb5_set_error_message(context
, ret
,
531 "Can't allocate client anchors: %d",
537 * If the client sent more than 10 EDIs, don't bother
538 * looking at more than 10 for performance reasons.
543 for (i
= 0; i
< maxedi
; i
++) {
544 IssuerAndSerialNumber iasn
;
549 if (edi
->val
[i
].issuerAndSerialNumber
== NULL
)
552 ret
= hx509_query_alloc(context
->hx509ctx
, &q
);
554 krb5_set_error_message(context
, ret
,
555 "Failed to allocate hx509_query");
559 ret
= decode_IssuerAndSerialNumber(edi
->val
[i
].issuerAndSerialNumber
->data
,
560 edi
->val
[i
].issuerAndSerialNumber
->length
,
564 hx509_query_free(context
->hx509ctx
, q
);
567 ret
= hx509_query_match_issuer_serial(q
, &iasn
.issuer
, &iasn
.serialNumber
);
568 free_IssuerAndSerialNumber(&iasn
);
570 hx509_query_free(context
->hx509ctx
, q
);
574 ret
= hx509_certs_find(context
->hx509ctx
,
578 hx509_query_free(context
->hx509ctx
, q
);
581 hx509_certs_add(context
->hx509ctx
,
582 cp
->client_anchors
, cert
);
583 hx509_cert_free(cert
);
587 ret
= hx509_cms_unwrap_ContentInfo(&r
.signedAuthPack
,
591 free_PA_PK_AS_REQ(&r
);
593 krb5_set_error_message(context
, ret
,
594 "Can't unwrap ContentInfo: %d", ret
);
599 krb5_clear_error_message(context
);
600 ret
= KRB5KDC_ERR_PADATA_TYPE_NOSUPP
;
604 ret
= der_heim_oid_cmp(&contentInfoOid
, &asn1_oid_id_pkcs7_signedData
);
606 ret
= KRB5KRB_ERR_GENERIC
;
607 krb5_set_error_message(context
, ret
,
608 "PK-AS-REQ-Win2k invalid content type oid");
613 ret
= KRB5KRB_ERR_GENERIC
;
614 krb5_set_error_message(context
, ret
,
615 "PK-AS-REQ-Win2k no signed auth pack");
620 hx509_certs signer_certs
;
621 int flags
= HX509_CMS_VS_ALLOW_DATA_OID_MISMATCH
; /* BTMM */
623 if (_kdc_is_anonymous(context
, client
->principal
)
624 || (config
->historical_anon_realm
&& _kdc_is_anon_request(req
)))
625 flags
|= HX509_CMS_VS_ALLOW_ZERO_SIGNER
;
627 ret
= hx509_cms_verify_signed(context
->hx509ctx
,
631 signed_content
.length
,
633 kdc_identity
->certpool
,
638 char *s
= hx509_get_error_string(context
->hx509ctx
, ret
);
639 krb5_warnx(context
, "PKINIT: failed to verify signature: %s: %d",
646 ret
= hx509_get_one_cert(context
->hx509ctx
, signer_certs
,
648 hx509_certs_free(&signer_certs
);
654 /* Signature is correct, now verify the signed message */
655 if (der_heim_oid_cmp(&eContentType
, &asn1_oid_id_pkcs7_data
) != 0 &&
656 der_heim_oid_cmp(&eContentType
, &asn1_oid_id_pkauthdata
) != 0)
658 ret
= KRB5_BADMSGTYPE
;
659 krb5_set_error_message(context
, ret
, "got wrong oid for PK AuthData");
663 if (pa
->padata_type
== KRB5_PADATA_PK_AS_REQ_WIN
) {
666 ret
= decode_AuthPack_Win2k(eContent
.data
,
671 krb5_set_error_message(context
, ret
,
672 "Can't decode AuthPack: %d", ret
);
676 ret
= pk_check_pkauthenticator_win2k(context
,
680 free_AuthPack_Win2k(&ap
);
684 cp
->type
= PKINIT_WIN2K
;
685 cp
->nonce
= ap
.pkAuthenticator
.nonce
;
687 if (ap
.clientPublicValue
) {
688 ret
= KRB5KRB_ERR_GENERIC
;
689 krb5_set_error_message(context
, ret
,
690 "DH not supported for Win2k");
691 free_AuthPack_Win2k(&ap
);
694 free_AuthPack_Win2k(&ap
);
696 } else if (pa
->padata_type
== KRB5_PADATA_PK_AS_REQ
) {
699 ret
= decode_AuthPack(eContent
.data
,
704 krb5_set_error_message(context
, ret
,
705 "Can't decode AuthPack: %d", ret
);
710 if (_kdc_is_anonymous(context
, client
->principal
) &&
711 ap
.clientPublicValue
== NULL
) {
713 ret
= KRB5_KDC_ERR_PUBLIC_KEY_ENCRYPTION_NOT_SUPPORTED
;
714 krb5_set_error_message(context
, ret
,
715 "Anonymous client not supported in RSA mode");
719 ret
= pk_check_pkauthenticator(context
,
727 cp
->type
= PKINIT_27
;
728 cp
->nonce
= ap
.pkAuthenticator
.nonce
;
730 if (ap
.clientPublicValue
) {
731 if (der_heim_oid_cmp(&ap
.clientPublicValue
->algorithm
.algorithm
, &asn1_oid_id_dhpublicnumber
) == 0) {
733 ret
= get_dh_param(context
, config
,
734 ap
.clientPublicValue
, cp
);
735 } else if (der_heim_oid_cmp(&ap
.clientPublicValue
->algorithm
.algorithm
, &asn1_oid_id_ecPublicKey
) == 0) {
736 cp
->keyex
= USE_ECDH
;
737 ret
= _kdc_get_ecdh_param(context
, config
,
738 ap
.clientPublicValue
,
739 &cp
->u
.ecdh
.public_key
);
741 ret
= KRB5_BADMSGTYPE
;
742 krb5_set_error_message(context
, ret
,
743 "PKINIT unknown DH mechanism");
752 ret
= hx509_peer_info_alloc(context
->hx509ctx
,
759 if (ap
.supportedCMSTypes
) {
760 ret
= hx509_peer_info_set_cms_algs(context
->hx509ctx
,
762 ap
.supportedCMSTypes
->val
,
763 ap
.supportedCMSTypes
->len
);
769 /* assume old client */
770 hx509_peer_info_add_cms_alg(context
->hx509ctx
, cp
->peer
,
771 hx509_crypto_des_rsdi_ede3_cbc());
772 hx509_peer_info_add_cms_alg(context
->hx509ctx
, cp
->peer
,
773 hx509_signature_rsa_with_sha1());
774 hx509_peer_info_add_cms_alg(context
->hx509ctx
, cp
->peer
,
775 hx509_signature_sha1());
779 * Copy the freshness token into the out parameters if it is present.
781 if (ap
.pkAuthenticator
.freshnessToken
!= NULL
) {
782 cp
->freshness_token
= calloc(1, sizeof (*cp
->freshness_token
));
783 if (cp
->freshness_token
== NULL
) {
789 ret
= der_copy_octet_string(ap
.pkAuthenticator
.freshnessToken
, cp
->freshness_token
);
798 krb5_abortx(context
, "internal pkinit error");
800 kdc_log(context
, config
, 0, "PKINIT request of type %s", type
);
804 krb5_warn(context
, ret
, "PKINIT");
806 if (signed_content
.data
)
807 free(signed_content
.data
);
808 krb5_data_free(&eContent
);
809 der_free_oid(&eContentType
);
810 der_free_oid(&contentInfoOid
);
812 _kdc_pk_free_client_param(context
, cp
);
819 _kdc_pk_endtime(pk_client_params
*pkp
)
825 _kdc_pk_max_life(pk_client_params
*pkp
)
827 return pkp
->max_life
;
831 _kdc_pk_nonce(pk_client_params
*pkp
)
840 static krb5_error_code
841 BN_to_integer(krb5_context context
, BIGNUM
*bn
, heim_integer
*integer
)
843 integer
->length
= BN_num_bytes(bn
);
844 integer
->data
= malloc(integer
->length
);
845 if (integer
->data
== NULL
) {
846 krb5_clear_error_message(context
);
849 BN_bn2bin(bn
, integer
->data
);
850 integer
->negative
= BN_is_negative(bn
);
854 static krb5_error_code
855 pk_mk_pa_reply_enckey(krb5_context context
,
856 krb5_kdc_configuration
*config
,
857 pk_client_params
*cp
,
859 const krb5_data
*req_buffer
,
860 krb5_keyblock
*reply_key
,
861 ContentInfo
*content_info
,
862 hx509_cert
*kdc_cert
)
864 const heim_oid
*envelopedAlg
= NULL
, *sdAlg
= NULL
, *evAlg
= NULL
;
866 krb5_data buf
, signed_data
;
870 krb5_data_zero(&buf
);
871 krb5_data_zero(&signed_data
);
876 * If the message client is a win2k-type but it sends pa data
877 * 09-binding it expects a IETF (checksum) reply so there can be
884 if (_kdc_find_padata(req
, &i
, KRB5_PADATA_PK_AS_09_BINDING
) == NULL
885 && config
->pkinit_require_binding
== 0)
889 sdAlg
= &asn1_oid_id_pkcs7_data
;
890 evAlg
= &asn1_oid_id_pkcs7_data
;
891 envelopedAlg
= &asn1_oid_id_rsadsi_des_ede3_cbc
;
895 sdAlg
= &asn1_oid_id_pkrkeydata
;
896 evAlg
= &asn1_oid_id_pkcs7_signedData
;
899 krb5_abortx(context
, "internal pkinit error");
903 ReplyKeyPack_Win2k kp
;
904 memset(&kp
, 0, sizeof(kp
));
906 ret
= copy_EncryptionKey(reply_key
, &kp
.replyKey
);
908 krb5_clear_error_message(context
);
911 kp
.nonce
= cp
->nonce
;
913 ASN1_MALLOC_ENCODE(ReplyKeyPack_Win2k
,
914 buf
.data
, buf
.length
,
916 free_ReplyKeyPack_Win2k(&kp
);
918 krb5_crypto ascrypto
;
920 memset(&kp
, 0, sizeof(kp
));
922 ret
= copy_EncryptionKey(reply_key
, &kp
.replyKey
);
924 krb5_clear_error_message(context
);
928 ret
= krb5_crypto_init(context
, reply_key
, 0, &ascrypto
);
930 krb5_clear_error_message(context
);
934 ret
= krb5_create_checksum(context
, ascrypto
, 6, 0,
935 req_buffer
->data
, req_buffer
->length
,
938 krb5_clear_error_message(context
);
942 ret
= krb5_crypto_destroy(context
, ascrypto
);
944 krb5_clear_error_message(context
);
947 ASN1_MALLOC_ENCODE(ReplyKeyPack
, buf
.data
, buf
.length
, &kp
, &size
,ret
);
948 free_ReplyKeyPack(&kp
);
951 krb5_set_error_message(context
, ret
, "ASN.1 encoding of ReplyKeyPack "
955 if (buf
.length
!= size
)
956 krb5_abortx(context
, "Internal ASN.1 encoder error");
962 ret
= hx509_query_alloc(context
->hx509ctx
, &q
);
966 hx509_query_match_option(q
, HX509_QUERY_OPTION_PRIVATE_KEY
);
967 if (config
->pkinit_kdc_friendly_name
)
968 hx509_query_match_friendly_name(q
, config
->pkinit_kdc_friendly_name
);
970 ret
= hx509_certs_find(context
->hx509ctx
,
974 hx509_query_free(context
->hx509ctx
, q
);
978 ret
= hx509_cms_create_signed_1(context
->hx509ctx
,
987 kdc_identity
->certpool
,
992 krb5_data_free(&buf
);
996 if (cp
->type
== PKINIT_WIN2K
) {
997 ret
= hx509_cms_wrap_ContentInfo(&asn1_oid_id_pkcs7_signedData
,
1002 krb5_data_free(&signed_data
);
1006 ret
= hx509_cms_envelope_1(context
->hx509ctx
,
1007 HX509_CMS_EV_NO_KU_CHECK
,
1009 signed_data
.data
, signed_data
.length
,
1015 ret
= _krb5_pk_mk_ContentInfo(context
,
1017 &asn1_oid_id_pkcs7_envelopedData
,
1020 if (ret
&& *kdc_cert
) {
1021 hx509_cert_free(*kdc_cert
);
1025 krb5_data_free(&buf
);
1026 krb5_data_free(&signed_data
);
1034 static krb5_error_code
1035 pk_mk_pa_reply_dh(krb5_context context
,
1036 krb5_kdc_configuration
*config
,
1037 pk_client_params
*cp
,
1038 ContentInfo
*content_info
,
1039 hx509_cert
*kdc_cert
)
1041 KDCDHKeyInfo dh_info
;
1042 krb5_data signed_data
, buf
;
1043 ContentInfo contentinfo
;
1044 krb5_error_code ret
;
1049 memset(&contentinfo
, 0, sizeof(contentinfo
));
1050 memset(&dh_info
, 0, sizeof(dh_info
));
1051 krb5_data_zero(&signed_data
);
1052 krb5_data_zero(&buf
);
1056 if (cp
->keyex
== USE_DH
) {
1057 DH
*kdc_dh
= cp
->u
.dh
.key
;
1060 ret
= BN_to_integer(context
, kdc_dh
->pub_key
, &i
);
1064 ASN1_MALLOC_ENCODE(DHPublicKey
, buf
.data
, buf
.length
, &i
, &size
, ret
);
1065 der_free_heim_integer(&i
);
1067 krb5_set_error_message(context
, ret
, "ASN.1 encoding of "
1068 "DHPublicKey failed (%d)", ret
);
1071 if (buf
.length
!= size
)
1072 krb5_abortx(context
, "Internal ASN.1 encoder error");
1074 dh_info
.subjectPublicKey
.length
= buf
.length
* 8;
1075 dh_info
.subjectPublicKey
.data
= buf
.data
;
1076 krb5_data_zero(&buf
);
1077 } else if (cp
->keyex
== USE_ECDH
) {
1079 ret
= _kdc_serialize_ecdh_key(context
, cp
->u
.ecdh
.key
, &p
,
1080 &dh_info
.subjectPublicKey
.length
);
1083 dh_info
.subjectPublicKey
.data
= p
;
1085 krb5_abortx(context
, "no keyex selected ?");
1088 dh_info
.nonce
= cp
->nonce
;
1090 ASN1_MALLOC_ENCODE(KDCDHKeyInfo
, buf
.data
, buf
.length
, &dh_info
, &size
,
1093 krb5_set_error_message(context
, ret
, "ASN.1 encoding of "
1094 "KdcDHKeyInfo failed (%d)", ret
);
1097 if (buf
.length
!= size
)
1098 krb5_abortx(context
, "Internal ASN.1 encoder error");
1101 * Create the SignedData structure and sign the KdcDHKeyInfo
1105 ret
= hx509_query_alloc(context
->hx509ctx
, &q
);
1109 hx509_query_match_option(q
, HX509_QUERY_OPTION_PRIVATE_KEY
);
1110 if (config
->pkinit_kdc_friendly_name
)
1111 hx509_query_match_friendly_name(q
, config
->pkinit_kdc_friendly_name
);
1113 ret
= hx509_certs_find(context
->hx509ctx
,
1114 kdc_identity
->certs
,
1117 hx509_query_free(context
->hx509ctx
, q
);
1121 ret
= hx509_cms_create_signed_1(context
->hx509ctx
,
1123 &asn1_oid_id_pkdhkeydata
,
1130 kdc_identity
->certpool
,
1133 kdc_log(context
, config
, 0, "Failed signing the DH* reply: %d", ret
);
1138 ret
= _krb5_pk_mk_ContentInfo(context
,
1140 &asn1_oid_id_pkcs7_signedData
,
1146 if (ret
&& *kdc_cert
) {
1147 hx509_cert_free(*kdc_cert
);
1151 krb5_data_free(&buf
);
1152 krb5_data_free(&signed_data
);
1153 free_KDCDHKeyInfo(&dh_info
);
1163 _kdc_pk_mk_pa_reply(astgs_request_t r
, pk_client_params
*cp
)
1165 krb5_kdc_configuration
*config
= r
->config
;
1166 krb5_enctype sessionetype
= r
->sessionetype
;
1167 const KDC_REQ
*req
= &r
->req
;
1168 const krb5_data
*req_buffer
= &r
->request
;
1169 krb5_keyblock
*reply_key
= &r
->reply_key
;
1170 krb5_keyblock
*sessionkey
= &r
->session_key
;
1171 METHOD_DATA
*md
= r
->rep
.padata
;
1172 krb5_error_code ret
;
1174 size_t len
= 0, size
= 0;
1175 krb5_enctype enctype
;
1177 hx509_cert kdc_cert
= NULL
;
1180 if (!config
->enable_pkinit
) {
1181 krb5_clear_error_message(r
->context
);
1185 if (req
->req_body
.etype
.len
> 0) {
1186 for (i
= 0; i
< req
->req_body
.etype
.len
; i
++)
1187 if (krb5_enctype_valid(r
->context
, req
->req_body
.etype
.val
[i
]) == 0)
1189 if (req
->req_body
.etype
.len
<= i
) {
1190 ret
= KRB5KRB_ERR_GENERIC
;
1191 krb5_set_error_message(r
->context
, ret
,
1192 "No valid enctype available from client");
1195 enctype
= req
->req_body
.etype
.val
[i
];
1197 enctype
= ETYPE_DES3_CBC_SHA1
;
1199 if (cp
->type
== PKINIT_27
) {
1201 const char *type
, *other
= "";
1203 memset(&rep
, 0, sizeof(rep
));
1205 pa_type
= KRB5_PADATA_PK_AS_REP
;
1207 if (cp
->keyex
== USE_RSA
) {
1212 rep
.element
= choice_PA_PK_AS_REP_encKeyPack
;
1214 ret
= krb5_generate_random_keyblock(r
->context
, enctype
,
1217 free_PA_PK_AS_REP(&rep
);
1220 ret
= pk_mk_pa_reply_enckey(r
->context
,
1229 free_PA_PK_AS_REP(&rep
);
1232 ASN1_MALLOC_ENCODE(ContentInfo
, rep
.u
.encKeyPack
.data
,
1233 rep
.u
.encKeyPack
.length
, &info
, &size
,
1235 free_ContentInfo(&info
);
1237 krb5_set_error_message(r
->context
, ret
, "encoding of Key ContentInfo "
1239 free_PA_PK_AS_REP(&rep
);
1242 if (rep
.u
.encKeyPack
.length
!= size
)
1243 krb5_abortx(r
->context
, "Internal ASN.1 encoder error");
1245 ret
= krb5_generate_random_keyblock(r
->context
, sessionetype
,
1248 free_PA_PK_AS_REP(&rep
);
1255 switch (cp
->keyex
) {
1256 case USE_DH
: type
= "dh"; break;
1257 case USE_ECDH
: type
= "ecdh"; break;
1258 default: krb5_abortx(r
->context
, "unknown keyex"); break;
1261 if (cp
->dh_group_name
)
1262 other
= cp
->dh_group_name
;
1264 rep
.element
= choice_PA_PK_AS_REP_dhInfo
;
1266 ret
= generate_dh_keyblock(r
->context
, cp
, enctype
);
1270 ret
= pk_mk_pa_reply_dh(r
->context
, config
,
1275 free_PA_PK_AS_REP(&rep
);
1276 krb5_set_error_message(r
->context
, ret
,
1277 "create pa-reply-dh "
1282 ASN1_MALLOC_ENCODE(ContentInfo
, rep
.u
.dhInfo
.dhSignedData
.data
,
1283 rep
.u
.dhInfo
.dhSignedData
.length
, &info
, &size
,
1285 free_ContentInfo(&info
);
1287 krb5_set_error_message(r
->context
, ret
,
1288 "encoding of Key ContentInfo "
1290 free_PA_PK_AS_REP(&rep
);
1293 if (rep
.u
.encKeyPack
.length
!= size
)
1294 krb5_abortx(r
->context
, "Internal ASN.1 encoder error");
1296 /* generate the session key using the method from RFC6112 */
1298 krb5_keyblock kdc_contribution_key
;
1299 krb5_crypto reply_crypto
;
1300 krb5_crypto kdccont_crypto
;
1301 krb5_data p1
= { strlen("PKINIT"), "PKINIT"};
1302 krb5_data p2
= { strlen("KEYEXCHANGE"), "KEYEXCHANGE"};
1309 ret
= krb5_generate_random_keyblock(r
->context
, sessionetype
,
1310 &kdc_contribution_key
);
1312 free_PA_PK_AS_REP(&rep
);
1315 ret
= krb5_crypto_init(r
->context
, &cp
->reply_key
, enctype
, &reply_crypto
);
1317 krb5_free_keyblock_contents(r
->context
, &kdc_contribution_key
);
1318 free_PA_PK_AS_REP(&rep
);
1321 ret
= krb5_crypto_init(r
->context
, &kdc_contribution_key
, sessionetype
, &kdccont_crypto
);
1323 krb5_crypto_destroy(r
->context
, reply_crypto
);
1324 krb5_free_keyblock_contents(r
->context
, &kdc_contribution_key
);
1325 free_PA_PK_AS_REP(&rep
);
1329 ret
= krb5_crypto_fx_cf2(r
->context
, kdccont_crypto
, reply_crypto
,
1330 &p1
, &p2
, sessionetype
, sessionkey
);
1331 krb5_crypto_destroy(r
->context
, kdccont_crypto
);
1333 krb5_crypto_destroy(r
->context
, reply_crypto
);
1334 krb5_free_keyblock_contents(r
->context
, &kdc_contribution_key
);
1335 free_PA_PK_AS_REP(&rep
);
1338 ASN1_MALLOC_ENCODE(EncryptionKey
, kckdata
, kcklen
,
1339 &kdc_contribution_key
, &size
, ret
);
1340 krb5_free_keyblock_contents(r
->context
, &kdc_contribution_key
);
1342 krb5_set_error_message(r
->context
, ret
, "encoding of PKINIT-KX Key failed %d", ret
);
1343 krb5_crypto_destroy(r
->context
, reply_crypto
);
1344 free_PA_PK_AS_REP(&rep
);
1348 krb5_abortx(r
->context
, "Internal ASN.1 encoder error");
1349 ret
= krb5_encrypt_EncryptedData(r
->context
, reply_crypto
, KRB5_KU_PA_PKINIT_KX
,
1350 kckdata
, kcklen
, 0, &kx
);
1351 krb5_crypto_destroy(r
->context
, reply_crypto
);
1354 free_PA_PK_AS_REP(&rep
);
1357 ASN1_MALLOC_ENCODE(EncryptedData
, kxdata
, kxlen
,
1359 free_EncryptedData(&kx
);
1361 krb5_set_error_message(r
->context
, ret
,
1362 "encoding of PKINIT-KX failed %d", ret
);
1363 free_PA_PK_AS_REP(&rep
);
1367 krb5_abortx(r
->context
, "Internal ASN.1 encoder error");
1368 /* Add PA-PKINIT-KX */
1369 ret
= krb5_padata_add(r
->context
, md
, KRB5_PADATA_PKINIT_KX
, kxdata
, kxlen
);
1371 krb5_set_error_message(r
->context
, ret
,
1372 "Failed adding PKINIT-KX %d", ret
);
1379 #define use_btmm_with_enckey 0
1380 if (use_btmm_with_enckey
&& rep
.element
== choice_PA_PK_AS_REP_encKeyPack
) {
1381 PA_PK_AS_REP_BTMM btmm
;
1384 any
.data
= rep
.u
.encKeyPack
.data
;
1385 any
.length
= rep
.u
.encKeyPack
.length
;
1387 btmm
.dhSignedData
= NULL
;
1388 btmm
.encKeyPack
= &any
;
1390 ASN1_MALLOC_ENCODE(PA_PK_AS_REP_BTMM
, buf
, len
, &btmm
, &size
, ret
);
1392 ASN1_MALLOC_ENCODE(PA_PK_AS_REP
, buf
, len
, &rep
, &size
, ret
);
1395 free_PA_PK_AS_REP(&rep
);
1397 krb5_set_error_message(r
->context
, ret
,
1398 "encode PA-PK-AS-REP failed %d", ret
);
1402 krb5_abortx(r
->context
, "Internal ASN.1 encoder error");
1404 kdc_log(r
->context
, config
, 0, "PKINIT using %s %s", type
, other
);
1406 } else if (cp
->type
== PKINIT_WIN2K
) {
1407 PA_PK_AS_REP_Win2k rep
;
1410 if (cp
->keyex
!= USE_RSA
) {
1411 ret
= KRB5KRB_ERR_GENERIC
;
1412 krb5_set_error_message(r
->context
, ret
,
1413 "Win2k PKINIT doesn't support DH");
1417 memset(&rep
, 0, sizeof(rep
));
1419 pa_type
= KRB5_PADATA_PK_AS_REP_19
;
1420 rep
.element
= choice_PA_PK_AS_REP_Win2k_encKeyPack
;
1422 ret
= krb5_generate_random_keyblock(r
->context
, enctype
,
1425 free_PA_PK_AS_REP_Win2k(&rep
);
1428 ret
= pk_mk_pa_reply_enckey(r
->context
,
1437 free_PA_PK_AS_REP_Win2k(&rep
);
1440 ASN1_MALLOC_ENCODE(ContentInfo
, rep
.u
.encKeyPack
.data
,
1441 rep
.u
.encKeyPack
.length
, &info
, &size
,
1443 free_ContentInfo(&info
);
1445 krb5_set_error_message(r
->context
, ret
, "encoding of Key ContentInfo "
1447 free_PA_PK_AS_REP_Win2k(&rep
);
1450 if (rep
.u
.encKeyPack
.length
!= size
)
1451 krb5_abortx(r
->context
, "Internal ASN.1 encoder error");
1453 ASN1_MALLOC_ENCODE(PA_PK_AS_REP_Win2k
, buf
, len
, &rep
, &size
, ret
);
1454 free_PA_PK_AS_REP_Win2k(&rep
);
1456 krb5_set_error_message(r
->context
, ret
,
1457 "encode PA-PK-AS-REP-Win2k failed %d", ret
);
1461 krb5_abortx(r
->context
, "Internal ASN.1 encoder error");
1463 ret
= krb5_generate_random_keyblock(r
->context
, sessionetype
,
1471 krb5_abortx(r
->context
, "PKINIT internal error");
1474 ret
= krb5_padata_add(r
->context
, md
, pa_type
, buf
, len
);
1476 krb5_set_error_message(r
->context
, ret
,
1477 "Failed adding PA-PK-AS-REP %d", ret
);
1482 if (config
->pkinit_kdc_ocsp_file
) {
1484 if (ocsp
.expire
== 0 && ocsp
.next_update
> kdc_time
) {
1488 krb5_data_free(&ocsp
.data
);
1491 ocsp
.next_update
= kdc_time
+ 60 * 5;
1493 fd
= open(config
->pkinit_kdc_ocsp_file
, O_RDONLY
);
1495 kdc_log(r
->context
, config
, 0,
1496 "PKINIT failed to open ocsp data file %d", errno
);
1499 ret
= fstat(fd
, &sb
);
1503 kdc_log(r
->context
, config
, 0,
1504 "PKINIT failed to stat ocsp data %d", ret
);
1508 ret
= krb5_data_alloc(&ocsp
.data
, sb
.st_size
);
1511 kdc_log(r
->context
, config
, 0,
1512 "PKINIT failed to allocate ocsp data %d", ret
);
1515 ocsp
.data
.length
= sb
.st_size
;
1516 ret
= read(fd
, ocsp
.data
.data
, sb
.st_size
);
1518 if (ret
!= sb
.st_size
) {
1519 kdc_log(r
->context
, config
, 0,
1520 "PKINIT failed to read ocsp data %d", errno
);
1524 ret
= hx509_ocsp_verify(r
->context
->hx509ctx
,
1528 ocsp
.data
.data
, ocsp
.data
.length
,
1531 kdc_log(r
->context
, config
, 0,
1532 "PKINIT failed to verify ocsp data %d", ret
);
1533 krb5_data_free(&ocsp
.data
);
1535 } else if (ocsp
.expire
> 180) {
1536 ocsp
.expire
-= 180; /* refetch the ocsp before it expires */
1537 ocsp
.next_update
= ocsp
.expire
;
1539 ocsp
.next_update
= kdc_time
;
1545 if (ocsp
.expire
!= 0 && ocsp
.expire
> kdc_time
) {
1547 ret
= krb5_padata_add(r
->context
, md
,
1548 KRB5_PADATA_PA_PK_OCSP_RESPONSE
,
1549 ocsp
.data
.data
, ocsp
.data
.length
);
1551 krb5_set_error_message(r
->context
, ret
,
1552 "Failed adding OCSP response %d", ret
);
1560 hx509_cert_free(kdc_cert
);
1563 ret
= krb5_copy_keyblock_contents(r
->context
, &cp
->reply_key
, reply_key
);
1568 match_rfc_san(krb5_context context
,
1569 krb5_kdc_configuration
*config
,
1570 hx509_context hx509ctx
,
1571 hx509_cert client_cert
,
1572 krb5_const_principal match
)
1574 hx509_octet_string_list list
;
1578 memset(&list
, 0 , sizeof(list
));
1580 ret
= hx509_cert_find_subjectAltName_otherName(hx509ctx
,
1582 &asn1_oid_id_pkinit_san
,
1587 for (i
= 0; !found
&& i
< list
.len
; i
++) {
1588 krb5_principal_data principal
;
1589 KRB5PrincipalName kn
;
1592 ret
= decode_KRB5PrincipalName(list
.val
[i
].data
,
1596 const char *msg
= krb5_get_error_message(context
, ret
);
1597 kdc_log(context
, config
, 0,
1598 "Decoding Kerberos principal name in certificate failed: %s", msg
);
1599 krb5_free_error_message(context
, msg
);
1602 if (size
!= list
.val
[i
].length
) {
1603 kdc_log(context
, config
, 0,
1604 "Decoded Kerberos principal name did not have expected length");
1605 return KRB5_KDC_ERR_CLIENT_NAME_MISMATCH
;
1608 memset(&principal
, 0, sizeof (principal
));
1609 principal
.name
= kn
.principalName
;
1610 principal
.realm
= kn
.realm
;
1612 if (krb5_principal_compare(context
, &principal
, match
) == TRUE
)
1614 free_KRB5PrincipalName(&kn
);
1618 hx509_free_octet_string_list(&list
);
1623 return KRB5_KDC_ERR_CLIENT_NAME_MISMATCH
;
1629 match_ms_upn_san(krb5_context context
,
1630 krb5_kdc_configuration
*config
,
1631 hx509_context hx509ctx
,
1632 hx509_cert client_cert
,
1636 hx509_octet_string_list list
;
1637 krb5_principal principal
= NULL
;
1642 memset(&list
, 0 , sizeof(list
));
1644 ret
= hx509_cert_find_subjectAltName_otherName(hx509ctx
,
1646 &asn1_oid_id_pkinit_ms_san
,
1651 if (list
.len
!= 1) {
1653 kdc_log(context
, config
, 0,
1654 "More than one PKINIT MS UPN SAN");
1656 kdc_log(context
, config
, 0,
1657 "No PKINIT MS UPN SAN");
1658 ret
= KRB5_KDC_ERR_CLIENT_NAME_MISMATCH
;
1662 ret
= decode_MS_UPN_SAN(list
.val
[0].data
, list
.val
[0].length
, &upn
, &size
);
1664 kdc_log(context
, config
, 0, "Decode of MS-UPN-SAN failed");
1667 if (size
!= list
.val
[0].length
) {
1668 free_MS_UPN_SAN(&upn
);
1669 kdc_log(context
, config
, 0, "Trailing data in MS UPN SAN");
1670 ret
= KRB5_KDC_ERR_CLIENT_NAME_MISMATCH
;
1674 kdc_log(context
, config
, 0, "found MS UPN SAN: %s", upn
);
1676 ret
= krb5_parse_name(context
, upn
, &principal
);
1677 free_MS_UPN_SAN(&upn
);
1679 kdc_log(context
, config
, 0, "Failed to parse principal in MS UPN SAN");
1683 if (clientdb
->hdb_check_pkinit_ms_upn_match
) {
1684 ret
= clientdb
->hdb_check_pkinit_ms_upn_match(context
, clientdb
, client
, principal
);
1688 * This is very wrong, but will do for a fallback
1690 strupr(principal
->realm
);
1692 if (krb5_principal_compare(context
, principal
, client
->principal
) == FALSE
)
1693 ret
= KRB5_KDC_ERR_CLIENT_NAME_MISMATCH
;
1698 krb5_free_principal(context
, principal
);
1699 hx509_free_octet_string_list(&list
);
1705 _kdc_pk_check_client(astgs_request_t r
,
1706 pk_client_params
*cp
,
1707 char **subject_name
)
1709 krb5_kdc_configuration
*config
= r
->config
;
1710 HDB
*clientdb
= r
->clientdb
;
1711 hdb_entry
*client
= r
->client
;
1712 const HDB_Ext_PKINIT_acl
*acl
;
1713 const HDB_Ext_PKINIT_cert
*pc
;
1714 krb5_error_code ret
;
1718 if (cp
->cert
== NULL
) {
1719 if (!_kdc_is_anonymous(r
->context
, client
->principal
)
1720 && !config
->historical_anon_realm
)
1721 return KRB5KDC_ERR_BADOPTION
;
1723 *subject_name
= strdup("<unauthenticated anonymous client>");
1724 if (*subject_name
== NULL
)
1729 cp
->endtime
= hx509_cert_get_notAfter(cp
->cert
);
1731 if (config
->pkinit_max_life_from_cert_extension
)
1733 hx509_cert_get_pkinit_max_life(r
->context
->hx509ctx
, cp
->cert
,
1734 config
->pkinit_max_life_bound
);
1735 if (cp
->max_life
== 0 && config
->pkinit_max_life_from_cert
> 0) {
1736 cp
->max_life
= cp
->endtime
- hx509_cert_get_notBefore(cp
->cert
);
1737 if (cp
->max_life
> config
->pkinit_max_life_from_cert
)
1738 cp
->max_life
= config
->pkinit_max_life_from_cert
;
1741 ret
= hx509_cert_get_base_subject(r
->context
->hx509ctx
,
1747 ret
= hx509_name_to_string(name
, subject_name
);
1748 hx509_name_free(&name
);
1752 kdc_log(r
->context
, config
, 0,
1753 "Trying to authorize PKINIT subject DN %s",
1756 ret
= hdb_entry_get_pkinit_cert(client
, &pc
);
1757 if (ret
== 0 && pc
) {
1761 for (j
= 0; j
< pc
->len
; j
++) {
1762 cert
= hx509_cert_init_data(r
->context
->hx509ctx
,
1763 pc
->val
[j
].cert
.data
,
1764 pc
->val
[j
].cert
.length
,
1768 ret
= hx509_cert_cmp(cert
, cp
->cert
);
1769 hx509_cert_free(cert
);
1771 kdc_log(r
->context
, config
, 5,
1772 "Found matching PKINIT cert in hdb");
1779 if (config
->pkinit_princ_in_cert
) {
1780 ret
= match_rfc_san(r
->context
, config
,
1781 r
->context
->hx509ctx
,
1785 kdc_log(r
->context
, config
, 5,
1786 "Found matching PKINIT SAN in certificate");
1789 ret
= match_ms_upn_san(r
->context
, config
,
1790 r
->context
->hx509ctx
,
1795 kdc_log(r
->context
, config
, 5,
1796 "Found matching MS UPN SAN in certificate");
1801 ret
= hdb_entry_get_pkinit_acl(client
, &acl
);
1802 if (ret
== 0 && acl
!= NULL
) {
1804 * Cheat here and compare the generated name with the string
1805 * and not the reverse.
1807 for (i
= 0; i
< acl
->len
; i
++) {
1808 if (strcmp(*subject_name
, acl
->val
[0].subject
) != 0)
1811 /* Don't support issuer and anchor checking right now */
1812 if (acl
->val
[0].issuer
)
1814 if (acl
->val
[0].anchor
)
1817 kdc_log(r
->context
, config
, 5,
1818 "Found matching PKINIT database ACL");
1823 for (i
= 0; i
< principal_mappings
.len
; i
++) {
1826 b
= krb5_principal_compare(r
->context
,
1828 principal_mappings
.val
[i
].principal
);
1831 if (strcmp(principal_mappings
.val
[i
].subject
, *subject_name
) != 0)
1833 kdc_log(r
->context
, config
, 5,
1834 "Found matching PKINIT FILE ACL");
1838 ret
= KRB5_KDC_ERR_CLIENT_NAME_MISMATCH
;
1839 krb5_set_error_message(r
->context
, ret
,
1840 "PKINIT no matching principals for %s",
1843 kdc_log(r
->context
, config
, 5,
1844 "PKINIT no matching principals for %s",
1847 free(*subject_name
);
1848 *subject_name
= NULL
;
1854 _kdc_pk_validate_freshness_token(astgs_request_t r
,
1855 pk_client_params
*cp
)
1857 krb5_error_code ret
= 0;
1858 uint8_t *token_data
= NULL
;
1860 uint8_t *remaining_token_data
= NULL
;
1861 size_t remaining_len
;
1862 EncryptedData enc_data
;
1864 const hdb_entry
*krbtgt
= NULL
;
1866 const Keys
*keys
= NULL
;
1870 PA_ENC_TS_ENC ts_enc
;
1873 if (cp
->freshness_token
== NULL
) {
1874 if (r
->config
->require_pkinit_freshness
) {
1875 ret
= KRB5KDC_ERR_PREAUTH_FAILED
;
1876 kdc_log(r
->context
, r
->config
, 0, "PKINIT request is missing required freshness token");
1882 token_data
= cp
->freshness_token
->data
;
1883 token_len
= cp
->freshness_token
->length
;
1885 /* Ensure that the token be not empty. */
1886 if (token_data
== NULL
) {
1887 kdc_log(r
->context
, r
->config
, 0, "Got empty freshness token");
1888 return KRB5KDC_ERR_PREAUTH_FAILED
;
1891 /* Ensure that the two leading bytes are zero. */
1892 if (token_len
< 2 || token_data
[0] || token_data
[1]) {
1893 kdc_log(r
->context
, r
->config
, 0, "Freshness token contains invalid data");
1894 return KRB5KRB_AP_ERR_MODIFIED
;
1897 /* Decrypt the freshness token. */
1899 remaining_token_data
= token_data
+ 2;
1900 remaining_len
= token_len
- 2;
1902 ret
= decode_EncryptedData(remaining_token_data
, remaining_len
, &enc_data
, &size
);
1904 kdc_log(r
->context
, r
->config
, 0, "Failed to decode freshness token");
1905 return KRB5KRB_AP_ERR_MODIFIED
;
1907 if (size
!= remaining_len
) {
1908 kdc_log(r
->context
, r
->config
, 0, "Trailing data in EncryptedData of freshness token");
1909 free_EncryptedData(&enc_data
);
1910 return KRB5KRB_AP_ERR_MODIFIED
;
1913 krbtgt
= (r
->krbtgt
!= NULL
) ? r
->krbtgt
: r
->server
;
1914 kvno
= (enc_data
.kvno
!= NULL
) ? *enc_data
.kvno
: 0;
1916 /* We will only accept freshness tokens signed by our local krbtgt. */
1917 keys
= hdb_kvno2keys(r
->context
, krbtgt
, kvno
);
1919 kdc_log(r
->context
, r
->config
, 0,
1920 "No key with kvno %"PRId32
" to decrypt freshness token",
1922 free_EncryptedData(&enc_data
);
1923 return KRB5KDC_ERR_PREAUTH_FAILED
;
1926 ret
= hdb_enctype2key(r
->context
, r
->client
, keys
,
1927 enc_data
.etype
, &key
);
1929 kdc_log(r
->context
, r
->config
, 0,
1930 "No key with kvno %"PRId32
", enctype %d to decrypt freshness token",
1931 kvno
, enc_data
.etype
);
1932 free_EncryptedData(&enc_data
);
1933 return KRB5KDC_ERR_PREAUTH_FAILED
;
1936 ret
= krb5_crypto_init(r
->context
, &key
->key
, 0, &crypto
);
1938 const char *msg
= krb5_get_error_message(r
->context
, ret
);
1939 kdc_log(r
->context
, r
->config
, 0,
1940 "While attempting to decrypt freshness token, krb5_crypto_init failed: %s", msg
);
1941 krb5_free_error_message(r
->context
, msg
);
1943 free_EncryptedData(&enc_data
);
1947 ret
= krb5_decrypt_EncryptedData(r
->context
,
1949 KRB5_KU_AS_FRESHNESS
,
1952 krb5_crypto_destroy(r
->context
, crypto
);
1953 free_EncryptedData(&enc_data
);
1955 kdc_log(r
->context
, r
->config
, 0, "Failed to decrypt freshness token");
1957 free_EncryptedData(&enc_data
);
1958 return KRB5KRB_AP_ERR_MODIFIED
;
1961 /* Decode the timestamp. */
1963 ret
= decode_PA_ENC_TS_ENC(ts_data
.data
,
1968 kdc_log(r
->context
, r
->config
, 0, "Failed to decode PA-ENC-TS-ENC in freshness token");
1969 krb5_data_free(&ts_data
);
1970 return KRB5KRB_AP_ERR_MODIFIED
;
1972 if (size
!= ts_data
.length
) {
1973 kdc_log(r
->context
, r
->config
, 0, "Trailing data in PA-ENC-TS-ENC of freshness token");
1974 free_PA_ENC_TS_ENC(&ts_enc
);
1975 krb5_data_free(&ts_data
);
1976 return KRB5KRB_AP_ERR_MODIFIED
;
1978 krb5_data_free(&ts_data
);
1980 time_diff
= labs(kdc_time
- ts_enc
.patimestamp
);
1981 if (time_diff
> r
->context
->max_skew
) {
1982 char token_time
[100];
1984 krb5_format_time(r
->context
, ts_enc
.patimestamp
,
1985 token_time
, sizeof(token_time
), TRUE
);
1987 kdc_log(r
->context
, r
->config
, 4, "Freshness token has too large time skew: "
1988 "time in token %s is out by %ld > %jd seconds — %s",
1991 (intmax_t)(r
->context
->max_skew
),
1995 free_PA_ENC_TS_ENC(&ts_enc
);
1996 return KRB5_KDC_ERR_PREAUTH_EXPIRED
;
1999 r
->pkinit_freshness_used
= TRUE
;
2001 free_PA_ENC_TS_ENC(&ts_enc
);
2005 static krb5_error_code
2006 add_principal_mapping(krb5_context context
,
2007 const char *principal_name
,
2008 const char * subject
)
2010 struct pk_allowed_princ
*tmp
;
2011 krb5_principal principal
;
2012 krb5_error_code ret
;
2014 tmp
= realloc(principal_mappings
.val
,
2015 (principal_mappings
.len
+ 1) * sizeof(*tmp
));
2018 principal_mappings
.val
= tmp
;
2020 ret
= krb5_parse_name(context
, principal_name
, &principal
);
2024 principal_mappings
.val
[principal_mappings
.len
].principal
= principal
;
2026 principal_mappings
.val
[principal_mappings
.len
].subject
= strdup(subject
);
2027 if (principal_mappings
.val
[principal_mappings
.len
].subject
== NULL
) {
2028 krb5_free_principal(context
, principal
);
2031 principal_mappings
.len
++;
2037 _kdc_add_initial_verified_cas(krb5_context context
,
2038 krb5_kdc_configuration
*config
,
2039 pk_client_params
*cp
,
2042 AD_INITIAL_VERIFIED_CAS cas
;
2043 krb5_error_code ret
;
2047 memset(&cas
, 0, sizeof(cas
));
2049 /* XXX add CAs to cas here */
2051 ASN1_MALLOC_ENCODE(AD_INITIAL_VERIFIED_CAS
, data
.data
, data
.length
,
2055 if (data
.length
!= size
)
2056 krb5_abortx(context
, "internal asn.1 encoder error");
2058 ret
= _kdc_tkt_add_if_relevant_ad(context
, tkt
,
2059 KRB5_AUTHDATA_INITIAL_VERIFIED_CAS
,
2061 krb5_data_free(&data
);
2070 load_mappings(krb5_context context
, const char *fn
)
2072 krb5_error_code ret
;
2074 unsigned long lineno
= 0;
2081 while (fgets(buf
, sizeof(buf
), f
) != NULL
) {
2082 char *subject_name
, *p
;
2084 buf
[strcspn(buf
, "\n")] = '\0';
2087 p
= buf
+ strspn(buf
, " \t");
2089 if (*p
== '#' || *p
== '\0')
2092 subject_name
= strchr(p
, ':');
2093 if (subject_name
== NULL
) {
2094 krb5_warnx(context
, "pkinit mapping file line %lu "
2095 "missing \":\" :%s",
2099 *subject_name
++ = '\0';
2101 ret
= add_principal_mapping(context
, p
, subject_name
);
2103 krb5_warn(context
, ret
, "failed to add line %lu \":\" :%s\n",
2116 KDC_LIB_FUNCTION krb5_error_code KDC_LIB_CALL
2117 krb5_kdc_pk_initialize(krb5_context context
,
2118 krb5_kdc_configuration
*config
,
2119 const char *user_id
,
2120 const char *anchors
,
2126 krb5_error_code ret
;
2128 file
= krb5_config_get_string(context
, NULL
,
2129 "libdefaults", "moduli", NULL
);
2131 ret
= _krb5_parse_moduli(context
, file
, &moduli
);
2133 krb5_err(context
, 1, ret
, "PKINIT: failed to load moduli file");
2135 principal_mappings
.len
= 0;
2136 principal_mappings
.val
= NULL
;
2138 ret
= _krb5_pk_load_id(context
,
2148 krb5_warn(context
, ret
, "PKINIT: failed to load ID");
2149 config
->enable_pkinit
= 0;
2157 ret
= hx509_query_alloc(context
->hx509ctx
, &q
);
2159 krb5_warnx(context
, "PKINIT: out of memory");
2163 hx509_query_match_option(q
, HX509_QUERY_OPTION_PRIVATE_KEY
);
2164 if (config
->pkinit_kdc_friendly_name
)
2165 hx509_query_match_friendly_name(q
, config
->pkinit_kdc_friendly_name
);
2167 ret
= hx509_certs_find(context
->hx509ctx
,
2168 kdc_identity
->certs
,
2171 hx509_query_free(context
->hx509ctx
, q
);
2173 if (hx509_cert_check_eku(context
->hx509ctx
, cert
,
2174 &asn1_oid_id_pkkdcekuoid
, 0)) {
2177 ret
= hx509_cert_get_subject(cert
, &name
);
2179 hx509_name_to_string(name
, &str
);
2180 krb5_warnx(context
, "WARNING Found KDC certificate (%s) "
2181 "is missing the PKINIT KDC EKU, this is bad for "
2182 "interoperability.", str
);
2183 hx509_name_free(&name
);
2187 hx509_cert_free(cert
);
2189 krb5_warnx(context
, "PKINIT: failed to find a signing "
2190 "certificate with a public key");
2193 if (krb5_config_get_bool_default(context
,
2197 "pkinit_allow_proxy_certificate",
2199 config
->pkinit_allow_proxy_certs
= 1;
2201 file
= krb5_config_get_string(context
,
2204 "pkinit_mappings_file",
2209 aret
= asprintf(&fn
, "%s/pki-mapping", hdb_db_dir(context
));
2211 krb5_warnx(context
, "PKINIT: out of memory");
2218 load_mappings(context
, file
);