2 * Copyright (c) 2003 - 2016 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
6 * Portions Copyright (c) 2009 Apple Inc. All rights reserved.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
19 * 3. Neither the name of the Institute nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
23 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
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>
62 pk_copy_error(krb5_context context,
63 hx509_context hx509ctx,
67 __attribute__ ((__format__ (__printf__, 4, 5)));
73 KRB5_LIB_FUNCTION void KRB5_LIB_CALL
74 _krb5_pk_cert_free(struct krb5_pk_cert *cert)
77 hx509_cert_free(cert->cert);
82 static krb5_error_code
83 BN_to_integer(krb5_context context, BIGNUM *bn, heim_integer *integer)
85 integer->length = BN_num_bytes(bn);
86 integer->data = malloc(integer->length);
87 if (integer->data == NULL) {
88 krb5_clear_error_message(context);
91 BN_bn2bin(bn, integer->data);
92 integer->negative = BN_is_negative(bn);
97 integer_to_BN(krb5_context context, const char *field, const heim_integer *f)
101 bn = BN_bin2bn((const unsigned char *)f->data, f->length, NULL);
103 krb5_set_error_message(context, ENOMEM,
104 N_("PKINIT: parsing BN failed %s", ""), field);
107 BN_set_negative(bn, f->negative);
111 static krb5_error_code
112 select_dh_group(krb5_context context, DH *dh, unsigned long bits,
113 struct krb5_dh_moduli **moduli)
115 const struct krb5_dh_moduli *m;
118 m = moduli[1]; /* XXX */
120 m = moduli[0]; /* XXX */
123 for (i = 0; moduli[i] != NULL; i++) {
124 if (bits < moduli[i]->bits)
127 if (moduli[i] == NULL) {
128 krb5_set_error_message(context, EINVAL,
129 N_("Did not find a DH group parameter "
130 "matching requirement of %lu bits", ""),
137 dh->p = integer_to_BN(context, "p", &m->p);
140 dh->g = integer_to_BN(context, "g", &m->g);
143 dh->q = integer_to_BN(context, "q", &m->q);
156 * Try searchin the key by to use by first looking for for PK-INIT
157 * EKU, then the Microsoft smart card EKU and last, no special EKU at all.
160 static krb5_error_code
161 find_cert(krb5_context context, struct krb5_pk_identity *id,
162 hx509_query *q, hx509_cert *cert)
164 struct certfind cf[4] = {
165 { "MobileMe EKU", NULL },
166 { "PKINIT EKU", NULL },
168 { "any (or no)", NULL }
170 int ret = HX509_CERT_NOT_FOUND;
172 unsigned oids[] = { 1, 2, 840, 113635, 100, 3, 2, 1 };
173 const heim_oid mobileMe = { sizeof(oids)/sizeof(oids[0]), oids };
176 if (id->flags & PKINIT_BTMM)
179 cf[0].oid = &mobileMe;
180 cf[1].oid = &asn1_oid_id_pkekuoid;
181 cf[2].oid = &asn1_oid_id_pkinit_ms_eku;
184 for (i = start; i < sizeof(cf)/sizeof(cf[0]); i++) {
185 ret = hx509_query_match_eku(q, cf[i].oid);
187 pk_copy_error(context, context->hx509ctx, ret,
188 "Failed setting %s OID", cf[i].type);
192 ret = hx509_certs_find(context->hx509ctx, id->certs, q, cert);
195 pk_copy_error(context, context->hx509ctx, ret,
196 "Failed finding certificate with %s OID", cf[i].type);
202 static krb5_error_code
203 create_signature(krb5_context context,
204 const heim_oid *eContentType,
206 struct krb5_pk_identity *id,
207 hx509_peer_info peer,
212 if (id->cert == NULL)
213 flags |= HX509_CMS_SIGNATURE_NO_SIGNER;
215 ret = hx509_cms_create_signed_1(context->hx509ctx,
227 pk_copy_error(context, context->hx509ctx, ret,
228 "Create CMS signedData");
235 static int KRB5_LIB_CALL
236 cert2epi(hx509_context context, void *ctx, hx509_cert c)
238 ExternalPrincipalIdentifiers *ids = ctx;
239 ExternalPrincipalIdentifier id;
240 hx509_name subject = NULL;
247 memset(&id, 0, sizeof(id));
249 ret = hx509_cert_get_subject(c, &subject);
253 if (hx509_name_is_null_p(subject) != 0) {
255 id.subjectName = calloc(1, sizeof(*id.subjectName));
256 if (id.subjectName == NULL) {
257 hx509_name_free(&subject);
258 free_ExternalPrincipalIdentifier(&id);
262 ret = hx509_name_binary(subject, id.subjectName);
264 hx509_name_free(&subject);
265 free_ExternalPrincipalIdentifier(&id);
269 hx509_name_free(&subject);
272 id.issuerAndSerialNumber = calloc(1, sizeof(*id.issuerAndSerialNumber));
273 if (id.issuerAndSerialNumber == NULL) {
274 free_ExternalPrincipalIdentifier(&id);
279 IssuerAndSerialNumber iasn;
283 memset(&iasn, 0, sizeof(iasn));
285 ret = hx509_cert_get_issuer(c, &issuer);
287 free_ExternalPrincipalIdentifier(&id);
291 ret = hx509_name_to_Name(issuer, &iasn.issuer);
292 hx509_name_free(&issuer);
294 free_ExternalPrincipalIdentifier(&id);
298 ret = hx509_cert_get_serialnumber(c, &iasn.serialNumber);
300 free_IssuerAndSerialNumber(&iasn);
301 free_ExternalPrincipalIdentifier(&id);
305 ASN1_MALLOC_ENCODE(IssuerAndSerialNumber,
306 id.issuerAndSerialNumber->data,
307 id.issuerAndSerialNumber->length,
309 free_IssuerAndSerialNumber(&iasn);
311 free_ExternalPrincipalIdentifier(&id);
314 if (id.issuerAndSerialNumber->length != size)
318 id.subjectKeyIdentifier = NULL;
320 p = realloc(ids->val, sizeof(ids->val[0]) * (ids->len + 1));
322 free_ExternalPrincipalIdentifier(&id);
327 ids->val[ids->len] = id;
333 static krb5_error_code
334 build_edi(krb5_context context,
335 hx509_context hx509ctx,
337 ExternalPrincipalIdentifiers *ids)
339 return hx509_certs_iter_f(hx509ctx, certs, cert2epi, ids);
342 static krb5_error_code
343 build_auth_pack(krb5_context context,
345 krb5_pk_init_ctx ctx,
346 const KDC_REQ_BODY *body,
349 size_t buf_size, len = 0;
356 krb5_clear_error_message(context);
358 memset(&checksum, 0, sizeof(checksum));
360 krb5_us_timeofday(context, &sec, &usec);
361 a->pkAuthenticator.ctime = sec;
362 a->pkAuthenticator.nonce = nonce;
364 ASN1_MALLOC_ENCODE(KDC_REQ_BODY, buf, buf_size, body, &len, ret);
368 krb5_abortx(context, "internal error in ASN.1 encoder");
370 ret = krb5_create_checksum(context,
381 ALLOC(a->pkAuthenticator.paChecksum, 1);
382 if (a->pkAuthenticator.paChecksum == NULL) {
383 return krb5_enomem(context);
386 ret = krb5_data_copy(a->pkAuthenticator.paChecksum,
387 checksum.checksum.data, checksum.checksum.length);
388 free_Checksum(&checksum);
392 if (ctx->keyex == USE_DH || ctx->keyex == USE_ECDH) {
393 const char *moduli_file;
394 unsigned long dh_min_bits;
398 krb5_data_zero(&dhbuf);
402 moduli_file = krb5_config_get_string(context, NULL,
408 krb5_config_get_int_default(context, NULL, 0,
410 "pkinit_dh_min_bits",
413 ret = _krb5_parse_moduli(context, moduli_file, &ctx->m);
417 ctx->u.dh = DH_new();
418 if (ctx->u.dh == NULL)
419 return krb5_enomem(context);
421 ret = select_dh_group(context, ctx->u.dh, dh_min_bits, ctx->m);
425 if (DH_generate_key(ctx->u.dh) != 1) {
426 krb5_set_error_message(context, ENOMEM,
427 N_("pkinit: failed to generate DH key", ""));
432 if (1 /* support_cached_dh */) {
433 ALLOC(a->clientDHNonce, 1);
434 if (a->clientDHNonce == NULL) {
435 krb5_clear_error_message(context);
438 ret = krb5_data_alloc(a->clientDHNonce, 40);
439 if (a->clientDHNonce == NULL) {
440 krb5_clear_error_message(context);
443 RAND_bytes(a->clientDHNonce->data, a->clientDHNonce->length);
444 ret = krb5_copy_data(context, a->clientDHNonce,
445 &ctx->clientDHNonce);
450 ALLOC(a->clientPublicValue, 1);
451 if (a->clientPublicValue == NULL)
454 if (ctx->keyex == USE_DH) {
457 heim_integer dh_pub_key;
459 ret = der_copy_oid(&asn1_oid_id_dhpublicnumber,
460 &a->clientPublicValue->algorithm.algorithm);
464 memset(&dp, 0, sizeof(dp));
466 ret = BN_to_integer(context, dh->p, &dp.p);
468 free_DomainParameters(&dp);
471 ret = BN_to_integer(context, dh->g, &dp.g);
473 free_DomainParameters(&dp);
476 if (dh->q && BN_num_bits(dh->q)) {
478 * The q parameter is required, but MSFT made it optional.
479 * It's only required in order to verify the domain parameters
480 * -- the security of the DH group --, but we validate groups
481 * against known groups rather than accepting arbitrary groups
482 * chosen by the peer, so we really don't need to have put it
483 * on the wire. Because these are Oakley groups, and the
484 * primes are Sophie Germain primes, q is p>>1 and we can
485 * compute it on the fly like MIT Kerberos does, but we'd have
486 * to implement BN_rshift1().
488 dp.q = calloc(1, sizeof(*dp.q));
490 free_DomainParameters(&dp);
493 ret = BN_to_integer(context, dh->q, dp.q);
495 free_DomainParameters(&dp);
500 dp.validationParms = NULL;
502 a->clientPublicValue->algorithm.parameters =
503 malloc(sizeof(*a->clientPublicValue->algorithm.parameters));
504 if (a->clientPublicValue->algorithm.parameters == NULL) {
505 free_DomainParameters(&dp);
509 ASN1_MALLOC_ENCODE(DomainParameters,
510 a->clientPublicValue->algorithm.parameters->data,
511 a->clientPublicValue->algorithm.parameters->length,
513 free_DomainParameters(&dp);
516 if (size != a->clientPublicValue->algorithm.parameters->length)
517 krb5_abortx(context, "Internal ASN1 encoder error");
519 ret = BN_to_integer(context, dh->pub_key, &dh_pub_key);
523 ASN1_MALLOC_ENCODE(DHPublicKey, dhbuf.data, dhbuf.length,
524 &dh_pub_key, &size, ret);
525 der_free_heim_integer(&dh_pub_key);
528 if (size != dhbuf.length)
529 krb5_abortx(context, "asn1 internal error");
530 a->clientPublicValue->subjectPublicKey.length = dhbuf.length * 8;
531 a->clientPublicValue->subjectPublicKey.data = dhbuf.data;
532 } else if (ctx->keyex == USE_ECDH) {
533 ret = _krb5_build_authpack_subjectPK_EC(context, ctx, a);
537 krb5_abortx(context, "internal error");
541 a->supportedCMSTypes = calloc(1, sizeof(*a->supportedCMSTypes));
542 if (a->supportedCMSTypes == NULL)
545 ret = hx509_crypto_available(context->hx509ctx, HX509_SELECT_ALL,
547 &a->supportedCMSTypes->val,
548 &a->supportedCMSTypes->len);
556 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
557 _krb5_pk_mk_ContentInfo(krb5_context context,
558 const krb5_data *buf,
560 struct ContentInfo *content_info)
564 ret = der_copy_oid(oid, &content_info->contentType);
567 ALLOC(content_info->content, 1);
568 if (content_info->content == NULL)
570 content_info->content->data = malloc(buf->length);
571 if (content_info->content->data == NULL)
573 memcpy(content_info->content->data, buf->data, buf->length);
574 content_info->content->length = buf->length;
578 static krb5_error_code
579 pk_mk_padata(krb5_context context,
580 krb5_pk_init_ctx ctx,
581 const KDC_REQ_BODY *req_body,
585 struct ContentInfo content_info;
587 const heim_oid *oid = NULL;
589 krb5_data buf, sd_buf;
592 krb5_data_zero(&buf);
593 krb5_data_zero(&sd_buf);
594 memset(&content_info, 0, sizeof(content_info));
596 if (ctx->type == PKINIT_WIN2K) {
601 memset(&ap, 0, sizeof(ap));
603 /* fill in PKAuthenticator */
604 ret = copy_PrincipalName(req_body->sname, &ap.pkAuthenticator.kdcName);
606 free_AuthPack_Win2k(&ap);
607 krb5_clear_error_message(context);
610 ret = copy_Realm(&req_body->realm, &ap.pkAuthenticator.kdcRealm);
612 free_AuthPack_Win2k(&ap);
613 krb5_clear_error_message(context);
617 krb5_us_timeofday(context, &sec, &usec);
618 ap.pkAuthenticator.ctime = sec;
619 ap.pkAuthenticator.cusec = usec;
620 ap.pkAuthenticator.nonce = nonce;
622 ASN1_MALLOC_ENCODE(AuthPack_Win2k, buf.data, buf.length,
624 free_AuthPack_Win2k(&ap);
626 krb5_set_error_message(context, ret,
627 N_("Failed encoding AuthPackWin: %d", ""),
631 if (buf.length != size)
632 krb5_abortx(context, "internal ASN1 encoder error");
634 oid = &asn1_oid_id_pkcs7_data;
635 } else if (ctx->type == PKINIT_27) {
638 memset(&ap, 0, sizeof(ap));
640 ret = build_auth_pack(context, nonce, ctx, req_body, &ap);
646 ASN1_MALLOC_ENCODE(AuthPack, buf.data, buf.length, &ap, &size, ret);
649 krb5_set_error_message(context, ret,
650 N_("Failed encoding AuthPack: %d", ""),
654 if (buf.length != size)
655 krb5_abortx(context, "internal ASN1 encoder error");
657 oid = &asn1_oid_id_pkauthdata;
659 krb5_abortx(context, "internal pkinit error");
661 ret = create_signature(context, oid, &buf, ctx->id,
663 krb5_data_free(&buf);
667 ret = hx509_cms_wrap_ContentInfo(&asn1_oid_id_pkcs7_signedData, &sd_buf, &buf);
668 krb5_data_free(&sd_buf);
670 krb5_set_error_message(context, ret,
671 N_("ContentInfo wrapping of signedData failed",""));
675 if (ctx->type == PKINIT_WIN2K) {
676 PA_PK_AS_REQ_Win2k winreq;
678 pa_type = KRB5_PADATA_PK_AS_REQ_WIN;
680 memset(&winreq, 0, sizeof(winreq));
682 winreq.signed_auth_pack = buf;
684 ASN1_MALLOC_ENCODE(PA_PK_AS_REQ_Win2k, buf.data, buf.length,
685 &winreq, &size, ret);
686 free_PA_PK_AS_REQ_Win2k(&winreq);
688 } else if (ctx->type == PKINIT_27) {
691 pa_type = KRB5_PADATA_PK_AS_REQ;
693 memset(&req, 0, sizeof(req));
694 req.signedAuthPack = buf;
696 if (ctx->trustedCertifiers) {
698 req.trustedCertifiers = calloc(1, sizeof(*req.trustedCertifiers));
699 if (req.trustedCertifiers == NULL) {
700 ret = krb5_enomem(context);
701 free_PA_PK_AS_REQ(&req);
704 ret = build_edi(context, context->hx509ctx,
705 ctx->id->anchors, req.trustedCertifiers);
707 krb5_set_error_message(context, ret,
708 N_("pk-init: failed to build "
709 "trustedCertifiers", ""));
710 free_PA_PK_AS_REQ(&req);
716 ASN1_MALLOC_ENCODE(PA_PK_AS_REQ, buf.data, buf.length,
719 free_PA_PK_AS_REQ(&req);
722 krb5_abortx(context, "internal pkinit error");
724 krb5_set_error_message(context, ret, "PA-PK-AS-REQ %d", (int)ret);
727 if (buf.length != size)
728 krb5_abortx(context, "Internal ASN1 encoder error");
730 ret = krb5_padata_add(context, md, pa_type, buf.data, buf.length);
735 ret = krb5_padata_add(context, md, KRB5_PADATA_PK_AS_09_BINDING, NULL, 0);
738 free_ContentInfo(&content_info);
744 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
745 _krb5_pk_mk_padata(krb5_context context,
749 const KDC_REQ_BODY *req_body,
753 krb5_pk_init_ctx ctx = c;
756 if (ctx->id->certs == NULL && ctx->anonymous == 0) {
757 krb5_set_error_message(context, HEIM_PKINIT_NO_PRIVATE_KEY,
758 N_("PKINIT: No user certificate given", ""));
759 return HEIM_PKINIT_NO_PRIVATE_KEY;
762 win2k_compat = krb5_config_get_bool_default(context, NULL,
770 ctx->require_binding =
771 krb5_config_get_bool_default(context, NULL,
775 "pkinit_win2k_require_binding",
777 ctx->type = PKINIT_WIN2K;
779 ctx->type = PKINIT_27;
782 krb5_config_get_bool_default(context, NULL,
786 "pkinit_require_eku",
788 if (ic_flags & KRB5_INIT_CREDS_NO_C_NO_EKU_CHECK)
789 ctx->require_eku = 0;
790 if (ctx->id->flags & (PKINIT_BTMM | PKINIT_NO_KDC_ANCHOR))
791 ctx->require_eku = 0;
793 ctx->require_krbtgt_otherName =
794 krb5_config_get_bool_default(context, NULL,
798 "pkinit_require_krbtgt_otherName",
800 if (ic_flags & KRB5_INIT_CREDS_PKINIT_NO_KRBTGT_OTHERNAME_CHECK)
801 ctx->require_krbtgt_otherName = FALSE;
803 ctx->require_hostname_match =
804 krb5_config_get_bool_default(context, NULL,
808 "pkinit_require_hostname_match",
811 ctx->trustedCertifiers =
812 krb5_config_get_bool_default(context, NULL,
816 "pkinit_trustedCertifiers",
819 return pk_mk_padata(context, ctx, req_body, nonce, md);
822 static krb5_error_code
823 pk_verify_sign(krb5_context context,
826 struct krb5_pk_identity *id,
827 heim_oid *contentType,
829 struct krb5_pk_cert **signer)
831 hx509_certs signer_certs;
833 unsigned flags = 0, verify_flags = 0;
837 if (id->flags & PKINIT_BTMM) {
838 flags |= HX509_CMS_VS_ALLOW_DATA_OID_MISMATCH;
839 flags |= HX509_CMS_VS_NO_KU_CHECK;
840 flags |= HX509_CMS_VS_NO_VALIDATE;
842 if (id->flags & PKINIT_NO_KDC_ANCHOR)
843 flags |= HX509_CMS_VS_NO_VALIDATE;
845 ret = hx509_cms_verify_signed_ext(context->hx509ctx,
857 pk_copy_error(context, context->hx509ctx, ret,
858 "CMS verify signed failed");
862 heim_assert((verify_flags & HX509_CMS_VSE_VALIDATED) ||
863 (id->flags & PKINIT_NO_KDC_ANCHOR),
864 "Either PKINIT signer must be validated, or NO_KDC_ANCHOR must be set");
866 if ((verify_flags & HX509_CMS_VSE_VALIDATED) == 0)
869 *signer = calloc(1, sizeof(**signer));
870 if (*signer == NULL) {
871 krb5_clear_error_message(context);
876 ret = hx509_get_one_cert(context->hx509ctx, signer_certs, &(*signer)->cert);
878 pk_copy_error(context, context->hx509ctx, ret,
879 "Failed to get on of the signer certs");
884 hx509_certs_free(&signer_certs);
887 hx509_cert_free((*signer)->cert);
896 static krb5_error_code
897 get_reply_key_win(krb5_context context,
898 const krb5_data *content,
902 ReplyKeyPack_Win2k key_pack;
906 ret = decode_ReplyKeyPack_Win2k(content->data,
911 krb5_set_error_message(context, ret,
912 N_("PKINIT decoding reply key failed", ""));
913 free_ReplyKeyPack_Win2k(&key_pack);
917 if ((unsigned)key_pack.nonce != nonce) {
918 krb5_set_error_message(context, ret,
919 N_("PKINIT enckey nonce is wrong", ""));
920 free_ReplyKeyPack_Win2k(&key_pack);
921 return KRB5KRB_AP_ERR_MODIFIED;
924 *key = malloc (sizeof (**key));
926 free_ReplyKeyPack_Win2k(&key_pack);
927 return krb5_enomem(context);
930 ret = copy_EncryptionKey(&key_pack.replyKey, *key);
931 free_ReplyKeyPack_Win2k(&key_pack);
933 krb5_set_error_message(context, ret,
934 N_("PKINIT failed copying reply key", ""));
942 static krb5_error_code
943 get_reply_key(krb5_context context,
944 const krb5_data *content,
945 const krb5_data *req_buffer,
948 ReplyKeyPack key_pack;
952 ret = decode_ReplyKeyPack(content->data,
957 krb5_set_error_message(context, ret,
958 N_("PKINIT decoding reply key failed", ""));
959 free_ReplyKeyPack(&key_pack);
967 * XXX Verify kp.replyKey is a allowed enctype in the
971 ret = krb5_crypto_init(context, &key_pack.replyKey, 0, &crypto);
973 free_ReplyKeyPack(&key_pack);
977 ret = krb5_verify_checksum(context, crypto, 6,
978 req_buffer->data, req_buffer->length,
979 &key_pack.asChecksum);
980 krb5_crypto_destroy(context, crypto);
982 free_ReplyKeyPack(&key_pack);
987 *key = malloc (sizeof (**key));
989 free_ReplyKeyPack(&key_pack);
990 return krb5_enomem(context);
993 ret = copy_EncryptionKey(&key_pack.replyKey, *key);
994 free_ReplyKeyPack(&key_pack);
996 krb5_set_error_message(context, ret,
997 N_("PKINIT failed copying reply key", ""));
1006 static krb5_error_code
1007 pk_verify_host(krb5_context context,
1009 const krb5_krbhst_info *hi,
1010 struct krb5_pk_init_ctx_data *ctx,
1011 struct krb5_pk_cert *host)
1013 krb5_error_code ret = 0;
1015 if (ctx->require_eku) {
1016 ret = hx509_cert_check_eku(context->hx509ctx, host->cert,
1017 &asn1_oid_id_pkkdcekuoid, 0);
1019 krb5_set_error_message(context, ret,
1020 N_("No PK-INIT KDC EKU in kdc certificate", ""));
1024 if (ctx->require_krbtgt_otherName) {
1025 hx509_octet_string_list list;
1029 ret = hx509_cert_find_subjectAltName_otherName(context->hx509ctx,
1031 &asn1_oid_id_pkinit_san,
1034 krb5_set_error_message(context, ret,
1035 N_("Failed to find the PK-INIT "
1036 "subjectAltName in the KDC "
1037 "certificate", ""));
1043 * subjectAltNames are multi-valued, and a single KDC may serve
1044 * multiple realms. The SAN validation here must accept
1045 * the KDC's cert if *any* of the SANs match the expected KDC.
1046 * It is OK for *some* of the SANs to not match, provided at least
1049 for (i = 0; matched == 0 && i < list.len; i++) {
1050 KRB5PrincipalName r;
1052 ret = decode_KRB5PrincipalName(list.val[i].data,
1057 krb5_set_error_message(context, ret,
1058 N_("Failed to decode the PK-INIT "
1059 "subjectAltName in the "
1060 "KDC certificate", ""));
1065 if (r.principalName.name_string.len == 2 &&
1066 strcmp(r.principalName.name_string.val[0], KRB5_TGS_NAME) == 0
1067 && strcmp(r.principalName.name_string.val[1], realm) == 0
1068 && strcmp(r.realm, realm) == 0)
1071 free_KRB5PrincipalName(&r);
1073 hx509_free_octet_string_list(&list);
1076 (ctx->id->flags & PKINIT_NO_KDC_ANCHOR) == 0) {
1077 ret = KRB5_KDC_ERR_INVALID_CERTIFICATE;
1078 /* XXX: Lost in translation... */
1079 krb5_set_error_message(context, ret,
1080 N_("KDC have wrong realm name in "
1081 "the certificate", ""));
1088 ret = hx509_verify_hostname(context->hx509ctx, host->cert,
1089 ctx->require_hostname_match,
1092 hi->ai->ai_addr, hi->ai->ai_addrlen);
1095 krb5_set_error_message(context, ret,
1096 N_("Address mismatch in "
1097 "the KDC certificate", ""));
1102 static krb5_error_code
1103 pk_rd_pa_reply_enckey(krb5_context context,
1105 const heim_octet_string *indata,
1106 const heim_oid *dataType,
1108 krb5_pk_init_ctx ctx,
1110 const krb5_krbhst_info *hi,
1112 const krb5_data *req_buffer,
1114 krb5_keyblock **key)
1116 krb5_error_code ret;
1117 struct krb5_pk_cert *host = NULL;
1119 heim_octet_string unwrapped;
1120 heim_oid contentType = { 0, NULL };
1121 int flags = HX509_CMS_UE_DONT_REQUIRE_KU_ENCIPHERMENT;
1123 if (der_heim_oid_cmp(&asn1_oid_id_pkcs7_envelopedData, dataType)) {
1124 krb5_set_error_message(context, EINVAL,
1125 N_("PKINIT: Invalid content type", ""));
1129 if (ctx->type == PKINIT_WIN2K)
1130 flags |= HX509_CMS_UE_ALLOW_WEAK;
1132 ret = hx509_cms_unenvelope(context->hx509ctx,
1142 pk_copy_error(context, context->hx509ctx, ret,
1143 "Failed to unenvelope CMS data in PK-INIT reply");
1146 der_free_oid(&contentType);
1148 /* win2k uses ContentInfo */
1149 if (type == PKINIT_WIN2K) {
1152 ret = hx509_cms_unwrap_ContentInfo(&content, &type2, &unwrapped, NULL);
1154 /* windows LH with interesting CMS packets */
1155 size_t ph = 1 + der_length_len(content.length);
1156 unsigned char *ptr = malloc(content.length + ph);
1159 memcpy(ptr + ph, content.data, content.length);
1161 ret = der_put_length_and_tag (ptr + ph - 1, ph, content.length,
1162 ASN1_C_UNIV, CONS, UT_Sequence, &l);
1169 content.length += ph;
1171 ret = hx509_cms_unwrap_ContentInfo(&content, &type2, &unwrapped, NULL);
1175 if (der_heim_oid_cmp(&type2, &asn1_oid_id_pkcs7_signedData)) {
1176 ret = EINVAL; /* XXX */
1177 krb5_set_error_message(context, ret,
1178 N_("PKINIT: Invalid content type", ""));
1179 der_free_oid(&type2);
1180 der_free_octet_string(&unwrapped);
1183 der_free_oid(&type2);
1184 krb5_data_free(&content);
1185 ret = krb5_data_copy(&content, unwrapped.data, unwrapped.length);
1186 der_free_octet_string(&unwrapped);
1188 krb5_set_error_message(context, ret,
1189 N_("malloc: out of memory", ""));
1194 ret = pk_verify_sign(context,
1203 krb5_data_free(&content);
1204 ret = krb5_data_copy(&content, unwrapped.data, unwrapped.length);
1205 der_free_octet_string(&unwrapped);
1207 heim_assert(host || (ctx->id->flags & PKINIT_NO_KDC_ANCHOR),
1208 "KDC signature must be verified unless PKINIT_NO_KDC_ANCHOR set");
1211 /* make sure that it is the kdc's certificate */
1212 ret = pk_verify_host(context, realm, hi, ctx, host);
1216 ctx->kdc_verified = 1;
1220 if (type == PKINIT_WIN2K) {
1221 if (der_heim_oid_cmp(&contentType, &asn1_oid_id_pkcs7_data) != 0) {
1222 ret = KRB5KRB_AP_ERR_MSG_TYPE;
1223 krb5_set_error_message(context, ret, "PKINIT: reply key, wrong oid");
1227 if (der_heim_oid_cmp(&contentType, &asn1_oid_id_pkrkeydata) != 0) {
1228 ret = KRB5KRB_AP_ERR_MSG_TYPE;
1229 krb5_set_error_message(context, ret, "PKINIT: reply key, wrong oid");
1237 ret = get_reply_key(context, &content, req_buffer, key);
1238 if (ret != 0 && ctx->require_binding == 0)
1239 ret = get_reply_key_win(context, &content, nonce, key);
1242 ret = get_reply_key(context, &content, req_buffer, key);
1248 /* XXX compare given etype with key->etype */
1252 _krb5_pk_cert_free(host);
1253 der_free_oid(&contentType);
1254 krb5_data_free(&content);
1260 * RFC 8062 section 7:
1262 * The client then decrypts the KDC contribution key and verifies that
1263 * the ticket session key in the returned ticket is the combined key of
1264 * the KDC contribution key and the reply key.
1266 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1267 _krb5_pk_kx_confirm(krb5_context context,
1268 krb5_pk_init_ctx ctx,
1269 krb5_keyblock *reply_key,
1270 krb5_keyblock *session_key,
1271 PA_DATA *pa_pkinit_kx)
1273 krb5_error_code ret;
1275 krb5_keyblock ck, sk_verify;
1276 krb5_crypto ck_crypto = NULL;
1277 krb5_crypto rk_crypto = NULL;
1280 krb5_data p1 = { sizeof("PKINIT") - 1, "PKINIT" };
1281 krb5_data p2 = { sizeof("KEYEXCHANGE") - 1, "KEYEXCHANGE" };
1283 heim_assert(ctx != NULL, "PKINIT context is non-NULL");
1284 heim_assert(reply_key != NULL, "reply key is non-NULL");
1285 heim_assert(session_key != NULL, "session key is non-NULL");
1287 /* PA-PKINIT-KX is optional unless anonymous */
1288 if (pa_pkinit_kx == NULL)
1289 return ctx->anonymous ? KRB5_KDCREP_MODIFIED : 0;
1291 memset(&ed, 0, sizeof(ed));
1292 krb5_keyblock_zero(&ck);
1293 krb5_keyblock_zero(&sk_verify);
1294 krb5_data_zero(&data);
1296 ret = decode_EncryptedData(pa_pkinit_kx->padata_value.data,
1297 pa_pkinit_kx->padata_value.length,
1302 if (len != pa_pkinit_kx->padata_value.length) {
1303 ret = KRB5_KDCREP_MODIFIED;
1307 ret = krb5_crypto_init(context, reply_key, 0, &rk_crypto);
1311 ret = krb5_decrypt_EncryptedData(context, rk_crypto,
1312 KRB5_KU_PA_PKINIT_KX,
1317 ret = decode_EncryptionKey(data.data, data.length,
1322 ret = krb5_crypto_init(context, &ck, 0, &ck_crypto);
1326 ret = krb5_crypto_fx_cf2(context, ck_crypto, rk_crypto,
1327 &p1, &p2, session_key->keytype,
1332 if (sk_verify.keytype != session_key->keytype ||
1333 krb5_data_ct_cmp(&sk_verify.keyvalue, &session_key->keyvalue) != 0) {
1334 ret = KRB5_KDCREP_MODIFIED;
1339 free_EncryptedData(&ed);
1340 krb5_free_keyblock_contents(context, &ck);
1341 krb5_free_keyblock_contents(context, &sk_verify);
1343 krb5_crypto_destroy(context, ck_crypto);
1345 krb5_crypto_destroy(context, rk_crypto);
1346 krb5_data_free(&data);
1351 static krb5_error_code
1352 pk_rd_pa_reply_dh(krb5_context context,
1353 const heim_octet_string *indata,
1354 const heim_oid *dataType,
1356 krb5_pk_init_ctx ctx,
1358 const krb5_krbhst_info *hi,
1363 krb5_keyblock **key)
1365 const unsigned char *p;
1366 unsigned char *dh_gen_key = NULL;
1367 struct krb5_pk_cert *host = NULL;
1368 BIGNUM *kdc_dh_pubkey = NULL;
1369 KDCDHKeyInfo kdc_dh_info;
1370 heim_oid contentType = { 0, NULL };
1372 krb5_error_code ret;
1373 int dh_gen_keylen = 0;
1376 krb5_data_zero(&content);
1377 memset(&kdc_dh_info, 0, sizeof(kdc_dh_info));
1379 if (der_heim_oid_cmp(&asn1_oid_id_pkcs7_signedData, dataType)) {
1380 krb5_set_error_message(context, EINVAL,
1381 N_("PKINIT: Invalid content type", ""));
1385 ret = pk_verify_sign(context,
1395 heim_assert(host || (ctx->id->flags & PKINIT_NO_KDC_ANCHOR),
1396 "KDC signature must be verified unless PKINIT_NO_KDC_ANCHOR set");
1399 /* make sure that it is the kdc's certificate */
1400 ret = pk_verify_host(context, realm, hi, ctx, host);
1404 ctx->kdc_verified = 1;
1407 if (der_heim_oid_cmp(&contentType, &asn1_oid_id_pkdhkeydata)) {
1408 ret = KRB5KRB_AP_ERR_MSG_TYPE;
1409 krb5_set_error_message(context, ret,
1410 N_("pkinit - dh reply contains wrong oid", ""));
1414 ret = decode_KDCDHKeyInfo(content.data,
1420 krb5_set_error_message(context, ret,
1421 N_("pkinit - failed to decode "
1422 "KDC DH Key Info", ""));
1426 if (kdc_dh_info.nonce != nonce) {
1427 ret = KRB5KRB_AP_ERR_MODIFIED;
1428 krb5_set_error_message(context, ret,
1429 N_("PKINIT: DH nonce is wrong", ""));
1433 if (kdc_dh_info.dhKeyExpiration) {
1435 ret = KRB5KRB_ERR_GENERIC;
1436 krb5_set_error_message(context, ret,
1437 N_("pkinit; got key expiration "
1438 "without server nonce", ""));
1442 ret = KRB5KRB_ERR_GENERIC;
1443 krb5_set_error_message(context, ret,
1444 N_("pkinit; got DH reuse but no "
1445 "client nonce", ""));
1450 ret = KRB5KRB_ERR_GENERIC;
1451 krb5_set_error_message(context, ret,
1452 N_("pkinit: got server nonce "
1453 "without key expiration", ""));
1460 p = kdc_dh_info.subjectPublicKey.data;
1461 size = (kdc_dh_info.subjectPublicKey.length + 7) / 8;
1463 if (ctx->keyex == USE_DH) {
1465 ret = decode_DHPublicKey(p, size, &k, NULL);
1467 krb5_set_error_message(context, ret,
1468 N_("pkinit: can't decode "
1469 "without key expiration", ""));
1473 kdc_dh_pubkey = integer_to_BN(context, "DHPublicKey", &k);
1474 free_DHPublicKey(&k);
1475 if (kdc_dh_pubkey == NULL) {
1481 size = DH_size(ctx->u.dh);
1483 dh_gen_key = malloc(size);
1484 if (dh_gen_key == NULL) {
1485 ret = krb5_enomem(context);
1489 dh_gen_keylen = DH_compute_key(dh_gen_key, kdc_dh_pubkey, ctx->u.dh);
1490 if (dh_gen_keylen == -1) {
1491 ret = KRB5KRB_ERR_GENERIC;
1493 krb5_set_error_message(context, ret,
1494 N_("PKINIT: Can't compute Diffie-Hellman key", ""));
1497 if (dh_gen_keylen < (int)size) {
1498 size -= dh_gen_keylen;
1499 memmove(dh_gen_key + size, dh_gen_key, dh_gen_keylen);
1500 memset(dh_gen_key, 0, size);
1504 ret = _krb5_pk_rd_pa_reply_ecdh_compute_key(context, ctx, p,
1511 if (dh_gen_keylen <= 0) {
1513 krb5_set_error_message(context, ret,
1514 N_("PKINIT: resulting DH key <= 0", ""));
1519 *key = malloc (sizeof (**key));
1521 ret = krb5_enomem(context);
1525 ret = _krb5_pk_octetstring2key(context,
1527 dh_gen_key, dh_gen_keylen,
1531 krb5_set_error_message(context, ret,
1532 N_("PKINIT: can't create key from DH key", ""));
1540 BN_free(kdc_dh_pubkey);
1542 memset(dh_gen_key, 0, dh_gen_keylen);
1546 _krb5_pk_cert_free(host);
1548 krb5_data_free(&content);
1549 der_free_oid(&contentType);
1550 free_KDCDHKeyInfo(&kdc_dh_info);
1555 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1556 _krb5_pk_rd_pa_reply(krb5_context context,
1560 const krb5_krbhst_info *hi,
1562 const krb5_data *req_buffer,
1564 krb5_keyblock **key)
1566 krb5_pk_init_ctx ctx = c;
1567 krb5_error_code ret;
1570 /* Check for IETF PK-INIT first */
1571 if (ctx->type == PKINIT_27) {
1573 heim_octet_string os, data;
1576 if (pa->padata_type != KRB5_PADATA_PK_AS_REP) {
1577 krb5_set_error_message(context, EINVAL,
1578 N_("PKINIT: wrong padata recv", ""));
1582 ret = decode_PA_PK_AS_REP(pa->padata_value.data,
1583 pa->padata_value.length,
1587 krb5_set_error_message(context, ret,
1588 N_("Failed to decode pkinit AS rep", ""));
1592 switch (rep.element) {
1593 case choice_PA_PK_AS_REP_dhInfo:
1594 _krb5_debug(context, 5, "krb5_get_init_creds: using pkinit dh");
1595 os = rep.u.dhInfo.dhSignedData;
1597 case choice_PA_PK_AS_REP_encKeyPack:
1598 _krb5_debug(context, 5, "krb5_get_init_creds: using kinit enc reply key");
1599 os = rep.u.encKeyPack;
1602 PA_PK_AS_REP_BTMM btmm;
1603 free_PA_PK_AS_REP(&rep);
1604 memset(&rep, 0, sizeof(rep));
1606 _krb5_debug(context, 5, "krb5_get_init_creds: using BTMM kinit enc reply key");
1608 ret = decode_PA_PK_AS_REP_BTMM(pa->padata_value.data,
1609 pa->padata_value.length,
1613 krb5_set_error_message(context, EINVAL,
1614 N_("PKINIT: -27 reply "
1615 "invalid content type", ""));
1619 if (btmm.dhSignedData || btmm.encKeyPack == NULL) {
1620 free_PA_PK_AS_REP_BTMM(&btmm);
1622 krb5_set_error_message(context, ret,
1623 N_("DH mode not supported for BTMM mode", ""));
1628 * Transform to IETF style PK-INIT reply so that free works below
1631 rep.element = choice_PA_PK_AS_REP_encKeyPack;
1632 rep.u.encKeyPack.data = btmm.encKeyPack->data;
1633 rep.u.encKeyPack.length = btmm.encKeyPack->length;
1634 btmm.encKeyPack->data = NULL;
1635 btmm.encKeyPack->length = 0;
1636 free_PA_PK_AS_REP_BTMM(&btmm);
1637 os = rep.u.encKeyPack;
1641 ret = hx509_cms_unwrap_ContentInfo(&os, &oid, &data, NULL);
1643 free_PA_PK_AS_REP(&rep);
1644 krb5_set_error_message(context, ret,
1645 N_("PKINIT: failed to unwrap CI", ""));
1649 switch (rep.element) {
1650 case choice_PA_PK_AS_REP_dhInfo:
1651 ret = pk_rd_pa_reply_dh(context, &data, &oid, realm, ctx, etype, hi,
1653 rep.u.dhInfo.serverDHNonce,
1656 case choice_PA_PK_AS_REP_encKeyPack:
1657 ret = pk_rd_pa_reply_enckey(context, PKINIT_27, &data, &oid, realm,
1658 ctx, etype, hi, nonce, req_buffer, pa, key);
1661 krb5_abortx(context, "pk-init as-rep case not possible to happen");
1663 der_free_octet_string(&data);
1665 free_PA_PK_AS_REP(&rep);
1667 } else if (ctx->type == PKINIT_WIN2K) {
1668 PA_PK_AS_REP_Win2k w2krep;
1670 /* Check for Windows encoding of the AS-REP pa data */
1672 #if 0 /* should this be ? */
1673 if (pa->padata_type != KRB5_PADATA_PK_AS_REP) {
1674 krb5_set_error_message(context, EINVAL,
1675 "PKINIT: wrong padata recv");
1680 memset(&w2krep, 0, sizeof(w2krep));
1682 ret = decode_PA_PK_AS_REP_Win2k(pa->padata_value.data,
1683 pa->padata_value.length,
1687 krb5_set_error_message(context, ret,
1688 N_("PKINIT: Failed decoding windows "
1689 "pkinit reply %d", ""), (int)ret);
1693 krb5_clear_error_message(context);
1695 switch (w2krep.element) {
1696 case choice_PA_PK_AS_REP_Win2k_encKeyPack: {
1697 heim_octet_string data;
1700 ret = hx509_cms_unwrap_ContentInfo(&w2krep.u.encKeyPack,
1702 free_PA_PK_AS_REP_Win2k(&w2krep);
1704 krb5_set_error_message(context, ret,
1705 N_("PKINIT: failed to unwrap CI", ""));
1709 ret = pk_rd_pa_reply_enckey(context, PKINIT_WIN2K, &data, &oid, realm,
1710 ctx, etype, hi, nonce, req_buffer, pa, key);
1711 der_free_octet_string(&data);
1717 free_PA_PK_AS_REP_Win2k(&w2krep);
1719 krb5_set_error_message(context, ret,
1720 N_("PKINIT: win2k reply invalid "
1721 "content type", ""));
1727 krb5_set_error_message(context, ret,
1728 N_("PKINIT: unknown reply type", ""));
1735 krb5_context context;
1736 krb5_prompter_fct prompter;
1737 void *prompter_data;
1741 hx_pass_prompter(void *data, const hx509_prompt *prompter)
1743 krb5_error_code ret;
1745 krb5_data password_data;
1746 struct prompter *p = data;
1748 password_data.data = prompter->reply.data;
1749 password_data.length = prompter->reply.length;
1751 prompt.prompt = prompter->prompt;
1752 prompt.hidden = hx509_prompt_hidden(prompter->type);
1753 prompt.reply = &password_data;
1755 switch (prompter->type) {
1756 case HX509_PROMPT_TYPE_INFO:
1757 prompt.type = KRB5_PROMPT_TYPE_INFO;
1759 case HX509_PROMPT_TYPE_PASSWORD:
1760 case HX509_PROMPT_TYPE_QUESTION:
1762 prompt.type = KRB5_PROMPT_TYPE_PASSWORD;
1766 ret = (*p->prompter)(p->context, p->prompter_data, NULL, NULL, 1, &prompt);
1768 memset (prompter->reply.data, 0, prompter->reply.length);
1774 static krb5_error_code
1775 _krb5_pk_set_user_id(krb5_context context,
1776 krb5_principal principal,
1777 krb5_pk_init_ctx ctx,
1778 struct hx509_certs_data *certs)
1780 hx509_certs c = hx509_certs_ref(certs);
1781 hx509_query *q = NULL;
1785 hx509_certs_free(&ctx->id->certs);
1786 if (ctx->id->cert) {
1787 hx509_cert_free(ctx->id->cert);
1788 ctx->id->cert = NULL;
1794 ret = hx509_query_alloc(context->hx509ctx, &q);
1796 pk_copy_error(context, context->hx509ctx, ret,
1797 "Allocate query to find signing certificate");
1801 hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY);
1802 hx509_query_match_option(q, HX509_QUERY_OPTION_KU_DIGITALSIGNATURE);
1804 if (principal && strncmp("LKDC:SHA1.", krb5_principal_get_realm(context, principal), 9) == 0) {
1805 ctx->id->flags |= PKINIT_BTMM;
1808 ret = find_cert(context, ctx->id, q, &ctx->id->cert);
1809 hx509_query_free(context->hx509ctx, q);
1811 if (ret == 0 && _krb5_have_debug(context, 2)) {
1816 ret = hx509_cert_get_subject(ctx->id->cert, &name);
1820 ret = hx509_name_to_string(name, &str);
1821 hx509_name_free(&name);
1825 ret = hx509_cert_get_serialnumber(ctx->id->cert, &i);
1831 ret = der_print_hex_heim_integer(&i, &sn);
1832 der_free_heim_integer(&i);
1838 _krb5_debug(context, 2, "using cert: subject: %s sn: %s", str, sn);
1847 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1848 _krb5_pk_load_id(krb5_context context,
1849 struct krb5_pk_identity **ret_id,
1850 const char *user_id,
1851 const char *anchor_id,
1852 char * const *chain_list,
1853 char * const *revoke_list,
1854 krb5_prompter_fct prompter,
1855 void *prompter_data,
1858 struct krb5_pk_identity *id = NULL;
1866 id = calloc(1, sizeof(*id));
1868 return krb5_enomem(context);
1873 ret = hx509_lock_init(context->hx509ctx, &lock);
1875 pk_copy_error(context, context->hx509ctx, ret, "Failed init lock");
1879 if (password && password[0])
1880 hx509_lock_add_password(lock, password);
1883 p.context = context;
1884 p.prompter = prompter;
1885 p.prompter_data = prompter_data;
1887 ret = hx509_lock_set_prompter(lock, hx_pass_prompter, &p);
1889 hx509_lock_free(lock);
1894 ret = hx509_certs_init(context->hx509ctx, user_id, 0, lock, &id->certs);
1895 hx509_lock_free(lock);
1897 pk_copy_error(context, context->hx509ctx, ret,
1898 "Failed to init cert certs");
1905 ret = hx509_certs_init(context->hx509ctx, anchor_id, 0, NULL, &id->anchors);
1907 pk_copy_error(context, context->hx509ctx, ret,
1908 "Failed to init anchors");
1912 ret = hx509_certs_init(context->hx509ctx, "MEMORY:pkinit-cert-chain",
1913 0, NULL, &id->certpool);
1915 pk_copy_error(context, context->hx509ctx, ret,
1916 "Failed to init chain");
1920 while (chain_list && *chain_list) {
1921 ret = hx509_certs_append(context->hx509ctx, id->certpool,
1924 pk_copy_error(context, context->hx509ctx, ret,
1925 "Failed to load chain %s",
1933 ret = hx509_revoke_init(context->hx509ctx, &id->revokectx);
1935 pk_copy_error(context, context->hx509ctx, ret,
1936 "Failed init revoke list");
1940 while (*revoke_list) {
1941 ret = hx509_revoke_add_crl(context->hx509ctx,
1945 pk_copy_error(context, context->hx509ctx, ret,
1946 "Failed load revoke list");
1952 hx509_context_set_missing_revoke(context->hx509ctx, 1);
1954 ret = hx509_verify_init_ctx(context->hx509ctx, &id->verify_ctx);
1956 pk_copy_error(context, context->hx509ctx, ret,
1957 "Failed init verify context");
1961 hx509_verify_attach_anchors(id->verify_ctx, id->anchors);
1962 hx509_verify_attach_revoke(id->verify_ctx, id->revokectx);
1966 hx509_verify_destroy_ctx(id->verify_ctx);
1967 hx509_certs_free(&id->certs);
1968 hx509_certs_free(&id->anchors);
1969 hx509_certs_free(&id->certpool);
1970 hx509_revoke_free(&id->revokectx);
1983 pk_copy_error(krb5_context context,
1984 hx509_context hx509ctx,
1994 ret = vasprintf(&f, fmt, va);
1996 if (ret == -1 || f == NULL) {
1997 krb5_clear_error_message(context);
2001 s = hx509_get_error_string(hx509ctx, hxret);
2003 krb5_clear_error_message(context);
2007 krb5_set_error_message(context, hxret, "%s: %s", f, s);
2013 parse_integer(krb5_context context, char **p, const char *file, int lineno,
2014 const char *name, heim_integer *integer)
2018 p1 = strsep(p, " \t");
2020 krb5_set_error_message(context, EINVAL,
2021 N_("moduli file %s missing %s on line %d", ""),
2022 file, name, lineno);
2025 ret = der_parse_hex_heim_integer(p1, integer);
2027 krb5_set_error_message(context, ret,
2028 N_("moduli file %s failed parsing %s "
2030 file, name, lineno);
2037 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2038 _krb5_parse_moduli_line(krb5_context context,
2042 struct krb5_dh_moduli **m)
2044 struct krb5_dh_moduli *m1;
2050 m1 = calloc(1, sizeof(*m1));
2052 return krb5_enomem(context);
2054 while (isspace((unsigned char)*p))
2062 p1 = strsep(&p, " \t");
2064 krb5_set_error_message(context, ret,
2065 N_("moduli file %s missing name on line %d", ""),
2069 m1->name = strdup(p1);
2070 if (m1->name == NULL) {
2071 ret = krb5_enomem(context);
2075 p1 = strsep(&p, " \t");
2077 krb5_set_error_message(context, ret,
2078 N_("moduli file %s missing bits on line %d", ""),
2083 m1->bits = atoi(p1);
2084 if (m1->bits == 0) {
2085 krb5_set_error_message(context, ret,
2086 N_("moduli file %s have un-parsable "
2087 "bits on line %d", ""), file, lineno);
2091 ret = parse_integer(context, &p, file, lineno, "p", &m1->p);
2094 ret = parse_integer(context, &p, file, lineno, "g", &m1->g);
2097 ret = parse_integer(context, &p, file, lineno, "q", &m1->q);
2102 krb5_clear_error_message(context);
2111 der_free_heim_integer(&m1->p);
2112 der_free_heim_integer(&m1->g);
2113 der_free_heim_integer(&m1->q);
2118 KRB5_LIB_FUNCTION void KRB5_LIB_CALL
2119 _krb5_free_moduli(struct krb5_dh_moduli **moduli)
2122 for (i = 0; moduli[i] != NULL; i++) {
2123 free(moduli[i]->name);
2124 der_free_heim_integer(&moduli[i]->p);
2125 der_free_heim_integer(&moduli[i]->g);
2126 der_free_heim_integer(&moduli[i]->q);
2132 static const char *default_moduli_RFC2412_MODP_group2 =
2134 "RFC2412-MODP-group2 "
2138 "FFFFFFFF" "FFFFFFFF" "C90FDAA2" "2168C234" "C4C6628B" "80DC1CD1"
2139 "29024E08" "8A67CC74" "020BBEA6" "3B139B22" "514A0879" "8E3404DD"
2140 "EF9519B3" "CD3A431B" "302B0A6D" "F25F1437" "4FE1356D" "6D51C245"
2141 "E485B576" "625E7EC6" "F44C42E9" "A637ED6B" "0BFF5CB6" "F406B7ED"
2142 "EE386BFB" "5A899FA5" "AE9F2411" "7C4B1FE6" "49286651" "ECE65381"
2143 "FFFFFFFF" "FFFFFFFF "
2147 "7FFFFFFF" "FFFFFFFF" "E487ED51" "10B4611A" "62633145" "C06E0E68"
2148 "94812704" "4533E63A" "0105DF53" "1D89CD91" "28A5043C" "C71A026E"
2149 "F7CA8CD9" "E69D218D" "98158536" "F92F8A1B" "A7F09AB6" "B6A8E122"
2150 "F242DABB" "312F3F63" "7A262174" "D31BF6B5" "85FFAE5B" "7A035BF6"
2151 "F71C35FD" "AD44CFD2" "D74F9208" "BE258FF3" "24943328" "F67329C0"
2152 "FFFFFFFF" "FFFFFFFF";
2154 static const char *default_moduli_rfc3526_MODP_group14 =
2156 "rfc3526-MODP-group14 "
2160 "FFFFFFFF" "FFFFFFFF" "C90FDAA2" "2168C234" "C4C6628B" "80DC1CD1"
2161 "29024E08" "8A67CC74" "020BBEA6" "3B139B22" "514A0879" "8E3404DD"
2162 "EF9519B3" "CD3A431B" "302B0A6D" "F25F1437" "4FE1356D" "6D51C245"
2163 "E485B576" "625E7EC6" "F44C42E9" "A637ED6B" "0BFF5CB6" "F406B7ED"
2164 "EE386BFB" "5A899FA5" "AE9F2411" "7C4B1FE6" "49286651" "ECE45B3D"
2165 "C2007CB8" "A163BF05" "98DA4836" "1C55D39A" "69163FA8" "FD24CF5F"
2166 "83655D23" "DCA3AD96" "1C62F356" "208552BB" "9ED52907" "7096966D"
2167 "670C354E" "4ABC9804" "F1746C08" "CA18217C" "32905E46" "2E36CE3B"
2168 "E39E772C" "180E8603" "9B2783A2" "EC07A28F" "B5C55DF0" "6F4C52C9"
2169 "DE2BCBF6" "95581718" "3995497C" "EA956AE5" "15D22618" "98FA0510"
2170 "15728E5A" "8AACAA68" "FFFFFFFF" "FFFFFFFF "
2174 "7FFFFFFF" "FFFFFFFF" "E487ED51" "10B4611A" "62633145" "C06E0E68"
2175 "94812704" "4533E63A" "0105DF53" "1D89CD91" "28A5043C" "C71A026E"
2176 "F7CA8CD9" "E69D218D" "98158536" "F92F8A1B" "A7F09AB6" "B6A8E122"
2177 "F242DABB" "312F3F63" "7A262174" "D31BF6B5" "85FFAE5B" "7A035BF6"
2178 "F71C35FD" "AD44CFD2" "D74F9208" "BE258FF3" "24943328" "F6722D9E"
2179 "E1003E5C" "50B1DF82" "CC6D241B" "0E2AE9CD" "348B1FD4" "7E9267AF"
2180 "C1B2AE91" "EE51D6CB" "0E3179AB" "1042A95D" "CF6A9483" "B84B4B36"
2181 "B3861AA7" "255E4C02" "78BA3604" "650C10BE" "19482F23" "171B671D"
2182 "F1CF3B96" "0C074301" "CD93C1D1" "7603D147" "DAE2AEF8" "37A62964"
2183 "EF15E5FB" "4AAC0B8C" "1CCAA4BE" "754AB572" "8AE9130C" "4C7D0288"
2184 "0AB9472D" "45565534" "7FFFFFFF" "FFFFFFFF";
2186 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2187 _krb5_parse_moduli(krb5_context context, const char *file,
2188 struct krb5_dh_moduli ***moduli)
2190 /* name bits P G Q */
2191 krb5_error_code ret;
2192 struct krb5_dh_moduli **m = NULL, **m2;
2195 int lineno = 0, n = 0;
2199 m = calloc(1, sizeof(m[0]) * 3);
2201 return krb5_enomem(context);
2203 strlcpy(buf, default_moduli_rfc3526_MODP_group14, sizeof(buf));
2204 ret = _krb5_parse_moduli_line(context, "builtin", 1, buf, &m[0]);
2206 _krb5_free_moduli(m);
2211 strlcpy(buf, default_moduli_RFC2412_MODP_group2, sizeof(buf));
2212 ret = _krb5_parse_moduli_line(context, "builtin", 1, buf, &m[1]);
2214 _krb5_free_moduli(m);
2226 if (_krb5_expand_path_tokens(context, file, 1, &exp_file) == 0) {
2227 f = fopen(exp_file, "r");
2228 krb5_xfree(exp_file);
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 return krb5_enomem(context);
2255 ret = _krb5_parse_moduli_line(context, file, lineno, buf, &element);
2257 _krb5_free_moduli(m);
2260 if (element == NULL)
2271 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2272 _krb5_dh_group_ok(krb5_context context, unsigned long bits,
2273 heim_integer *p, heim_integer *g, heim_integer *q,
2274 struct krb5_dh_moduli **moduli,
2282 for (i = 0; moduli[i] != NULL; i++) {
2283 if (der_heim_integer_cmp(&moduli[i]->g, g) == 0 &&
2284 der_heim_integer_cmp(&moduli[i]->p, p) == 0 &&
2285 (q == NULL || moduli[i]->q.length == 0 ||
2286 der_heim_integer_cmp(&moduli[i]->q, q) == 0))
2288 if (bits && bits > moduli[i]->bits) {
2289 krb5_set_error_message(context,
2290 KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED,
2291 N_("PKINIT: DH group parameter %s "
2292 "no accepted, not enough bits "
2295 return KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED;
2298 *name = strdup(moduli[i]->name);
2302 krb5_set_error_message(context,
2303 KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED,
2304 N_("PKINIT: DH group parameter no ok", ""));
2305 return KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED;
2309 KRB5_LIB_FUNCTION void KRB5_LIB_CALL
2310 _krb5_get_init_creds_opt_free_pkinit(krb5_get_init_creds_opt *opt)
2313 krb5_pk_init_ctx ctx;
2315 if (opt->opt_private == NULL || opt->opt_private->pk_init_ctx == NULL)
2317 ctx = opt->opt_private->pk_init_ctx;
2318 switch (ctx->keyex) {
2327 _krb5_pk_eckey_free(ctx->u.eckey);
2331 hx509_verify_destroy_ctx(ctx->id->verify_ctx);
2332 hx509_certs_free(&ctx->id->certs);
2333 hx509_cert_free(ctx->id->cert);
2334 hx509_certs_free(&ctx->id->anchors);
2335 hx509_certs_free(&ctx->id->certpool);
2337 if (ctx->clientDHNonce) {
2338 krb5_free_data(NULL, ctx->clientDHNonce);
2339 ctx->clientDHNonce = NULL;
2342 _krb5_free_moduli(ctx->m);
2346 free(opt->opt_private->pk_init_ctx);
2347 opt->opt_private->pk_init_ctx = NULL;
2351 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2352 krb5_get_init_creds_opt_set_pkinit(krb5_context context,
2353 krb5_get_init_creds_opt *opt,
2354 krb5_principal principal,
2355 const char *user_id,
2356 const char *x509_anchors,
2357 char * const * pool,
2358 char * const * pki_revoke,
2360 krb5_prompter_fct prompter,
2361 void *prompter_data,
2365 krb5_error_code ret;
2366 char **freeme1 = NULL;
2367 char **freeme2 = NULL;
2368 char *anchors = NULL;
2370 if (opt->opt_private == NULL) {
2371 krb5_set_error_message(context, EINVAL,
2372 N_("PKINIT: on non extendable opt", ""));
2376 opt->opt_private->pk_init_ctx =
2377 calloc(1, sizeof(*opt->opt_private->pk_init_ctx));
2378 if (opt->opt_private->pk_init_ctx == NULL)
2379 return krb5_enomem(context);
2380 opt->opt_private->pk_init_ctx->require_binding = 0;
2381 opt->opt_private->pk_init_ctx->require_eku = 1;
2382 opt->opt_private->pk_init_ctx->require_krbtgt_otherName = 1;
2383 opt->opt_private->pk_init_ctx->peer = NULL;
2385 /* XXX implement krb5_appdefault_strings */
2387 pool = freeme1 = krb5_config_get_strings(context, NULL, "appdefaults",
2388 "pkinit_pool", NULL);
2390 if (pki_revoke == NULL)
2391 pki_revoke = freeme2 = krb5_config_get_strings(context, NULL,
2393 "pkinit_revoke", NULL);
2395 if (x509_anchors == NULL) {
2396 krb5_appdefault_string(context, "kinit",
2397 krb5_principal_get_realm(context, principal),
2398 "pkinit_anchors", NULL, &anchors);
2399 x509_anchors = anchors;
2402 if (flags & KRB5_GIC_OPT_PKINIT_ANONYMOUS)
2403 opt->opt_private->pk_init_ctx->anonymous = 1;
2405 if ((flags & KRB5_GIC_OPT_PKINIT_NO_KDC_ANCHOR) == 0 &&
2406 x509_anchors == NULL) {
2407 krb5_set_error_message(context, HEIM_PKINIT_NO_VALID_CA,
2408 N_("PKINIT: No anchor given", ""));
2409 return HEIM_PKINIT_NO_VALID_CA;
2412 ret = _krb5_pk_load_id(context,
2413 &opt->opt_private->pk_init_ctx->id,
2421 krb5_config_free_strings(freeme2);
2422 krb5_config_free_strings(freeme1);
2425 free(opt->opt_private->pk_init_ctx);
2426 opt->opt_private->pk_init_ctx = NULL;
2429 if (flags & KRB5_GIC_OPT_PKINIT_BTMM)
2430 opt->opt_private->pk_init_ctx->id->flags |= PKINIT_BTMM;
2431 if (principal && krb5_principal_is_lkdc(context, principal))
2432 opt->opt_private->pk_init_ctx->id->flags |= PKINIT_BTMM;
2433 if (flags & KRB5_GIC_OPT_PKINIT_NO_KDC_ANCHOR)
2434 opt->opt_private->pk_init_ctx->id->flags |= PKINIT_NO_KDC_ANCHOR;
2436 if (opt->opt_private->pk_init_ctx->id->certs) {
2437 ret = _krb5_pk_set_user_id(context,
2439 opt->opt_private->pk_init_ctx,
2440 opt->opt_private->pk_init_ctx->id->certs);
2442 free(opt->opt_private->pk_init_ctx);
2443 opt->opt_private->pk_init_ctx = NULL;
2447 opt->opt_private->pk_init_ctx->id->cert = NULL;
2449 if ((flags & KRB5_GIC_OPT_PKINIT_USE_ENCKEY) == 0) {
2450 hx509_context hx509ctx = context->hx509ctx;
2451 hx509_cert cert = opt->opt_private->pk_init_ctx->id->cert;
2453 opt->opt_private->pk_init_ctx->keyex = USE_DH;
2456 * If its a ECDSA certs, lets select ECDSA as the keyex algorithm.
2459 AlgorithmIdentifier alg;
2461 ret = hx509_cert_get_SPKI_AlgorithmIdentifier(hx509ctx, cert, &alg);
2463 if (der_heim_oid_cmp(&alg.algorithm, &asn1_oid_id_ecPublicKey) == 0)
2464 opt->opt_private->pk_init_ctx->keyex = USE_ECDH;
2465 free_AlgorithmIdentifier(&alg);
2470 opt->opt_private->pk_init_ctx->keyex = USE_RSA;
2472 if (opt->opt_private->pk_init_ctx->id->certs == NULL) {
2473 krb5_set_error_message(context, EINVAL,
2474 N_("No anonymous pkinit support in RSA mode", ""));
2481 krb5_set_error_message(context, EINVAL,
2482 N_("no support for PKINIT compiled in", ""));
2487 krb5_error_code KRB5_LIB_FUNCTION
2488 krb5_get_init_creds_opt_set_pkinit_user_certs(krb5_context context,
2489 krb5_get_init_creds_opt *opt,
2490 struct hx509_certs_data *certs)
2493 if (opt->opt_private == NULL) {
2494 krb5_set_error_message(context, EINVAL,
2495 N_("PKINIT: on non extendable opt", ""));
2498 if (opt->opt_private->pk_init_ctx == NULL) {
2499 krb5_set_error_message(context, EINVAL,
2500 N_("PKINIT: on pkinit context", ""));
2504 return _krb5_pk_set_user_id(context, NULL, opt->opt_private->pk_init_ctx, certs);
2506 krb5_set_error_message(context, EINVAL,
2507 N_("no support for PKINIT compiled in", ""));
2515 get_ms_san(hx509_context context, hx509_cert cert, char **upn)
2517 hx509_octet_string_list list;
2522 ret = hx509_cert_find_subjectAltName_otherName(context,
2524 &asn1_oid_id_pkinit_ms_san,
2529 if (list.len > 0 && list.val[0].length > 0)
2530 ret = decode_MS_UPN_SAN(list.val[0].data, list.val[0].length,
2534 hx509_free_octet_string_list(&list);
2540 find_ms_san(hx509_context context, hx509_cert cert, void *ctx)
2545 ret = get_ms_san(context, cert, &upn);
2556 * Private since it need to be redesigned using krb5_get_init_creds()
2559 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2560 krb5_pk_enterprise_cert(krb5_context context,
2561 const char *user_id,
2562 krb5_const_realm realm,
2563 krb5_principal *principal,
2564 struct hx509_certs_data **res)
2567 krb5_error_code ret;
2568 hx509_certs certs, result;
2569 hx509_cert cert = NULL;
2577 if (user_id == NULL) {
2578 krb5_set_error_message(context, ENOENT, "no user id");
2582 ret = hx509_certs_init(context->hx509ctx, user_id, 0, NULL, &certs);
2584 pk_copy_error(context, context->hx509ctx, ret,
2585 "Failed to init cert certs");
2589 ret = hx509_query_alloc(context->hx509ctx, &q);
2591 krb5_set_error_message(context, ret, "out of memory");
2592 hx509_certs_free(&certs);
2596 hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY);
2597 hx509_query_match_option(q, HX509_QUERY_OPTION_KU_DIGITALSIGNATURE);
2598 hx509_query_match_eku(q, &asn1_oid_id_pkinit_ms_eku);
2599 hx509_query_match_cmp_func(q, find_ms_san, NULL);
2601 ret = hx509_certs_filter(context->hx509ctx, certs, q, &result);
2602 hx509_query_free(context->hx509ctx, q);
2603 hx509_certs_free(&certs);
2605 pk_copy_error(context, context->hx509ctx, ret,
2606 "Failed to find PKINIT certificate");
2610 ret = hx509_get_one_cert(context->hx509ctx, result, &cert);
2611 hx509_certs_free(&result);
2613 pk_copy_error(context, context->hx509ctx, ret,
2614 "Failed to get one cert");
2618 ret = get_ms_san(context->hx509ctx, cert, &name);
2620 pk_copy_error(context, context->hx509ctx, ret,
2621 "Failed to get MS SAN");
2625 ret = krb5_make_principal(context, principal, realm, name, NULL);
2630 krb5_principal_set_type(context, *principal, KRB5_NT_ENTERPRISE_PRINCIPAL);
2633 ret = hx509_certs_init(context->hx509ctx, "MEMORY:", 0, NULL, res);
2637 ret = hx509_certs_add(context->hx509ctx, *res, cert);
2639 hx509_certs_free(res);
2645 hx509_cert_free(cert);
2649 krb5_set_error_message(context, EINVAL,
2650 N_("no support for PKINIT compiled in", ""));
2655 KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
2656 _krb5_pk_is_kdc_verified(krb5_context context,
2657 krb5_get_init_creds_opt *opt)
2660 opt->opt_private == NULL ||
2661 opt->opt_private->pk_init_ctx == NULL)
2664 return opt->opt_private->pk_init_ctx->kdc_verified;