2 * Copyright (c) 2003 - 2007 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
36 #include "krb5_locl.h"
38 struct krb5_dh_moduli
{
49 #include <pkcs8_asn1.h>
50 #include <pkcs9_asn1.h>
51 #include <pkcs12_asn1.h>
52 #include <pkinit_asn1.h>
61 struct krb5_pk_init_ctx_data
{
62 struct krb5_pk_identity
*id
;
63 enum { USE_RSA
, USE_DH
, USE_ECDH
} keyex
;
70 krb5_data
*clientDHNonce
;
71 struct krb5_dh_moduli
**m
;
73 enum krb5_pk_type type
;
74 unsigned int require_binding
:1;
75 unsigned int require_eku
:1;
76 unsigned int require_krbtgt_otherName
:1;
77 unsigned int require_hostname_match
:1;
78 unsigned int trustedCertifiers
:1;
79 unsigned int anonymous
:1;
83 pk_copy_error(krb5_context context
,
84 hx509_context hx509ctx
,
88 __attribute__ ((format (printf
, 4, 5)));
94 KRB5_LIB_FUNCTION
void KRB5_LIB_CALL
95 _krb5_pk_cert_free(struct krb5_pk_cert
*cert
)
98 hx509_cert_free(cert
->cert
);
103 static krb5_error_code
104 BN_to_integer(krb5_context context
, BIGNUM
*bn
, heim_integer
*integer
)
106 integer
->length
= BN_num_bytes(bn
);
107 integer
->data
= malloc(integer
->length
);
108 if (integer
->data
== NULL
) {
109 krb5_clear_error_message(context
);
112 BN_bn2bin(bn
, integer
->data
);
113 integer
->negative
= BN_is_negative(bn
);
118 integer_to_BN(krb5_context context
, const char *field
, const heim_integer
*f
)
122 bn
= BN_bin2bn((const unsigned char *)f
->data
, f
->length
, NULL
);
124 krb5_set_error_message(context
, ENOMEM
,
125 N_("PKINIT: parsing BN failed %s", ""), field
);
128 BN_set_negative(bn
, f
->negative
);
132 static krb5_error_code
133 select_dh_group(krb5_context context
, DH
*dh
, unsigned long bits
,
134 struct krb5_dh_moduli
**moduli
)
136 const struct krb5_dh_moduli
*m
;
139 m
= moduli
[1]; /* XXX */
141 m
= moduli
[0]; /* XXX */
144 for (i
= 0; moduli
[i
] != NULL
; i
++) {
145 if (bits
< moduli
[i
]->bits
)
148 if (moduli
[i
] == NULL
) {
149 krb5_set_error_message(context
, EINVAL
,
150 N_("Did not find a DH group parameter "
151 "matching requirement of %lu bits", ""),
158 dh
->p
= integer_to_BN(context
, "p", &m
->p
);
161 dh
->g
= integer_to_BN(context
, "g", &m
->g
);
164 dh
->q
= integer_to_BN(context
, "q", &m
->q
);
177 * Try searchin the key by to use by first looking for for PK-INIT
178 * EKU, then the Microsoft smart card EKU and last, no special EKU at all.
181 static krb5_error_code
182 find_cert(krb5_context context
, struct krb5_pk_identity
*id
,
183 hx509_query
*q
, hx509_cert
*cert
)
185 struct certfind cf
[4] = {
186 { "MobileMe EKU", NULL
},
187 { "PKINIT EKU", NULL
},
189 { "any (or no)", NULL
}
191 int ret
= HX509_CERT_NOT_FOUND
;
193 unsigned oids
[] = { 1, 2, 840, 113635, 100, 3, 2, 1 };
194 const heim_oid mobileMe
= { sizeof(oids
)/sizeof(oids
[0]), oids
};
197 if (id
->flags
& PKINIT_BTMM
)
200 cf
[0].oid
= &mobileMe
;
201 cf
[1].oid
= &asn1_oid_id_pkekuoid
;
202 cf
[2].oid
= &asn1_oid_id_pkinit_ms_eku
;
205 for (i
= start
; i
< sizeof(cf
)/sizeof(cf
[0]); i
++) {
206 ret
= hx509_query_match_eku(q
, cf
[i
].oid
);
208 pk_copy_error(context
, context
->hx509ctx
, ret
,
209 "Failed setting %s OID", cf
[i
].type
);
213 ret
= hx509_certs_find(context
->hx509ctx
, id
->certs
, q
, cert
);
216 pk_copy_error(context
, context
->hx509ctx
, ret
,
217 "Failed finding certificate with %s OID", cf
[i
].type
);
223 static krb5_error_code
224 create_signature(krb5_context context
,
225 const heim_oid
*eContentType
,
227 struct krb5_pk_identity
*id
,
228 hx509_peer_info peer
,
233 if (id
->cert
== NULL
)
234 flags
|= HX509_CMS_SIGNATURE_NO_SIGNER
;
236 ret
= hx509_cms_create_signed_1(context
->hx509ctx
,
248 pk_copy_error(context
, context
->hx509ctx
, ret
,
249 "Create CMS signedData");
257 cert2epi(hx509_context context
, void *ctx
, hx509_cert c
)
259 ExternalPrincipalIdentifiers
*ids
= ctx
;
260 ExternalPrincipalIdentifier id
;
261 hx509_name subject
= NULL
;
268 memset(&id
, 0, sizeof(id
));
270 ret
= hx509_cert_get_subject(c
, &subject
);
274 if (hx509_name_is_null_p(subject
) != 0) {
276 id
.subjectName
= calloc(1, sizeof(*id
.subjectName
));
277 if (id
.subjectName
== NULL
) {
278 hx509_name_free(&subject
);
279 free_ExternalPrincipalIdentifier(&id
);
283 ret
= hx509_name_binary(subject
, id
.subjectName
);
285 hx509_name_free(&subject
);
286 free_ExternalPrincipalIdentifier(&id
);
290 hx509_name_free(&subject
);
293 id
.issuerAndSerialNumber
= calloc(1, sizeof(*id
.issuerAndSerialNumber
));
294 if (id
.issuerAndSerialNumber
== NULL
) {
295 free_ExternalPrincipalIdentifier(&id
);
300 IssuerAndSerialNumber iasn
;
304 memset(&iasn
, 0, sizeof(iasn
));
306 ret
= hx509_cert_get_issuer(c
, &issuer
);
308 free_ExternalPrincipalIdentifier(&id
);
312 ret
= hx509_name_to_Name(issuer
, &iasn
.issuer
);
313 hx509_name_free(&issuer
);
315 free_ExternalPrincipalIdentifier(&id
);
319 ret
= hx509_cert_get_serialnumber(c
, &iasn
.serialNumber
);
321 free_IssuerAndSerialNumber(&iasn
);
322 free_ExternalPrincipalIdentifier(&id
);
326 ASN1_MALLOC_ENCODE(IssuerAndSerialNumber
,
327 id
.issuerAndSerialNumber
->data
,
328 id
.issuerAndSerialNumber
->length
,
330 free_IssuerAndSerialNumber(&iasn
);
333 if (id
.issuerAndSerialNumber
->length
!= size
)
337 id
.subjectKeyIdentifier
= NULL
;
339 p
= realloc(ids
->val
, sizeof(ids
->val
[0]) * (ids
->len
+ 1));
341 free_ExternalPrincipalIdentifier(&id
);
346 ids
->val
[ids
->len
] = id
;
352 static krb5_error_code
353 build_edi(krb5_context context
,
354 hx509_context hx509ctx
,
356 ExternalPrincipalIdentifiers
*ids
)
358 return hx509_certs_iter_f(hx509ctx
, certs
, cert2epi
, ids
);
361 static krb5_error_code
362 build_auth_pack(krb5_context context
,
364 krb5_pk_init_ctx ctx
,
365 const KDC_REQ_BODY
*body
,
368 size_t buf_size
, len
= 0;
375 krb5_clear_error_message(context
);
377 memset(&checksum
, 0, sizeof(checksum
));
379 krb5_us_timeofday(context
, &sec
, &usec
);
380 a
->pkAuthenticator
.ctime
= sec
;
381 a
->pkAuthenticator
.nonce
= nonce
;
383 ASN1_MALLOC_ENCODE(KDC_REQ_BODY
, buf
, buf_size
, body
, &len
, ret
);
387 krb5_abortx(context
, "internal error in ASN.1 encoder");
389 ret
= krb5_create_checksum(context
,
400 ALLOC(a
->pkAuthenticator
.paChecksum
, 1);
401 if (a
->pkAuthenticator
.paChecksum
== NULL
) {
402 return krb5_enomem(context
);
405 ret
= krb5_data_copy(a
->pkAuthenticator
.paChecksum
,
406 checksum
.checksum
.data
, checksum
.checksum
.length
);
407 free_Checksum(&checksum
);
411 if (ctx
->keyex
== USE_DH
|| ctx
->keyex
== USE_ECDH
) {
412 const char *moduli_file
;
413 unsigned long dh_min_bits
;
417 krb5_data_zero(&dhbuf
);
421 moduli_file
= krb5_config_get_string(context
, NULL
,
427 krb5_config_get_int_default(context
, NULL
, 0,
429 "pkinit_dh_min_bits",
432 ret
= _krb5_parse_moduli(context
, moduli_file
, &ctx
->m
);
436 ctx
->u
.dh
= DH_new();
437 if (ctx
->u
.dh
== NULL
)
438 return krb5_enomem(context
);
440 ret
= select_dh_group(context
, ctx
->u
.dh
, dh_min_bits
, ctx
->m
);
444 if (DH_generate_key(ctx
->u
.dh
) != 1) {
445 krb5_set_error_message(context
, ENOMEM
,
446 N_("pkinit: failed to generate DH key", ""));
451 if (1 /* support_cached_dh */) {
452 ALLOC(a
->clientDHNonce
, 1);
453 if (a
->clientDHNonce
== NULL
) {
454 krb5_clear_error_message(context
);
457 ret
= krb5_data_alloc(a
->clientDHNonce
, 40);
458 if (a
->clientDHNonce
== NULL
) {
459 krb5_clear_error_message(context
);
462 RAND_bytes(a
->clientDHNonce
->data
, a
->clientDHNonce
->length
);
463 ret
= krb5_copy_data(context
, a
->clientDHNonce
,
464 &ctx
->clientDHNonce
);
469 ALLOC(a
->clientPublicValue
, 1);
470 if (a
->clientPublicValue
== NULL
)
473 if (ctx
->keyex
== USE_DH
) {
476 heim_integer dh_pub_key
;
478 ret
= der_copy_oid(&asn1_oid_id_dhpublicnumber
,
479 &a
->clientPublicValue
->algorithm
.algorithm
);
483 memset(&dp
, 0, sizeof(dp
));
485 ret
= BN_to_integer(context
, dh
->p
, &dp
.p
);
487 free_DomainParameters(&dp
);
490 ret
= BN_to_integer(context
, dh
->g
, &dp
.g
);
492 free_DomainParameters(&dp
);
495 ret
= BN_to_integer(context
, dh
->q
, &dp
.q
);
497 free_DomainParameters(&dp
);
501 dp
.validationParms
= NULL
;
503 a
->clientPublicValue
->algorithm
.parameters
=
504 malloc(sizeof(*a
->clientPublicValue
->algorithm
.parameters
));
505 if (a
->clientPublicValue
->algorithm
.parameters
== NULL
) {
506 free_DomainParameters(&dp
);
510 ASN1_MALLOC_ENCODE(DomainParameters
,
511 a
->clientPublicValue
->algorithm
.parameters
->data
,
512 a
->clientPublicValue
->algorithm
.parameters
->length
,
514 free_DomainParameters(&dp
);
517 if (size
!= a
->clientPublicValue
->algorithm
.parameters
->length
)
518 krb5_abortx(context
, "Internal ASN1 encoder error");
520 ret
= BN_to_integer(context
, dh
->pub_key
, &dh_pub_key
);
524 ASN1_MALLOC_ENCODE(DHPublicKey
, dhbuf
.data
, dhbuf
.length
,
525 &dh_pub_key
, &size
, ret
);
526 der_free_heim_integer(&dh_pub_key
);
529 if (size
!= dhbuf
.length
)
530 krb5_abortx(context
, "asn1 internal error");
531 } else if (ctx
->keyex
== USE_ECDH
) {
537 /* copy in public key, XXX find the best curve that the server support or use the clients curve if possible */
539 ecp
.element
= choice_ECParameters_namedCurve
;
540 ret
= der_copy_oid(&asn1_oid_id_ec_group_secp256r1
,
545 ALLOC(a
->clientPublicValue
->algorithm
.parameters
, 1);
546 if (a
->clientPublicValue
->algorithm
.parameters
== NULL
) {
547 free_ECParameters(&ecp
);
550 ASN1_MALLOC_ENCODE(ECParameters
, p
, xlen
, &ecp
, &size
, ret
);
551 free_ECParameters(&ecp
);
554 if ((int)size
!= xlen
)
555 krb5_abortx(context
, "asn1 internal error");
557 a
->clientPublicValue
->algorithm
.parameters
->data
= p
;
558 a
->clientPublicValue
->algorithm
.parameters
->length
= size
;
560 /* copy in public key */
562 ret
= der_copy_oid(&asn1_oid_id_ecPublicKey
,
563 &a
->clientPublicValue
->algorithm
.algorithm
);
567 ctx
->u
.eckey
= EC_KEY_new_by_curve_name(NID_X9_62_prime256v1
);
568 if (ctx
->u
.eckey
== NULL
)
571 ret
= EC_KEY_generate_key(ctx
->u
.eckey
);
575 /* encode onto dhkey */
577 xlen
= i2o_ECPublicKey(ctx
->u
.eckey
, NULL
);
581 dhbuf
.data
= malloc(xlen
);
582 if (dhbuf
.data
== NULL
)
587 xlen
= i2o_ECPublicKey(ctx
->u
.eckey
, &p
);
591 /* XXX verify that this is right with RFC3279 */
596 krb5_abortx(context
, "internal error");
597 a
->clientPublicValue
->subjectPublicKey
.length
= dhbuf
.length
* 8;
598 a
->clientPublicValue
->subjectPublicKey
.data
= dhbuf
.data
;
602 a
->supportedCMSTypes
= calloc(1, sizeof(*a
->supportedCMSTypes
));
603 if (a
->supportedCMSTypes
== NULL
)
606 ret
= hx509_crypto_available(context
->hx509ctx
, HX509_SELECT_ALL
,
608 &a
->supportedCMSTypes
->val
,
609 &a
->supportedCMSTypes
->len
);
617 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
618 _krb5_pk_mk_ContentInfo(krb5_context context
,
619 const krb5_data
*buf
,
621 struct ContentInfo
*content_info
)
625 ret
= der_copy_oid(oid
, &content_info
->contentType
);
628 ALLOC(content_info
->content
, 1);
629 if (content_info
->content
== NULL
)
631 content_info
->content
->data
= malloc(buf
->length
);
632 if (content_info
->content
->data
== NULL
)
634 memcpy(content_info
->content
->data
, buf
->data
, buf
->length
);
635 content_info
->content
->length
= buf
->length
;
639 static krb5_error_code
640 pk_mk_padata(krb5_context context
,
641 krb5_pk_init_ctx ctx
,
642 const KDC_REQ_BODY
*req_body
,
646 struct ContentInfo content_info
;
648 const heim_oid
*oid
= NULL
;
650 krb5_data buf
, sd_buf
;
653 krb5_data_zero(&buf
);
654 krb5_data_zero(&sd_buf
);
655 memset(&content_info
, 0, sizeof(content_info
));
657 if (ctx
->type
== PKINIT_WIN2K
) {
662 memset(&ap
, 0, sizeof(ap
));
664 /* fill in PKAuthenticator */
665 ret
= copy_PrincipalName(req_body
->sname
, &ap
.pkAuthenticator
.kdcName
);
667 free_AuthPack_Win2k(&ap
);
668 krb5_clear_error_message(context
);
671 ret
= copy_Realm(&req_body
->realm
, &ap
.pkAuthenticator
.kdcRealm
);
673 free_AuthPack_Win2k(&ap
);
674 krb5_clear_error_message(context
);
678 krb5_us_timeofday(context
, &sec
, &usec
);
679 ap
.pkAuthenticator
.ctime
= sec
;
680 ap
.pkAuthenticator
.cusec
= usec
;
681 ap
.pkAuthenticator
.nonce
= nonce
;
683 ASN1_MALLOC_ENCODE(AuthPack_Win2k
, buf
.data
, buf
.length
,
685 free_AuthPack_Win2k(&ap
);
687 krb5_set_error_message(context
, ret
,
688 N_("Failed encoding AuthPackWin: %d", ""),
692 if (buf
.length
!= size
)
693 krb5_abortx(context
, "internal ASN1 encoder error");
695 oid
= &asn1_oid_id_pkcs7_data
;
696 } else if (ctx
->type
== PKINIT_27
) {
699 memset(&ap
, 0, sizeof(ap
));
701 ret
= build_auth_pack(context
, nonce
, ctx
, req_body
, &ap
);
707 ASN1_MALLOC_ENCODE(AuthPack
, buf
.data
, buf
.length
, &ap
, &size
, ret
);
710 krb5_set_error_message(context
, ret
,
711 N_("Failed encoding AuthPack: %d", ""),
715 if (buf
.length
!= size
)
716 krb5_abortx(context
, "internal ASN1 encoder error");
718 oid
= &asn1_oid_id_pkauthdata
;
720 krb5_abortx(context
, "internal pkinit error");
722 ret
= create_signature(context
, oid
, &buf
, ctx
->id
,
724 krb5_data_free(&buf
);
728 ret
= hx509_cms_wrap_ContentInfo(&asn1_oid_id_pkcs7_signedData
, &sd_buf
, &buf
);
729 krb5_data_free(&sd_buf
);
731 krb5_set_error_message(context
, ret
,
732 N_("ContentInfo wrapping of signedData failed",""));
736 if (ctx
->type
== PKINIT_WIN2K
) {
737 PA_PK_AS_REQ_Win2k winreq
;
739 pa_type
= KRB5_PADATA_PK_AS_REQ_WIN
;
741 memset(&winreq
, 0, sizeof(winreq
));
743 winreq
.signed_auth_pack
= buf
;
745 ASN1_MALLOC_ENCODE(PA_PK_AS_REQ_Win2k
, buf
.data
, buf
.length
,
746 &winreq
, &size
, ret
);
747 free_PA_PK_AS_REQ_Win2k(&winreq
);
749 } else if (ctx
->type
== PKINIT_27
) {
752 pa_type
= KRB5_PADATA_PK_AS_REQ
;
754 memset(&req
, 0, sizeof(req
));
755 req
.signedAuthPack
= buf
;
757 if (ctx
->trustedCertifiers
) {
759 req
.trustedCertifiers
= calloc(1, sizeof(*req
.trustedCertifiers
));
760 if (req
.trustedCertifiers
== NULL
) {
761 ret
= krb5_enomem(context
);
762 free_PA_PK_AS_REQ(&req
);
765 ret
= build_edi(context
, context
->hx509ctx
,
766 ctx
->id
->anchors
, req
.trustedCertifiers
);
768 krb5_set_error_message(context
, ret
,
769 N_("pk-init: failed to build "
770 "trustedCertifiers", ""));
771 free_PA_PK_AS_REQ(&req
);
777 ASN1_MALLOC_ENCODE(PA_PK_AS_REQ
, buf
.data
, buf
.length
,
780 free_PA_PK_AS_REQ(&req
);
783 krb5_abortx(context
, "internal pkinit error");
785 krb5_set_error_message(context
, ret
, "PA-PK-AS-REQ %d", (int)ret
);
788 if (buf
.length
!= size
)
789 krb5_abortx(context
, "Internal ASN1 encoder error");
791 ret
= krb5_padata_add(context
, md
, pa_type
, buf
.data
, buf
.length
);
796 krb5_padata_add(context
, md
, KRB5_PADATA_PK_AS_09_BINDING
, NULL
, 0);
799 free_ContentInfo(&content_info
);
805 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
806 _krb5_pk_mk_padata(krb5_context context
,
810 const KDC_REQ_BODY
*req_body
,
814 krb5_pk_init_ctx ctx
= c
;
817 if (ctx
->id
->certs
== NULL
&& ctx
->anonymous
== 0) {
818 krb5_set_error_message(context
, HEIM_PKINIT_NO_PRIVATE_KEY
,
819 N_("PKINIT: No user certificate given", ""));
820 return HEIM_PKINIT_NO_PRIVATE_KEY
;
823 win2k_compat
= krb5_config_get_bool_default(context
, NULL
,
831 ctx
->require_binding
=
832 krb5_config_get_bool_default(context
, NULL
,
836 "pkinit_win2k_require_binding",
838 ctx
->type
= PKINIT_WIN2K
;
840 ctx
->type
= PKINIT_27
;
843 krb5_config_get_bool_default(context
, NULL
,
847 "pkinit_require_eku",
849 if (ic_flags
& KRB5_INIT_CREDS_NO_C_NO_EKU_CHECK
)
850 ctx
->require_eku
= 0;
851 if (ctx
->id
->flags
& PKINIT_BTMM
)
852 ctx
->require_eku
= 0;
854 ctx
->require_krbtgt_otherName
=
855 krb5_config_get_bool_default(context
, NULL
,
859 "pkinit_require_krbtgt_otherName",
862 ctx
->require_hostname_match
=
863 krb5_config_get_bool_default(context
, NULL
,
867 "pkinit_require_hostname_match",
870 ctx
->trustedCertifiers
=
871 krb5_config_get_bool_default(context
, NULL
,
875 "pkinit_trustedCertifiers",
878 return pk_mk_padata(context
, ctx
, req_body
, nonce
, md
);
881 static krb5_error_code
882 pk_verify_sign(krb5_context context
,
885 struct krb5_pk_identity
*id
,
886 heim_oid
*contentType
,
888 struct krb5_pk_cert
**signer
)
890 hx509_certs signer_certs
;
893 /* BTMM is broken in Leo and SnowLeo */
894 if (id
->flags
& PKINIT_BTMM
) {
895 flags
|= HX509_CMS_VS_ALLOW_DATA_OID_MISMATCH
;
896 flags
|= HX509_CMS_VS_NO_KU_CHECK
;
897 flags
|= HX509_CMS_VS_NO_VALIDATE
;
902 ret
= hx509_cms_verify_signed(context
->hx509ctx
,
913 pk_copy_error(context
, context
->hx509ctx
, ret
,
914 "CMS verify signed failed");
918 *signer
= calloc(1, sizeof(**signer
));
919 if (*signer
== NULL
) {
920 krb5_clear_error_message(context
);
925 ret
= hx509_get_one_cert(context
->hx509ctx
, signer_certs
, &(*signer
)->cert
);
927 pk_copy_error(context
, context
->hx509ctx
, ret
,
928 "Failed to get on of the signer certs");
933 hx509_certs_free(&signer_certs
);
936 hx509_cert_free((*signer
)->cert
);
945 static krb5_error_code
946 get_reply_key_win(krb5_context context
,
947 const krb5_data
*content
,
951 ReplyKeyPack_Win2k key_pack
;
955 ret
= decode_ReplyKeyPack_Win2k(content
->data
,
960 krb5_set_error_message(context
, ret
,
961 N_("PKINIT decoding reply key failed", ""));
962 free_ReplyKeyPack_Win2k(&key_pack
);
966 if ((unsigned)key_pack
.nonce
!= nonce
) {
967 krb5_set_error_message(context
, ret
,
968 N_("PKINIT enckey nonce is wrong", ""));
969 free_ReplyKeyPack_Win2k(&key_pack
);
970 return KRB5KRB_AP_ERR_MODIFIED
;
973 *key
= malloc (sizeof (**key
));
975 free_ReplyKeyPack_Win2k(&key_pack
);
976 return krb5_enomem(context
);
979 ret
= copy_EncryptionKey(&key_pack
.replyKey
, *key
);
980 free_ReplyKeyPack_Win2k(&key_pack
);
982 krb5_set_error_message(context
, ret
,
983 N_("PKINIT failed copying reply key", ""));
991 static krb5_error_code
992 get_reply_key(krb5_context context
,
993 const krb5_data
*content
,
994 const krb5_data
*req_buffer
,
997 ReplyKeyPack key_pack
;
1001 ret
= decode_ReplyKeyPack(content
->data
,
1006 krb5_set_error_message(context
, ret
,
1007 N_("PKINIT decoding reply key failed", ""));
1008 free_ReplyKeyPack(&key_pack
);
1016 * XXX Verify kp.replyKey is a allowed enctype in the
1017 * configuration file
1020 ret
= krb5_crypto_init(context
, &key_pack
.replyKey
, 0, &crypto
);
1022 free_ReplyKeyPack(&key_pack
);
1026 ret
= krb5_verify_checksum(context
, crypto
, 6,
1027 req_buffer
->data
, req_buffer
->length
,
1028 &key_pack
.asChecksum
);
1029 krb5_crypto_destroy(context
, crypto
);
1031 free_ReplyKeyPack(&key_pack
);
1036 *key
= malloc (sizeof (**key
));
1038 free_ReplyKeyPack(&key_pack
);
1039 return krb5_enomem(context
);
1042 ret
= copy_EncryptionKey(&key_pack
.replyKey
, *key
);
1043 free_ReplyKeyPack(&key_pack
);
1045 krb5_set_error_message(context
, ret
,
1046 N_("PKINIT failed copying reply key", ""));
1055 static krb5_error_code
1056 pk_verify_host(krb5_context context
,
1058 const krb5_krbhst_info
*hi
,
1059 struct krb5_pk_init_ctx_data
*ctx
,
1060 struct krb5_pk_cert
*host
)
1062 krb5_error_code ret
= 0;
1064 if (ctx
->require_eku
) {
1065 ret
= hx509_cert_check_eku(context
->hx509ctx
, host
->cert
,
1066 &asn1_oid_id_pkkdcekuoid
, 0);
1068 krb5_set_error_message(context
, ret
,
1069 N_("No PK-INIT KDC EKU in kdc certificate", ""));
1073 if (ctx
->require_krbtgt_otherName
) {
1074 hx509_octet_string_list list
;
1078 ret
= hx509_cert_find_subjectAltName_otherName(context
->hx509ctx
,
1080 &asn1_oid_id_pkinit_san
,
1083 krb5_set_error_message(context
, ret
,
1084 N_("Failed to find the PK-INIT "
1085 "subjectAltName in the KDC "
1086 "certificate", ""));
1092 * subjectAltNames are multi-valued, and a single KDC may serve
1093 * multiple realms. The SAN validation here must accept
1094 * the KDC's cert if *any* of the SANs match the expected KDC.
1095 * It is OK for *some* of the SANs to not match, provided at least
1098 for (i
= 0; matched
== 0 && i
< list
.len
; i
++) {
1099 KRB5PrincipalName r
;
1101 ret
= decode_KRB5PrincipalName(list
.val
[i
].data
,
1106 krb5_set_error_message(context
, ret
,
1107 N_("Failed to decode the PK-INIT "
1108 "subjectAltName in the "
1109 "KDC certificate", ""));
1114 if (r
.principalName
.name_string
.len
== 2 &&
1115 strcmp(r
.principalName
.name_string
.val
[0], KRB5_TGS_NAME
) == 0
1116 && strcmp(r
.principalName
.name_string
.val
[1], realm
) == 0
1117 && strcmp(r
.realm
, realm
) == 0)
1120 free_KRB5PrincipalName(&r
);
1122 hx509_free_octet_string_list(&list
);
1124 ret
= KRB5_KDC_ERR_INVALID_CERTIFICATE
;
1125 /* XXX: Lost in translation... */
1126 krb5_set_error_message(context
, ret
,
1127 N_("KDC have wrong realm name in "
1128 "the certificate", ""));
1135 ret
= hx509_verify_hostname(context
->hx509ctx
, host
->cert
,
1136 ctx
->require_hostname_match
,
1139 hi
->ai
->ai_addr
, hi
->ai
->ai_addrlen
);
1142 krb5_set_error_message(context
, ret
,
1143 N_("Address mismatch in "
1144 "the KDC certificate", ""));
1149 static krb5_error_code
1150 pk_rd_pa_reply_enckey(krb5_context context
,
1152 const heim_octet_string
*indata
,
1153 const heim_oid
*dataType
,
1155 krb5_pk_init_ctx ctx
,
1157 const krb5_krbhst_info
*hi
,
1159 const krb5_data
*req_buffer
,
1161 krb5_keyblock
**key
)
1163 krb5_error_code ret
;
1164 struct krb5_pk_cert
*host
= NULL
;
1166 heim_oid contentType
= { 0, NULL
};
1167 int flags
= HX509_CMS_UE_DONT_REQUIRE_KU_ENCIPHERMENT
;
1169 if (der_heim_oid_cmp(&asn1_oid_id_pkcs7_envelopedData
, dataType
)) {
1170 krb5_set_error_message(context
, EINVAL
,
1171 N_("PKINIT: Invalid content type", ""));
1175 if (ctx
->type
== PKINIT_WIN2K
)
1176 flags
|= HX509_CMS_UE_ALLOW_WEAK
;
1178 ret
= hx509_cms_unenvelope(context
->hx509ctx
,
1188 pk_copy_error(context
, context
->hx509ctx
, ret
,
1189 "Failed to unenvelope CMS data in PK-INIT reply");
1192 der_free_oid(&contentType
);
1194 /* win2k uses ContentInfo */
1195 if (type
== PKINIT_WIN2K
) {
1197 heim_octet_string out
;
1199 ret
= hx509_cms_unwrap_ContentInfo(&content
, &type2
, &out
, NULL
);
1201 /* windows LH with interesting CMS packets */
1202 size_t ph
= 1 + der_length_len(content
.length
);
1203 unsigned char *ptr
= malloc(content
.length
+ ph
);
1206 memcpy(ptr
+ ph
, content
.data
, content
.length
);
1208 ret
= der_put_length_and_tag (ptr
+ ph
- 1, ph
, content
.length
,
1209 ASN1_C_UNIV
, CONS
, UT_Sequence
, &l
);
1214 content
.length
+= ph
;
1216 ret
= hx509_cms_unwrap_ContentInfo(&content
, &type2
, &out
, NULL
);
1220 if (der_heim_oid_cmp(&type2
, &asn1_oid_id_pkcs7_signedData
)) {
1221 ret
= EINVAL
; /* XXX */
1222 krb5_set_error_message(context
, ret
,
1223 N_("PKINIT: Invalid content type", ""));
1224 der_free_oid(&type2
);
1225 der_free_octet_string(&out
);
1228 der_free_oid(&type2
);
1229 krb5_data_free(&content
);
1230 ret
= krb5_data_copy(&content
, out
.data
, out
.length
);
1231 der_free_octet_string(&out
);
1233 krb5_set_error_message(context
, ret
,
1234 N_("malloc: out of memory", ""));
1239 ret
= pk_verify_sign(context
,
1249 /* make sure that it is the kdc's certificate */
1250 ret
= pk_verify_host(context
, realm
, hi
, ctx
, host
);
1256 if (type
== PKINIT_WIN2K
) {
1257 if (der_heim_oid_cmp(&contentType
, &asn1_oid_id_pkcs7_data
) != 0) {
1258 ret
= KRB5KRB_AP_ERR_MSG_TYPE
;
1259 krb5_set_error_message(context
, ret
, "PKINIT: reply key, wrong oid");
1263 if (der_heim_oid_cmp(&contentType
, &asn1_oid_id_pkrkeydata
) != 0) {
1264 ret
= KRB5KRB_AP_ERR_MSG_TYPE
;
1265 krb5_set_error_message(context
, ret
, "PKINIT: reply key, wrong oid");
1273 ret
= get_reply_key(context
, &content
, req_buffer
, key
);
1274 if (ret
!= 0 && ctx
->require_binding
== 0)
1275 ret
= get_reply_key_win(context
, &content
, nonce
, key
);
1278 ret
= get_reply_key(context
, &content
, req_buffer
, key
);
1284 /* XXX compare given etype with key->etype */
1288 _krb5_pk_cert_free(host
);
1289 der_free_oid(&contentType
);
1290 krb5_data_free(&content
);
1295 static krb5_error_code
1296 pk_rd_pa_reply_dh(krb5_context context
,
1297 const heim_octet_string
*indata
,
1298 const heim_oid
*dataType
,
1300 krb5_pk_init_ctx ctx
,
1302 const krb5_krbhst_info
*hi
,
1307 krb5_keyblock
**key
)
1309 const unsigned char *p
;
1310 unsigned char *dh_gen_key
= NULL
;
1311 struct krb5_pk_cert
*host
= NULL
;
1312 BIGNUM
*kdc_dh_pubkey
= NULL
;
1313 KDCDHKeyInfo kdc_dh_info
;
1314 heim_oid contentType
= { 0, NULL
};
1316 krb5_error_code ret
;
1317 int dh_gen_keylen
= 0;
1320 krb5_data_zero(&content
);
1321 memset(&kdc_dh_info
, 0, sizeof(kdc_dh_info
));
1323 if (der_heim_oid_cmp(&asn1_oid_id_pkcs7_signedData
, dataType
)) {
1324 krb5_set_error_message(context
, EINVAL
,
1325 N_("PKINIT: Invalid content type", ""));
1329 ret
= pk_verify_sign(context
,
1339 /* make sure that it is the kdc's certificate */
1340 ret
= pk_verify_host(context
, realm
, hi
, ctx
, host
);
1344 if (der_heim_oid_cmp(&contentType
, &asn1_oid_id_pkdhkeydata
)) {
1345 ret
= KRB5KRB_AP_ERR_MSG_TYPE
;
1346 krb5_set_error_message(context
, ret
,
1347 N_("pkinit - dh reply contains wrong oid", ""));
1351 ret
= decode_KDCDHKeyInfo(content
.data
,
1357 krb5_set_error_message(context
, ret
,
1358 N_("pkinit - failed to decode "
1359 "KDC DH Key Info", ""));
1363 if (kdc_dh_info
.nonce
!= nonce
) {
1364 ret
= KRB5KRB_AP_ERR_MODIFIED
;
1365 krb5_set_error_message(context
, ret
,
1366 N_("PKINIT: DH nonce is wrong", ""));
1370 if (kdc_dh_info
.dhKeyExpiration
) {
1372 ret
= KRB5KRB_ERR_GENERIC
;
1373 krb5_set_error_message(context
, ret
,
1374 N_("pkinit; got key expiration "
1375 "without server nonce", ""));
1379 ret
= KRB5KRB_ERR_GENERIC
;
1380 krb5_set_error_message(context
, ret
,
1381 N_("pkinit; got DH reuse but no "
1382 "client nonce", ""));
1387 ret
= KRB5KRB_ERR_GENERIC
;
1388 krb5_set_error_message(context
, ret
,
1389 N_("pkinit: got server nonce "
1390 "without key expiration", ""));
1397 p
= kdc_dh_info
.subjectPublicKey
.data
;
1398 size
= (kdc_dh_info
.subjectPublicKey
.length
+ 7) / 8;
1400 if (ctx
->keyex
== USE_DH
) {
1402 ret
= decode_DHPublicKey(p
, size
, &k
, NULL
);
1404 krb5_set_error_message(context
, ret
,
1405 N_("pkinit: can't decode "
1406 "without key expiration", ""));
1410 kdc_dh_pubkey
= integer_to_BN(context
, "DHPublicKey", &k
);
1411 free_DHPublicKey(&k
);
1412 if (kdc_dh_pubkey
== NULL
) {
1418 size
= DH_size(ctx
->u
.dh
);
1420 dh_gen_key
= malloc(size
);
1421 if (dh_gen_key
== NULL
) {
1422 ret
= krb5_enomem(context
);
1426 dh_gen_keylen
= DH_compute_key(dh_gen_key
, kdc_dh_pubkey
, ctx
->u
.dh
);
1427 if (dh_gen_keylen
== -1) {
1428 ret
= KRB5KRB_ERR_GENERIC
;
1430 krb5_set_error_message(context
, ret
,
1431 N_("PKINIT: Can't compute Diffie-Hellman key", ""));
1434 if (dh_gen_keylen
< (int)size
) {
1435 size
-= dh_gen_keylen
;
1436 memmove(dh_gen_key
+ size
, dh_gen_key
, dh_gen_keylen
);
1437 memset(dh_gen_key
, 0, size
);
1442 const EC_GROUP
*group
;
1443 EC_KEY
*public = NULL
;
1445 group
= EC_KEY_get0_group(ctx
->u
.eckey
);
1447 public = EC_KEY_new();
1448 if (public == NULL
) {
1452 if (EC_KEY_set_group(public, group
) != 1) {
1453 EC_KEY_free(public);
1458 if (o2i_ECPublicKey(&public, &p
, size
) == NULL
) {
1459 EC_KEY_free(public);
1460 ret
= KRB5KRB_ERR_GENERIC
;
1461 krb5_set_error_message(context
, ret
,
1462 N_("PKINIT: Can't parse ECDH public key", ""));
1466 size
= (EC_GROUP_get_degree(group
) + 7) / 8;
1467 dh_gen_key
= malloc(size
);
1468 if (dh_gen_key
== NULL
) {
1469 EC_KEY_free(public);
1470 ret
= krb5_enomem(context
);
1473 dh_gen_keylen
= ECDH_compute_key(dh_gen_key
, size
,
1474 EC_KEY_get0_public_key(public), ctx
->u
.eckey
, NULL
);
1475 EC_KEY_free(public);
1476 if (dh_gen_keylen
== -1) {
1477 ret
= KRB5KRB_ERR_GENERIC
;
1479 krb5_set_error_message(context
, ret
,
1480 N_("PKINIT: Can't compute ECDH public key", ""));
1488 if (dh_gen_keylen
<= 0) {
1490 krb5_set_error_message(context
, ret
,
1491 N_("PKINIT: resulting DH key <= 0", ""));
1496 *key
= malloc (sizeof (**key
));
1498 ret
= krb5_enomem(context
);
1502 ret
= _krb5_pk_octetstring2key(context
,
1504 dh_gen_key
, dh_gen_keylen
,
1508 krb5_set_error_message(context
, ret
,
1509 N_("PKINIT: can't create key from DH key", ""));
1517 BN_free(kdc_dh_pubkey
);
1519 memset(dh_gen_key
, 0, dh_gen_keylen
);
1523 _krb5_pk_cert_free(host
);
1525 krb5_data_free(&content
);
1526 der_free_oid(&contentType
);
1527 free_KDCDHKeyInfo(&kdc_dh_info
);
1532 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1533 _krb5_pk_rd_pa_reply(krb5_context context
,
1537 const krb5_krbhst_info
*hi
,
1539 const krb5_data
*req_buffer
,
1541 krb5_keyblock
**key
)
1543 krb5_pk_init_ctx ctx
= c
;
1544 krb5_error_code ret
;
1547 /* Check for IETF PK-INIT first */
1548 if (ctx
->type
== PKINIT_27
) {
1550 heim_octet_string os
, data
;
1553 if (pa
->padata_type
!= KRB5_PADATA_PK_AS_REP
) {
1554 krb5_set_error_message(context
, EINVAL
,
1555 N_("PKINIT: wrong padata recv", ""));
1559 ret
= decode_PA_PK_AS_REP(pa
->padata_value
.data
,
1560 pa
->padata_value
.length
,
1564 krb5_set_error_message(context
, ret
,
1565 N_("Failed to decode pkinit AS rep", ""));
1569 switch (rep
.element
) {
1570 case choice_PA_PK_AS_REP_dhInfo
:
1571 _krb5_debug(context
, 5, "krb5_get_init_creds: using pkinit dh");
1572 os
= rep
.u
.dhInfo
.dhSignedData
;
1574 case choice_PA_PK_AS_REP_encKeyPack
:
1575 _krb5_debug(context
, 5, "krb5_get_init_creds: using kinit enc reply key");
1576 os
= rep
.u
.encKeyPack
;
1579 PA_PK_AS_REP_BTMM btmm
;
1580 free_PA_PK_AS_REP(&rep
);
1581 memset(&rep
, 0, sizeof(rep
));
1583 _krb5_debug(context
, 5, "krb5_get_init_creds: using BTMM kinit enc reply key");
1585 ret
= decode_PA_PK_AS_REP_BTMM(pa
->padata_value
.data
,
1586 pa
->padata_value
.length
,
1590 krb5_set_error_message(context
, EINVAL
,
1591 N_("PKINIT: -27 reply "
1592 "invalid content type", ""));
1596 if (btmm
.dhSignedData
|| btmm
.encKeyPack
== NULL
) {
1597 free_PA_PK_AS_REP_BTMM(&btmm
);
1599 krb5_set_error_message(context
, ret
,
1600 N_("DH mode not supported for BTMM mode", ""));
1605 * Transform to IETF style PK-INIT reply so that free works below
1608 rep
.element
= choice_PA_PK_AS_REP_encKeyPack
;
1609 rep
.u
.encKeyPack
.data
= btmm
.encKeyPack
->data
;
1610 rep
.u
.encKeyPack
.length
= btmm
.encKeyPack
->length
;
1611 btmm
.encKeyPack
->data
= NULL
;
1612 btmm
.encKeyPack
->length
= 0;
1613 free_PA_PK_AS_REP_BTMM(&btmm
);
1614 os
= rep
.u
.encKeyPack
;
1618 ret
= hx509_cms_unwrap_ContentInfo(&os
, &oid
, &data
, NULL
);
1620 free_PA_PK_AS_REP(&rep
);
1621 krb5_set_error_message(context
, ret
,
1622 N_("PKINIT: failed to unwrap CI", ""));
1626 switch (rep
.element
) {
1627 case choice_PA_PK_AS_REP_dhInfo
:
1628 ret
= pk_rd_pa_reply_dh(context
, &data
, &oid
, realm
, ctx
, etype
, hi
,
1630 rep
.u
.dhInfo
.serverDHNonce
,
1633 case choice_PA_PK_AS_REP_encKeyPack
:
1634 ret
= pk_rd_pa_reply_enckey(context
, PKINIT_27
, &data
, &oid
, realm
,
1635 ctx
, etype
, hi
, nonce
, req_buffer
, pa
, key
);
1638 krb5_abortx(context
, "pk-init as-rep case not possible to happen");
1640 der_free_octet_string(&data
);
1642 free_PA_PK_AS_REP(&rep
);
1644 } else if (ctx
->type
== PKINIT_WIN2K
) {
1645 PA_PK_AS_REP_Win2k w2krep
;
1647 /* Check for Windows encoding of the AS-REP pa data */
1649 #if 0 /* should this be ? */
1650 if (pa
->padata_type
!= KRB5_PADATA_PK_AS_REP
) {
1651 krb5_set_error_message(context
, EINVAL
,
1652 "PKINIT: wrong padata recv");
1657 memset(&w2krep
, 0, sizeof(w2krep
));
1659 ret
= decode_PA_PK_AS_REP_Win2k(pa
->padata_value
.data
,
1660 pa
->padata_value
.length
,
1664 krb5_set_error_message(context
, ret
,
1665 N_("PKINIT: Failed decoding windows "
1666 "pkinit reply %d", ""), (int)ret
);
1670 krb5_clear_error_message(context
);
1672 switch (w2krep
.element
) {
1673 case choice_PA_PK_AS_REP_Win2k_encKeyPack
: {
1674 heim_octet_string data
;
1677 ret
= hx509_cms_unwrap_ContentInfo(&w2krep
.u
.encKeyPack
,
1679 free_PA_PK_AS_REP_Win2k(&w2krep
);
1681 krb5_set_error_message(context
, ret
,
1682 N_("PKINIT: failed to unwrap CI", ""));
1686 ret
= pk_rd_pa_reply_enckey(context
, PKINIT_WIN2K
, &data
, &oid
, realm
,
1687 ctx
, etype
, hi
, nonce
, req_buffer
, pa
, key
);
1688 der_free_octet_string(&data
);
1694 free_PA_PK_AS_REP_Win2k(&w2krep
);
1696 krb5_set_error_message(context
, ret
,
1697 N_("PKINIT: win2k reply invalid "
1698 "content type", ""));
1704 krb5_set_error_message(context
, ret
,
1705 N_("PKINIT: unknown reply type", ""));
1712 krb5_context context
;
1713 krb5_prompter_fct prompter
;
1714 void *prompter_data
;
1718 hx_pass_prompter(void *data
, const hx509_prompt
*prompter
)
1720 krb5_error_code ret
;
1722 krb5_data password_data
;
1723 struct prompter
*p
= data
;
1725 password_data
.data
= prompter
->reply
.data
;
1726 password_data
.length
= prompter
->reply
.length
;
1728 prompt
.prompt
= prompter
->prompt
;
1729 prompt
.hidden
= hx509_prompt_hidden(prompter
->type
);
1730 prompt
.reply
= &password_data
;
1732 switch (prompter
->type
) {
1733 case HX509_PROMPT_TYPE_INFO
:
1734 prompt
.type
= KRB5_PROMPT_TYPE_INFO
;
1736 case HX509_PROMPT_TYPE_PASSWORD
:
1737 case HX509_PROMPT_TYPE_QUESTION
:
1739 prompt
.type
= KRB5_PROMPT_TYPE_PASSWORD
;
1743 ret
= (*p
->prompter
)(p
->context
, p
->prompter_data
, NULL
, NULL
, 1, &prompt
);
1745 memset (prompter
->reply
.data
, 0, prompter
->reply
.length
);
1751 static krb5_error_code
1752 _krb5_pk_set_user_id(krb5_context context
,
1753 krb5_principal principal
,
1754 krb5_pk_init_ctx ctx
,
1755 struct hx509_certs_data
*certs
)
1757 hx509_certs c
= hx509_certs_ref(certs
);
1758 hx509_query
*q
= NULL
;
1762 hx509_certs_free(&ctx
->id
->certs
);
1763 if (ctx
->id
->cert
) {
1764 hx509_cert_free(ctx
->id
->cert
);
1765 ctx
->id
->cert
= NULL
;
1771 ret
= hx509_query_alloc(context
->hx509ctx
, &q
);
1773 pk_copy_error(context
, context
->hx509ctx
, ret
,
1774 "Allocate query to find signing certificate");
1778 hx509_query_match_option(q
, HX509_QUERY_OPTION_PRIVATE_KEY
);
1779 hx509_query_match_option(q
, HX509_QUERY_OPTION_KU_DIGITALSIGNATURE
);
1781 if (principal
&& strncmp("LKDC:SHA1.", krb5_principal_get_realm(context
, principal
), 9) == 0) {
1782 ctx
->id
->flags
|= PKINIT_BTMM
;
1785 ret
= find_cert(context
, ctx
->id
, q
, &ctx
->id
->cert
);
1786 hx509_query_free(context
->hx509ctx
, q
);
1788 if (ret
== 0 && _krb5_have_debug(context
, 2)) {
1793 ret
= hx509_cert_get_subject(ctx
->id
->cert
, &name
);
1797 ret
= hx509_name_to_string(name
, &str
);
1798 hx509_name_free(&name
);
1802 ret
= hx509_cert_get_serialnumber(ctx
->id
->cert
, &i
);
1808 ret
= der_print_hex_heim_integer(&i
, &sn
);
1809 der_free_heim_integer(&i
);
1815 _krb5_debug(context
, 2, "using cert: subject: %s sn: %s", str
, sn
);
1824 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1825 _krb5_pk_load_id(krb5_context context
,
1826 struct krb5_pk_identity
**ret_id
,
1827 const char *user_id
,
1828 const char *anchor_id
,
1829 char * const *chain_list
,
1830 char * const *revoke_list
,
1831 krb5_prompter_fct prompter
,
1832 void *prompter_data
,
1835 struct krb5_pk_identity
*id
= NULL
;
1841 if (anchor_id
== NULL
) {
1842 krb5_set_error_message(context
, HEIM_PKINIT_NO_VALID_CA
,
1843 N_("PKINIT: No anchor given", ""));
1844 return HEIM_PKINIT_NO_VALID_CA
;
1849 id
= calloc(1, sizeof(*id
));
1851 return krb5_enomem(context
);
1856 ret
= hx509_lock_init(context
->hx509ctx
, &lock
);
1858 pk_copy_error(context
, context
->hx509ctx
, ret
, "Failed init lock");
1862 if (password
&& password
[0])
1863 hx509_lock_add_password(lock
, password
);
1866 p
.context
= context
;
1867 p
.prompter
= prompter
;
1868 p
.prompter_data
= prompter_data
;
1870 ret
= hx509_lock_set_prompter(lock
, hx_pass_prompter
, &p
);
1872 hx509_lock_free(lock
);
1877 ret
= hx509_certs_init(context
->hx509ctx
, user_id
, 0, lock
, &id
->certs
);
1878 hx509_lock_free(lock
);
1880 pk_copy_error(context
, context
->hx509ctx
, ret
,
1881 "Failed to init cert certs");
1888 ret
= hx509_certs_init(context
->hx509ctx
, anchor_id
, 0, NULL
, &id
->anchors
);
1890 pk_copy_error(context
, context
->hx509ctx
, ret
,
1891 "Failed to init anchors");
1895 ret
= hx509_certs_init(context
->hx509ctx
, "MEMORY:pkinit-cert-chain",
1896 0, NULL
, &id
->certpool
);
1898 pk_copy_error(context
, context
->hx509ctx
, ret
,
1899 "Failed to init chain");
1903 while (chain_list
&& *chain_list
) {
1904 ret
= hx509_certs_append(context
->hx509ctx
, id
->certpool
,
1907 pk_copy_error(context
, context
->hx509ctx
, ret
,
1908 "Failed to laod chain %s",
1916 ret
= hx509_revoke_init(context
->hx509ctx
, &id
->revokectx
);
1918 pk_copy_error(context
, context
->hx509ctx
, ret
,
1919 "Failed init revoke list");
1923 while (*revoke_list
) {
1924 ret
= hx509_revoke_add_crl(context
->hx509ctx
,
1928 pk_copy_error(context
, context
->hx509ctx
, ret
,
1929 "Failed load revoke list");
1935 hx509_context_set_missing_revoke(context
->hx509ctx
, 1);
1937 ret
= hx509_verify_init_ctx(context
->hx509ctx
, &id
->verify_ctx
);
1939 pk_copy_error(context
, context
->hx509ctx
, ret
,
1940 "Failed init verify context");
1944 hx509_verify_attach_anchors(id
->verify_ctx
, id
->anchors
);
1945 hx509_verify_attach_revoke(id
->verify_ctx
, id
->revokectx
);
1949 hx509_verify_destroy_ctx(id
->verify_ctx
);
1950 hx509_certs_free(&id
->certs
);
1951 hx509_certs_free(&id
->anchors
);
1952 hx509_certs_free(&id
->certpool
);
1953 hx509_revoke_free(&id
->revokectx
);
1966 pk_copy_error(krb5_context context
,
1967 hx509_context hx509ctx
,
1977 ret
= vasprintf(&f
, fmt
, va
);
1979 if (ret
== -1 || f
== NULL
) {
1980 krb5_clear_error_message(context
);
1984 s
= hx509_get_error_string(hx509ctx
, hxret
);
1986 krb5_clear_error_message(context
);
1990 krb5_set_error_message(context
, hxret
, "%s: %s", f
, s
);
1996 parse_integer(krb5_context context
, char **p
, const char *file
, int lineno
,
1997 const char *name
, heim_integer
*integer
)
2001 p1
= strsep(p
, " \t");
2003 krb5_set_error_message(context
, EINVAL
,
2004 N_("moduli file %s missing %s on line %d", ""),
2005 file
, name
, lineno
);
2008 ret
= der_parse_hex_heim_integer(p1
, integer
);
2010 krb5_set_error_message(context
, ret
,
2011 N_("moduli file %s failed parsing %s "
2013 file
, name
, lineno
);
2021 _krb5_parse_moduli_line(krb5_context context
,
2025 struct krb5_dh_moduli
**m
)
2027 struct krb5_dh_moduli
*m1
;
2033 m1
= calloc(1, sizeof(*m1
));
2035 return krb5_enomem(context
);
2037 while (isspace((unsigned char)*p
))
2045 p1
= strsep(&p
, " \t");
2047 krb5_set_error_message(context
, ret
,
2048 N_("moduli file %s missing name on line %d", ""),
2052 m1
->name
= strdup(p1
);
2053 if (m1
->name
== NULL
) {
2054 ret
= krb5_enomem(context
);
2058 p1
= strsep(&p
, " \t");
2060 krb5_set_error_message(context
, ret
,
2061 N_("moduli file %s missing bits on line %d", ""),
2066 m1
->bits
= atoi(p1
);
2067 if (m1
->bits
== 0) {
2068 krb5_set_error_message(context
, ret
,
2069 N_("moduli file %s have un-parsable "
2070 "bits on line %d", ""), file
, lineno
);
2074 ret
= parse_integer(context
, &p
, file
, lineno
, "p", &m1
->p
);
2077 ret
= parse_integer(context
, &p
, file
, lineno
, "g", &m1
->g
);
2080 ret
= parse_integer(context
, &p
, file
, lineno
, "q", &m1
->q
);
2089 der_free_heim_integer(&m1
->p
);
2090 der_free_heim_integer(&m1
->g
);
2091 der_free_heim_integer(&m1
->q
);
2097 _krb5_free_moduli(struct krb5_dh_moduli
**moduli
)
2100 for (i
= 0; moduli
[i
] != NULL
; i
++) {
2101 free(moduli
[i
]->name
);
2102 der_free_heim_integer(&moduli
[i
]->p
);
2103 der_free_heim_integer(&moduli
[i
]->g
);
2104 der_free_heim_integer(&moduli
[i
]->q
);
2110 static const char *default_moduli_RFC2412_MODP_group2
=
2112 "RFC2412-MODP-group2 "
2116 "FFFFFFFF" "FFFFFFFF" "C90FDAA2" "2168C234" "C4C6628B" "80DC1CD1"
2117 "29024E08" "8A67CC74" "020BBEA6" "3B139B22" "514A0879" "8E3404DD"
2118 "EF9519B3" "CD3A431B" "302B0A6D" "F25F1437" "4FE1356D" "6D51C245"
2119 "E485B576" "625E7EC6" "F44C42E9" "A637ED6B" "0BFF5CB6" "F406B7ED"
2120 "EE386BFB" "5A899FA5" "AE9F2411" "7C4B1FE6" "49286651" "ECE65381"
2121 "FFFFFFFF" "FFFFFFFF "
2125 "7FFFFFFF" "FFFFFFFF" "E487ED51" "10B4611A" "62633145" "C06E0E68"
2126 "94812704" "4533E63A" "0105DF53" "1D89CD91" "28A5043C" "C71A026E"
2127 "F7CA8CD9" "E69D218D" "98158536" "F92F8A1B" "A7F09AB6" "B6A8E122"
2128 "F242DABB" "312F3F63" "7A262174" "D31BF6B5" "85FFAE5B" "7A035BF6"
2129 "F71C35FD" "AD44CFD2" "D74F9208" "BE258FF3" "24943328" "F67329C0"
2130 "FFFFFFFF" "FFFFFFFF";
2132 static const char *default_moduli_rfc3526_MODP_group14
=
2134 "rfc3526-MODP-group14 "
2138 "FFFFFFFF" "FFFFFFFF" "C90FDAA2" "2168C234" "C4C6628B" "80DC1CD1"
2139 "29024E08" "8A67CC74" "020BBEA6" "3B139B22" "514A0879" "8E3404DD"
2140 "EF9519B3" "CD3A431B" "302B0A6D" "F25F1437" "4FE1356D" "6D51C245"
2141 "E485B576" "625E7EC6" "F44C42E9" "A637ED6B" "0BFF5CB6" "F406B7ED"
2142 "EE386BFB" "5A899FA5" "AE9F2411" "7C4B1FE6" "49286651" "ECE45B3D"
2143 "C2007CB8" "A163BF05" "98DA4836" "1C55D39A" "69163FA8" "FD24CF5F"
2144 "83655D23" "DCA3AD96" "1C62F356" "208552BB" "9ED52907" "7096966D"
2145 "670C354E" "4ABC9804" "F1746C08" "CA18217C" "32905E46" "2E36CE3B"
2146 "E39E772C" "180E8603" "9B2783A2" "EC07A28F" "B5C55DF0" "6F4C52C9"
2147 "DE2BCBF6" "95581718" "3995497C" "EA956AE5" "15D22618" "98FA0510"
2148 "15728E5A" "8AACAA68" "FFFFFFFF" "FFFFFFFF "
2152 "7FFFFFFF" "FFFFFFFF" "E487ED51" "10B4611A" "62633145" "C06E0E68"
2153 "94812704" "4533E63A" "0105DF53" "1D89CD91" "28A5043C" "C71A026E"
2154 "F7CA8CD9" "E69D218D" "98158536" "F92F8A1B" "A7F09AB6" "B6A8E122"
2155 "F242DABB" "312F3F63" "7A262174" "D31BF6B5" "85FFAE5B" "7A035BF6"
2156 "F71C35FD" "AD44CFD2" "D74F9208" "BE258FF3" "24943328" "F6722D9E"
2157 "E1003E5C" "50B1DF82" "CC6D241B" "0E2AE9CD" "348B1FD4" "7E9267AF"
2158 "C1B2AE91" "EE51D6CB" "0E3179AB" "1042A95D" "CF6A9483" "B84B4B36"
2159 "B3861AA7" "255E4C02" "78BA3604" "650C10BE" "19482F23" "171B671D"
2160 "F1CF3B96" "0C074301" "CD93C1D1" "7603D147" "DAE2AEF8" "37A62964"
2161 "EF15E5FB" "4AAC0B8C" "1CCAA4BE" "754AB572" "8AE9130C" "4C7D0288"
2162 "0AB9472D" "45565534" "7FFFFFFF" "FFFFFFFF";
2165 _krb5_parse_moduli(krb5_context context
, const char *file
,
2166 struct krb5_dh_moduli
***moduli
)
2168 /* name bits P G Q */
2169 krb5_error_code ret
;
2170 struct krb5_dh_moduli
**m
= NULL
, **m2
;
2173 int lineno
= 0, n
= 0;
2177 m
= calloc(1, sizeof(m
[0]) * 3);
2179 return krb5_enomem(context
);
2181 strlcpy(buf
, default_moduli_rfc3526_MODP_group14
, sizeof(buf
));
2182 ret
= _krb5_parse_moduli_line(context
, "builtin", 1, buf
, &m
[0]);
2184 _krb5_free_moduli(m
);
2189 strlcpy(buf
, default_moduli_RFC2412_MODP_group2
, sizeof(buf
));
2190 ret
= _krb5_parse_moduli_line(context
, "builtin", 1, buf
, &m
[1]);
2192 _krb5_free_moduli(m
);
2201 #ifdef KRB5_USE_PATH_TOKENS
2205 if (_krb5_expand_path_tokens(context
, file
, &exp_file
) == 0) {
2206 f
= fopen(exp_file
, "r");
2207 krb5_xfree(exp_file
);
2213 f
= fopen(file
, "r");
2222 while(fgets(buf
, sizeof(buf
), f
) != NULL
) {
2223 struct krb5_dh_moduli
*element
;
2225 buf
[strcspn(buf
, "\n")] = '\0';
2228 m2
= realloc(m
, (n
+ 2) * sizeof(m
[0]));
2230 _krb5_free_moduli(m
);
2231 return krb5_enomem(context
);
2237 ret
= _krb5_parse_moduli_line(context
, file
, lineno
, buf
, &element
);
2239 _krb5_free_moduli(m
);
2242 if (element
== NULL
)
2254 _krb5_dh_group_ok(krb5_context context
, unsigned long bits
,
2255 heim_integer
*p
, heim_integer
*g
, heim_integer
*q
,
2256 struct krb5_dh_moduli
**moduli
,
2264 for (i
= 0; moduli
[i
] != NULL
; i
++) {
2265 if (der_heim_integer_cmp(&moduli
[i
]->g
, g
) == 0 &&
2266 der_heim_integer_cmp(&moduli
[i
]->p
, p
) == 0 &&
2267 (q
== NULL
|| der_heim_integer_cmp(&moduli
[i
]->q
, q
) == 0))
2269 if (bits
&& bits
> moduli
[i
]->bits
) {
2270 krb5_set_error_message(context
,
2271 KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED
,
2272 N_("PKINIT: DH group parameter %s "
2273 "no accepted, not enough bits "
2276 return KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED
;
2279 *name
= strdup(moduli
[i
]->name
);
2283 krb5_set_error_message(context
,
2284 KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED
,
2285 N_("PKINIT: DH group parameter no ok", ""));
2286 return KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED
;
2290 KRB5_LIB_FUNCTION
void KRB5_LIB_CALL
2291 _krb5_get_init_creds_opt_free_pkinit(krb5_get_init_creds_opt
*opt
)
2294 krb5_pk_init_ctx ctx
;
2296 if (opt
->opt_private
== NULL
|| opt
->opt_private
->pk_init_ctx
== NULL
)
2298 ctx
= opt
->opt_private
->pk_init_ctx
;
2299 switch (ctx
->keyex
) {
2309 EC_KEY_free(ctx
->u
.eckey
);
2314 hx509_verify_destroy_ctx(ctx
->id
->verify_ctx
);
2315 hx509_certs_free(&ctx
->id
->certs
);
2316 hx509_cert_free(ctx
->id
->cert
);
2317 hx509_certs_free(&ctx
->id
->anchors
);
2318 hx509_certs_free(&ctx
->id
->certpool
);
2320 if (ctx
->clientDHNonce
) {
2321 krb5_free_data(NULL
, ctx
->clientDHNonce
);
2322 ctx
->clientDHNonce
= NULL
;
2325 _krb5_free_moduli(ctx
->m
);
2329 free(opt
->opt_private
->pk_init_ctx
);
2330 opt
->opt_private
->pk_init_ctx
= NULL
;
2334 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2335 krb5_get_init_creds_opt_set_pkinit(krb5_context context
,
2336 krb5_get_init_creds_opt
*opt
,
2337 krb5_principal principal
,
2338 const char *user_id
,
2339 const char *x509_anchors
,
2340 char * const * pool
,
2341 char * const * pki_revoke
,
2343 krb5_prompter_fct prompter
,
2344 void *prompter_data
,
2348 krb5_error_code ret
;
2349 char *anchors
= NULL
;
2351 if (opt
->opt_private
== NULL
) {
2352 krb5_set_error_message(context
, EINVAL
,
2353 N_("PKINIT: on non extendable opt", ""));
2357 opt
->opt_private
->pk_init_ctx
=
2358 calloc(1, sizeof(*opt
->opt_private
->pk_init_ctx
));
2359 if (opt
->opt_private
->pk_init_ctx
== NULL
)
2360 return krb5_enomem(context
);
2361 opt
->opt_private
->pk_init_ctx
->require_binding
= 0;
2362 opt
->opt_private
->pk_init_ctx
->require_eku
= 1;
2363 opt
->opt_private
->pk_init_ctx
->require_krbtgt_otherName
= 1;
2364 opt
->opt_private
->pk_init_ctx
->peer
= NULL
;
2366 /* XXX implement krb5_appdefault_strings */
2368 pool
= krb5_config_get_strings(context
, NULL
,
2373 if (pki_revoke
== NULL
)
2374 pki_revoke
= krb5_config_get_strings(context
, NULL
,
2379 if (x509_anchors
== NULL
) {
2380 krb5_appdefault_string(context
, "kinit",
2381 krb5_principal_get_realm(context
, principal
),
2382 "pkinit_anchors", NULL
, &anchors
);
2383 x509_anchors
= anchors
;
2387 opt
->opt_private
->pk_init_ctx
->anonymous
= 1;
2389 ret
= _krb5_pk_load_id(context
,
2390 &opt
->opt_private
->pk_init_ctx
->id
,
2399 free(opt
->opt_private
->pk_init_ctx
);
2400 opt
->opt_private
->pk_init_ctx
= NULL
;
2404 if (opt
->opt_private
->pk_init_ctx
->id
->certs
) {
2405 _krb5_pk_set_user_id(context
,
2407 opt
->opt_private
->pk_init_ctx
,
2408 opt
->opt_private
->pk_init_ctx
->id
->certs
);
2410 opt
->opt_private
->pk_init_ctx
->id
->cert
= NULL
;
2412 if ((flags
& 2) == 0) {
2413 hx509_context hx509ctx
= context
->hx509ctx
;
2414 hx509_cert cert
= opt
->opt_private
->pk_init_ctx
->id
->cert
;
2416 opt
->opt_private
->pk_init_ctx
->keyex
= USE_DH
;
2419 * If its a ECDSA certs, lets select ECDSA as the keyex algorithm.
2422 AlgorithmIdentifier alg
;
2424 ret
= hx509_cert_get_SPKI_AlgorithmIdentifier(hx509ctx
, cert
, &alg
);
2426 if (der_heim_oid_cmp(&alg
.algorithm
, &asn1_oid_id_ecPublicKey
) == 0)
2427 opt
->opt_private
->pk_init_ctx
->keyex
= USE_ECDH
;
2428 free_AlgorithmIdentifier(&alg
);
2433 opt
->opt_private
->pk_init_ctx
->keyex
= USE_RSA
;
2435 if (opt
->opt_private
->pk_init_ctx
->id
->certs
== NULL
) {
2436 krb5_set_error_message(context
, EINVAL
,
2437 N_("No anonymous pkinit support in RSA mode", ""));
2444 krb5_set_error_message(context
, EINVAL
,
2445 N_("no support for PKINIT compiled in", ""));
2450 krb5_error_code KRB5_LIB_FUNCTION
2451 krb5_get_init_creds_opt_set_pkinit_user_certs(krb5_context context
,
2452 krb5_get_init_creds_opt
*opt
,
2453 struct hx509_certs_data
*certs
)
2456 if (opt
->opt_private
== NULL
) {
2457 krb5_set_error_message(context
, EINVAL
,
2458 N_("PKINIT: on non extendable opt", ""));
2461 if (opt
->opt_private
->pk_init_ctx
== NULL
) {
2462 krb5_set_error_message(context
, EINVAL
,
2463 N_("PKINIT: on pkinit context", ""));
2467 _krb5_pk_set_user_id(context
, NULL
, opt
->opt_private
->pk_init_ctx
, certs
);
2471 krb5_set_error_message(context
, EINVAL
,
2472 N_("no support for PKINIT compiled in", ""));
2480 get_ms_san(hx509_context context
, hx509_cert cert
, char **upn
)
2482 hx509_octet_string_list list
;
2487 ret
= hx509_cert_find_subjectAltName_otherName(context
,
2489 &asn1_oid_id_pkinit_ms_san
,
2494 if (list
.len
> 0 && list
.val
[0].length
> 0)
2495 ret
= decode_MS_UPN_SAN(list
.val
[0].data
, list
.val
[0].length
,
2499 hx509_free_octet_string_list(&list
);
2505 find_ms_san(hx509_context context
, hx509_cert cert
, void *ctx
)
2510 ret
= get_ms_san(context
, cert
, &upn
);
2521 * Private since it need to be redesigned using krb5_get_init_creds()
2524 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2525 krb5_pk_enterprise_cert(krb5_context context
,
2526 const char *user_id
,
2527 krb5_const_realm realm
,
2528 krb5_principal
*principal
,
2529 struct hx509_certs_data
**res
)
2532 krb5_error_code ret
;
2533 hx509_certs certs
, result
;
2534 hx509_cert cert
= NULL
;
2542 if (user_id
== NULL
) {
2543 krb5_set_error_message(context
, ENOENT
, "no user id");
2547 ret
= hx509_certs_init(context
->hx509ctx
, user_id
, 0, NULL
, &certs
);
2549 pk_copy_error(context
, context
->hx509ctx
, ret
,
2550 "Failed to init cert certs");
2554 ret
= hx509_query_alloc(context
->hx509ctx
, &q
);
2556 krb5_set_error_message(context
, ret
, "out of memory");
2557 hx509_certs_free(&certs
);
2561 hx509_query_match_option(q
, HX509_QUERY_OPTION_PRIVATE_KEY
);
2562 hx509_query_match_option(q
, HX509_QUERY_OPTION_KU_DIGITALSIGNATURE
);
2563 hx509_query_match_eku(q
, &asn1_oid_id_pkinit_ms_eku
);
2564 hx509_query_match_cmp_func(q
, find_ms_san
, NULL
);
2566 ret
= hx509_certs_filter(context
->hx509ctx
, certs
, q
, &result
);
2567 hx509_query_free(context
->hx509ctx
, q
);
2568 hx509_certs_free(&certs
);
2570 pk_copy_error(context
, context
->hx509ctx
, ret
,
2571 "Failed to find PKINIT certificate");
2575 ret
= hx509_get_one_cert(context
->hx509ctx
, result
, &cert
);
2576 hx509_certs_free(&result
);
2578 pk_copy_error(context
, context
->hx509ctx
, ret
,
2579 "Failed to get one cert");
2583 ret
= get_ms_san(context
->hx509ctx
, cert
, &name
);
2585 pk_copy_error(context
, context
->hx509ctx
, ret
,
2586 "Failed to get MS SAN");
2590 ret
= krb5_make_principal(context
, principal
, realm
, name
, NULL
);
2595 krb5_principal_set_type(context
, *principal
, KRB5_NT_ENTERPRISE_PRINCIPAL
);
2598 ret
= hx509_certs_init(context
->hx509ctx
, "MEMORY:", 0, NULL
, res
);
2602 ret
= hx509_certs_add(context
->hx509ctx
, *res
, cert
);
2604 hx509_certs_free(res
);
2610 hx509_cert_free(cert
);
2614 krb5_set_error_message(context
, EINVAL
,
2615 N_("no support for PKINIT compiled in", ""));