2 * Copyright (c) 2003 - 2006 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
17 * 3. Neither the name of the Institute nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * 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 BIGNUM
*dh_public_key
;
54 EncryptionKey reply_key
;
57 hx509_certs client_anchors
;
60 struct pk_principal_mapping
{
62 struct pk_allowed_princ
{
63 krb5_principal principal
;
68 static struct krb5_pk_identity
*kdc_identity
;
69 static struct pk_principal_mapping principal_mappings
;
70 static struct krb5_dh_moduli
**moduli
;
82 static krb5_error_code
83 pk_check_pkauthenticator_win2k(krb5_context context
,
84 PKAuthenticator_Win2k
*a
,
89 krb5_timeofday (context
, &now
);
92 if (a
->ctime
== 0 || abs(a
->ctime
- now
) > context
->max_skew
) {
93 krb5_clear_error_string(context
);
94 return KRB5KRB_AP_ERR_SKEW
;
99 static krb5_error_code
100 pk_check_pkauthenticator(krb5_context context
,
111 krb5_timeofday (context
, &now
);
114 if (a
->ctime
== 0 || abs(a
->ctime
- now
) > context
->max_skew
) {
115 krb5_clear_error_string(context
);
116 return KRB5KRB_AP_ERR_SKEW
;
119 ASN1_MALLOC_ENCODE(KDC_REQ_BODY
, buf
, buf_size
, &req
->req_body
, &len
, ret
);
121 krb5_clear_error_string(context
);
125 krb5_abortx(context
, "Internal error in ASN.1 encoder");
127 ret
= krb5_create_checksum(context
,
136 krb5_clear_error_string(context
);
140 if (a
->paChecksum
== NULL
) {
141 krb5_clear_error_string(context
);
142 ret
= KRB5_KDC_ERR_PA_CHECKSUM_MUST_BE_INCLUDED
;
146 if (der_heim_octet_string_cmp(a
->paChecksum
, &checksum
.checksum
) != 0) {
147 krb5_clear_error_string(context
);
148 ret
= KRB5KRB_ERR_GENERIC
;
152 free_Checksum(&checksum
);
158 _kdc_pk_free_client_param(krb5_context context
,
159 pk_client_params
*client_params
)
161 if (client_params
->cert
)
162 hx509_cert_free(client_params
->cert
);
163 if (client_params
->dh
)
164 DH_free(client_params
->dh
);
165 if (client_params
->dh_public_key
)
166 BN_free(client_params
->dh_public_key
);
167 krb5_free_keyblock_contents(context
, &client_params
->reply_key
);
168 if (client_params
->dh_group_name
)
169 free(client_params
->dh_group_name
);
170 if (client_params
->peer
)
171 hx509_peer_info_free(client_params
->peer
);
172 if (client_params
->client_anchors
)
173 hx509_certs_free(&client_params
->client_anchors
);
174 memset(client_params
, 0, sizeof(*client_params
));
178 static krb5_error_code
179 generate_dh_keyblock(krb5_context context
, pk_client_params
*client_params
,
180 krb5_enctype enctype
, krb5_keyblock
*reply_key
)
182 unsigned char *dh_gen_key
= NULL
;
185 size_t dh_gen_keylen
, size
;
187 memset(&key
, 0, sizeof(key
));
189 if (!DH_generate_key(client_params
->dh
)) {
190 krb5_set_error_string(context
, "Can't generate Diffie-Hellman keys");
191 ret
= KRB5KRB_ERR_GENERIC
;
194 if (client_params
->dh_public_key
== NULL
) {
195 krb5_set_error_string(context
, "dh_public_key");
196 ret
= KRB5KRB_ERR_GENERIC
;
200 dh_gen_keylen
= DH_size(client_params
->dh
);
201 size
= BN_num_bytes(client_params
->dh
->p
);
202 if (size
< dh_gen_keylen
)
203 size
= dh_gen_keylen
;
205 dh_gen_key
= malloc(size
);
206 if (dh_gen_key
== NULL
) {
207 krb5_set_error_string(context
, "malloc: out of memory");
211 memset(dh_gen_key
, 0, size
- dh_gen_keylen
);
213 dh_gen_keylen
= DH_compute_key(dh_gen_key
+ (size
- dh_gen_keylen
),
214 client_params
->dh_public_key
,
216 if (dh_gen_keylen
== -1) {
217 krb5_set_error_string(context
, "Can't compute Diffie-Hellman key");
218 ret
= KRB5KRB_ERR_GENERIC
;
222 ret
= _krb5_pk_octetstring2key(context
,
224 dh_gen_key
, dh_gen_keylen
,
231 if (key
.keyvalue
.data
)
232 krb5_free_keyblock_contents(context
, &key
);
238 integer_to_BN(krb5_context context
, const char *field
, heim_integer
*f
)
242 bn
= BN_bin2bn((const unsigned char *)f
->data
, f
->length
, NULL
);
244 krb5_set_error_string(context
, "PKINIT: parsing BN failed %s", field
);
247 BN_set_negative(bn
, f
->negative
);
251 static krb5_error_code
252 get_dh_param(krb5_context context
,
253 krb5_kdc_configuration
*config
,
254 SubjectPublicKeyInfo
*dh_key_info
,
255 pk_client_params
*client_params
)
257 DomainParameters dhparam
;
261 memset(&dhparam
, 0, sizeof(dhparam
));
263 if (der_heim_oid_cmp(&dh_key_info
->algorithm
.algorithm
, oid_id_dhpublicnumber())) {
264 krb5_set_error_string(context
,
265 "PKINIT invalid oid in clientPublicValue");
266 return KRB5_BADMSGTYPE
;
269 if (dh_key_info
->algorithm
.parameters
== NULL
) {
270 krb5_set_error_string(context
, "PKINIT missing algorithm parameter "
271 "in clientPublicValue");
272 return KRB5_BADMSGTYPE
;
275 ret
= decode_DomainParameters(dh_key_info
->algorithm
.parameters
->data
,
276 dh_key_info
->algorithm
.parameters
->length
,
280 krb5_set_error_string(context
, "Can't decode algorithm "
281 "parameters in clientPublicValue");
285 if ((dh_key_info
->subjectPublicKey
.length
% 8) != 0) {
286 ret
= KRB5_BADMSGTYPE
;
287 krb5_set_error_string(context
, "PKINIT: subjectPublicKey not aligned "
288 "to 8 bit boundary");
293 ret
= _krb5_dh_group_ok(context
, config
->pkinit_dh_min_bits
,
294 &dhparam
.p
, &dhparam
.g
, &dhparam
.q
, moduli
,
295 &client_params
->dh_group_name
);
297 /* XXX send back proposal of better group */
303 krb5_set_error_string(context
, "Cannot create DH structure");
307 ret
= KRB5_BADMSGTYPE
;
308 dh
->p
= integer_to_BN(context
, "DH prime", &dhparam
.p
);
311 dh
->g
= integer_to_BN(context
, "DH base", &dhparam
.g
);
314 dh
->q
= integer_to_BN(context
, "DH p-1 factor", &dhparam
.q
);
322 ret
= decode_DHPublicKey(dh_key_info
->subjectPublicKey
.data
,
323 dh_key_info
->subjectPublicKey
.length
/ 8,
327 krb5_clear_error_string(context
);
331 client_params
->dh_public_key
= integer_to_BN(context
,
334 der_free_heim_integer(&glue
);
335 if (client_params
->dh_public_key
== NULL
)
339 client_params
->dh
= dh
;
346 free_DomainParameters(&dhparam
);
351 _kdc_pk_rd_padata(krb5_context context
,
352 krb5_kdc_configuration
*config
,
355 pk_client_params
**ret_params
)
357 pk_client_params
*client_params
;
359 heim_oid eContentType
= { 0, NULL
}, contentInfoOid
= { 0, NULL
};
360 krb5_data eContent
= { 0, NULL
};
361 krb5_data signed_content
= { 0, NULL
};
362 const char *type
= "unknown type";
367 if (!config
->enable_pkinit
) {
368 kdc_log(context
, config
, 0, "PK-INIT request but PK-INIT not enabled");
369 krb5_clear_error_string(context
);
373 hx509_verify_set_time(kdc_identity
->verify_ctx
, _kdc_now
.tv_sec
);
375 client_params
= calloc(1, sizeof(*client_params
));
376 if (client_params
== NULL
) {
377 krb5_clear_error_string(context
);
382 if (pa
->padata_type
== KRB5_PADATA_PK_AS_REQ_WIN
) {
383 PA_PK_AS_REQ_Win2k r
;
385 type
= "PK-INIT-Win2k";
387 ret
= decode_PA_PK_AS_REQ_Win2k(pa
->padata_value
.data
,
388 pa
->padata_value
.length
,
392 krb5_set_error_string(context
, "Can't decode "
393 "PK-AS-REQ-Win2k: %d", ret
);
397 ret
= hx509_cms_unwrap_ContentInfo(&r
.signed_auth_pack
,
401 free_PA_PK_AS_REQ_Win2k(&r
);
403 krb5_set_error_string(context
, "Can't decode PK-AS-REQ: %d", ret
);
407 } else if (pa
->padata_type
== KRB5_PADATA_PK_AS_REQ
) {
410 type
= "PK-INIT-IETF";
412 ret
= decode_PA_PK_AS_REQ(pa
->padata_value
.data
,
413 pa
->padata_value
.length
,
417 krb5_set_error_string(context
, "Can't decode PK-AS-REQ: %d", ret
);
421 /* XXX look at r.kdcPkId */
422 if (r
.trustedCertifiers
) {
423 ExternalPrincipalIdentifiers
*edi
= r
.trustedCertifiers
;
426 ret
= hx509_certs_init(kdc_identity
->hx509ctx
,
427 "MEMORY:client-anchors",
429 &client_params
->client_anchors
);
431 krb5_set_error_string(context
, "Can't allocate client anchors: %d", ret
);
435 for (i
= 0; i
< edi
->len
; i
++) {
436 IssuerAndSerialNumber iasn
;
441 if (edi
->val
[i
].issuerAndSerialNumber
== NULL
)
444 ret
= hx509_query_alloc(kdc_identity
->hx509ctx
, &q
);
446 krb5_set_error_string(context
,
447 "Failed to allocate hx509_query");
451 ret
= decode_IssuerAndSerialNumber(edi
->val
[i
].issuerAndSerialNumber
->data
,
452 edi
->val
[i
].issuerAndSerialNumber
->length
,
456 hx509_query_free(kdc_identity
->hx509ctx
, q
);
459 ret
= hx509_query_match_issuer_serial(q
, &iasn
.issuer
, &iasn
.serialNumber
);
460 free_IssuerAndSerialNumber(&iasn
);
464 ret
= hx509_certs_find(kdc_identity
->hx509ctx
,
468 hx509_query_free(kdc_identity
->hx509ctx
, q
);
471 hx509_certs_add(kdc_identity
->hx509ctx
,
472 client_params
->client_anchors
, cert
);
473 hx509_cert_free(cert
);
477 ret
= hx509_cms_unwrap_ContentInfo(&r
.signedAuthPack
,
481 free_PA_PK_AS_REQ(&r
);
483 krb5_set_error_string(context
, "Can't unwrap ContentInfo: %d", ret
);
488 krb5_clear_error_string(context
);
489 ret
= KRB5KDC_ERR_PADATA_TYPE_NOSUPP
;
493 ret
= der_heim_oid_cmp(&contentInfoOid
, oid_id_pkcs7_signedData());
495 krb5_set_error_string(context
, "PK-AS-REQ-Win2k invalid content "
497 ret
= KRB5KRB_ERR_GENERIC
;
502 krb5_set_error_string(context
,
503 "PK-AS-REQ-Win2k no signed auth pack");
504 ret
= KRB5KRB_ERR_GENERIC
;
509 hx509_certs signer_certs
;
511 ret
= hx509_cms_verify_signed(kdc_identity
->hx509ctx
,
512 kdc_identity
->verify_ctx
,
514 signed_content
.length
,
516 kdc_identity
->certpool
,
521 char *s
= hx509_get_error_string(kdc_identity
->hx509ctx
, ret
);
522 krb5_warnx(context
, "PKINIT: failed to verify signature: %s: %d",
528 ret
= hx509_get_one_cert(kdc_identity
->hx509ctx
, signer_certs
,
529 &client_params
->cert
);
530 hx509_certs_free(&signer_certs
);
535 /* Signature is correct, now verify the signed message */
536 if (der_heim_oid_cmp(&eContentType
, oid_id_pkcs7_data()) != 0 &&
537 der_heim_oid_cmp(&eContentType
, oid_id_pkauthdata()) != 0)
539 krb5_set_error_string(context
, "got wrong oid for pkauthdata");
540 ret
= KRB5_BADMSGTYPE
;
544 if (pa
->padata_type
== KRB5_PADATA_PK_AS_REQ_WIN
) {
547 ret
= decode_AuthPack_Win2k(eContent
.data
,
552 krb5_set_error_string(context
, "can't decode AuthPack: %d", ret
);
556 ret
= pk_check_pkauthenticator_win2k(context
,
560 free_AuthPack_Win2k(&ap
);
564 client_params
->type
= PKINIT_WIN2K
;
565 client_params
->nonce
= ap
.pkAuthenticator
.nonce
;
567 if (ap
.clientPublicValue
) {
568 krb5_set_error_string(context
, "DH not supported for windows");
569 ret
= KRB5KRB_ERR_GENERIC
;
572 free_AuthPack_Win2k(&ap
);
574 } else if (pa
->padata_type
== KRB5_PADATA_PK_AS_REQ
) {
577 ret
= decode_AuthPack(eContent
.data
,
582 krb5_set_error_string(context
, "can't decode AuthPack: %d", ret
);
587 ret
= pk_check_pkauthenticator(context
,
595 client_params
->type
= PKINIT_27
;
596 client_params
->nonce
= ap
.pkAuthenticator
.nonce
;
598 if (ap
.clientPublicValue
) {
599 ret
= get_dh_param(context
, config
,
600 ap
.clientPublicValue
, client_params
);
607 if (ap
.supportedCMSTypes
) {
608 ret
= hx509_peer_info_alloc(kdc_identity
->hx509ctx
,
609 &client_params
->peer
);
614 ret
= hx509_peer_info_set_cms_algs(kdc_identity
->hx509ctx
,
616 ap
.supportedCMSTypes
->val
,
617 ap
.supportedCMSTypes
->len
);
625 krb5_abortx(context
, "internal pkinit error");
627 kdc_log(context
, config
, 0, "PK-INIT request of type %s", type
);
631 krb5_warn(context
, ret
, "PKINIT");
633 if (signed_content
.data
)
634 free(signed_content
.data
);
635 krb5_data_free(&eContent
);
636 der_free_oid(&eContentType
);
637 der_free_oid(&contentInfoOid
);
639 _kdc_pk_free_client_param(context
, client_params
);
641 *ret_params
= client_params
;
649 static krb5_error_code
650 BN_to_integer(krb5_context context
, BIGNUM
*bn
, heim_integer
*integer
)
652 integer
->length
= BN_num_bytes(bn
);
653 integer
->data
= malloc(integer
->length
);
654 if (integer
->data
== NULL
) {
655 krb5_clear_error_string(context
);
658 BN_bn2bin(bn
, integer
->data
);
659 integer
->negative
= BN_is_negative(bn
);
663 static krb5_error_code
664 pk_mk_pa_reply_enckey(krb5_context context
,
665 krb5_kdc_configuration
*config
,
666 pk_client_params
*client_params
,
668 const krb5_data
*req_buffer
,
669 krb5_keyblock
*reply_key
,
670 ContentInfo
*content_info
)
672 const heim_oid
*envelopedAlg
= NULL
, *sdAlg
= NULL
;
674 krb5_data buf
, signed_data
;
678 krb5_data_zero(&buf
);
679 krb5_data_zero(&signed_data
);
682 * If the message client is a win2k-type but it send pa data
683 * 09-binding it expects a IETF (checksum) reply so there can be
687 switch (client_params
->type
) {
690 if (_kdc_find_padata(req
, &i
, KRB5_PADATA_PK_AS_09_BINDING
) == NULL
691 && config
->pkinit_require_binding
== 0)
700 krb5_abortx(context
, "internal pkinit error");
704 ReplyKeyPack_Win2k kp
;
705 memset(&kp
, 0, sizeof(kp
));
707 envelopedAlg
= oid_id_rsadsi_des_ede3_cbc();
708 sdAlg
= oid_id_pkcs7_data();
710 ret
= copy_EncryptionKey(reply_key
, &kp
.replyKey
);
712 krb5_clear_error_string(context
);
715 kp
.nonce
= client_params
->nonce
;
717 ASN1_MALLOC_ENCODE(ReplyKeyPack_Win2k
,
718 buf
.data
, buf
.length
,
720 free_ReplyKeyPack_Win2k(&kp
);
722 krb5_crypto ascrypto
;
724 memset(&kp
, 0, sizeof(kp
));
726 sdAlg
= oid_id_pkrkeydata();
728 ret
= copy_EncryptionKey(reply_key
, &kp
.replyKey
);
730 krb5_clear_error_string(context
);
734 ret
= krb5_crypto_init(context
, reply_key
, 0, &ascrypto
);
736 krb5_clear_error_string(context
);
740 ret
= krb5_create_checksum(context
, ascrypto
, 6, 0,
741 req_buffer
->data
, req_buffer
->length
,
744 krb5_clear_error_string(context
);
748 ret
= krb5_crypto_destroy(context
, ascrypto
);
750 krb5_clear_error_string(context
);
753 ASN1_MALLOC_ENCODE(ReplyKeyPack
, buf
.data
, buf
.length
, &kp
, &size
,ret
);
754 free_ReplyKeyPack(&kp
);
757 krb5_set_error_string(context
, "ASN.1 encoding of ReplyKeyPack "
761 if (buf
.length
!= size
)
762 krb5_abortx(context
, "Internal ASN.1 encoder error");
768 ret
= hx509_query_alloc(kdc_identity
->hx509ctx
, &q
);
772 hx509_query_match_option(q
, HX509_QUERY_OPTION_PRIVATE_KEY
);
773 hx509_query_match_option(q
, HX509_QUERY_OPTION_KU_DIGITALSIGNATURE
);
775 ret
= hx509_certs_find(kdc_identity
->hx509ctx
,
779 hx509_query_free(kdc_identity
->hx509ctx
, q
);
783 ret
= hx509_cms_create_signed_1(kdc_identity
->hx509ctx
,
791 client_params
->client_anchors
,
792 kdc_identity
->certpool
,
794 hx509_cert_free(cert
);
797 krb5_data_free(&buf
);
801 if (client_params
->type
== PKINIT_WIN2K
) {
802 ret
= hx509_cms_wrap_ContentInfo(oid_id_pkcs7_signedData(),
807 krb5_data_free(&signed_data
);
811 ret
= hx509_cms_envelope_1(kdc_identity
->hx509ctx
,
814 signed_data
.data
, signed_data
.length
,
816 oid_id_pkcs7_signedData(), &buf
);
820 ret
= _krb5_pk_mk_ContentInfo(context
,
822 oid_id_pkcs7_envelopedData(),
825 krb5_data_free(&buf
);
826 krb5_data_free(&signed_data
);
834 static krb5_error_code
835 pk_mk_pa_reply_dh(krb5_context context
,
837 pk_client_params
*client_params
,
838 krb5_keyblock
*reply_key
,
839 ContentInfo
*content_info
,
840 hx509_cert
*kdc_cert
)
842 KDCDHKeyInfo dh_info
;
843 krb5_data signed_data
, buf
;
844 ContentInfo contentinfo
;
849 memset(&contentinfo
, 0, sizeof(contentinfo
));
850 memset(&dh_info
, 0, sizeof(dh_info
));
851 krb5_data_zero(&buf
);
852 krb5_data_zero(&signed_data
);
856 ret
= BN_to_integer(context
, kdc_dh
->pub_key
, &i
);
860 ASN1_MALLOC_ENCODE(DHPublicKey
, buf
.data
, buf
.length
, &i
, &size
, ret
);
862 krb5_set_error_string(context
, "ASN.1 encoding of "
863 "DHPublicKey failed (%d)", ret
);
864 krb5_clear_error_string(context
);
867 if (buf
.length
!= size
)
868 krb5_abortx(context
, "Internal ASN.1 encoder error");
870 dh_info
.subjectPublicKey
.length
= buf
.length
* 8;
871 dh_info
.subjectPublicKey
.data
= buf
.data
;
873 dh_info
.nonce
= client_params
->nonce
;
875 ASN1_MALLOC_ENCODE(KDCDHKeyInfo
, buf
.data
, buf
.length
, &dh_info
, &size
,
878 krb5_set_error_string(context
, "ASN.1 encoding of "
879 "KdcDHKeyInfo failed (%d)", ret
);
882 if (buf
.length
!= size
)
883 krb5_abortx(context
, "Internal ASN.1 encoder error");
886 * Create the SignedData structure and sign the KdcDHKeyInfo
894 ret
= hx509_query_alloc(kdc_identity
->hx509ctx
, &q
);
898 hx509_query_match_option(q
, HX509_QUERY_OPTION_PRIVATE_KEY
);
899 hx509_query_match_option(q
, HX509_QUERY_OPTION_KU_DIGITALSIGNATURE
);
901 ret
= hx509_certs_find(kdc_identity
->hx509ctx
,
905 hx509_query_free(kdc_identity
->hx509ctx
, q
);
909 ret
= hx509_cms_create_signed_1(kdc_identity
->hx509ctx
,
911 oid_id_pkdhkeydata(),
917 client_params
->client_anchors
,
918 kdc_identity
->certpool
,
925 ret
= _krb5_pk_mk_ContentInfo(context
,
927 oid_id_pkcs7_signedData(),
933 if (ret
&& *kdc_cert
) {
934 hx509_cert_free(*kdc_cert
);
938 krb5_data_free(&buf
);
939 krb5_data_free(&signed_data
);
940 free_KDCDHKeyInfo(&dh_info
);
950 _kdc_pk_mk_pa_reply(krb5_context context
,
951 krb5_kdc_configuration
*config
,
952 pk_client_params
*client_params
,
953 const hdb_entry_ex
*client
,
955 const krb5_data
*req_buffer
,
956 krb5_keyblock
**reply_key
,
962 krb5_enctype enctype
;
964 hx509_cert kdc_cert
= NULL
;
967 if (!config
->enable_pkinit
) {
968 krb5_clear_error_string(context
);
972 if (req
->req_body
.etype
.len
> 0) {
973 for (i
= 0; i
< req
->req_body
.etype
.len
; i
++)
974 if (krb5_enctype_valid(context
, req
->req_body
.etype
.val
[i
]) == 0)
976 if (req
->req_body
.etype
.len
<= i
) {
977 ret
= KRB5KRB_ERR_GENERIC
;
978 krb5_set_error_string(context
,
979 "No valid enctype available from client");
982 enctype
= req
->req_body
.etype
.val
[i
];
984 enctype
= ETYPE_DES3_CBC_SHA1
;
986 if (client_params
->type
== PKINIT_27
) {
988 const char *type
, *other
= "";
990 memset(&rep
, 0, sizeof(rep
));
992 pa_type
= KRB5_PADATA_PK_AS_REP
;
994 if (client_params
->dh
== NULL
) {
999 rep
.element
= choice_PA_PK_AS_REP_encKeyPack
;
1001 ret
= krb5_generate_random_keyblock(context
, enctype
,
1002 &client_params
->reply_key
);
1004 free_PA_PK_AS_REP(&rep
);
1007 ret
= pk_mk_pa_reply_enckey(context
,
1012 &client_params
->reply_key
,
1015 free_PA_PK_AS_REP(&rep
);
1018 ASN1_MALLOC_ENCODE(ContentInfo
, rep
.u
.encKeyPack
.data
,
1019 rep
.u
.encKeyPack
.length
, &info
, &size
,
1021 free_ContentInfo(&info
);
1023 krb5_set_error_string(context
, "encoding of Key ContentInfo "
1025 free_PA_PK_AS_REP(&rep
);
1028 if (rep
.u
.encKeyPack
.length
!= size
)
1029 krb5_abortx(context
, "Internal ASN.1 encoder error");
1035 if (client_params
->dh_group_name
)
1036 other
= client_params
->dh_group_name
;
1038 rep
.element
= choice_PA_PK_AS_REP_dhInfo
;
1040 ret
= generate_dh_keyblock(context
, client_params
, enctype
,
1041 &client_params
->reply_key
);
1045 ret
= pk_mk_pa_reply_dh(context
, client_params
->dh
,
1047 &client_params
->reply_key
,
1051 ASN1_MALLOC_ENCODE(ContentInfo
, rep
.u
.dhInfo
.dhSignedData
.data
,
1052 rep
.u
.dhInfo
.dhSignedData
.length
, &info
, &size
,
1054 free_ContentInfo(&info
);
1056 krb5_set_error_string(context
, "encoding of Key ContentInfo "
1058 free_PA_PK_AS_REP(&rep
);
1061 if (rep
.u
.encKeyPack
.length
!= size
)
1062 krb5_abortx(context
, "Internal ASN.1 encoder error");
1066 free_PA_PK_AS_REP(&rep
);
1070 ASN1_MALLOC_ENCODE(PA_PK_AS_REP
, buf
, len
, &rep
, &size
, ret
);
1071 free_PA_PK_AS_REP(&rep
);
1073 krb5_set_error_string(context
, "encode PA-PK-AS-REP failed %d",
1078 krb5_abortx(context
, "Internal ASN.1 encoder error");
1080 kdc_log(context
, config
, 0, "PK-INIT using %s %s", type
, other
);
1082 } else if (client_params
->type
== PKINIT_WIN2K
) {
1083 PA_PK_AS_REP_Win2k rep
;
1086 if (client_params
->dh
) {
1087 krb5_set_error_string(context
, "Windows PK-INIT doesn't support DH");
1088 ret
= KRB5KRB_ERR_GENERIC
;
1092 memset(&rep
, 0, sizeof(rep
));
1094 pa_type
= KRB5_PADATA_PK_AS_REP_19
;
1095 rep
.element
= choice_PA_PK_AS_REP_encKeyPack
;
1097 ret
= krb5_generate_random_keyblock(context
, enctype
,
1098 &client_params
->reply_key
);
1100 free_PA_PK_AS_REP_Win2k(&rep
);
1103 ret
= pk_mk_pa_reply_enckey(context
,
1108 &client_params
->reply_key
,
1111 free_PA_PK_AS_REP_Win2k(&rep
);
1114 ASN1_MALLOC_ENCODE(ContentInfo
, rep
.u
.encKeyPack
.data
,
1115 rep
.u
.encKeyPack
.length
, &info
, &size
,
1117 free_ContentInfo(&info
);
1119 krb5_set_error_string(context
, "encoding of Key ContentInfo "
1121 free_PA_PK_AS_REP_Win2k(&rep
);
1124 if (rep
.u
.encKeyPack
.length
!= size
)
1125 krb5_abortx(context
, "Internal ASN.1 encoder error");
1127 ASN1_MALLOC_ENCODE(PA_PK_AS_REP_Win2k
, buf
, len
, &rep
, &size
, ret
);
1128 free_PA_PK_AS_REP_Win2k(&rep
);
1130 krb5_set_error_string(context
,
1131 "encode PA-PK-AS-REP-Win2k failed %d", ret
);
1135 krb5_abortx(context
, "Internal ASN.1 encoder error");
1138 krb5_abortx(context
, "PK-INIT internal error");
1141 ret
= krb5_padata_add(context
, md
, pa_type
, buf
, len
);
1143 krb5_set_error_string(context
, "failed adding PA-PK-AS-REP %d", ret
);
1148 if (config
->pkinit_kdc_ocsp_file
) {
1150 if (ocsp
.expire
== 0 && ocsp
.next_update
> kdc_time
) {
1154 krb5_data_free(&ocsp
.data
);
1157 ocsp
.next_update
= kdc_time
+ 60 * 5;
1159 fd
= open(config
->pkinit_kdc_ocsp_file
, O_RDONLY
);
1161 kdc_log(context
, config
, 0,
1162 "PK-INIT failed to open ocsp data file %d", errno
);
1165 ret
= fstat(fd
, &sb
);
1169 kdc_log(context
, config
, 0,
1170 "PK-INIT failed to stat ocsp data %d", ret
);
1174 ret
= krb5_data_alloc(&ocsp
.data
, sb
.st_size
);
1177 kdc_log(context
, config
, 0,
1178 "PK-INIT failed to stat ocsp data %d", ret
);
1181 ocsp
.data
.length
= sb
.st_size
;
1182 ret
= read(fd
, ocsp
.data
.data
, sb
.st_size
);
1184 if (ret
!= sb
.st_size
) {
1185 kdc_log(context
, config
, 0,
1186 "PK-INIT failed to read ocsp data %d", errno
);
1190 ret
= hx509_ocsp_verify(kdc_identity
->hx509ctx
,
1194 ocsp
.data
.data
, ocsp
.data
.length
,
1197 kdc_log(context
, config
, 0,
1198 "PK-INIT failed to verify ocsp data %d", ret
);
1199 krb5_data_free(&ocsp
.data
);
1201 } else if (ocsp
.expire
> 180) {
1202 ocsp
.expire
-= 180; /* refetch the ocsp before it expire */
1203 ocsp
.next_update
= ocsp
.expire
;
1205 ocsp
.next_update
= kdc_time
;
1211 if (ocsp
.expire
!= 0 && ocsp
.expire
> kdc_time
) {
1213 ret
= krb5_padata_add(context
, md
,
1214 KRB5_PADATA_PA_PK_OCSP_RESPONSE
,
1215 ocsp
.data
.data
, ocsp
.data
.length
);
1217 krb5_set_error_string(context
,
1218 "Failed adding OCSP response %d", ret
);
1226 hx509_cert_free(kdc_cert
);
1229 *reply_key
= &client_params
->reply_key
;
1234 match_rfc_san(krb5_context context
,
1235 krb5_kdc_configuration
*config
,
1236 hx509_context hx509ctx
,
1237 hx509_cert client_cert
,
1238 krb5_const_principal match
)
1240 hx509_octet_string_list list
;
1241 int ret
, i
, found
= 0;
1243 memset(&list
, 0 , sizeof(list
));
1245 ret
= hx509_cert_find_subjectAltName_otherName(hx509ctx
,
1247 oid_id_pkinit_san(),
1252 for (i
= 0; !found
&& i
< list
.len
; i
++) {
1253 krb5_principal_data principal
;
1254 KRB5PrincipalName kn
;
1257 ret
= decode_KRB5PrincipalName(list
.val
[i
].data
,
1261 kdc_log(context
, config
, 0,
1262 "Decoding kerberos name in certificate failed: %s",
1263 krb5_get_err_text(context
, ret
));
1266 if (size
!= list
.val
[i
].length
) {
1267 kdc_log(context
, config
, 0,
1268 "Decoding kerberos name have extra bits on the end");
1269 return KRB5_KDC_ERR_CLIENT_NAME_MISMATCH
;
1272 principal
.name
= kn
.principalName
;
1273 principal
.realm
= kn
.realm
;
1275 if (krb5_principal_compare(context
, &principal
, match
) == TRUE
)
1277 free_KRB5PrincipalName(&kn
);
1281 hx509_free_octet_string_list(&list
);
1286 return KRB5_KDC_ERR_CLIENT_NAME_MISMATCH
;
1292 match_ms_upn_san(krb5_context context
,
1293 krb5_kdc_configuration
*config
,
1294 hx509_context hx509ctx
,
1295 hx509_cert client_cert
,
1296 krb5_const_principal match
)
1298 hx509_octet_string_list list
;
1299 krb5_principal principal
= NULL
;
1304 memset(&list
, 0 , sizeof(list
));
1306 ret
= hx509_cert_find_subjectAltName_otherName(hx509ctx
,
1308 oid_id_pkinit_ms_san(),
1313 if (list
.len
!= 1) {
1314 kdc_log(context
, config
, 0,
1315 "More then one PK-INIT MS UPN SAN");
1319 ret
= decode_MS_UPN_SAN(list
.val
[0].data
, list
.val
[0].length
, &upn
, &size
);
1321 kdc_log(context
, config
, 0, "Decode of MS-UPN-SAN failed");
1325 kdc_log(context
, config
, 0, "found MS UPN SAN: %s", upn
);
1327 ret
= krb5_parse_name(context
, upn
, &principal
);
1328 free_MS_UPN_SAN(&upn
);
1330 kdc_log(context
, config
, 0, "Failed to parse principal in MS UPN SAN");
1335 * This is very wrong, but will do for now, should really and a
1336 * plugin to the windc layer to very this ACL.
1338 strupr(principal
->realm
);
1340 if (krb5_principal_compare(context
, principal
, match
) == TRUE
)
1345 krb5_free_principal(context
, principal
);
1346 hx509_free_octet_string_list(&list
);
1351 return KRB5_KDC_ERR_CLIENT_NAME_MISMATCH
;
1357 _kdc_pk_check_client(krb5_context context
,
1358 krb5_kdc_configuration
*config
,
1359 const hdb_entry_ex
*client
,
1360 pk_client_params
*client_params
,
1361 char **subject_name
)
1363 const HDB_Ext_PKINIT_acl
*acl
;
1364 krb5_error_code ret
;
1368 ret
= hx509_cert_get_base_subject(kdc_identity
->hx509ctx
,
1369 client_params
->cert
,
1374 ret
= hx509_name_to_string(name
, subject_name
);
1375 hx509_name_free(&name
);
1379 kdc_log(context
, config
, 0,
1380 "Trying to authorize PK-INIT subject DN %s",
1383 if (config
->pkinit_princ_in_cert
) {
1384 ret
= match_rfc_san(context
, config
,
1385 kdc_identity
->hx509ctx
,
1386 client_params
->cert
,
1387 client
->entry
.principal
);
1389 kdc_log(context
, config
, 5,
1390 "Found matching PK-INIT SAN in certificate");
1393 ret
= match_ms_upn_san(context
, config
,
1394 kdc_identity
->hx509ctx
,
1395 client_params
->cert
,
1396 client
->entry
.principal
);
1398 kdc_log(context
, config
, 5,
1399 "Found matching MS UPN SAN in certificate");
1404 ret
= hdb_entry_get_pkinit_acl(&client
->entry
, &acl
);
1405 if (ret
== 0 && acl
!= NULL
) {
1407 * Cheat here and compare the generated name with the string
1408 * and not the reverse.
1410 for (i
= 0; i
< acl
->len
; i
++) {
1411 if (strcmp(*subject_name
, acl
->val
[0].subject
) != 0)
1414 /* Don't support isser and anchor checking right now */
1415 if (acl
->val
[0].issuer
)
1417 if (acl
->val
[0].anchor
)
1420 kdc_log(context
, config
, 5,
1421 "Found matching PK-INIT database ACL");
1426 for (i
= 0; i
< principal_mappings
.len
; i
++) {
1429 b
= krb5_principal_compare(context
,
1430 client
->entry
.principal
,
1431 principal_mappings
.val
[i
].principal
);
1434 if (strcmp(principal_mappings
.val
[i
].subject
, *subject_name
) != 0)
1436 kdc_log(context
, config
, 5,
1437 "Found matching PK-INIT FILE ACL");
1441 krb5_set_error_string(context
,
1442 "PKINIT no matching principals for %s",
1445 kdc_log(context
, config
, 5,
1446 "PKINIT no matching principals for %s",
1449 free(*subject_name
);
1450 *subject_name
= NULL
;
1452 return KRB5_KDC_ERR_CLIENT_NAME_MISMATCH
;
1455 static krb5_error_code
1456 add_principal_mapping(krb5_context context
,
1457 const char *principal_name
,
1458 const char * subject
)
1460 struct pk_allowed_princ
*tmp
;
1461 krb5_principal principal
;
1462 krb5_error_code ret
;
1464 tmp
= realloc(principal_mappings
.val
,
1465 (principal_mappings
.len
+ 1) * sizeof(*tmp
));
1468 principal_mappings
.val
= tmp
;
1470 ret
= krb5_parse_name(context
, principal_name
, &principal
);
1474 principal_mappings
.val
[principal_mappings
.len
].principal
= principal
;
1476 principal_mappings
.val
[principal_mappings
.len
].subject
= strdup(subject
);
1477 if (principal_mappings
.val
[principal_mappings
.len
].subject
== NULL
) {
1478 krb5_free_principal(context
, principal
);
1481 principal_mappings
.len
++;
1487 _kdc_add_inital_verified_cas(krb5_context context
,
1488 krb5_kdc_configuration
*config
,
1489 pk_client_params
*params
,
1492 AD_INITIAL_VERIFIED_CAS cas
;
1493 krb5_error_code ret
;
1497 memset(&cas
, 0, sizeof(cas
));
1499 /* XXX add CAs to cas here */
1501 ASN1_MALLOC_ENCODE(AD_INITIAL_VERIFIED_CAS
, data
.data
, data
.length
,
1505 if (data
.length
!= size
)
1506 krb5_abortx(context
, "internal asn.1 encoder error");
1508 ret
= _kdc_tkt_add_if_relevant_ad(context
, tkt
,
1509 KRB5_AUTHDATA_INITIAL_VERIFIED_CAS
,
1511 krb5_data_free(&data
);
1520 load_mappings(krb5_context context
, const char *fn
)
1522 krb5_error_code ret
;
1524 unsigned long lineno
= 0;
1531 while (fgets(buf
, sizeof(buf
), f
) != NULL
) {
1532 char *subject_name
, *p
;
1534 buf
[strcspn(buf
, "\n")] = '\0';
1537 p
= buf
+ strspn(buf
, " \t");
1539 if (*p
== '#' || *p
== '\0')
1542 subject_name
= strchr(p
, ':');
1543 if (subject_name
== NULL
) {
1544 krb5_warnx(context
, "pkinit mapping file line %lu "
1545 "missing \":\" :%s",
1549 *subject_name
++ = '\0';
1551 ret
= add_principal_mapping(context
, p
, subject_name
);
1553 krb5_warn(context
, ret
, "failed to add line %lu \":\" :%s\n",
1567 _kdc_pk_initialize(krb5_context context
,
1568 krb5_kdc_configuration
*config
,
1569 const char *user_id
,
1570 const char *anchors
,
1576 krb5_error_code ret
;
1578 file
= krb5_config_get_string(context
, NULL
,
1579 "libdefaults", "moduli", NULL
);
1581 ret
= _krb5_parse_moduli(context
, file
, &moduli
);
1583 krb5_err(context
, 1, ret
, "PKINIT: failed to load modidi file");
1585 principal_mappings
.len
= 0;
1586 principal_mappings
.val
= NULL
;
1588 ret
= _krb5_pk_load_id(context
,
1598 krb5_warn(context
, ret
, "PKINIT: ");
1599 config
->enable_pkinit
= 0;
1607 ret
= hx509_query_alloc(kdc_identity
->hx509ctx
, &q
);
1609 krb5_warnx(context
, "PKINIT: out of memory");
1613 hx509_query_match_option(q
, HX509_QUERY_OPTION_PRIVATE_KEY
);
1614 hx509_query_match_option(q
, HX509_QUERY_OPTION_KU_DIGITALSIGNATURE
);
1616 ret
= hx509_certs_find(kdc_identity
->hx509ctx
,
1617 kdc_identity
->certs
,
1620 hx509_query_free(kdc_identity
->hx509ctx
, q
);
1622 if (hx509_cert_check_eku(kdc_identity
->hx509ctx
, cert
,
1623 oid_id_pkkdcekuoid(), 0))
1624 krb5_warnx(context
, "WARNING Found KDC certificate "
1625 "is missing the PK-INIT KDC EKU, this is bad for "
1626 "interoperability.");
1627 hx509_cert_free(cert
);
1629 krb5_warnx(context
, "PKINIT: failed to find a signing "
1630 "certifiate with a public key");
1633 ret
= krb5_config_get_bool_default(context
,
1637 "pkinit_allow_proxy_certificate",
1639 _krb5_pk_allow_proxy_certificate(kdc_identity
, ret
);
1641 file
= krb5_config_get_string(context
,
1644 "pkinit_mappings_file",
1647 asprintf(&fn
, "%s/pki-mapping", hdb_db_dir(context
));
1651 load_mappings(context
, file
);