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] = {
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 krb5_set_error_message(context
, ENOMEM
,
403 N_("malloc: out of memory", ""));
407 ret
= krb5_data_copy(a
->pkAuthenticator
.paChecksum
,
408 checksum
.checksum
.data
, checksum
.checksum
.length
);
409 free_Checksum(&checksum
);
413 if (ctx
->keyex
== USE_DH
|| ctx
->keyex
== USE_ECDH
) {
414 const char *moduli_file
;
415 unsigned long dh_min_bits
;
419 krb5_data_zero(&dhbuf
);
423 moduli_file
= krb5_config_get_string(context
, NULL
,
429 krb5_config_get_int_default(context
, NULL
, 0,
431 "pkinit_dh_min_bits",
434 ret
= _krb5_parse_moduli(context
, moduli_file
, &ctx
->m
);
438 ctx
->u
.dh
= DH_new();
439 if (ctx
->u
.dh
== NULL
) {
440 krb5_set_error_message(context
, ENOMEM
,
441 N_("malloc: out of memory", ""));
445 ret
= select_dh_group(context
, ctx
->u
.dh
, dh_min_bits
, ctx
->m
);
449 if (DH_generate_key(ctx
->u
.dh
) != 1) {
450 krb5_set_error_message(context
, ENOMEM
,
451 N_("pkinit: failed to generate DH key", ""));
456 if (1 /* support_cached_dh */) {
457 ALLOC(a
->clientDHNonce
, 1);
458 if (a
->clientDHNonce
== NULL
) {
459 krb5_clear_error_message(context
);
462 ret
= krb5_data_alloc(a
->clientDHNonce
, 40);
463 if (a
->clientDHNonce
== NULL
) {
464 krb5_clear_error_message(context
);
467 RAND_bytes(a
->clientDHNonce
->data
, a
->clientDHNonce
->length
);
468 ret
= krb5_copy_data(context
, a
->clientDHNonce
,
469 &ctx
->clientDHNonce
);
474 ALLOC(a
->clientPublicValue
, 1);
475 if (a
->clientPublicValue
== NULL
)
478 if (ctx
->keyex
== USE_DH
) {
481 heim_integer dh_pub_key
;
483 ret
= der_copy_oid(&asn1_oid_id_dhpublicnumber
,
484 &a
->clientPublicValue
->algorithm
.algorithm
);
488 memset(&dp
, 0, sizeof(dp
));
490 ret
= BN_to_integer(context
, dh
->p
, &dp
.p
);
492 free_DomainParameters(&dp
);
495 ret
= BN_to_integer(context
, dh
->g
, &dp
.g
);
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
) {
767 krb5_set_error_message(context
, ret
,
768 N_("malloc: out of memory", ""));
769 free_PA_PK_AS_REQ(&req
);
772 ret
= build_edi(context
, context
->hx509ctx
,
773 ctx
->id
->anchors
, req
.trustedCertifiers
);
775 krb5_set_error_message(context
, ret
,
776 N_("pk-init: failed to build "
777 "trustedCertifiers", ""));
778 free_PA_PK_AS_REQ(&req
);
784 ASN1_MALLOC_ENCODE(PA_PK_AS_REQ
, buf
.data
, buf
.length
,
787 free_PA_PK_AS_REQ(&req
);
790 krb5_abortx(context
, "internal pkinit error");
792 krb5_set_error_message(context
, ret
, "PA-PK-AS-REQ %d", (int)ret
);
795 if (buf
.length
!= size
)
796 krb5_abortx(context
, "Internal ASN1 encoder error");
798 ret
= krb5_padata_add(context
, md
, pa_type
, buf
.data
, buf
.length
);
803 krb5_padata_add(context
, md
, KRB5_PADATA_PK_AS_09_BINDING
, NULL
, 0);
806 free_ContentInfo(&content_info
);
812 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
813 _krb5_pk_mk_padata(krb5_context context
,
817 const KDC_REQ_BODY
*req_body
,
821 krb5_pk_init_ctx ctx
= c
;
824 if (ctx
->id
->certs
== NULL
&& ctx
->anonymous
== 0) {
825 krb5_set_error_message(context
, HEIM_PKINIT_NO_PRIVATE_KEY
,
826 N_("PKINIT: No user certificate given", ""));
827 return HEIM_PKINIT_NO_PRIVATE_KEY
;
830 win2k_compat
= krb5_config_get_bool_default(context
, NULL
,
838 ctx
->require_binding
=
839 krb5_config_get_bool_default(context
, NULL
,
843 "pkinit_win2k_require_binding",
845 ctx
->type
= PKINIT_WIN2K
;
847 ctx
->type
= PKINIT_27
;
850 krb5_config_get_bool_default(context
, NULL
,
854 "pkinit_require_eku",
856 if (ic_flags
& KRB5_INIT_CREDS_NO_C_NO_EKU_CHECK
)
857 ctx
->require_eku
= 0;
858 if (ctx
->id
->flags
& PKINIT_BTMM
)
859 ctx
->require_eku
= 0;
861 ctx
->require_krbtgt_otherName
=
862 krb5_config_get_bool_default(context
, NULL
,
866 "pkinit_require_krbtgt_otherName",
869 ctx
->require_hostname_match
=
870 krb5_config_get_bool_default(context
, NULL
,
874 "pkinit_require_hostname_match",
877 ctx
->trustedCertifiers
=
878 krb5_config_get_bool_default(context
, NULL
,
882 "pkinit_trustedCertifiers",
885 return pk_mk_padata(context
, ctx
, req_body
, nonce
, md
);
888 static krb5_error_code
889 pk_verify_sign(krb5_context context
,
892 struct krb5_pk_identity
*id
,
893 heim_oid
*contentType
,
895 struct krb5_pk_cert
**signer
)
897 hx509_certs signer_certs
;
900 /* BTMM is broken in Leo and SnowLeo */
901 if (id
->flags
& PKINIT_BTMM
) {
902 flags
|= HX509_CMS_VS_ALLOW_DATA_OID_MISMATCH
;
903 flags
|= HX509_CMS_VS_NO_KU_CHECK
;
904 flags
|= HX509_CMS_VS_NO_VALIDATE
;
909 ret
= hx509_cms_verify_signed(context
->hx509ctx
,
920 pk_copy_error(context
, context
->hx509ctx
, ret
,
921 "CMS verify signed failed");
925 *signer
= calloc(1, sizeof(**signer
));
926 if (*signer
== NULL
) {
927 krb5_clear_error_message(context
);
932 ret
= hx509_get_one_cert(context
->hx509ctx
, signer_certs
, &(*signer
)->cert
);
934 pk_copy_error(context
, context
->hx509ctx
, ret
,
935 "Failed to get on of the signer certs");
940 hx509_certs_free(&signer_certs
);
943 hx509_cert_free((*signer
)->cert
);
952 static krb5_error_code
953 get_reply_key_win(krb5_context context
,
954 const krb5_data
*content
,
958 ReplyKeyPack_Win2k key_pack
;
962 ret
= decode_ReplyKeyPack_Win2k(content
->data
,
967 krb5_set_error_message(context
, ret
,
968 N_("PKINIT decoding reply key failed", ""));
969 free_ReplyKeyPack_Win2k(&key_pack
);
973 if ((unsigned)key_pack
.nonce
!= nonce
) {
974 krb5_set_error_message(context
, ret
,
975 N_("PKINIT enckey nonce is wrong", ""));
976 free_ReplyKeyPack_Win2k(&key_pack
);
977 return KRB5KRB_AP_ERR_MODIFIED
;
980 *key
= malloc (sizeof (**key
));
982 free_ReplyKeyPack_Win2k(&key_pack
);
983 krb5_set_error_message(context
, ENOMEM
,
984 N_("malloc: out of memory", ""));
988 ret
= copy_EncryptionKey(&key_pack
.replyKey
, *key
);
989 free_ReplyKeyPack_Win2k(&key_pack
);
991 krb5_set_error_message(context
, ret
,
992 N_("PKINIT failed copying reply key", ""));
1000 static krb5_error_code
1001 get_reply_key(krb5_context context
,
1002 const krb5_data
*content
,
1003 const krb5_data
*req_buffer
,
1004 krb5_keyblock
**key
)
1006 ReplyKeyPack key_pack
;
1007 krb5_error_code ret
;
1010 ret
= decode_ReplyKeyPack(content
->data
,
1015 krb5_set_error_message(context
, ret
,
1016 N_("PKINIT decoding reply key failed", ""));
1017 free_ReplyKeyPack(&key_pack
);
1025 * XXX Verify kp.replyKey is a allowed enctype in the
1026 * configuration file
1029 ret
= krb5_crypto_init(context
, &key_pack
.replyKey
, 0, &crypto
);
1031 free_ReplyKeyPack(&key_pack
);
1035 ret
= krb5_verify_checksum(context
, crypto
, 6,
1036 req_buffer
->data
, req_buffer
->length
,
1037 &key_pack
.asChecksum
);
1038 krb5_crypto_destroy(context
, crypto
);
1040 free_ReplyKeyPack(&key_pack
);
1045 *key
= malloc (sizeof (**key
));
1047 free_ReplyKeyPack(&key_pack
);
1048 krb5_set_error_message(context
, ENOMEM
,
1049 N_("malloc: out of memory", ""));
1053 ret
= copy_EncryptionKey(&key_pack
.replyKey
, *key
);
1054 free_ReplyKeyPack(&key_pack
);
1056 krb5_set_error_message(context
, ret
,
1057 N_("PKINIT failed copying reply key", ""));
1066 static krb5_error_code
1067 pk_verify_host(krb5_context context
,
1069 const krb5_krbhst_info
*hi
,
1070 struct krb5_pk_init_ctx_data
*ctx
,
1071 struct krb5_pk_cert
*host
)
1073 krb5_error_code ret
= 0;
1075 if (ctx
->require_eku
) {
1076 ret
= hx509_cert_check_eku(context
->hx509ctx
, host
->cert
,
1077 &asn1_oid_id_pkkdcekuoid
, 0);
1079 krb5_set_error_message(context
, ret
,
1080 N_("No PK-INIT KDC EKU in kdc certificate", ""));
1084 if (ctx
->require_krbtgt_otherName
) {
1085 hx509_octet_string_list list
;
1088 ret
= hx509_cert_find_subjectAltName_otherName(context
->hx509ctx
,
1090 &asn1_oid_id_pkinit_san
,
1093 krb5_set_error_message(context
, ret
,
1094 N_("Failed to find the PK-INIT "
1095 "subjectAltName in the KDC "
1096 "certificate", ""));
1101 for (i
= 0; i
< list
.len
; i
++) {
1102 KRB5PrincipalName r
;
1104 ret
= decode_KRB5PrincipalName(list
.val
[i
].data
,
1109 krb5_set_error_message(context
, ret
,
1110 N_("Failed to decode the PK-INIT "
1111 "subjectAltName in the "
1112 "KDC certificate", ""));
1117 if (r
.principalName
.name_string
.len
!= 2 ||
1118 strcmp(r
.principalName
.name_string
.val
[0], KRB5_TGS_NAME
) != 0 ||
1119 strcmp(r
.principalName
.name_string
.val
[1], realm
) != 0 ||
1120 strcmp(r
.realm
, realm
) != 0)
1122 ret
= KRB5_KDC_ERR_INVALID_CERTIFICATE
;
1123 krb5_set_error_message(context
, ret
,
1124 N_("KDC have wrong realm name in "
1125 "the certificate", ""));
1128 free_KRB5PrincipalName(&r
);
1132 hx509_free_octet_string_list(&list
);
1138 ret
= hx509_verify_hostname(context
->hx509ctx
, host
->cert
,
1139 ctx
->require_hostname_match
,
1142 hi
->ai
->ai_addr
, hi
->ai
->ai_addrlen
);
1145 krb5_set_error_message(context
, ret
,
1146 N_("Address mismatch in "
1147 "the KDC certificate", ""));
1152 static krb5_error_code
1153 pk_rd_pa_reply_enckey(krb5_context context
,
1155 const heim_octet_string
*indata
,
1156 const heim_oid
*dataType
,
1158 krb5_pk_init_ctx ctx
,
1160 const krb5_krbhst_info
*hi
,
1162 const krb5_data
*req_buffer
,
1164 krb5_keyblock
**key
)
1166 krb5_error_code ret
;
1167 struct krb5_pk_cert
*host
= NULL
;
1169 heim_oid contentType
= { 0, NULL
};
1170 int flags
= HX509_CMS_UE_DONT_REQUIRE_KU_ENCIPHERMENT
;
1172 if (der_heim_oid_cmp(&asn1_oid_id_pkcs7_envelopedData
, dataType
)) {
1173 krb5_set_error_message(context
, EINVAL
,
1174 N_("PKINIT: Invalid content type", ""));
1178 if (ctx
->type
== PKINIT_WIN2K
)
1179 flags
|= HX509_CMS_UE_ALLOW_WEAK
;
1181 ret
= hx509_cms_unenvelope(context
->hx509ctx
,
1191 pk_copy_error(context
, context
->hx509ctx
, ret
,
1192 "Failed to unenvelope CMS data in PK-INIT reply");
1195 der_free_oid(&contentType
);
1197 /* win2k uses ContentInfo */
1198 if (type
== PKINIT_WIN2K
) {
1200 heim_octet_string out
;
1202 ret
= hx509_cms_unwrap_ContentInfo(&content
, &type2
, &out
, NULL
);
1204 /* windows LH with interesting CMS packets */
1205 size_t ph
= 1 + der_length_len(content
.length
);
1206 unsigned char *ptr
= malloc(content
.length
+ ph
);
1209 memcpy(ptr
+ ph
, content
.data
, content
.length
);
1211 ret
= der_put_length_and_tag (ptr
+ ph
- 1, ph
, content
.length
,
1212 ASN1_C_UNIV
, CONS
, UT_Sequence
, &l
);
1217 content
.length
+= ph
;
1219 ret
= hx509_cms_unwrap_ContentInfo(&content
, &type2
, &out
, NULL
);
1223 if (der_heim_oid_cmp(&type2
, &asn1_oid_id_pkcs7_signedData
)) {
1224 ret
= EINVAL
; /* XXX */
1225 krb5_set_error_message(context
, ret
,
1226 N_("PKINIT: Invalid content type", ""));
1227 der_free_oid(&type2
);
1228 der_free_octet_string(&out
);
1231 der_free_oid(&type2
);
1232 krb5_data_free(&content
);
1233 ret
= krb5_data_copy(&content
, out
.data
, out
.length
);
1234 der_free_octet_string(&out
);
1236 krb5_set_error_message(context
, ret
,
1237 N_("malloc: out of memory", ""));
1242 ret
= pk_verify_sign(context
,
1252 /* make sure that it is the kdc's certificate */
1253 ret
= pk_verify_host(context
, realm
, hi
, ctx
, host
);
1259 if (type
== PKINIT_WIN2K
) {
1260 if (der_heim_oid_cmp(&contentType
, &asn1_oid_id_pkcs7_data
) != 0) {
1261 ret
= KRB5KRB_AP_ERR_MSG_TYPE
;
1262 krb5_set_error_message(context
, ret
, "PKINIT: reply key, wrong oid");
1266 if (der_heim_oid_cmp(&contentType
, &asn1_oid_id_pkrkeydata
) != 0) {
1267 ret
= KRB5KRB_AP_ERR_MSG_TYPE
;
1268 krb5_set_error_message(context
, ret
, "PKINIT: reply key, wrong oid");
1276 ret
= get_reply_key(context
, &content
, req_buffer
, key
);
1277 if (ret
!= 0 && ctx
->require_binding
== 0)
1278 ret
= get_reply_key_win(context
, &content
, nonce
, key
);
1281 ret
= get_reply_key(context
, &content
, req_buffer
, key
);
1287 /* XXX compare given etype with key->etype */
1291 _krb5_pk_cert_free(host
);
1292 der_free_oid(&contentType
);
1293 krb5_data_free(&content
);
1298 static krb5_error_code
1299 pk_rd_pa_reply_dh(krb5_context context
,
1300 const heim_octet_string
*indata
,
1301 const heim_oid
*dataType
,
1303 krb5_pk_init_ctx ctx
,
1305 const krb5_krbhst_info
*hi
,
1310 krb5_keyblock
**key
)
1312 const unsigned char *p
;
1313 unsigned char *dh_gen_key
= NULL
;
1314 struct krb5_pk_cert
*host
= NULL
;
1315 BIGNUM
*kdc_dh_pubkey
= NULL
;
1316 KDCDHKeyInfo kdc_dh_info
;
1317 heim_oid contentType
= { 0, NULL
};
1319 krb5_error_code ret
;
1320 int dh_gen_keylen
= 0;
1323 krb5_data_zero(&content
);
1324 memset(&kdc_dh_info
, 0, sizeof(kdc_dh_info
));
1326 if (der_heim_oid_cmp(&asn1_oid_id_pkcs7_signedData
, dataType
)) {
1327 krb5_set_error_message(context
, EINVAL
,
1328 N_("PKINIT: Invalid content type", ""));
1332 ret
= pk_verify_sign(context
,
1342 /* make sure that it is the kdc's certificate */
1343 ret
= pk_verify_host(context
, realm
, hi
, ctx
, host
);
1347 if (der_heim_oid_cmp(&contentType
, &asn1_oid_id_pkdhkeydata
)) {
1348 ret
= KRB5KRB_AP_ERR_MSG_TYPE
;
1349 krb5_set_error_message(context
, ret
,
1350 N_("pkinit - dh reply contains wrong oid", ""));
1354 ret
= decode_KDCDHKeyInfo(content
.data
,
1360 krb5_set_error_message(context
, ret
,
1361 N_("pkinit - failed to decode "
1362 "KDC DH Key Info", ""));
1366 if (kdc_dh_info
.nonce
!= nonce
) {
1367 ret
= KRB5KRB_AP_ERR_MODIFIED
;
1368 krb5_set_error_message(context
, ret
,
1369 N_("PKINIT: DH nonce is wrong", ""));
1373 if (kdc_dh_info
.dhKeyExpiration
) {
1375 ret
= KRB5KRB_ERR_GENERIC
;
1376 krb5_set_error_message(context
, ret
,
1377 N_("pkinit; got key expiration "
1378 "without server nonce", ""));
1382 ret
= KRB5KRB_ERR_GENERIC
;
1383 krb5_set_error_message(context
, ret
,
1384 N_("pkinit; got DH reuse but no "
1385 "client nonce", ""));
1390 ret
= KRB5KRB_ERR_GENERIC
;
1391 krb5_set_error_message(context
, ret
,
1392 N_("pkinit: got server nonce "
1393 "without key expiration", ""));
1400 p
= kdc_dh_info
.subjectPublicKey
.data
;
1401 size
= (kdc_dh_info
.subjectPublicKey
.length
+ 7) / 8;
1403 if (ctx
->keyex
== USE_DH
) {
1405 ret
= decode_DHPublicKey(p
, size
, &k
, NULL
);
1407 krb5_set_error_message(context
, ret
,
1408 N_("pkinit: can't decode "
1409 "without key expiration", ""));
1413 kdc_dh_pubkey
= integer_to_BN(context
, "DHPublicKey", &k
);
1414 free_DHPublicKey(&k
);
1415 if (kdc_dh_pubkey
== NULL
) {
1421 size
= DH_size(ctx
->u
.dh
);
1423 dh_gen_key
= malloc(size
);
1424 if (dh_gen_key
== NULL
) {
1426 krb5_set_error_message(context
, ret
, N_("malloc: out of memory", ""));
1430 dh_gen_keylen
= DH_compute_key(dh_gen_key
, kdc_dh_pubkey
, ctx
->u
.dh
);
1431 if (dh_gen_keylen
== -1) {
1432 ret
= KRB5KRB_ERR_GENERIC
;
1434 krb5_set_error_message(context
, ret
,
1435 N_("PKINIT: Can't compute Diffie-Hellman key", ""));
1438 if (dh_gen_keylen
< (int)size
) {
1439 size
-= dh_gen_keylen
;
1440 memmove(dh_gen_key
+ size
, dh_gen_key
, dh_gen_keylen
);
1441 memset(dh_gen_key
, 0, size
);
1446 const EC_GROUP
*group
;
1447 EC_KEY
*public = NULL
;
1449 group
= EC_KEY_get0_group(ctx
->u
.eckey
);
1451 public = EC_KEY_new();
1452 if (public == NULL
) {
1456 if (EC_KEY_set_group(public, group
) != 1) {
1457 EC_KEY_free(public);
1462 if (o2i_ECPublicKey(&public, &p
, size
) == NULL
) {
1463 EC_KEY_free(public);
1464 ret
= KRB5KRB_ERR_GENERIC
;
1465 krb5_set_error_message(context
, ret
,
1466 N_("PKINIT: Can't parse ECDH public key", ""));
1470 size
= (EC_GROUP_get_degree(group
) + 7) / 8;
1471 dh_gen_key
= malloc(size
);
1472 if (dh_gen_key
== NULL
) {
1473 EC_KEY_free(public);
1475 krb5_set_error_message(context
, ret
,
1476 N_("malloc: out of memory", ""));
1479 dh_gen_keylen
= ECDH_compute_key(dh_gen_key
, size
,
1480 EC_KEY_get0_public_key(public), ctx
->u
.eckey
, NULL
);
1481 EC_KEY_free(public);
1482 if (dh_gen_keylen
== -1) {
1483 ret
= KRB5KRB_ERR_GENERIC
;
1485 krb5_set_error_message(context
, ret
,
1486 N_("PKINIT: Can't compute ECDH public key", ""));
1494 if (dh_gen_keylen
<= 0) {
1496 krb5_set_error_message(context
, ret
,
1497 N_("PKINIT: resulting DH key <= 0", ""));
1502 *key
= malloc (sizeof (**key
));
1505 krb5_set_error_message(context
, ret
,
1506 N_("malloc: out of memory", ""));
1510 ret
= _krb5_pk_octetstring2key(context
,
1512 dh_gen_key
, dh_gen_keylen
,
1516 krb5_set_error_message(context
, ret
,
1517 N_("PKINIT: can't create key from DH key", ""));
1525 BN_free(kdc_dh_pubkey
);
1527 memset(dh_gen_key
, 0, dh_gen_keylen
);
1531 _krb5_pk_cert_free(host
);
1533 krb5_data_free(&content
);
1534 der_free_oid(&contentType
);
1535 free_KDCDHKeyInfo(&kdc_dh_info
);
1540 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1541 _krb5_pk_rd_pa_reply(krb5_context context
,
1545 const krb5_krbhst_info
*hi
,
1547 const krb5_data
*req_buffer
,
1549 krb5_keyblock
**key
)
1551 krb5_pk_init_ctx ctx
= c
;
1552 krb5_error_code ret
;
1555 /* Check for IETF PK-INIT first */
1556 if (ctx
->type
== PKINIT_27
) {
1558 heim_octet_string os
, data
;
1561 if (pa
->padata_type
!= KRB5_PADATA_PK_AS_REP
) {
1562 krb5_set_error_message(context
, EINVAL
,
1563 N_("PKINIT: wrong padata recv", ""));
1567 ret
= decode_PA_PK_AS_REP(pa
->padata_value
.data
,
1568 pa
->padata_value
.length
,
1572 krb5_set_error_message(context
, ret
,
1573 N_("Failed to decode pkinit AS rep", ""));
1577 switch (rep
.element
) {
1578 case choice_PA_PK_AS_REP_dhInfo
:
1579 _krb5_debug(context
, 5, "krb5_get_init_creds: using pkinit dh");
1580 os
= rep
.u
.dhInfo
.dhSignedData
;
1582 case choice_PA_PK_AS_REP_encKeyPack
:
1583 _krb5_debug(context
, 5, "krb5_get_init_creds: using kinit enc reply key");
1584 os
= rep
.u
.encKeyPack
;
1587 PA_PK_AS_REP_BTMM btmm
;
1588 free_PA_PK_AS_REP(&rep
);
1589 memset(&rep
, 0, sizeof(rep
));
1591 _krb5_debug(context
, 5, "krb5_get_init_creds: using BTMM kinit enc reply key");
1593 ret
= decode_PA_PK_AS_REP_BTMM(pa
->padata_value
.data
,
1594 pa
->padata_value
.length
,
1598 krb5_set_error_message(context
, EINVAL
,
1599 N_("PKINIT: -27 reply "
1600 "invalid content type", ""));
1604 if (btmm
.dhSignedData
|| btmm
.encKeyPack
== NULL
) {
1605 free_PA_PK_AS_REP_BTMM(&btmm
);
1607 krb5_set_error_message(context
, ret
,
1608 N_("DH mode not supported for BTMM mode", ""));
1613 * Transform to IETF style PK-INIT reply so that free works below
1616 rep
.element
= choice_PA_PK_AS_REP_encKeyPack
;
1617 rep
.u
.encKeyPack
.data
= btmm
.encKeyPack
->data
;
1618 rep
.u
.encKeyPack
.length
= btmm
.encKeyPack
->length
;
1619 btmm
.encKeyPack
->data
= NULL
;
1620 btmm
.encKeyPack
->length
= 0;
1621 free_PA_PK_AS_REP_BTMM(&btmm
);
1622 os
= rep
.u
.encKeyPack
;
1626 ret
= hx509_cms_unwrap_ContentInfo(&os
, &oid
, &data
, NULL
);
1628 free_PA_PK_AS_REP(&rep
);
1629 krb5_set_error_message(context
, ret
,
1630 N_("PKINIT: failed to unwrap CI", ""));
1634 switch (rep
.element
) {
1635 case choice_PA_PK_AS_REP_dhInfo
:
1636 ret
= pk_rd_pa_reply_dh(context
, &data
, &oid
, realm
, ctx
, etype
, hi
,
1638 rep
.u
.dhInfo
.serverDHNonce
,
1641 case choice_PA_PK_AS_REP_encKeyPack
:
1642 ret
= pk_rd_pa_reply_enckey(context
, PKINIT_27
, &data
, &oid
, realm
,
1643 ctx
, etype
, hi
, nonce
, req_buffer
, pa
, key
);
1646 krb5_abortx(context
, "pk-init as-rep case not possible to happen");
1648 der_free_octet_string(&data
);
1650 free_PA_PK_AS_REP(&rep
);
1652 } else if (ctx
->type
== PKINIT_WIN2K
) {
1653 PA_PK_AS_REP_Win2k w2krep
;
1655 /* Check for Windows encoding of the AS-REP pa data */
1657 #if 0 /* should this be ? */
1658 if (pa
->padata_type
!= KRB5_PADATA_PK_AS_REP
) {
1659 krb5_set_error_message(context
, EINVAL
,
1660 "PKINIT: wrong padata recv");
1665 memset(&w2krep
, 0, sizeof(w2krep
));
1667 ret
= decode_PA_PK_AS_REP_Win2k(pa
->padata_value
.data
,
1668 pa
->padata_value
.length
,
1672 krb5_set_error_message(context
, ret
,
1673 N_("PKINIT: Failed decoding windows "
1674 "pkinit reply %d", ""), (int)ret
);
1678 krb5_clear_error_message(context
);
1680 switch (w2krep
.element
) {
1681 case choice_PA_PK_AS_REP_Win2k_encKeyPack
: {
1682 heim_octet_string data
;
1685 ret
= hx509_cms_unwrap_ContentInfo(&w2krep
.u
.encKeyPack
,
1687 free_PA_PK_AS_REP_Win2k(&w2krep
);
1689 krb5_set_error_message(context
, ret
,
1690 N_("PKINIT: failed to unwrap CI", ""));
1694 ret
= pk_rd_pa_reply_enckey(context
, PKINIT_WIN2K
, &data
, &oid
, realm
,
1695 ctx
, etype
, hi
, nonce
, req_buffer
, pa
, key
);
1696 der_free_octet_string(&data
);
1702 free_PA_PK_AS_REP_Win2k(&w2krep
);
1704 krb5_set_error_message(context
, ret
,
1705 N_("PKINIT: win2k reply invalid "
1706 "content type", ""));
1712 krb5_set_error_message(context
, ret
,
1713 N_("PKINIT: unknown reply type", ""));
1720 krb5_context context
;
1721 krb5_prompter_fct prompter
;
1722 void *prompter_data
;
1726 hx_pass_prompter(void *data
, const hx509_prompt
*prompter
)
1728 krb5_error_code ret
;
1730 krb5_data password_data
;
1731 struct prompter
*p
= data
;
1733 password_data
.data
= prompter
->reply
.data
;
1734 password_data
.length
= prompter
->reply
.length
;
1736 prompt
.prompt
= prompter
->prompt
;
1737 prompt
.hidden
= hx509_prompt_hidden(prompter
->type
);
1738 prompt
.reply
= &password_data
;
1740 switch (prompter
->type
) {
1741 case HX509_PROMPT_TYPE_INFO
:
1742 prompt
.type
= KRB5_PROMPT_TYPE_INFO
;
1744 case HX509_PROMPT_TYPE_PASSWORD
:
1745 case HX509_PROMPT_TYPE_QUESTION
:
1747 prompt
.type
= KRB5_PROMPT_TYPE_PASSWORD
;
1751 ret
= (*p
->prompter
)(p
->context
, p
->prompter_data
, NULL
, NULL
, 1, &prompt
);
1753 memset (prompter
->reply
.data
, 0, prompter
->reply
.length
);
1759 static krb5_error_code
1760 _krb5_pk_set_user_id(krb5_context context
,
1761 krb5_principal principal
,
1762 krb5_pk_init_ctx ctx
,
1763 struct hx509_certs_data
*certs
)
1765 hx509_certs c
= hx509_certs_ref(certs
);
1766 hx509_query
*q
= NULL
;
1770 hx509_certs_free(&ctx
->id
->certs
);
1771 if (ctx
->id
->cert
) {
1772 hx509_cert_free(ctx
->id
->cert
);
1773 ctx
->id
->cert
= NULL
;
1779 ret
= hx509_query_alloc(context
->hx509ctx
, &q
);
1781 pk_copy_error(context
, context
->hx509ctx
, ret
,
1782 "Allocate query to find signing certificate");
1786 hx509_query_match_option(q
, HX509_QUERY_OPTION_PRIVATE_KEY
);
1787 hx509_query_match_option(q
, HX509_QUERY_OPTION_KU_DIGITALSIGNATURE
);
1789 if (principal
&& strncmp("LKDC:SHA1.", krb5_principal_get_realm(context
, principal
), 9) == 0) {
1790 ctx
->id
->flags
|= PKINIT_BTMM
;
1793 ret
= find_cert(context
, ctx
->id
, q
, &ctx
->id
->cert
);
1794 hx509_query_free(context
->hx509ctx
, q
);
1796 if (ret
== 0 && _krb5_have_debug(context
, 2)) {
1801 ret
= hx509_cert_get_subject(ctx
->id
->cert
, &name
);
1805 ret
= hx509_name_to_string(name
, &str
);
1806 hx509_name_free(&name
);
1810 ret
= hx509_cert_get_serialnumber(ctx
->id
->cert
, &i
);
1816 ret
= der_print_hex_heim_integer(&i
, &sn
);
1817 der_free_heim_integer(&i
);
1823 _krb5_debug(context
, 2, "using cert: subject: %s sn: %s", str
, sn
);
1832 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1833 _krb5_pk_load_id(krb5_context context
,
1834 struct krb5_pk_identity
**ret_id
,
1835 const char *user_id
,
1836 const char *anchor_id
,
1837 char * const *chain_list
,
1838 char * const *revoke_list
,
1839 krb5_prompter_fct prompter
,
1840 void *prompter_data
,
1843 struct krb5_pk_identity
*id
= NULL
;
1849 if (anchor_id
== NULL
) {
1850 krb5_set_error_message(context
, HEIM_PKINIT_NO_VALID_CA
,
1851 N_("PKINIT: No anchor given", ""));
1852 return HEIM_PKINIT_NO_VALID_CA
;
1857 id
= calloc(1, sizeof(*id
));
1859 krb5_set_error_message(context
, ENOMEM
,
1860 N_("malloc: out of memory", ""));
1867 ret
= hx509_lock_init(context
->hx509ctx
, &lock
);
1869 pk_copy_error(context
, context
->hx509ctx
, ret
, "Failed init lock");
1873 if (password
&& password
[0])
1874 hx509_lock_add_password(lock
, password
);
1877 p
.context
= context
;
1878 p
.prompter
= prompter
;
1879 p
.prompter_data
= prompter_data
;
1881 ret
= hx509_lock_set_prompter(lock
, hx_pass_prompter
, &p
);
1883 hx509_lock_free(lock
);
1888 ret
= hx509_certs_init(context
->hx509ctx
, user_id
, 0, lock
, &id
->certs
);
1889 hx509_lock_free(lock
);
1891 pk_copy_error(context
, context
->hx509ctx
, ret
,
1892 "Failed to init cert certs");
1899 ret
= hx509_certs_init(context
->hx509ctx
, anchor_id
, 0, NULL
, &id
->anchors
);
1901 pk_copy_error(context
, context
->hx509ctx
, ret
,
1902 "Failed to init anchors");
1906 ret
= hx509_certs_init(context
->hx509ctx
, "MEMORY:pkinit-cert-chain",
1907 0, NULL
, &id
->certpool
);
1909 pk_copy_error(context
, context
->hx509ctx
, ret
,
1910 "Failed to init chain");
1914 while (chain_list
&& *chain_list
) {
1915 ret
= hx509_certs_append(context
->hx509ctx
, id
->certpool
,
1918 pk_copy_error(context
, context
->hx509ctx
, ret
,
1919 "Failed to laod chain %s",
1927 ret
= hx509_revoke_init(context
->hx509ctx
, &id
->revokectx
);
1929 pk_copy_error(context
, context
->hx509ctx
, ret
,
1930 "Failed init revoke list");
1934 while (*revoke_list
) {
1935 ret
= hx509_revoke_add_crl(context
->hx509ctx
,
1939 pk_copy_error(context
, context
->hx509ctx
, ret
,
1940 "Failed load revoke list");
1946 hx509_context_set_missing_revoke(context
->hx509ctx
, 1);
1948 ret
= hx509_verify_init_ctx(context
->hx509ctx
, &id
->verify_ctx
);
1950 pk_copy_error(context
, context
->hx509ctx
, ret
,
1951 "Failed init verify context");
1955 hx509_verify_attach_anchors(id
->verify_ctx
, id
->anchors
);
1956 hx509_verify_attach_revoke(id
->verify_ctx
, id
->revokectx
);
1960 hx509_verify_destroy_ctx(id
->verify_ctx
);
1961 hx509_certs_free(&id
->certs
);
1962 hx509_certs_free(&id
->anchors
);
1963 hx509_certs_free(&id
->certpool
);
1964 hx509_revoke_free(&id
->revokectx
);
1977 pk_copy_error(krb5_context context
,
1978 hx509_context hx509ctx
,
1988 ret
= vasprintf(&f
, fmt
, va
);
1990 if (ret
== -1 || f
== NULL
) {
1991 krb5_clear_error_message(context
);
1995 s
= hx509_get_error_string(hx509ctx
, hxret
);
1997 krb5_clear_error_message(context
);
2001 krb5_set_error_message(context
, hxret
, "%s: %s", f
, s
);
2007 parse_integer(krb5_context context
, char **p
, const char *file
, int lineno
,
2008 const char *name
, heim_integer
*integer
)
2012 p1
= strsep(p
, " \t");
2014 krb5_set_error_message(context
, EINVAL
,
2015 N_("moduli file %s missing %s on line %d", ""),
2016 file
, name
, lineno
);
2019 ret
= der_parse_hex_heim_integer(p1
, integer
);
2021 krb5_set_error_message(context
, ret
,
2022 N_("moduli file %s failed parsing %s "
2024 file
, name
, lineno
);
2032 _krb5_parse_moduli_line(krb5_context context
,
2036 struct krb5_dh_moduli
**m
)
2038 struct krb5_dh_moduli
*m1
;
2044 m1
= calloc(1, sizeof(*m1
));
2046 krb5_set_error_message(context
, ENOMEM
,
2047 N_("malloc: out of memory", ""));
2051 while (isspace((unsigned char)*p
))
2059 p1
= strsep(&p
, " \t");
2061 krb5_set_error_message(context
, ret
,
2062 N_("moduli file %s missing name on line %d", ""),
2066 m1
->name
= strdup(p1
);
2067 if (m1
->name
== NULL
) {
2069 krb5_set_error_message(context
, ret
, N_("malloc: out of memeory", ""));
2073 p1
= strsep(&p
, " \t");
2075 krb5_set_error_message(context
, ret
,
2076 N_("moduli file %s missing bits on line %d", ""),
2081 m1
->bits
= atoi(p1
);
2082 if (m1
->bits
== 0) {
2083 krb5_set_error_message(context
, ret
,
2084 N_("moduli file %s have un-parsable "
2085 "bits on line %d", ""), file
, lineno
);
2089 ret
= parse_integer(context
, &p
, file
, lineno
, "p", &m1
->p
);
2092 ret
= parse_integer(context
, &p
, file
, lineno
, "g", &m1
->g
);
2095 ret
= parse_integer(context
, &p
, file
, lineno
, "q", &m1
->q
);
2104 der_free_heim_integer(&m1
->p
);
2105 der_free_heim_integer(&m1
->g
);
2106 der_free_heim_integer(&m1
->q
);
2112 _krb5_free_moduli(struct krb5_dh_moduli
**moduli
)
2115 for (i
= 0; moduli
[i
] != NULL
; i
++) {
2116 free(moduli
[i
]->name
);
2117 der_free_heim_integer(&moduli
[i
]->p
);
2118 der_free_heim_integer(&moduli
[i
]->g
);
2119 der_free_heim_integer(&moduli
[i
]->q
);
2125 static const char *default_moduli_RFC2412_MODP_group2
=
2127 "RFC2412-MODP-group2 "
2131 "FFFFFFFF" "FFFFFFFF" "C90FDAA2" "2168C234" "C4C6628B" "80DC1CD1"
2132 "29024E08" "8A67CC74" "020BBEA6" "3B139B22" "514A0879" "8E3404DD"
2133 "EF9519B3" "CD3A431B" "302B0A6D" "F25F1437" "4FE1356D" "6D51C245"
2134 "E485B576" "625E7EC6" "F44C42E9" "A637ED6B" "0BFF5CB6" "F406B7ED"
2135 "EE386BFB" "5A899FA5" "AE9F2411" "7C4B1FE6" "49286651" "ECE65381"
2136 "FFFFFFFF" "FFFFFFFF "
2140 "7FFFFFFF" "FFFFFFFF" "E487ED51" "10B4611A" "62633145" "C06E0E68"
2141 "94812704" "4533E63A" "0105DF53" "1D89CD91" "28A5043C" "C71A026E"
2142 "F7CA8CD9" "E69D218D" "98158536" "F92F8A1B" "A7F09AB6" "B6A8E122"
2143 "F242DABB" "312F3F63" "7A262174" "D31BF6B5" "85FFAE5B" "7A035BF6"
2144 "F71C35FD" "AD44CFD2" "D74F9208" "BE258FF3" "24943328" "F67329C0"
2145 "FFFFFFFF" "FFFFFFFF";
2147 static const char *default_moduli_rfc3526_MODP_group14
=
2149 "rfc3526-MODP-group14 "
2153 "FFFFFFFF" "FFFFFFFF" "C90FDAA2" "2168C234" "C4C6628B" "80DC1CD1"
2154 "29024E08" "8A67CC74" "020BBEA6" "3B139B22" "514A0879" "8E3404DD"
2155 "EF9519B3" "CD3A431B" "302B0A6D" "F25F1437" "4FE1356D" "6D51C245"
2156 "E485B576" "625E7EC6" "F44C42E9" "A637ED6B" "0BFF5CB6" "F406B7ED"
2157 "EE386BFB" "5A899FA5" "AE9F2411" "7C4B1FE6" "49286651" "ECE45B3D"
2158 "C2007CB8" "A163BF05" "98DA4836" "1C55D39A" "69163FA8" "FD24CF5F"
2159 "83655D23" "DCA3AD96" "1C62F356" "208552BB" "9ED52907" "7096966D"
2160 "670C354E" "4ABC9804" "F1746C08" "CA18217C" "32905E46" "2E36CE3B"
2161 "E39E772C" "180E8603" "9B2783A2" "EC07A28F" "B5C55DF0" "6F4C52C9"
2162 "DE2BCBF6" "95581718" "3995497C" "EA956AE5" "15D22618" "98FA0510"
2163 "15728E5A" "8AACAA68" "FFFFFFFF" "FFFFFFFF "
2167 "7FFFFFFF" "FFFFFFFF" "E487ED51" "10B4611A" "62633145" "C06E0E68"
2168 "94812704" "4533E63A" "0105DF53" "1D89CD91" "28A5043C" "C71A026E"
2169 "F7CA8CD9" "E69D218D" "98158536" "F92F8A1B" "A7F09AB6" "B6A8E122"
2170 "F242DABB" "312F3F63" "7A262174" "D31BF6B5" "85FFAE5B" "7A035BF6"
2171 "F71C35FD" "AD44CFD2" "D74F9208" "BE258FF3" "24943328" "F6722D9E"
2172 "E1003E5C" "50B1DF82" "CC6D241B" "0E2AE9CD" "348B1FD4" "7E9267AF"
2173 "C1B2AE91" "EE51D6CB" "0E3179AB" "1042A95D" "CF6A9483" "B84B4B36"
2174 "B3861AA7" "255E4C02" "78BA3604" "650C10BE" "19482F23" "171B671D"
2175 "F1CF3B96" "0C074301" "CD93C1D1" "7603D147" "DAE2AEF8" "37A62964"
2176 "EF15E5FB" "4AAC0B8C" "1CCAA4BE" "754AB572" "8AE9130C" "4C7D0288"
2177 "0AB9472D" "45565534" "7FFFFFFF" "FFFFFFFF";
2180 _krb5_parse_moduli(krb5_context context
, const char *file
,
2181 struct krb5_dh_moduli
***moduli
)
2183 /* name bits P G Q */
2184 krb5_error_code ret
;
2185 struct krb5_dh_moduli
**m
= NULL
, **m2
;
2188 int lineno
= 0, n
= 0;
2192 m
= calloc(1, sizeof(m
[0]) * 3);
2194 krb5_set_error_message(context
, ENOMEM
,
2195 N_("malloc: out of memory", ""));
2199 strlcpy(buf
, default_moduli_rfc3526_MODP_group14
, sizeof(buf
));
2200 ret
= _krb5_parse_moduli_line(context
, "builtin", 1, buf
, &m
[0]);
2202 _krb5_free_moduli(m
);
2207 strlcpy(buf
, default_moduli_RFC2412_MODP_group2
, sizeof(buf
));
2208 ret
= _krb5_parse_moduli_line(context
, "builtin", 1, buf
, &m
[1]);
2210 _krb5_free_moduli(m
);
2219 #ifdef KRB5_USE_PATH_TOKENS
2223 if (_krb5_expand_path_tokens(context
, file
, &exp_file
) == 0) {
2224 f
= fopen(exp_file
, "r");
2225 krb5_xfree(exp_file
);
2231 f
= fopen(file
, "r");
2240 while(fgets(buf
, sizeof(buf
), f
) != NULL
) {
2241 struct krb5_dh_moduli
*element
;
2243 buf
[strcspn(buf
, "\n")] = '\0';
2246 m2
= realloc(m
, (n
+ 2) * sizeof(m
[0]));
2248 _krb5_free_moduli(m
);
2249 krb5_set_error_message(context
, ENOMEM
,
2250 N_("malloc: out of memory", ""));
2257 ret
= _krb5_parse_moduli_line(context
, file
, lineno
, buf
, &element
);
2259 _krb5_free_moduli(m
);
2262 if (element
== NULL
)
2274 _krb5_dh_group_ok(krb5_context context
, unsigned long bits
,
2275 heim_integer
*p
, heim_integer
*g
, heim_integer
*q
,
2276 struct krb5_dh_moduli
**moduli
,
2284 for (i
= 0; moduli
[i
] != NULL
; i
++) {
2285 if (der_heim_integer_cmp(&moduli
[i
]->g
, g
) == 0 &&
2286 der_heim_integer_cmp(&moduli
[i
]->p
, p
) == 0 &&
2287 (q
== NULL
|| der_heim_integer_cmp(&moduli
[i
]->q
, q
) == 0))
2289 if (bits
&& bits
> moduli
[i
]->bits
) {
2290 krb5_set_error_message(context
,
2291 KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED
,
2292 N_("PKINIT: DH group parameter %s "
2293 "no accepted, not enough bits "
2296 return KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED
;
2299 *name
= strdup(moduli
[i
]->name
);
2303 krb5_set_error_message(context
,
2304 KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED
,
2305 N_("PKINIT: DH group parameter no ok", ""));
2306 return KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED
;
2310 KRB5_LIB_FUNCTION
void KRB5_LIB_CALL
2311 _krb5_get_init_creds_opt_free_pkinit(krb5_get_init_creds_opt
*opt
)
2314 krb5_pk_init_ctx ctx
;
2316 if (opt
->opt_private
== NULL
|| opt
->opt_private
->pk_init_ctx
== NULL
)
2318 ctx
= opt
->opt_private
->pk_init_ctx
;
2319 switch (ctx
->keyex
) {
2329 EC_KEY_free(ctx
->u
.eckey
);
2334 hx509_verify_destroy_ctx(ctx
->id
->verify_ctx
);
2335 hx509_certs_free(&ctx
->id
->certs
);
2336 hx509_cert_free(ctx
->id
->cert
);
2337 hx509_certs_free(&ctx
->id
->anchors
);
2338 hx509_certs_free(&ctx
->id
->certpool
);
2340 if (ctx
->clientDHNonce
) {
2341 krb5_free_data(NULL
, ctx
->clientDHNonce
);
2342 ctx
->clientDHNonce
= NULL
;
2345 _krb5_free_moduli(ctx
->m
);
2349 free(opt
->opt_private
->pk_init_ctx
);
2350 opt
->opt_private
->pk_init_ctx
= NULL
;
2354 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2355 krb5_get_init_creds_opt_set_pkinit(krb5_context context
,
2356 krb5_get_init_creds_opt
*opt
,
2357 krb5_principal principal
,
2358 const char *user_id
,
2359 const char *x509_anchors
,
2360 char * const * pool
,
2361 char * const * pki_revoke
,
2363 krb5_prompter_fct prompter
,
2364 void *prompter_data
,
2368 krb5_error_code ret
;
2369 char *anchors
= NULL
;
2371 if (opt
->opt_private
== NULL
) {
2372 krb5_set_error_message(context
, EINVAL
,
2373 N_("PKINIT: on non extendable opt", ""));
2377 opt
->opt_private
->pk_init_ctx
=
2378 calloc(1, sizeof(*opt
->opt_private
->pk_init_ctx
));
2379 if (opt
->opt_private
->pk_init_ctx
== NULL
) {
2380 krb5_set_error_message(context
, ENOMEM
,
2381 N_("malloc: out of memory", ""));
2384 opt
->opt_private
->pk_init_ctx
->require_binding
= 0;
2385 opt
->opt_private
->pk_init_ctx
->require_eku
= 1;
2386 opt
->opt_private
->pk_init_ctx
->require_krbtgt_otherName
= 1;
2387 opt
->opt_private
->pk_init_ctx
->peer
= NULL
;
2389 /* XXX implement krb5_appdefault_strings */
2391 pool
= krb5_config_get_strings(context
, NULL
,
2396 if (pki_revoke
== NULL
)
2397 pki_revoke
= krb5_config_get_strings(context
, NULL
,
2402 if (x509_anchors
== NULL
) {
2403 krb5_appdefault_string(context
, "kinit",
2404 krb5_principal_get_realm(context
, principal
),
2405 "pkinit_anchors", NULL
, &anchors
);
2406 x509_anchors
= anchors
;
2410 opt
->opt_private
->pk_init_ctx
->anonymous
= 1;
2412 ret
= _krb5_pk_load_id(context
,
2413 &opt
->opt_private
->pk_init_ctx
->id
,
2422 free(opt
->opt_private
->pk_init_ctx
);
2423 opt
->opt_private
->pk_init_ctx
= NULL
;
2427 if (opt
->opt_private
->pk_init_ctx
->id
->certs
) {
2428 _krb5_pk_set_user_id(context
,
2430 opt
->opt_private
->pk_init_ctx
,
2431 opt
->opt_private
->pk_init_ctx
->id
->certs
);
2433 opt
->opt_private
->pk_init_ctx
->id
->cert
= NULL
;
2435 if ((flags
& 2) == 0) {
2436 hx509_context hx509ctx
= context
->hx509ctx
;
2437 hx509_cert cert
= opt
->opt_private
->pk_init_ctx
->id
->cert
;
2439 opt
->opt_private
->pk_init_ctx
->keyex
= USE_DH
;
2442 * If its a ECDSA certs, lets select ECDSA as the keyex algorithm.
2445 AlgorithmIdentifier alg
;
2447 ret
= hx509_cert_get_SPKI_AlgorithmIdentifier(hx509ctx
, cert
, &alg
);
2449 if (der_heim_oid_cmp(&alg
.algorithm
, &asn1_oid_id_ecPublicKey
) == 0)
2450 opt
->opt_private
->pk_init_ctx
->keyex
= USE_ECDH
;
2451 free_AlgorithmIdentifier(&alg
);
2456 opt
->opt_private
->pk_init_ctx
->keyex
= USE_RSA
;
2458 if (opt
->opt_private
->pk_init_ctx
->id
->certs
== NULL
) {
2459 krb5_set_error_message(context
, EINVAL
,
2460 N_("No anonymous pkinit support in RSA mode", ""));
2467 krb5_set_error_message(context
, EINVAL
,
2468 N_("no support for PKINIT compiled in", ""));
2473 krb5_error_code KRB5_LIB_FUNCTION
2474 krb5_get_init_creds_opt_set_pkinit_user_certs(krb5_context context
,
2475 krb5_get_init_creds_opt
*opt
,
2476 struct hx509_certs_data
*certs
)
2479 if (opt
->opt_private
== NULL
) {
2480 krb5_set_error_message(context
, EINVAL
,
2481 N_("PKINIT: on non extendable opt", ""));
2484 if (opt
->opt_private
->pk_init_ctx
== NULL
) {
2485 krb5_set_error_message(context
, EINVAL
,
2486 N_("PKINIT: on pkinit context", ""));
2490 _krb5_pk_set_user_id(context
, NULL
, opt
->opt_private
->pk_init_ctx
, certs
);
2494 krb5_set_error_message(context
, EINVAL
,
2495 N_("no support for PKINIT compiled in", ""));
2503 get_ms_san(hx509_context context
, hx509_cert cert
, char **upn
)
2505 hx509_octet_string_list list
;
2510 ret
= hx509_cert_find_subjectAltName_otherName(context
,
2512 &asn1_oid_id_pkinit_ms_san
,
2517 if (list
.len
> 0 && list
.val
[0].length
> 0)
2518 ret
= decode_MS_UPN_SAN(list
.val
[0].data
, list
.val
[0].length
,
2522 hx509_free_octet_string_list(&list
);
2528 find_ms_san(hx509_context context
, hx509_cert cert
, void *ctx
)
2533 ret
= get_ms_san(context
, cert
, &upn
);
2544 * Private since it need to be redesigned using krb5_get_init_creds()
2547 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2548 krb5_pk_enterprise_cert(krb5_context context
,
2549 const char *user_id
,
2550 krb5_const_realm realm
,
2551 krb5_principal
*principal
,
2552 struct hx509_certs_data
**res
)
2555 krb5_error_code ret
;
2556 hx509_certs certs
, result
;
2557 hx509_cert cert
= NULL
;
2565 if (user_id
== NULL
) {
2566 krb5_set_error_message(context
, ENOENT
, "no user id");
2570 ret
= hx509_certs_init(context
->hx509ctx
, user_id
, 0, NULL
, &certs
);
2572 pk_copy_error(context
, context
->hx509ctx
, ret
,
2573 "Failed to init cert certs");
2577 ret
= hx509_query_alloc(context
->hx509ctx
, &q
);
2579 krb5_set_error_message(context
, ret
, "out of memory");
2580 hx509_certs_free(&certs
);
2584 hx509_query_match_option(q
, HX509_QUERY_OPTION_PRIVATE_KEY
);
2585 hx509_query_match_option(q
, HX509_QUERY_OPTION_KU_DIGITALSIGNATURE
);
2586 hx509_query_match_eku(q
, &asn1_oid_id_pkinit_ms_eku
);
2587 hx509_query_match_cmp_func(q
, find_ms_san
, NULL
);
2589 ret
= hx509_certs_filter(context
->hx509ctx
, certs
, q
, &result
);
2590 hx509_query_free(context
->hx509ctx
, q
);
2591 hx509_certs_free(&certs
);
2593 pk_copy_error(context
, context
->hx509ctx
, ret
,
2594 "Failed to find PKINIT certificate");
2598 ret
= hx509_get_one_cert(context
->hx509ctx
, result
, &cert
);
2599 hx509_certs_free(&result
);
2601 pk_copy_error(context
, context
->hx509ctx
, ret
,
2602 "Failed to get one cert");
2606 ret
= get_ms_san(context
->hx509ctx
, cert
, &name
);
2608 pk_copy_error(context
, context
->hx509ctx
, ret
,
2609 "Failed to get MS SAN");
2613 ret
= krb5_make_principal(context
, principal
, realm
, name
, NULL
);
2618 krb5_principal_set_type(context
, *principal
, KRB5_NT_ENTERPRISE_PRINCIPAL
);
2621 ret
= hx509_certs_init(context
->hx509ctx
, "MEMORY:", 0, NULL
, res
);
2625 ret
= hx509_certs_add(context
->hx509ctx
, *res
, cert
);
2627 hx509_certs_free(res
);
2633 hx509_cert_free(cert
);
2637 krb5_set_error_message(context
, EINVAL
,
2638 N_("no support for PKINIT compiled in", ""));