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 dp
.q
= calloc(1, sizeof(*dp
.q
));
497 free_DomainParameters(&dp
);
500 ret
= BN_to_integer(context
, dh
->q
, dp
.q
);
502 free_DomainParameters(&dp
);
506 dp
.validationParms
= NULL
;
508 a
->clientPublicValue
->algorithm
.parameters
=
509 malloc(sizeof(*a
->clientPublicValue
->algorithm
.parameters
));
510 if (a
->clientPublicValue
->algorithm
.parameters
== NULL
) {
511 free_DomainParameters(&dp
);
515 ASN1_MALLOC_ENCODE(DomainParameters
,
516 a
->clientPublicValue
->algorithm
.parameters
->data
,
517 a
->clientPublicValue
->algorithm
.parameters
->length
,
519 free_DomainParameters(&dp
);
522 if (size
!= a
->clientPublicValue
->algorithm
.parameters
->length
)
523 krb5_abortx(context
, "Internal ASN1 encoder error");
525 ret
= BN_to_integer(context
, dh
->pub_key
, &dh_pub_key
);
529 ASN1_MALLOC_ENCODE(DHPublicKey
, dhbuf
.data
, dhbuf
.length
,
530 &dh_pub_key
, &size
, ret
);
531 der_free_heim_integer(&dh_pub_key
);
534 if (size
!= dhbuf
.length
)
535 krb5_abortx(context
, "asn1 internal error");
536 } else if (ctx
->keyex
== USE_ECDH
) {
542 /* copy in public key, XXX find the best curve that the server support or use the clients curve if possible */
544 ecp
.element
= choice_ECParameters_namedCurve
;
545 ret
= der_copy_oid(&asn1_oid_id_ec_group_secp256r1
,
550 ALLOC(a
->clientPublicValue
->algorithm
.parameters
, 1);
551 if (a
->clientPublicValue
->algorithm
.parameters
== NULL
) {
552 free_ECParameters(&ecp
);
555 ASN1_MALLOC_ENCODE(ECParameters
, p
, xlen
, &ecp
, &size
, ret
);
556 free_ECParameters(&ecp
);
559 if ((int)size
!= xlen
)
560 krb5_abortx(context
, "asn1 internal error");
562 a
->clientPublicValue
->algorithm
.parameters
->data
= p
;
563 a
->clientPublicValue
->algorithm
.parameters
->length
= size
;
565 /* copy in public key */
567 ret
= der_copy_oid(&asn1_oid_id_ecPublicKey
,
568 &a
->clientPublicValue
->algorithm
.algorithm
);
572 ctx
->u
.eckey
= EC_KEY_new_by_curve_name(NID_X9_62_prime256v1
);
573 if (ctx
->u
.eckey
== NULL
)
576 ret
= EC_KEY_generate_key(ctx
->u
.eckey
);
580 /* encode onto dhkey */
582 xlen
= i2o_ECPublicKey(ctx
->u
.eckey
, NULL
);
586 dhbuf
.data
= malloc(xlen
);
587 if (dhbuf
.data
== NULL
)
592 xlen
= i2o_ECPublicKey(ctx
->u
.eckey
, &p
);
596 /* XXX verify that this is right with RFC3279 */
601 krb5_abortx(context
, "internal error");
602 a
->clientPublicValue
->subjectPublicKey
.length
= dhbuf
.length
* 8;
603 a
->clientPublicValue
->subjectPublicKey
.data
= dhbuf
.data
;
607 a
->supportedCMSTypes
= calloc(1, sizeof(*a
->supportedCMSTypes
));
608 if (a
->supportedCMSTypes
== NULL
)
611 ret
= hx509_crypto_available(context
->hx509ctx
, HX509_SELECT_ALL
,
613 &a
->supportedCMSTypes
->val
,
614 &a
->supportedCMSTypes
->len
);
622 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
623 _krb5_pk_mk_ContentInfo(krb5_context context
,
624 const krb5_data
*buf
,
626 struct ContentInfo
*content_info
)
630 ret
= der_copy_oid(oid
, &content_info
->contentType
);
633 ALLOC(content_info
->content
, 1);
634 if (content_info
->content
== NULL
)
636 content_info
->content
->data
= malloc(buf
->length
);
637 if (content_info
->content
->data
== NULL
)
639 memcpy(content_info
->content
->data
, buf
->data
, buf
->length
);
640 content_info
->content
->length
= buf
->length
;
644 static krb5_error_code
645 pk_mk_padata(krb5_context context
,
646 krb5_pk_init_ctx ctx
,
647 const KDC_REQ_BODY
*req_body
,
651 struct ContentInfo content_info
;
653 const heim_oid
*oid
= NULL
;
655 krb5_data buf
, sd_buf
;
658 krb5_data_zero(&buf
);
659 krb5_data_zero(&sd_buf
);
660 memset(&content_info
, 0, sizeof(content_info
));
662 if (ctx
->type
== PKINIT_WIN2K
) {
667 memset(&ap
, 0, sizeof(ap
));
669 /* fill in PKAuthenticator */
670 ret
= copy_PrincipalName(req_body
->sname
, &ap
.pkAuthenticator
.kdcName
);
672 free_AuthPack_Win2k(&ap
);
673 krb5_clear_error_message(context
);
676 ret
= copy_Realm(&req_body
->realm
, &ap
.pkAuthenticator
.kdcRealm
);
678 free_AuthPack_Win2k(&ap
);
679 krb5_clear_error_message(context
);
683 krb5_us_timeofday(context
, &sec
, &usec
);
684 ap
.pkAuthenticator
.ctime
= sec
;
685 ap
.pkAuthenticator
.cusec
= usec
;
686 ap
.pkAuthenticator
.nonce
= nonce
;
688 ASN1_MALLOC_ENCODE(AuthPack_Win2k
, buf
.data
, buf
.length
,
690 free_AuthPack_Win2k(&ap
);
692 krb5_set_error_message(context
, ret
,
693 N_("Failed encoding AuthPackWin: %d", ""),
697 if (buf
.length
!= size
)
698 krb5_abortx(context
, "internal ASN1 encoder error");
700 oid
= &asn1_oid_id_pkcs7_data
;
701 } else if (ctx
->type
== PKINIT_27
) {
704 memset(&ap
, 0, sizeof(ap
));
706 ret
= build_auth_pack(context
, nonce
, ctx
, req_body
, &ap
);
712 ASN1_MALLOC_ENCODE(AuthPack
, buf
.data
, buf
.length
, &ap
, &size
, ret
);
715 krb5_set_error_message(context
, ret
,
716 N_("Failed encoding AuthPack: %d", ""),
720 if (buf
.length
!= size
)
721 krb5_abortx(context
, "internal ASN1 encoder error");
723 oid
= &asn1_oid_id_pkauthdata
;
725 krb5_abortx(context
, "internal pkinit error");
727 ret
= create_signature(context
, oid
, &buf
, ctx
->id
,
729 krb5_data_free(&buf
);
733 ret
= hx509_cms_wrap_ContentInfo(&asn1_oid_id_pkcs7_signedData
, &sd_buf
, &buf
);
734 krb5_data_free(&sd_buf
);
736 krb5_set_error_message(context
, ret
,
737 N_("ContentInfo wrapping of signedData failed",""));
741 if (ctx
->type
== PKINIT_WIN2K
) {
742 PA_PK_AS_REQ_Win2k winreq
;
744 pa_type
= KRB5_PADATA_PK_AS_REQ_WIN
;
746 memset(&winreq
, 0, sizeof(winreq
));
748 winreq
.signed_auth_pack
= buf
;
750 ASN1_MALLOC_ENCODE(PA_PK_AS_REQ_Win2k
, buf
.data
, buf
.length
,
751 &winreq
, &size
, ret
);
752 free_PA_PK_AS_REQ_Win2k(&winreq
);
754 } else if (ctx
->type
== PKINIT_27
) {
757 pa_type
= KRB5_PADATA_PK_AS_REQ
;
759 memset(&req
, 0, sizeof(req
));
760 req
.signedAuthPack
= buf
;
762 if (ctx
->trustedCertifiers
) {
764 req
.trustedCertifiers
= calloc(1, sizeof(*req
.trustedCertifiers
));
765 if (req
.trustedCertifiers
== NULL
) {
766 ret
= krb5_enomem(context
);
767 free_PA_PK_AS_REQ(&req
);
770 ret
= build_edi(context
, context
->hx509ctx
,
771 ctx
->id
->anchors
, req
.trustedCertifiers
);
773 krb5_set_error_message(context
, ret
,
774 N_("pk-init: failed to build "
775 "trustedCertifiers", ""));
776 free_PA_PK_AS_REQ(&req
);
782 ASN1_MALLOC_ENCODE(PA_PK_AS_REQ
, buf
.data
, buf
.length
,
785 free_PA_PK_AS_REQ(&req
);
788 krb5_abortx(context
, "internal pkinit error");
790 krb5_set_error_message(context
, ret
, "PA-PK-AS-REQ %d", (int)ret
);
793 if (buf
.length
!= size
)
794 krb5_abortx(context
, "Internal ASN1 encoder error");
796 ret
= krb5_padata_add(context
, md
, pa_type
, buf
.data
, buf
.length
);
801 krb5_padata_add(context
, md
, KRB5_PADATA_PK_AS_09_BINDING
, NULL
, 0);
804 free_ContentInfo(&content_info
);
810 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
811 _krb5_pk_mk_padata(krb5_context context
,
815 const KDC_REQ_BODY
*req_body
,
819 krb5_pk_init_ctx ctx
= c
;
822 if (ctx
->id
->certs
== NULL
&& ctx
->anonymous
== 0) {
823 krb5_set_error_message(context
, HEIM_PKINIT_NO_PRIVATE_KEY
,
824 N_("PKINIT: No user certificate given", ""));
825 return HEIM_PKINIT_NO_PRIVATE_KEY
;
828 win2k_compat
= krb5_config_get_bool_default(context
, NULL
,
836 ctx
->require_binding
=
837 krb5_config_get_bool_default(context
, NULL
,
841 "pkinit_win2k_require_binding",
843 ctx
->type
= PKINIT_WIN2K
;
845 ctx
->type
= PKINIT_27
;
848 krb5_config_get_bool_default(context
, NULL
,
852 "pkinit_require_eku",
854 if (ic_flags
& KRB5_INIT_CREDS_NO_C_NO_EKU_CHECK
)
855 ctx
->require_eku
= 0;
856 if (ctx
->id
->flags
& PKINIT_BTMM
)
857 ctx
->require_eku
= 0;
859 ctx
->require_krbtgt_otherName
=
860 krb5_config_get_bool_default(context
, NULL
,
864 "pkinit_require_krbtgt_otherName",
867 ctx
->require_hostname_match
=
868 krb5_config_get_bool_default(context
, NULL
,
872 "pkinit_require_hostname_match",
875 ctx
->trustedCertifiers
=
876 krb5_config_get_bool_default(context
, NULL
,
880 "pkinit_trustedCertifiers",
883 return pk_mk_padata(context
, ctx
, req_body
, nonce
, md
);
886 static krb5_error_code
887 pk_verify_sign(krb5_context context
,
890 struct krb5_pk_identity
*id
,
891 heim_oid
*contentType
,
893 struct krb5_pk_cert
**signer
)
895 hx509_certs signer_certs
;
898 /* BTMM is broken in Leo and SnowLeo */
899 if (id
->flags
& PKINIT_BTMM
) {
900 flags
|= HX509_CMS_VS_ALLOW_DATA_OID_MISMATCH
;
901 flags
|= HX509_CMS_VS_NO_KU_CHECK
;
902 flags
|= HX509_CMS_VS_NO_VALIDATE
;
907 ret
= hx509_cms_verify_signed(context
->hx509ctx
,
918 pk_copy_error(context
, context
->hx509ctx
, ret
,
919 "CMS verify signed failed");
923 *signer
= calloc(1, sizeof(**signer
));
924 if (*signer
== NULL
) {
925 krb5_clear_error_message(context
);
930 ret
= hx509_get_one_cert(context
->hx509ctx
, signer_certs
, &(*signer
)->cert
);
932 pk_copy_error(context
, context
->hx509ctx
, ret
,
933 "Failed to get on of the signer certs");
938 hx509_certs_free(&signer_certs
);
941 hx509_cert_free((*signer
)->cert
);
950 static krb5_error_code
951 get_reply_key_win(krb5_context context
,
952 const krb5_data
*content
,
956 ReplyKeyPack_Win2k key_pack
;
960 ret
= decode_ReplyKeyPack_Win2k(content
->data
,
965 krb5_set_error_message(context
, ret
,
966 N_("PKINIT decoding reply key failed", ""));
967 free_ReplyKeyPack_Win2k(&key_pack
);
971 if ((unsigned)key_pack
.nonce
!= nonce
) {
972 krb5_set_error_message(context
, ret
,
973 N_("PKINIT enckey nonce is wrong", ""));
974 free_ReplyKeyPack_Win2k(&key_pack
);
975 return KRB5KRB_AP_ERR_MODIFIED
;
978 *key
= malloc (sizeof (**key
));
980 free_ReplyKeyPack_Win2k(&key_pack
);
981 return krb5_enomem(context
);
984 ret
= copy_EncryptionKey(&key_pack
.replyKey
, *key
);
985 free_ReplyKeyPack_Win2k(&key_pack
);
987 krb5_set_error_message(context
, ret
,
988 N_("PKINIT failed copying reply key", ""));
996 static krb5_error_code
997 get_reply_key(krb5_context context
,
998 const krb5_data
*content
,
999 const krb5_data
*req_buffer
,
1000 krb5_keyblock
**key
)
1002 ReplyKeyPack key_pack
;
1003 krb5_error_code ret
;
1006 ret
= decode_ReplyKeyPack(content
->data
,
1011 krb5_set_error_message(context
, ret
,
1012 N_("PKINIT decoding reply key failed", ""));
1013 free_ReplyKeyPack(&key_pack
);
1021 * XXX Verify kp.replyKey is a allowed enctype in the
1022 * configuration file
1025 ret
= krb5_crypto_init(context
, &key_pack
.replyKey
, 0, &crypto
);
1027 free_ReplyKeyPack(&key_pack
);
1031 ret
= krb5_verify_checksum(context
, crypto
, 6,
1032 req_buffer
->data
, req_buffer
->length
,
1033 &key_pack
.asChecksum
);
1034 krb5_crypto_destroy(context
, crypto
);
1036 free_ReplyKeyPack(&key_pack
);
1041 *key
= malloc (sizeof (**key
));
1043 free_ReplyKeyPack(&key_pack
);
1044 return krb5_enomem(context
);
1047 ret
= copy_EncryptionKey(&key_pack
.replyKey
, *key
);
1048 free_ReplyKeyPack(&key_pack
);
1050 krb5_set_error_message(context
, ret
,
1051 N_("PKINIT failed copying reply key", ""));
1060 static krb5_error_code
1061 pk_verify_host(krb5_context context
,
1063 const krb5_krbhst_info
*hi
,
1064 struct krb5_pk_init_ctx_data
*ctx
,
1065 struct krb5_pk_cert
*host
)
1067 krb5_error_code ret
= 0;
1069 if (ctx
->require_eku
) {
1070 ret
= hx509_cert_check_eku(context
->hx509ctx
, host
->cert
,
1071 &asn1_oid_id_pkkdcekuoid
, 0);
1073 krb5_set_error_message(context
, ret
,
1074 N_("No PK-INIT KDC EKU in kdc certificate", ""));
1078 if (ctx
->require_krbtgt_otherName
) {
1079 hx509_octet_string_list list
;
1083 ret
= hx509_cert_find_subjectAltName_otherName(context
->hx509ctx
,
1085 &asn1_oid_id_pkinit_san
,
1088 krb5_set_error_message(context
, ret
,
1089 N_("Failed to find the PK-INIT "
1090 "subjectAltName in the KDC "
1091 "certificate", ""));
1097 * subjectAltNames are multi-valued, and a single KDC may serve
1098 * multiple realms. The SAN validation here must accept
1099 * the KDC's cert if *any* of the SANs match the expected KDC.
1100 * It is OK for *some* of the SANs to not match, provided at least
1103 for (i
= 0; matched
== 0 && i
< list
.len
; i
++) {
1104 KRB5PrincipalName r
;
1106 ret
= decode_KRB5PrincipalName(list
.val
[i
].data
,
1111 krb5_set_error_message(context
, ret
,
1112 N_("Failed to decode the PK-INIT "
1113 "subjectAltName in the "
1114 "KDC certificate", ""));
1119 if (r
.principalName
.name_string
.len
== 2 &&
1120 strcmp(r
.principalName
.name_string
.val
[0], KRB5_TGS_NAME
) == 0
1121 && strcmp(r
.principalName
.name_string
.val
[1], realm
) == 0
1122 && strcmp(r
.realm
, realm
) == 0)
1125 free_KRB5PrincipalName(&r
);
1127 hx509_free_octet_string_list(&list
);
1129 ret
= KRB5_KDC_ERR_INVALID_CERTIFICATE
;
1130 /* XXX: Lost in translation... */
1131 krb5_set_error_message(context
, ret
,
1132 N_("KDC have wrong realm name in "
1133 "the certificate", ""));
1140 ret
= hx509_verify_hostname(context
->hx509ctx
, host
->cert
,
1141 ctx
->require_hostname_match
,
1144 hi
->ai
->ai_addr
, hi
->ai
->ai_addrlen
);
1147 krb5_set_error_message(context
, ret
,
1148 N_("Address mismatch in "
1149 "the KDC certificate", ""));
1154 static krb5_error_code
1155 pk_rd_pa_reply_enckey(krb5_context context
,
1157 const heim_octet_string
*indata
,
1158 const heim_oid
*dataType
,
1160 krb5_pk_init_ctx ctx
,
1162 const krb5_krbhst_info
*hi
,
1164 const krb5_data
*req_buffer
,
1166 krb5_keyblock
**key
)
1168 krb5_error_code ret
;
1169 struct krb5_pk_cert
*host
= NULL
;
1171 heim_oid contentType
= { 0, NULL
};
1172 int flags
= HX509_CMS_UE_DONT_REQUIRE_KU_ENCIPHERMENT
;
1174 if (der_heim_oid_cmp(&asn1_oid_id_pkcs7_envelopedData
, dataType
)) {
1175 krb5_set_error_message(context
, EINVAL
,
1176 N_("PKINIT: Invalid content type", ""));
1180 if (ctx
->type
== PKINIT_WIN2K
)
1181 flags
|= HX509_CMS_UE_ALLOW_WEAK
;
1183 ret
= hx509_cms_unenvelope(context
->hx509ctx
,
1193 pk_copy_error(context
, context
->hx509ctx
, ret
,
1194 "Failed to unenvelope CMS data in PK-INIT reply");
1197 der_free_oid(&contentType
);
1199 /* win2k uses ContentInfo */
1200 if (type
== PKINIT_WIN2K
) {
1202 heim_octet_string out
;
1204 ret
= hx509_cms_unwrap_ContentInfo(&content
, &type2
, &out
, NULL
);
1206 /* windows LH with interesting CMS packets */
1207 size_t ph
= 1 + der_length_len(content
.length
);
1208 unsigned char *ptr
= malloc(content
.length
+ ph
);
1211 memcpy(ptr
+ ph
, content
.data
, content
.length
);
1213 ret
= der_put_length_and_tag (ptr
+ ph
- 1, ph
, content
.length
,
1214 ASN1_C_UNIV
, CONS
, UT_Sequence
, &l
);
1219 content
.length
+= ph
;
1221 ret
= hx509_cms_unwrap_ContentInfo(&content
, &type2
, &out
, NULL
);
1225 if (der_heim_oid_cmp(&type2
, &asn1_oid_id_pkcs7_signedData
)) {
1226 ret
= EINVAL
; /* XXX */
1227 krb5_set_error_message(context
, ret
,
1228 N_("PKINIT: Invalid content type", ""));
1229 der_free_oid(&type2
);
1230 der_free_octet_string(&out
);
1233 der_free_oid(&type2
);
1234 krb5_data_free(&content
);
1235 ret
= krb5_data_copy(&content
, out
.data
, out
.length
);
1236 der_free_octet_string(&out
);
1238 krb5_set_error_message(context
, ret
,
1239 N_("malloc: out of memory", ""));
1244 ret
= pk_verify_sign(context
,
1254 /* make sure that it is the kdc's certificate */
1255 ret
= pk_verify_host(context
, realm
, hi
, ctx
, host
);
1261 if (type
== PKINIT_WIN2K
) {
1262 if (der_heim_oid_cmp(&contentType
, &asn1_oid_id_pkcs7_data
) != 0) {
1263 ret
= KRB5KRB_AP_ERR_MSG_TYPE
;
1264 krb5_set_error_message(context
, ret
, "PKINIT: reply key, wrong oid");
1268 if (der_heim_oid_cmp(&contentType
, &asn1_oid_id_pkrkeydata
) != 0) {
1269 ret
= KRB5KRB_AP_ERR_MSG_TYPE
;
1270 krb5_set_error_message(context
, ret
, "PKINIT: reply key, wrong oid");
1278 ret
= get_reply_key(context
, &content
, req_buffer
, key
);
1279 if (ret
!= 0 && ctx
->require_binding
== 0)
1280 ret
= get_reply_key_win(context
, &content
, nonce
, key
);
1283 ret
= get_reply_key(context
, &content
, req_buffer
, key
);
1289 /* XXX compare given etype with key->etype */
1293 _krb5_pk_cert_free(host
);
1294 der_free_oid(&contentType
);
1295 krb5_data_free(&content
);
1300 static krb5_error_code
1301 pk_rd_pa_reply_dh(krb5_context context
,
1302 const heim_octet_string
*indata
,
1303 const heim_oid
*dataType
,
1305 krb5_pk_init_ctx ctx
,
1307 const krb5_krbhst_info
*hi
,
1312 krb5_keyblock
**key
)
1314 const unsigned char *p
;
1315 unsigned char *dh_gen_key
= NULL
;
1316 struct krb5_pk_cert
*host
= NULL
;
1317 BIGNUM
*kdc_dh_pubkey
= NULL
;
1318 KDCDHKeyInfo kdc_dh_info
;
1319 heim_oid contentType
= { 0, NULL
};
1321 krb5_error_code ret
;
1322 int dh_gen_keylen
= 0;
1325 krb5_data_zero(&content
);
1326 memset(&kdc_dh_info
, 0, sizeof(kdc_dh_info
));
1328 if (der_heim_oid_cmp(&asn1_oid_id_pkcs7_signedData
, dataType
)) {
1329 krb5_set_error_message(context
, EINVAL
,
1330 N_("PKINIT: Invalid content type", ""));
1334 ret
= pk_verify_sign(context
,
1344 /* make sure that it is the kdc's certificate */
1345 ret
= pk_verify_host(context
, realm
, hi
, ctx
, host
);
1349 if (der_heim_oid_cmp(&contentType
, &asn1_oid_id_pkdhkeydata
)) {
1350 ret
= KRB5KRB_AP_ERR_MSG_TYPE
;
1351 krb5_set_error_message(context
, ret
,
1352 N_("pkinit - dh reply contains wrong oid", ""));
1356 ret
= decode_KDCDHKeyInfo(content
.data
,
1362 krb5_set_error_message(context
, ret
,
1363 N_("pkinit - failed to decode "
1364 "KDC DH Key Info", ""));
1368 if (kdc_dh_info
.nonce
!= nonce
) {
1369 ret
= KRB5KRB_AP_ERR_MODIFIED
;
1370 krb5_set_error_message(context
, ret
,
1371 N_("PKINIT: DH nonce is wrong", ""));
1375 if (kdc_dh_info
.dhKeyExpiration
) {
1377 ret
= KRB5KRB_ERR_GENERIC
;
1378 krb5_set_error_message(context
, ret
,
1379 N_("pkinit; got key expiration "
1380 "without server nonce", ""));
1384 ret
= KRB5KRB_ERR_GENERIC
;
1385 krb5_set_error_message(context
, ret
,
1386 N_("pkinit; got DH reuse but no "
1387 "client nonce", ""));
1392 ret
= KRB5KRB_ERR_GENERIC
;
1393 krb5_set_error_message(context
, ret
,
1394 N_("pkinit: got server nonce "
1395 "without key expiration", ""));
1402 p
= kdc_dh_info
.subjectPublicKey
.data
;
1403 size
= (kdc_dh_info
.subjectPublicKey
.length
+ 7) / 8;
1405 if (ctx
->keyex
== USE_DH
) {
1407 ret
= decode_DHPublicKey(p
, size
, &k
, NULL
);
1409 krb5_set_error_message(context
, ret
,
1410 N_("pkinit: can't decode "
1411 "without key expiration", ""));
1415 kdc_dh_pubkey
= integer_to_BN(context
, "DHPublicKey", &k
);
1416 free_DHPublicKey(&k
);
1417 if (kdc_dh_pubkey
== NULL
) {
1423 size
= DH_size(ctx
->u
.dh
);
1425 dh_gen_key
= malloc(size
);
1426 if (dh_gen_key
== NULL
) {
1427 ret
= krb5_enomem(context
);
1431 dh_gen_keylen
= DH_compute_key(dh_gen_key
, kdc_dh_pubkey
, ctx
->u
.dh
);
1432 if (dh_gen_keylen
== -1) {
1433 ret
= KRB5KRB_ERR_GENERIC
;
1435 krb5_set_error_message(context
, ret
,
1436 N_("PKINIT: Can't compute Diffie-Hellman key", ""));
1439 if (dh_gen_keylen
< (int)size
) {
1440 size
-= dh_gen_keylen
;
1441 memmove(dh_gen_key
+ size
, dh_gen_key
, dh_gen_keylen
);
1442 memset(dh_gen_key
, 0, size
);
1447 const EC_GROUP
*group
;
1448 EC_KEY
*public = NULL
;
1450 group
= EC_KEY_get0_group(ctx
->u
.eckey
);
1452 public = EC_KEY_new();
1453 if (public == NULL
) {
1457 if (EC_KEY_set_group(public, group
) != 1) {
1458 EC_KEY_free(public);
1463 if (o2i_ECPublicKey(&public, &p
, size
) == NULL
) {
1464 EC_KEY_free(public);
1465 ret
= KRB5KRB_ERR_GENERIC
;
1466 krb5_set_error_message(context
, ret
,
1467 N_("PKINIT: Can't parse ECDH public key", ""));
1471 size
= (EC_GROUP_get_degree(group
) + 7) / 8;
1472 dh_gen_key
= malloc(size
);
1473 if (dh_gen_key
== NULL
) {
1474 EC_KEY_free(public);
1475 ret
= krb5_enomem(context
);
1478 dh_gen_keylen
= ECDH_compute_key(dh_gen_key
, size
,
1479 EC_KEY_get0_public_key(public), ctx
->u
.eckey
, NULL
);
1480 EC_KEY_free(public);
1481 if (dh_gen_keylen
== -1) {
1482 ret
= KRB5KRB_ERR_GENERIC
;
1484 krb5_set_error_message(context
, ret
,
1485 N_("PKINIT: Can't compute ECDH public key", ""));
1493 if (dh_gen_keylen
<= 0) {
1495 krb5_set_error_message(context
, ret
,
1496 N_("PKINIT: resulting DH key <= 0", ""));
1501 *key
= malloc (sizeof (**key
));
1503 ret
= krb5_enomem(context
);
1507 ret
= _krb5_pk_octetstring2key(context
,
1509 dh_gen_key
, dh_gen_keylen
,
1513 krb5_set_error_message(context
, ret
,
1514 N_("PKINIT: can't create key from DH key", ""));
1522 BN_free(kdc_dh_pubkey
);
1524 memset(dh_gen_key
, 0, dh_gen_keylen
);
1528 _krb5_pk_cert_free(host
);
1530 krb5_data_free(&content
);
1531 der_free_oid(&contentType
);
1532 free_KDCDHKeyInfo(&kdc_dh_info
);
1537 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1538 _krb5_pk_rd_pa_reply(krb5_context context
,
1542 const krb5_krbhst_info
*hi
,
1544 const krb5_data
*req_buffer
,
1546 krb5_keyblock
**key
)
1548 krb5_pk_init_ctx ctx
= c
;
1549 krb5_error_code ret
;
1552 /* Check for IETF PK-INIT first */
1553 if (ctx
->type
== PKINIT_27
) {
1555 heim_octet_string os
, data
;
1558 if (pa
->padata_type
!= KRB5_PADATA_PK_AS_REP
) {
1559 krb5_set_error_message(context
, EINVAL
,
1560 N_("PKINIT: wrong padata recv", ""));
1564 ret
= decode_PA_PK_AS_REP(pa
->padata_value
.data
,
1565 pa
->padata_value
.length
,
1569 krb5_set_error_message(context
, ret
,
1570 N_("Failed to decode pkinit AS rep", ""));
1574 switch (rep
.element
) {
1575 case choice_PA_PK_AS_REP_dhInfo
:
1576 _krb5_debug(context
, 5, "krb5_get_init_creds: using pkinit dh");
1577 os
= rep
.u
.dhInfo
.dhSignedData
;
1579 case choice_PA_PK_AS_REP_encKeyPack
:
1580 _krb5_debug(context
, 5, "krb5_get_init_creds: using kinit enc reply key");
1581 os
= rep
.u
.encKeyPack
;
1584 PA_PK_AS_REP_BTMM btmm
;
1585 free_PA_PK_AS_REP(&rep
);
1586 memset(&rep
, 0, sizeof(rep
));
1588 _krb5_debug(context
, 5, "krb5_get_init_creds: using BTMM kinit enc reply key");
1590 ret
= decode_PA_PK_AS_REP_BTMM(pa
->padata_value
.data
,
1591 pa
->padata_value
.length
,
1595 krb5_set_error_message(context
, EINVAL
,
1596 N_("PKINIT: -27 reply "
1597 "invalid content type", ""));
1601 if (btmm
.dhSignedData
|| btmm
.encKeyPack
== NULL
) {
1602 free_PA_PK_AS_REP_BTMM(&btmm
);
1604 krb5_set_error_message(context
, ret
,
1605 N_("DH mode not supported for BTMM mode", ""));
1610 * Transform to IETF style PK-INIT reply so that free works below
1613 rep
.element
= choice_PA_PK_AS_REP_encKeyPack
;
1614 rep
.u
.encKeyPack
.data
= btmm
.encKeyPack
->data
;
1615 rep
.u
.encKeyPack
.length
= btmm
.encKeyPack
->length
;
1616 btmm
.encKeyPack
->data
= NULL
;
1617 btmm
.encKeyPack
->length
= 0;
1618 free_PA_PK_AS_REP_BTMM(&btmm
);
1619 os
= rep
.u
.encKeyPack
;
1623 ret
= hx509_cms_unwrap_ContentInfo(&os
, &oid
, &data
, NULL
);
1625 free_PA_PK_AS_REP(&rep
);
1626 krb5_set_error_message(context
, ret
,
1627 N_("PKINIT: failed to unwrap CI", ""));
1631 switch (rep
.element
) {
1632 case choice_PA_PK_AS_REP_dhInfo
:
1633 ret
= pk_rd_pa_reply_dh(context
, &data
, &oid
, realm
, ctx
, etype
, hi
,
1635 rep
.u
.dhInfo
.serverDHNonce
,
1638 case choice_PA_PK_AS_REP_encKeyPack
:
1639 ret
= pk_rd_pa_reply_enckey(context
, PKINIT_27
, &data
, &oid
, realm
,
1640 ctx
, etype
, hi
, nonce
, req_buffer
, pa
, key
);
1643 krb5_abortx(context
, "pk-init as-rep case not possible to happen");
1645 der_free_octet_string(&data
);
1647 free_PA_PK_AS_REP(&rep
);
1649 } else if (ctx
->type
== PKINIT_WIN2K
) {
1650 PA_PK_AS_REP_Win2k w2krep
;
1652 /* Check for Windows encoding of the AS-REP pa data */
1654 #if 0 /* should this be ? */
1655 if (pa
->padata_type
!= KRB5_PADATA_PK_AS_REP
) {
1656 krb5_set_error_message(context
, EINVAL
,
1657 "PKINIT: wrong padata recv");
1662 memset(&w2krep
, 0, sizeof(w2krep
));
1664 ret
= decode_PA_PK_AS_REP_Win2k(pa
->padata_value
.data
,
1665 pa
->padata_value
.length
,
1669 krb5_set_error_message(context
, ret
,
1670 N_("PKINIT: Failed decoding windows "
1671 "pkinit reply %d", ""), (int)ret
);
1675 krb5_clear_error_message(context
);
1677 switch (w2krep
.element
) {
1678 case choice_PA_PK_AS_REP_Win2k_encKeyPack
: {
1679 heim_octet_string data
;
1682 ret
= hx509_cms_unwrap_ContentInfo(&w2krep
.u
.encKeyPack
,
1684 free_PA_PK_AS_REP_Win2k(&w2krep
);
1686 krb5_set_error_message(context
, ret
,
1687 N_("PKINIT: failed to unwrap CI", ""));
1691 ret
= pk_rd_pa_reply_enckey(context
, PKINIT_WIN2K
, &data
, &oid
, realm
,
1692 ctx
, etype
, hi
, nonce
, req_buffer
, pa
, key
);
1693 der_free_octet_string(&data
);
1699 free_PA_PK_AS_REP_Win2k(&w2krep
);
1701 krb5_set_error_message(context
, ret
,
1702 N_("PKINIT: win2k reply invalid "
1703 "content type", ""));
1709 krb5_set_error_message(context
, ret
,
1710 N_("PKINIT: unknown reply type", ""));
1717 krb5_context context
;
1718 krb5_prompter_fct prompter
;
1719 void *prompter_data
;
1723 hx_pass_prompter(void *data
, const hx509_prompt
*prompter
)
1725 krb5_error_code ret
;
1727 krb5_data password_data
;
1728 struct prompter
*p
= data
;
1730 password_data
.data
= prompter
->reply
.data
;
1731 password_data
.length
= prompter
->reply
.length
;
1733 prompt
.prompt
= prompter
->prompt
;
1734 prompt
.hidden
= hx509_prompt_hidden(prompter
->type
);
1735 prompt
.reply
= &password_data
;
1737 switch (prompter
->type
) {
1738 case HX509_PROMPT_TYPE_INFO
:
1739 prompt
.type
= KRB5_PROMPT_TYPE_INFO
;
1741 case HX509_PROMPT_TYPE_PASSWORD
:
1742 case HX509_PROMPT_TYPE_QUESTION
:
1744 prompt
.type
= KRB5_PROMPT_TYPE_PASSWORD
;
1748 ret
= (*p
->prompter
)(p
->context
, p
->prompter_data
, NULL
, NULL
, 1, &prompt
);
1750 memset (prompter
->reply
.data
, 0, prompter
->reply
.length
);
1756 static krb5_error_code
1757 _krb5_pk_set_user_id(krb5_context context
,
1758 krb5_principal principal
,
1759 krb5_pk_init_ctx ctx
,
1760 struct hx509_certs_data
*certs
)
1762 hx509_certs c
= hx509_certs_ref(certs
);
1763 hx509_query
*q
= NULL
;
1767 hx509_certs_free(&ctx
->id
->certs
);
1768 if (ctx
->id
->cert
) {
1769 hx509_cert_free(ctx
->id
->cert
);
1770 ctx
->id
->cert
= NULL
;
1776 ret
= hx509_query_alloc(context
->hx509ctx
, &q
);
1778 pk_copy_error(context
, context
->hx509ctx
, ret
,
1779 "Allocate query to find signing certificate");
1783 hx509_query_match_option(q
, HX509_QUERY_OPTION_PRIVATE_KEY
);
1784 hx509_query_match_option(q
, HX509_QUERY_OPTION_KU_DIGITALSIGNATURE
);
1786 if (principal
&& strncmp("LKDC:SHA1.", krb5_principal_get_realm(context
, principal
), 9) == 0) {
1787 ctx
->id
->flags
|= PKINIT_BTMM
;
1790 ret
= find_cert(context
, ctx
->id
, q
, &ctx
->id
->cert
);
1791 hx509_query_free(context
->hx509ctx
, q
);
1793 if (ret
== 0 && _krb5_have_debug(context
, 2)) {
1798 ret
= hx509_cert_get_subject(ctx
->id
->cert
, &name
);
1802 ret
= hx509_name_to_string(name
, &str
);
1803 hx509_name_free(&name
);
1807 ret
= hx509_cert_get_serialnumber(ctx
->id
->cert
, &i
);
1813 ret
= der_print_hex_heim_integer(&i
, &sn
);
1814 der_free_heim_integer(&i
);
1820 _krb5_debug(context
, 2, "using cert: subject: %s sn: %s", str
, sn
);
1829 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1830 _krb5_pk_load_id(krb5_context context
,
1831 struct krb5_pk_identity
**ret_id
,
1832 const char *user_id
,
1833 const char *anchor_id
,
1834 char * const *chain_list
,
1835 char * const *revoke_list
,
1836 krb5_prompter_fct prompter
,
1837 void *prompter_data
,
1840 struct krb5_pk_identity
*id
= NULL
;
1846 if (anchor_id
== NULL
) {
1847 krb5_set_error_message(context
, HEIM_PKINIT_NO_VALID_CA
,
1848 N_("PKINIT: No anchor given", ""));
1849 return HEIM_PKINIT_NO_VALID_CA
;
1854 id
= calloc(1, sizeof(*id
));
1856 return krb5_enomem(context
);
1861 ret
= hx509_lock_init(context
->hx509ctx
, &lock
);
1863 pk_copy_error(context
, context
->hx509ctx
, ret
, "Failed init lock");
1867 if (password
&& password
[0])
1868 hx509_lock_add_password(lock
, password
);
1871 p
.context
= context
;
1872 p
.prompter
= prompter
;
1873 p
.prompter_data
= prompter_data
;
1875 ret
= hx509_lock_set_prompter(lock
, hx_pass_prompter
, &p
);
1877 hx509_lock_free(lock
);
1882 ret
= hx509_certs_init(context
->hx509ctx
, user_id
, 0, lock
, &id
->certs
);
1883 hx509_lock_free(lock
);
1885 pk_copy_error(context
, context
->hx509ctx
, ret
,
1886 "Failed to init cert certs");
1893 ret
= hx509_certs_init(context
->hx509ctx
, anchor_id
, 0, NULL
, &id
->anchors
);
1895 pk_copy_error(context
, context
->hx509ctx
, ret
,
1896 "Failed to init anchors");
1900 ret
= hx509_certs_init(context
->hx509ctx
, "MEMORY:pkinit-cert-chain",
1901 0, NULL
, &id
->certpool
);
1903 pk_copy_error(context
, context
->hx509ctx
, ret
,
1904 "Failed to init chain");
1908 while (chain_list
&& *chain_list
) {
1909 ret
= hx509_certs_append(context
->hx509ctx
, id
->certpool
,
1912 pk_copy_error(context
, context
->hx509ctx
, ret
,
1913 "Failed to laod chain %s",
1921 ret
= hx509_revoke_init(context
->hx509ctx
, &id
->revokectx
);
1923 pk_copy_error(context
, context
->hx509ctx
, ret
,
1924 "Failed init revoke list");
1928 while (*revoke_list
) {
1929 ret
= hx509_revoke_add_crl(context
->hx509ctx
,
1933 pk_copy_error(context
, context
->hx509ctx
, ret
,
1934 "Failed load revoke list");
1940 hx509_context_set_missing_revoke(context
->hx509ctx
, 1);
1942 ret
= hx509_verify_init_ctx(context
->hx509ctx
, &id
->verify_ctx
);
1944 pk_copy_error(context
, context
->hx509ctx
, ret
,
1945 "Failed init verify context");
1949 hx509_verify_attach_anchors(id
->verify_ctx
, id
->anchors
);
1950 hx509_verify_attach_revoke(id
->verify_ctx
, id
->revokectx
);
1954 hx509_verify_destroy_ctx(id
->verify_ctx
);
1955 hx509_certs_free(&id
->certs
);
1956 hx509_certs_free(&id
->anchors
);
1957 hx509_certs_free(&id
->certpool
);
1958 hx509_revoke_free(&id
->revokectx
);
1971 pk_copy_error(krb5_context context
,
1972 hx509_context hx509ctx
,
1982 ret
= vasprintf(&f
, fmt
, va
);
1984 if (ret
== -1 || f
== NULL
) {
1985 krb5_clear_error_message(context
);
1989 s
= hx509_get_error_string(hx509ctx
, hxret
);
1991 krb5_clear_error_message(context
);
1995 krb5_set_error_message(context
, hxret
, "%s: %s", f
, s
);
2001 parse_integer(krb5_context context
, char **p
, const char *file
, int lineno
,
2002 const char *name
, heim_integer
*integer
)
2006 p1
= strsep(p
, " \t");
2008 krb5_set_error_message(context
, EINVAL
,
2009 N_("moduli file %s missing %s on line %d", ""),
2010 file
, name
, lineno
);
2013 ret
= der_parse_hex_heim_integer(p1
, integer
);
2015 krb5_set_error_message(context
, ret
,
2016 N_("moduli file %s failed parsing %s "
2018 file
, name
, lineno
);
2025 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2026 _krb5_parse_moduli_line(krb5_context context
,
2030 struct krb5_dh_moduli
**m
)
2032 struct krb5_dh_moduli
*m1
;
2038 m1
= calloc(1, sizeof(*m1
));
2040 return krb5_enomem(context
);
2042 while (isspace((unsigned char)*p
))
2050 p1
= strsep(&p
, " \t");
2052 krb5_set_error_message(context
, ret
,
2053 N_("moduli file %s missing name on line %d", ""),
2057 m1
->name
= strdup(p1
);
2058 if (m1
->name
== NULL
) {
2059 ret
= krb5_enomem(context
);
2063 p1
= strsep(&p
, " \t");
2065 krb5_set_error_message(context
, ret
,
2066 N_("moduli file %s missing bits on line %d", ""),
2071 m1
->bits
= atoi(p1
);
2072 if (m1
->bits
== 0) {
2073 krb5_set_error_message(context
, ret
,
2074 N_("moduli file %s have un-parsable "
2075 "bits on line %d", ""), file
, lineno
);
2079 ret
= parse_integer(context
, &p
, file
, lineno
, "p", &m1
->p
);
2082 ret
= parse_integer(context
, &p
, file
, lineno
, "g", &m1
->g
);
2085 ret
= parse_integer(context
, &p
, file
, lineno
, "q", &m1
->q
);
2094 der_free_heim_integer(&m1
->p
);
2095 der_free_heim_integer(&m1
->g
);
2096 der_free_heim_integer(&m1
->q
);
2101 KRB5_LIB_FUNCTION
void KRB5_LIB_CALL
2102 _krb5_free_moduli(struct krb5_dh_moduli
**moduli
)
2105 for (i
= 0; moduli
[i
] != NULL
; i
++) {
2106 free(moduli
[i
]->name
);
2107 der_free_heim_integer(&moduli
[i
]->p
);
2108 der_free_heim_integer(&moduli
[i
]->g
);
2109 der_free_heim_integer(&moduli
[i
]->q
);
2115 static const char *default_moduli_RFC2412_MODP_group2
=
2117 "RFC2412-MODP-group2 "
2121 "FFFFFFFF" "FFFFFFFF" "C90FDAA2" "2168C234" "C4C6628B" "80DC1CD1"
2122 "29024E08" "8A67CC74" "020BBEA6" "3B139B22" "514A0879" "8E3404DD"
2123 "EF9519B3" "CD3A431B" "302B0A6D" "F25F1437" "4FE1356D" "6D51C245"
2124 "E485B576" "625E7EC6" "F44C42E9" "A637ED6B" "0BFF5CB6" "F406B7ED"
2125 "EE386BFB" "5A899FA5" "AE9F2411" "7C4B1FE6" "49286651" "ECE65381"
2126 "FFFFFFFF" "FFFFFFFF "
2130 "7FFFFFFF" "FFFFFFFF" "E487ED51" "10B4611A" "62633145" "C06E0E68"
2131 "94812704" "4533E63A" "0105DF53" "1D89CD91" "28A5043C" "C71A026E"
2132 "F7CA8CD9" "E69D218D" "98158536" "F92F8A1B" "A7F09AB6" "B6A8E122"
2133 "F242DABB" "312F3F63" "7A262174" "D31BF6B5" "85FFAE5B" "7A035BF6"
2134 "F71C35FD" "AD44CFD2" "D74F9208" "BE258FF3" "24943328" "F67329C0"
2135 "FFFFFFFF" "FFFFFFFF";
2137 static const char *default_moduli_rfc3526_MODP_group14
=
2139 "rfc3526-MODP-group14 "
2143 "FFFFFFFF" "FFFFFFFF" "C90FDAA2" "2168C234" "C4C6628B" "80DC1CD1"
2144 "29024E08" "8A67CC74" "020BBEA6" "3B139B22" "514A0879" "8E3404DD"
2145 "EF9519B3" "CD3A431B" "302B0A6D" "F25F1437" "4FE1356D" "6D51C245"
2146 "E485B576" "625E7EC6" "F44C42E9" "A637ED6B" "0BFF5CB6" "F406B7ED"
2147 "EE386BFB" "5A899FA5" "AE9F2411" "7C4B1FE6" "49286651" "ECE45B3D"
2148 "C2007CB8" "A163BF05" "98DA4836" "1C55D39A" "69163FA8" "FD24CF5F"
2149 "83655D23" "DCA3AD96" "1C62F356" "208552BB" "9ED52907" "7096966D"
2150 "670C354E" "4ABC9804" "F1746C08" "CA18217C" "32905E46" "2E36CE3B"
2151 "E39E772C" "180E8603" "9B2783A2" "EC07A28F" "B5C55DF0" "6F4C52C9"
2152 "DE2BCBF6" "95581718" "3995497C" "EA956AE5" "15D22618" "98FA0510"
2153 "15728E5A" "8AACAA68" "FFFFFFFF" "FFFFFFFF "
2157 "7FFFFFFF" "FFFFFFFF" "E487ED51" "10B4611A" "62633145" "C06E0E68"
2158 "94812704" "4533E63A" "0105DF53" "1D89CD91" "28A5043C" "C71A026E"
2159 "F7CA8CD9" "E69D218D" "98158536" "F92F8A1B" "A7F09AB6" "B6A8E122"
2160 "F242DABB" "312F3F63" "7A262174" "D31BF6B5" "85FFAE5B" "7A035BF6"
2161 "F71C35FD" "AD44CFD2" "D74F9208" "BE258FF3" "24943328" "F6722D9E"
2162 "E1003E5C" "50B1DF82" "CC6D241B" "0E2AE9CD" "348B1FD4" "7E9267AF"
2163 "C1B2AE91" "EE51D6CB" "0E3179AB" "1042A95D" "CF6A9483" "B84B4B36"
2164 "B3861AA7" "255E4C02" "78BA3604" "650C10BE" "19482F23" "171B671D"
2165 "F1CF3B96" "0C074301" "CD93C1D1" "7603D147" "DAE2AEF8" "37A62964"
2166 "EF15E5FB" "4AAC0B8C" "1CCAA4BE" "754AB572" "8AE9130C" "4C7D0288"
2167 "0AB9472D" "45565534" "7FFFFFFF" "FFFFFFFF";
2169 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2170 _krb5_parse_moduli(krb5_context context
, const char *file
,
2171 struct krb5_dh_moduli
***moduli
)
2173 /* name bits P G Q */
2174 krb5_error_code ret
;
2175 struct krb5_dh_moduli
**m
= NULL
, **m2
;
2178 int lineno
= 0, n
= 0;
2182 m
= calloc(1, sizeof(m
[0]) * 3);
2184 return krb5_enomem(context
);
2186 strlcpy(buf
, default_moduli_rfc3526_MODP_group14
, sizeof(buf
));
2187 ret
= _krb5_parse_moduli_line(context
, "builtin", 1, buf
, &m
[0]);
2189 _krb5_free_moduli(m
);
2194 strlcpy(buf
, default_moduli_RFC2412_MODP_group2
, sizeof(buf
));
2195 ret
= _krb5_parse_moduli_line(context
, "builtin", 1, buf
, &m
[1]);
2197 _krb5_free_moduli(m
);
2206 #ifdef KRB5_USE_PATH_TOKENS
2210 if (_krb5_expand_path_tokens(context
, file
, &exp_file
) == 0) {
2211 f
= fopen(exp_file
, "r");
2212 krb5_xfree(exp_file
);
2218 f
= fopen(file
, "r");
2227 while(fgets(buf
, sizeof(buf
), f
) != NULL
) {
2228 struct krb5_dh_moduli
*element
;
2230 buf
[strcspn(buf
, "\n")] = '\0';
2233 m2
= realloc(m
, (n
+ 2) * sizeof(m
[0]));
2235 _krb5_free_moduli(m
);
2236 return krb5_enomem(context
);
2242 ret
= _krb5_parse_moduli_line(context
, file
, lineno
, buf
, &element
);
2244 _krb5_free_moduli(m
);
2247 if (element
== NULL
)
2258 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2259 _krb5_dh_group_ok(krb5_context context
, unsigned long bits
,
2260 heim_integer
*p
, heim_integer
*g
, heim_integer
*q
,
2261 struct krb5_dh_moduli
**moduli
,
2269 for (i
= 0; moduli
[i
] != NULL
; i
++) {
2270 if (der_heim_integer_cmp(&moduli
[i
]->g
, g
) == 0 &&
2271 der_heim_integer_cmp(&moduli
[i
]->p
, p
) == 0 &&
2272 (q
== NULL
|| der_heim_integer_cmp(&moduli
[i
]->q
, q
) == 0))
2274 if (bits
&& bits
> moduli
[i
]->bits
) {
2275 krb5_set_error_message(context
,
2276 KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED
,
2277 N_("PKINIT: DH group parameter %s "
2278 "no accepted, not enough bits "
2281 return KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED
;
2284 *name
= strdup(moduli
[i
]->name
);
2288 krb5_set_error_message(context
,
2289 KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED
,
2290 N_("PKINIT: DH group parameter no ok", ""));
2291 return KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED
;
2295 KRB5_LIB_FUNCTION
void KRB5_LIB_CALL
2296 _krb5_get_init_creds_opt_free_pkinit(krb5_get_init_creds_opt
*opt
)
2299 krb5_pk_init_ctx ctx
;
2301 if (opt
->opt_private
== NULL
|| opt
->opt_private
->pk_init_ctx
== NULL
)
2303 ctx
= opt
->opt_private
->pk_init_ctx
;
2304 switch (ctx
->keyex
) {
2314 EC_KEY_free(ctx
->u
.eckey
);
2319 hx509_verify_destroy_ctx(ctx
->id
->verify_ctx
);
2320 hx509_certs_free(&ctx
->id
->certs
);
2321 hx509_cert_free(ctx
->id
->cert
);
2322 hx509_certs_free(&ctx
->id
->anchors
);
2323 hx509_certs_free(&ctx
->id
->certpool
);
2325 if (ctx
->clientDHNonce
) {
2326 krb5_free_data(NULL
, ctx
->clientDHNonce
);
2327 ctx
->clientDHNonce
= NULL
;
2330 _krb5_free_moduli(ctx
->m
);
2334 free(opt
->opt_private
->pk_init_ctx
);
2335 opt
->opt_private
->pk_init_ctx
= NULL
;
2339 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2340 krb5_get_init_creds_opt_set_pkinit(krb5_context context
,
2341 krb5_get_init_creds_opt
*opt
,
2342 krb5_principal principal
,
2343 const char *user_id
,
2344 const char *x509_anchors
,
2345 char * const * pool
,
2346 char * const * pki_revoke
,
2348 krb5_prompter_fct prompter
,
2349 void *prompter_data
,
2353 krb5_error_code ret
;
2354 char *anchors
= NULL
;
2356 if (opt
->opt_private
== NULL
) {
2357 krb5_set_error_message(context
, EINVAL
,
2358 N_("PKINIT: on non extendable opt", ""));
2362 opt
->opt_private
->pk_init_ctx
=
2363 calloc(1, sizeof(*opt
->opt_private
->pk_init_ctx
));
2364 if (opt
->opt_private
->pk_init_ctx
== NULL
)
2365 return krb5_enomem(context
);
2366 opt
->opt_private
->pk_init_ctx
->require_binding
= 0;
2367 opt
->opt_private
->pk_init_ctx
->require_eku
= 1;
2368 opt
->opt_private
->pk_init_ctx
->require_krbtgt_otherName
= 1;
2369 opt
->opt_private
->pk_init_ctx
->peer
= NULL
;
2371 /* XXX implement krb5_appdefault_strings */
2373 pool
= krb5_config_get_strings(context
, NULL
,
2378 if (pki_revoke
== NULL
)
2379 pki_revoke
= krb5_config_get_strings(context
, NULL
,
2384 if (x509_anchors
== NULL
) {
2385 krb5_appdefault_string(context
, "kinit",
2386 krb5_principal_get_realm(context
, principal
),
2387 "pkinit_anchors", NULL
, &anchors
);
2388 x509_anchors
= anchors
;
2392 opt
->opt_private
->pk_init_ctx
->anonymous
= 1;
2394 ret
= _krb5_pk_load_id(context
,
2395 &opt
->opt_private
->pk_init_ctx
->id
,
2404 free(opt
->opt_private
->pk_init_ctx
);
2405 opt
->opt_private
->pk_init_ctx
= NULL
;
2409 if (opt
->opt_private
->pk_init_ctx
->id
->certs
) {
2410 _krb5_pk_set_user_id(context
,
2412 opt
->opt_private
->pk_init_ctx
,
2413 opt
->opt_private
->pk_init_ctx
->id
->certs
);
2415 opt
->opt_private
->pk_init_ctx
->id
->cert
= NULL
;
2417 if ((flags
& 2) == 0) {
2418 hx509_context hx509ctx
= context
->hx509ctx
;
2419 hx509_cert cert
= opt
->opt_private
->pk_init_ctx
->id
->cert
;
2421 opt
->opt_private
->pk_init_ctx
->keyex
= USE_DH
;
2424 * If its a ECDSA certs, lets select ECDSA as the keyex algorithm.
2427 AlgorithmIdentifier alg
;
2429 ret
= hx509_cert_get_SPKI_AlgorithmIdentifier(hx509ctx
, cert
, &alg
);
2431 if (der_heim_oid_cmp(&alg
.algorithm
, &asn1_oid_id_ecPublicKey
) == 0)
2432 opt
->opt_private
->pk_init_ctx
->keyex
= USE_ECDH
;
2433 free_AlgorithmIdentifier(&alg
);
2438 opt
->opt_private
->pk_init_ctx
->keyex
= USE_RSA
;
2440 if (opt
->opt_private
->pk_init_ctx
->id
->certs
== NULL
) {
2441 krb5_set_error_message(context
, EINVAL
,
2442 N_("No anonymous pkinit support in RSA mode", ""));
2449 krb5_set_error_message(context
, EINVAL
,
2450 N_("no support for PKINIT compiled in", ""));
2455 krb5_error_code KRB5_LIB_FUNCTION
2456 krb5_get_init_creds_opt_set_pkinit_user_certs(krb5_context context
,
2457 krb5_get_init_creds_opt
*opt
,
2458 struct hx509_certs_data
*certs
)
2461 if (opt
->opt_private
== NULL
) {
2462 krb5_set_error_message(context
, EINVAL
,
2463 N_("PKINIT: on non extendable opt", ""));
2466 if (opt
->opt_private
->pk_init_ctx
== NULL
) {
2467 krb5_set_error_message(context
, EINVAL
,
2468 N_("PKINIT: on pkinit context", ""));
2472 _krb5_pk_set_user_id(context
, NULL
, opt
->opt_private
->pk_init_ctx
, certs
);
2476 krb5_set_error_message(context
, EINVAL
,
2477 N_("no support for PKINIT compiled in", ""));
2485 get_ms_san(hx509_context context
, hx509_cert cert
, char **upn
)
2487 hx509_octet_string_list list
;
2492 ret
= hx509_cert_find_subjectAltName_otherName(context
,
2494 &asn1_oid_id_pkinit_ms_san
,
2499 if (list
.len
> 0 && list
.val
[0].length
> 0)
2500 ret
= decode_MS_UPN_SAN(list
.val
[0].data
, list
.val
[0].length
,
2504 hx509_free_octet_string_list(&list
);
2510 find_ms_san(hx509_context context
, hx509_cert cert
, void *ctx
)
2515 ret
= get_ms_san(context
, cert
, &upn
);
2526 * Private since it need to be redesigned using krb5_get_init_creds()
2529 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2530 krb5_pk_enterprise_cert(krb5_context context
,
2531 const char *user_id
,
2532 krb5_const_realm realm
,
2533 krb5_principal
*principal
,
2534 struct hx509_certs_data
**res
)
2537 krb5_error_code ret
;
2538 hx509_certs certs
, result
;
2539 hx509_cert cert
= NULL
;
2547 if (user_id
== NULL
) {
2548 krb5_set_error_message(context
, ENOENT
, "no user id");
2552 ret
= hx509_certs_init(context
->hx509ctx
, user_id
, 0, NULL
, &certs
);
2554 pk_copy_error(context
, context
->hx509ctx
, ret
,
2555 "Failed to init cert certs");
2559 ret
= hx509_query_alloc(context
->hx509ctx
, &q
);
2561 krb5_set_error_message(context
, ret
, "out of memory");
2562 hx509_certs_free(&certs
);
2566 hx509_query_match_option(q
, HX509_QUERY_OPTION_PRIVATE_KEY
);
2567 hx509_query_match_option(q
, HX509_QUERY_OPTION_KU_DIGITALSIGNATURE
);
2568 hx509_query_match_eku(q
, &asn1_oid_id_pkinit_ms_eku
);
2569 hx509_query_match_cmp_func(q
, find_ms_san
, NULL
);
2571 ret
= hx509_certs_filter(context
->hx509ctx
, certs
, q
, &result
);
2572 hx509_query_free(context
->hx509ctx
, q
);
2573 hx509_certs_free(&certs
);
2575 pk_copy_error(context
, context
->hx509ctx
, ret
,
2576 "Failed to find PKINIT certificate");
2580 ret
= hx509_get_one_cert(context
->hx509ctx
, result
, &cert
);
2581 hx509_certs_free(&result
);
2583 pk_copy_error(context
, context
->hx509ctx
, ret
,
2584 "Failed to get one cert");
2588 ret
= get_ms_san(context
->hx509ctx
, cert
, &name
);
2590 pk_copy_error(context
, context
->hx509ctx
, ret
,
2591 "Failed to get MS SAN");
2595 ret
= krb5_make_principal(context
, principal
, realm
, name
, NULL
);
2600 krb5_principal_set_type(context
, *principal
, KRB5_NT_ENTERPRISE_PRINCIPAL
);
2603 ret
= hx509_certs_init(context
->hx509ctx
, "MEMORY:", 0, NULL
, res
);
2607 ret
= hx509_certs_add(context
->hx509ctx
, *res
, cert
);
2609 hx509_certs_free(res
);
2615 hx509_cert_free(cert
);
2619 krb5_set_error_message(context
, EINVAL
,
2620 N_("no support for PKINIT compiled in", ""));