libtommath: Fix possible integer overflow CVE-2023-36328
[heimdal.git] / lib / krb5 / pkinit.c
blob1b34c18ab810fa46f4cc2186ab529ee10f5b445f
1 /*
2 * Copyright (c) 2003 - 2016 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
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
10 * are met:
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
33 * SUCH DAMAGE.
36 #include "krb5_locl.h"
38 struct krb5_dh_moduli {
39 char *name;
40 unsigned long bits;
41 heim_integer p;
42 heim_integer g;
43 heim_integer q;
46 #ifdef PKINIT
48 #include <cms_asn1.h>
49 #include <pkcs8_asn1.h>
50 #include <pkcs9_asn1.h>
51 #include <pkcs12_asn1.h>
52 #include <pkinit_asn1.h>
53 #include <asn1_err.h>
55 #include <der.h>
57 struct krb5_pk_cert {
58 hx509_cert cert;
61 static void
62 pk_copy_error(krb5_context context,
63 hx509_context hx509ctx,
64 int hxret,
65 const char *fmt,
66 ...)
67 __attribute__ ((__format__ (__printf__, 4, 5)));
73 KRB5_LIB_FUNCTION void KRB5_LIB_CALL
74 _krb5_pk_cert_free(struct krb5_pk_cert *cert)
76 if (cert->cert) {
77 hx509_cert_free(cert->cert);
79 free(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);
89 return ENOMEM;
91 BN_bn2bin(bn, integer->data);
92 integer->negative = BN_is_negative(bn);
93 return 0;
96 static BIGNUM *
97 integer_to_BN(krb5_context context, const char *field, const heim_integer *f)
99 BIGNUM *bn;
101 bn = BN_bin2bn((const unsigned char *)f->data, f->length, NULL);
102 if (bn == NULL) {
103 krb5_set_error_message(context, ENOMEM,
104 N_("PKINIT: parsing BN failed %s", ""), field);
105 return NULL;
107 BN_set_negative(bn, f->negative);
108 return bn;
111 static krb5_error_code
112 select_dh_group(krb5_context context, DH *dh, unsigned long min_bits,
113 struct krb5_dh_moduli **moduli)
115 const struct krb5_dh_moduli *m;
117 if (moduli[0] == NULL) {
118 krb5_set_error_message(context, EINVAL,
119 N_("Did not find a DH group parameter "
120 "matching requirement of %lu bits", ""),
121 min_bits);
122 return EINVAL;
125 if (min_bits == 0) {
126 m = moduli[1]; /* XXX */
127 if (m == NULL)
128 m = moduli[0]; /* XXX */
129 } else {
130 int i;
131 for (i = 0; moduli[i] != NULL; i++) {
132 if (moduli[i]->bits >= min_bits)
133 break;
135 if (moduli[i] == NULL) {
136 krb5_set_error_message(context, EINVAL,
137 N_("Did not find a DH group parameter "
138 "matching requirement of %lu bits", ""),
139 min_bits);
140 return EINVAL;
142 m = moduli[i];
145 dh->p = integer_to_BN(context, "p", &m->p);
146 if (dh->p == NULL)
147 return ENOMEM;
148 dh->g = integer_to_BN(context, "g", &m->g);
149 if (dh->g == NULL)
150 return ENOMEM;
151 dh->q = integer_to_BN(context, "q", &m->q);
152 if (dh->q == NULL)
153 return ENOMEM;
155 return 0;
158 struct certfind {
159 const char *type;
160 const heim_oid *oid;
164 * Try searchin the key by to use by first looking for for PK-INIT
165 * EKU, then the Microsoft smart card EKU and last, no special EKU at all.
168 static krb5_error_code
169 find_cert(krb5_context context, struct krb5_pk_identity *id,
170 hx509_query *q, hx509_cert *cert)
172 struct certfind cf[4] = {
173 { "MobileMe EKU", NULL },
174 { "PKINIT EKU", NULL },
175 { "MS EKU", NULL },
176 { "any (or no)", NULL }
178 int ret = HX509_CERT_NOT_FOUND;
179 size_t i, start = 1;
180 unsigned oids[] = { 1, 2, 840, 113635, 100, 3, 2, 1 };
181 const heim_oid mobileMe = { sizeof(oids)/sizeof(oids[0]), oids };
184 if (id->flags & PKINIT_BTMM)
185 start = 0;
187 cf[0].oid = &mobileMe;
188 cf[1].oid = &asn1_oid_id_pkekuoid;
189 cf[2].oid = &asn1_oid_id_pkinit_ms_eku;
190 cf[3].oid = NULL;
192 for (i = start; i < sizeof(cf)/sizeof(cf[0]); i++) {
193 ret = hx509_query_match_eku(q, cf[i].oid);
194 if (ret) {
195 pk_copy_error(context, context->hx509ctx, ret,
196 "Failed setting %s OID", cf[i].type);
197 return ret;
200 ret = hx509_certs_find(context->hx509ctx, id->certs, q, cert);
201 if (ret == 0)
202 break;
203 pk_copy_error(context, context->hx509ctx, ret,
204 "Failed finding certificate with %s OID", cf[i].type);
206 return ret;
210 static krb5_error_code
211 create_signature(krb5_context context,
212 const heim_oid *eContentType,
213 krb5_data *eContent,
214 struct krb5_pk_identity *id,
215 hx509_peer_info peer,
216 krb5_data *sd_data)
218 int ret, flags = 0;
220 if (id->cert == NULL)
221 flags |= HX509_CMS_SIGNATURE_NO_SIGNER;
223 ret = hx509_cms_create_signed_1(context->hx509ctx,
224 flags,
225 eContentType,
226 eContent->data,
227 eContent->length,
228 NULL,
229 id->cert,
230 peer,
231 NULL,
232 id->certs,
233 sd_data);
234 if (ret) {
235 pk_copy_error(context, context->hx509ctx, ret,
236 "Create CMS signedData");
237 return ret;
240 return 0;
243 static int KRB5_LIB_CALL
244 cert2epi(hx509_context context, void *ctx, hx509_cert c)
246 ExternalPrincipalIdentifiers *ids = ctx;
247 ExternalPrincipalIdentifier id;
248 hx509_name subject = NULL;
249 void *p;
250 int ret;
252 if (ids->len > 10)
253 return 0;
255 memset(&id, 0, sizeof(id));
257 ret = hx509_cert_get_subject(c, &subject);
258 if (ret)
259 return ret;
261 if (hx509_name_is_null_p(subject) != 0) {
263 id.subjectName = calloc(1, sizeof(*id.subjectName));
264 if (id.subjectName == NULL) {
265 hx509_name_free(&subject);
266 free_ExternalPrincipalIdentifier(&id);
267 return ENOMEM;
270 ret = hx509_name_binary(subject, id.subjectName);
271 if (ret) {
272 hx509_name_free(&subject);
273 free_ExternalPrincipalIdentifier(&id);
274 return ret;
277 hx509_name_free(&subject);
280 id.issuerAndSerialNumber = calloc(1, sizeof(*id.issuerAndSerialNumber));
281 if (id.issuerAndSerialNumber == NULL) {
282 free_ExternalPrincipalIdentifier(&id);
283 return ENOMEM;
287 IssuerAndSerialNumber iasn;
288 hx509_name issuer;
289 size_t size = 0;
291 memset(&iasn, 0, sizeof(iasn));
293 ret = hx509_cert_get_issuer(c, &issuer);
294 if (ret) {
295 free_ExternalPrincipalIdentifier(&id);
296 return ret;
299 ret = hx509_name_to_Name(issuer, &iasn.issuer);
300 hx509_name_free(&issuer);
301 if (ret) {
302 free_ExternalPrincipalIdentifier(&id);
303 return ret;
306 ret = hx509_cert_get_serialnumber(c, &iasn.serialNumber);
307 if (ret) {
308 free_IssuerAndSerialNumber(&iasn);
309 free_ExternalPrincipalIdentifier(&id);
310 return ret;
313 ASN1_MALLOC_ENCODE(IssuerAndSerialNumber,
314 id.issuerAndSerialNumber->data,
315 id.issuerAndSerialNumber->length,
316 &iasn, &size, ret);
317 free_IssuerAndSerialNumber(&iasn);
318 if (ret) {
319 free_ExternalPrincipalIdentifier(&id);
320 return ret;
322 if (id.issuerAndSerialNumber->length != size)
323 abort();
326 id.subjectKeyIdentifier = NULL;
328 p = realloc(ids->val, sizeof(ids->val[0]) * (ids->len + 1));
329 if (p == NULL) {
330 free_ExternalPrincipalIdentifier(&id);
331 return ENOMEM;
334 ids->val = p;
335 ids->val[ids->len] = id;
336 ids->len++;
338 return 0;
341 static krb5_error_code
342 build_edi(krb5_context context,
343 hx509_context hx509ctx,
344 hx509_certs certs,
345 ExternalPrincipalIdentifiers *ids)
347 return hx509_certs_iter_f(hx509ctx, certs, cert2epi, ids);
350 static krb5_error_code
351 build_auth_pack(krb5_context context,
352 unsigned nonce,
353 krb5_pk_init_ctx ctx,
354 const KDC_REQ_BODY *body,
355 AuthPack *a)
357 size_t buf_size, len = 0;
358 krb5_error_code ret;
359 void *buf;
360 krb5_timestamp sec;
361 int32_t usec;
362 Checksum checksum;
364 krb5_clear_error_message(context);
366 memset(&checksum, 0, sizeof(checksum));
368 krb5_us_timeofday(context, &sec, &usec);
369 a->pkAuthenticator.ctime = sec;
370 a->pkAuthenticator.nonce = nonce;
372 ASN1_MALLOC_ENCODE(KDC_REQ_BODY, buf, buf_size, body, &len, ret);
373 if (ret)
374 return ret;
375 if (buf_size != len)
376 krb5_abortx(context, "internal error in ASN.1 encoder");
378 ret = krb5_create_checksum(context,
379 NULL,
381 CKSUMTYPE_SHA1,
382 buf,
383 len,
384 &checksum);
385 free(buf);
386 if (ret)
387 return ret;
389 ALLOC(a->pkAuthenticator.paChecksum, 1);
390 if (a->pkAuthenticator.paChecksum == NULL) {
391 return krb5_enomem(context);
394 ret = krb5_data_copy(a->pkAuthenticator.paChecksum,
395 checksum.checksum.data, checksum.checksum.length);
396 free_Checksum(&checksum);
397 if (ret)
398 return ret;
400 if (ctx->keyex == USE_DH || ctx->keyex == USE_ECDH) {
401 const char *moduli_file;
402 unsigned long dh_min_bits;
403 krb5_data dhbuf;
404 size_t size = 0;
406 krb5_data_zero(&dhbuf);
410 moduli_file = krb5_config_get_string(context, NULL,
411 "libdefaults",
412 "moduli",
413 NULL);
415 dh_min_bits =
416 krb5_config_get_int_default(context, NULL, 0,
417 "libdefaults",
418 "pkinit_dh_min_bits",
419 NULL);
421 ret = _krb5_parse_moduli(context, moduli_file, &ctx->m);
422 if (ret)
423 return ret;
425 ctx->u.dh = DH_new();
426 if (ctx->u.dh == NULL)
427 return krb5_enomem(context);
429 ret = select_dh_group(context, ctx->u.dh, dh_min_bits, ctx->m);
430 if (ret)
431 return ret;
433 if (DH_generate_key(ctx->u.dh) != 1) {
434 krb5_set_error_message(context, ENOMEM,
435 N_("pkinit: failed to generate DH key", ""));
436 return ENOMEM;
440 if (1 /* support_cached_dh */) {
441 ALLOC(a->clientDHNonce, 1);
442 if (a->clientDHNonce == NULL) {
443 krb5_clear_error_message(context);
444 return ENOMEM;
446 ret = krb5_data_alloc(a->clientDHNonce, 40);
447 if (a->clientDHNonce == NULL) {
448 krb5_clear_error_message(context);
449 return ret;
451 ret = RAND_bytes(a->clientDHNonce->data, a->clientDHNonce->length);
452 if (ret != 1)
453 return KRB5_CRYPTO_INTERNAL;
454 ret = krb5_copy_data(context, a->clientDHNonce,
455 &ctx->clientDHNonce);
456 if (ret)
457 return ret;
460 ALLOC(a->clientPublicValue, 1);
461 if (a->clientPublicValue == NULL)
462 return ENOMEM;
464 if (ctx->keyex == USE_DH) {
465 DH *dh = ctx->u.dh;
466 DomainParameters dp;
467 heim_integer dh_pub_key;
469 ret = der_copy_oid(&asn1_oid_id_dhpublicnumber,
470 &a->clientPublicValue->algorithm.algorithm);
471 if (ret)
472 return ret;
474 memset(&dp, 0, sizeof(dp));
476 ret = BN_to_integer(context, dh->p, &dp.p);
477 if (ret) {
478 free_DomainParameters(&dp);
479 return ret;
481 ret = BN_to_integer(context, dh->g, &dp.g);
482 if (ret) {
483 free_DomainParameters(&dp);
484 return ret;
486 if (dh->q && BN_num_bits(dh->q)) {
488 * The q parameter is required, but MSFT made it optional.
489 * It's only required in order to verify the domain parameters
490 * -- the security of the DH group --, but we validate groups
491 * against known groups rather than accepting arbitrary groups
492 * chosen by the peer, so we really don't need to have put it
493 * on the wire. Because these are Oakley groups, and the
494 * primes are Sophie Germain primes, q is p>>1 and we can
495 * compute it on the fly like MIT Kerberos does, but we'd have
496 * to implement BN_rshift1().
498 dp.q = calloc(1, sizeof(*dp.q));
499 if (dp.q == NULL) {
500 free_DomainParameters(&dp);
501 return ENOMEM;
503 ret = BN_to_integer(context, dh->q, dp.q);
504 if (ret) {
505 free_DomainParameters(&dp);
506 return ret;
509 dp.j = NULL;
510 dp.validationParms = NULL;
512 a->clientPublicValue->algorithm.parameters =
513 malloc(sizeof(*a->clientPublicValue->algorithm.parameters));
514 if (a->clientPublicValue->algorithm.parameters == NULL) {
515 free_DomainParameters(&dp);
516 return ret;
519 ASN1_MALLOC_ENCODE(DomainParameters,
520 a->clientPublicValue->algorithm.parameters->data,
521 a->clientPublicValue->algorithm.parameters->length,
522 &dp, &size, ret);
523 free_DomainParameters(&dp);
524 if (ret)
525 return ret;
526 if (size != a->clientPublicValue->algorithm.parameters->length)
527 krb5_abortx(context, "Internal ASN1 encoder error");
529 ret = BN_to_integer(context, dh->pub_key, &dh_pub_key);
530 if (ret)
531 return ret;
533 ASN1_MALLOC_ENCODE(DHPublicKey, dhbuf.data, dhbuf.length,
534 &dh_pub_key, &size, ret);
535 der_free_heim_integer(&dh_pub_key);
536 if (ret)
537 return ret;
538 if (size != dhbuf.length)
539 krb5_abortx(context, "asn1 internal error");
540 a->clientPublicValue->subjectPublicKey.length = dhbuf.length * 8;
541 a->clientPublicValue->subjectPublicKey.data = dhbuf.data;
542 } else if (ctx->keyex == USE_ECDH) {
543 ret = _krb5_build_authpack_subjectPK_EC(context, ctx, a);
544 if (ret)
545 return ret;
546 } else
547 krb5_abortx(context, "internal error");
551 a->supportedCMSTypes = calloc(1, sizeof(*a->supportedCMSTypes));
552 if (a->supportedCMSTypes == NULL)
553 return ENOMEM;
555 ret = hx509_crypto_available(context->hx509ctx, HX509_SELECT_ALL,
556 ctx->id->cert,
557 &a->supportedCMSTypes->val,
558 &a->supportedCMSTypes->len);
559 if (ret)
560 return ret;
563 return ret;
566 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
567 _krb5_pk_mk_ContentInfo(krb5_context context,
568 const krb5_data *buf,
569 const heim_oid *oid,
570 struct ContentInfo *content_info)
572 krb5_error_code ret;
574 ret = der_copy_oid(oid, &content_info->contentType);
575 if (ret)
576 return ret;
577 ALLOC(content_info->content, 1);
578 if (content_info->content == NULL)
579 return ENOMEM;
580 content_info->content->data = malloc(buf->length);
581 if (content_info->content->data == NULL)
582 return ENOMEM;
583 memcpy(content_info->content->data, buf->data, buf->length);
584 content_info->content->length = buf->length;
585 return 0;
588 static krb5_error_code
589 pk_mk_padata(krb5_context context,
590 krb5_pk_init_ctx ctx,
591 const KDC_REQ_BODY *req_body,
592 unsigned nonce,
593 METHOD_DATA *md)
595 struct ContentInfo content_info;
596 krb5_error_code ret;
597 const heim_oid *oid = NULL;
598 size_t size = 0;
599 krb5_data buf, sd_buf;
600 int pa_type = -1;
602 krb5_data_zero(&buf);
603 krb5_data_zero(&sd_buf);
604 memset(&content_info, 0, sizeof(content_info));
606 if (ctx->type == PKINIT_WIN2K) {
607 AuthPack_Win2k ap;
608 krb5_timestamp sec;
609 int32_t usec;
611 memset(&ap, 0, sizeof(ap));
613 /* fill in PKAuthenticator */
614 ret = copy_PrincipalName(req_body->sname, &ap.pkAuthenticator.kdcName);
615 if (ret) {
616 free_AuthPack_Win2k(&ap);
617 krb5_clear_error_message(context);
618 goto out;
620 ret = copy_Realm(&req_body->realm, &ap.pkAuthenticator.kdcRealm);
621 if (ret) {
622 free_AuthPack_Win2k(&ap);
623 krb5_clear_error_message(context);
624 goto out;
627 krb5_us_timeofday(context, &sec, &usec);
628 ap.pkAuthenticator.ctime = sec;
629 ap.pkAuthenticator.cusec = usec;
630 ap.pkAuthenticator.nonce = nonce;
632 ASN1_MALLOC_ENCODE(AuthPack_Win2k, buf.data, buf.length,
633 &ap, &size, ret);
634 free_AuthPack_Win2k(&ap);
635 if (ret) {
636 krb5_set_error_message(context, ret,
637 N_("Failed encoding AuthPackWin: %d", ""),
638 (int)ret);
639 goto out;
641 if (buf.length != size)
642 krb5_abortx(context, "internal ASN1 encoder error");
644 oid = &asn1_oid_id_pkcs7_data;
645 } else if (ctx->type == PKINIT_27) {
646 AuthPack ap;
648 memset(&ap, 0, sizeof(ap));
650 ret = build_auth_pack(context, nonce, ctx, req_body, &ap);
651 if (ret) {
652 free_AuthPack(&ap);
653 goto out;
656 ASN1_MALLOC_ENCODE(AuthPack, buf.data, buf.length, &ap, &size, ret);
657 free_AuthPack(&ap);
658 if (ret) {
659 krb5_set_error_message(context, ret,
660 N_("Failed encoding AuthPack: %d", ""),
661 (int)ret);
662 goto out;
664 if (buf.length != size)
665 krb5_abortx(context, "internal ASN1 encoder error");
667 oid = &asn1_oid_id_pkauthdata;
668 } else
669 krb5_abortx(context, "internal pkinit error");
671 ret = create_signature(context, oid, &buf, ctx->id,
672 ctx->peer, &sd_buf);
673 krb5_data_free(&buf);
674 if (ret)
675 goto out;
677 ret = hx509_cms_wrap_ContentInfo(&asn1_oid_id_pkcs7_signedData, &sd_buf, &buf);
678 krb5_data_free(&sd_buf);
679 if (ret) {
680 krb5_set_error_message(context, ret,
681 N_("ContentInfo wrapping of signedData failed",""));
682 goto out;
685 if (ctx->type == PKINIT_WIN2K) {
686 PA_PK_AS_REQ_Win2k winreq;
688 pa_type = KRB5_PADATA_PK_AS_REQ_WIN;
690 memset(&winreq, 0, sizeof(winreq));
692 winreq.signed_auth_pack = buf;
694 ASN1_MALLOC_ENCODE(PA_PK_AS_REQ_Win2k, buf.data, buf.length,
695 &winreq, &size, ret);
696 free_PA_PK_AS_REQ_Win2k(&winreq);
698 } else if (ctx->type == PKINIT_27) {
699 PA_PK_AS_REQ req;
701 pa_type = KRB5_PADATA_PK_AS_REQ;
703 memset(&req, 0, sizeof(req));
704 req.signedAuthPack = buf;
706 if (ctx->trustedCertifiers) {
708 req.trustedCertifiers = calloc(1, sizeof(*req.trustedCertifiers));
709 if (req.trustedCertifiers == NULL) {
710 ret = krb5_enomem(context);
711 free_PA_PK_AS_REQ(&req);
712 goto out;
714 ret = build_edi(context, context->hx509ctx,
715 ctx->id->anchors, req.trustedCertifiers);
716 if (ret) {
717 krb5_set_error_message(context, ret,
718 N_("pk-init: failed to build "
719 "trustedCertifiers", ""));
720 free_PA_PK_AS_REQ(&req);
721 goto out;
724 req.kdcPkId = NULL;
726 ASN1_MALLOC_ENCODE(PA_PK_AS_REQ, buf.data, buf.length,
727 &req, &size, ret);
729 free_PA_PK_AS_REQ(&req);
731 } else
732 krb5_abortx(context, "internal pkinit error");
733 if (ret) {
734 krb5_set_error_message(context, ret, "PA-PK-AS-REQ %d", (int)ret);
735 goto out;
737 if (buf.length != size)
738 krb5_abortx(context, "Internal ASN1 encoder error");
740 ret = krb5_padata_add(context, md, pa_type, buf.data, buf.length);
741 if (ret)
742 free(buf.data);
744 if (ret == 0)
745 ret = krb5_padata_add(context, md, KRB5_PADATA_PK_AS_09_BINDING, NULL, 0);
747 out:
748 free_ContentInfo(&content_info);
750 return ret;
754 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
755 _krb5_pk_mk_padata(krb5_context context,
756 void *c,
757 int ic_flags,
758 int win2k,
759 const KDC_REQ_BODY *req_body,
760 unsigned nonce,
761 METHOD_DATA *md)
763 krb5_pk_init_ctx ctx = c;
764 int win2k_compat;
766 if (ctx->id->certs == NULL && ctx->anonymous == 0) {
767 krb5_set_error_message(context, HEIM_PKINIT_NO_PRIVATE_KEY,
768 N_("PKINIT: No user certificate given", ""));
769 return HEIM_PKINIT_NO_PRIVATE_KEY;
772 win2k_compat = krb5_config_get_bool_default(context, NULL,
773 win2k,
774 "realms",
775 req_body->realm,
776 "pkinit_win2k",
777 NULL);
779 if (win2k_compat) {
780 ctx->require_binding =
781 krb5_config_get_bool_default(context, NULL,
782 TRUE,
783 "realms",
784 req_body->realm,
785 "pkinit_win2k_require_binding",
786 NULL);
787 ctx->type = PKINIT_WIN2K;
788 } else
789 ctx->type = PKINIT_27;
791 ctx->require_eku =
792 krb5_config_get_bool_default(context, NULL,
793 TRUE,
794 "realms",
795 req_body->realm,
796 "pkinit_require_eku",
797 NULL);
798 if (ic_flags & KRB5_INIT_CREDS_NO_C_NO_EKU_CHECK)
799 ctx->require_eku = 0;
800 if (ctx->id->flags & (PKINIT_BTMM | PKINIT_NO_KDC_ANCHOR))
801 ctx->require_eku = 0;
803 ctx->require_krbtgt_otherName =
804 krb5_config_get_bool_default(context, NULL,
805 TRUE,
806 "realms",
807 req_body->realm,
808 "pkinit_require_krbtgt_otherName",
809 NULL);
810 if (ic_flags & KRB5_INIT_CREDS_PKINIT_NO_KRBTGT_OTHERNAME_CHECK)
811 ctx->require_krbtgt_otherName = FALSE;
813 ctx->require_hostname_match =
814 krb5_config_get_bool_default(context, NULL,
815 FALSE,
816 "realms",
817 req_body->realm,
818 "pkinit_require_hostname_match",
819 NULL);
821 ctx->trustedCertifiers =
822 krb5_config_get_bool_default(context, NULL,
823 TRUE,
824 "realms",
825 req_body->realm,
826 "pkinit_trustedCertifiers",
827 NULL);
829 return pk_mk_padata(context, ctx, req_body, nonce, md);
832 static krb5_error_code
833 pk_verify_sign(krb5_context context,
834 const void *data,
835 size_t length,
836 struct krb5_pk_identity *id,
837 heim_oid *contentType,
838 krb5_data *content,
839 struct krb5_pk_cert **signer)
841 hx509_certs signer_certs;
842 int ret;
843 unsigned flags = 0, verify_flags = 0;
845 *signer = NULL;
847 if (id->flags & PKINIT_BTMM) {
848 flags |= HX509_CMS_VS_ALLOW_DATA_OID_MISMATCH;
849 flags |= HX509_CMS_VS_NO_KU_CHECK;
850 flags |= HX509_CMS_VS_NO_VALIDATE;
852 if (id->flags & PKINIT_NO_KDC_ANCHOR)
853 flags |= HX509_CMS_VS_NO_VALIDATE;
855 ret = hx509_cms_verify_signed_ext(context->hx509ctx,
856 id->verify_ctx,
857 flags,
858 data,
859 length,
860 NULL,
861 id->certpool,
862 contentType,
863 content,
864 &signer_certs,
865 &verify_flags);
866 if (ret) {
867 pk_copy_error(context, context->hx509ctx, ret,
868 "CMS verify signed failed");
869 return ret;
872 heim_assert((verify_flags & HX509_CMS_VSE_VALIDATED) ||
873 (id->flags & PKINIT_NO_KDC_ANCHOR),
874 "Either PKINIT signer must be validated, or NO_KDC_ANCHOR must be set");
876 if ((verify_flags & HX509_CMS_VSE_VALIDATED) == 0)
877 goto out;
879 *signer = calloc(1, sizeof(**signer));
880 if (*signer == NULL) {
881 krb5_clear_error_message(context);
882 ret = ENOMEM;
883 goto out;
886 ret = hx509_get_one_cert(context->hx509ctx, signer_certs, &(*signer)->cert);
887 if (ret) {
888 pk_copy_error(context, context->hx509ctx, ret,
889 "Failed to get one of the signer certs");
890 goto out;
893 out:
894 hx509_certs_free(&signer_certs);
895 if (ret) {
896 if (*signer) {
897 hx509_cert_free((*signer)->cert);
898 free(*signer);
899 *signer = NULL;
903 return ret;
906 static krb5_error_code
907 get_reply_key_win(krb5_context context,
908 const krb5_data *content,
909 unsigned nonce,
910 krb5_keyblock **key)
912 ReplyKeyPack_Win2k key_pack;
913 krb5_error_code ret;
914 size_t size;
916 ret = decode_ReplyKeyPack_Win2k(content->data,
917 content->length,
918 &key_pack,
919 &size);
920 if (ret) {
921 krb5_set_error_message(context, ret,
922 N_("PKINIT decoding reply key failed", ""));
923 free_ReplyKeyPack_Win2k(&key_pack);
924 return ret;
927 if ((unsigned)key_pack.nonce != nonce) {
928 krb5_set_error_message(context, ret,
929 N_("PKINIT enckey nonce is wrong", ""));
930 free_ReplyKeyPack_Win2k(&key_pack);
931 return KRB5KRB_AP_ERR_MODIFIED;
934 *key = malloc (sizeof (**key));
935 if (*key == NULL) {
936 free_ReplyKeyPack_Win2k(&key_pack);
937 return krb5_enomem(context);
940 ret = copy_EncryptionKey(&key_pack.replyKey, *key);
941 free_ReplyKeyPack_Win2k(&key_pack);
942 if (ret) {
943 krb5_set_error_message(context, ret,
944 N_("PKINIT failed copying reply key", ""));
945 free(*key);
946 *key = NULL;
949 return ret;
952 static krb5_error_code
953 get_reply_key(krb5_context context,
954 const krb5_data *content,
955 const krb5_data *req_buffer,
956 krb5_keyblock **key)
958 ReplyKeyPack key_pack;
959 krb5_error_code ret;
960 size_t size;
962 ret = decode_ReplyKeyPack(content->data,
963 content->length,
964 &key_pack,
965 &size);
966 if (ret) {
967 krb5_set_error_message(context, ret,
968 N_("PKINIT decoding reply key failed", ""));
969 free_ReplyKeyPack(&key_pack);
970 return ret;
974 krb5_crypto crypto;
977 * XXX Verify kp.replyKey is a allowed enctype in the
978 * configuration file
981 ret = krb5_crypto_init(context, &key_pack.replyKey, 0, &crypto);
982 if (ret) {
983 free_ReplyKeyPack(&key_pack);
984 return ret;
987 ret = krb5_verify_checksum(context, crypto, 6,
988 req_buffer->data, req_buffer->length,
989 &key_pack.asChecksum);
990 krb5_crypto_destroy(context, crypto);
991 if (ret) {
992 free_ReplyKeyPack(&key_pack);
993 return ret;
997 *key = malloc (sizeof (**key));
998 if (*key == NULL) {
999 free_ReplyKeyPack(&key_pack);
1000 return krb5_enomem(context);
1003 ret = copy_EncryptionKey(&key_pack.replyKey, *key);
1004 free_ReplyKeyPack(&key_pack);
1005 if (ret) {
1006 krb5_set_error_message(context, ret,
1007 N_("PKINIT failed copying reply key", ""));
1008 free(*key);
1009 *key = NULL;
1012 return ret;
1016 static krb5_error_code
1017 pk_verify_host(krb5_context context,
1018 const char *realm,
1019 struct krb5_pk_init_ctx_data *ctx,
1020 struct krb5_pk_cert *host)
1022 krb5_error_code ret = 0;
1024 if (ctx->require_eku) {
1025 ret = hx509_cert_check_eku(context->hx509ctx, host->cert,
1026 &asn1_oid_id_pkkdcekuoid, 0);
1027 if (ret) {
1028 krb5_set_error_message(context, ret,
1029 N_("No PK-INIT KDC EKU in kdc certificate", ""));
1030 return ret;
1033 if (ctx->require_krbtgt_otherName) {
1034 hx509_octet_string_list list;
1035 size_t i;
1036 int matched = 0;
1038 ret = hx509_cert_find_subjectAltName_otherName(context->hx509ctx,
1039 host->cert,
1040 &asn1_oid_id_pkinit_san,
1041 &list);
1042 if (ret) {
1043 krb5_set_error_message(context, ret,
1044 N_("Failed to find the PK-INIT "
1045 "subjectAltName in the KDC "
1046 "certificate", ""));
1048 return ret;
1052 * subjectAltNames are multi-valued, and a single KDC may serve
1053 * multiple realms. The SAN validation here must accept
1054 * the KDC's cert if *any* of the SANs match the expected KDC.
1055 * It is OK for *some* of the SANs to not match, provided at least
1056 * one does.
1058 for (i = 0; matched == 0 && i < list.len; i++) {
1059 KRB5PrincipalName r;
1061 ret = decode_KRB5PrincipalName(list.val[i].data,
1062 list.val[i].length,
1064 NULL);
1065 if (ret) {
1066 krb5_set_error_message(context, ret,
1067 N_("Failed to decode the PK-INIT "
1068 "subjectAltName in the "
1069 "KDC certificate", ""));
1071 break;
1074 if (r.principalName.name_string.len == 2 &&
1075 strcmp(r.principalName.name_string.val[0], KRB5_TGS_NAME) == 0
1076 && strcmp(r.principalName.name_string.val[1], realm) == 0
1077 && strcmp(r.realm, realm) == 0)
1078 matched = 1;
1080 free_KRB5PrincipalName(&r);
1082 hx509_free_octet_string_list(&list);
1084 if (matched == 0 &&
1085 (ctx->id->flags & PKINIT_NO_KDC_ANCHOR) == 0) {
1086 ret = KRB5_KDC_ERR_INVALID_CERTIFICATE;
1087 /* XXX: Lost in translation... */
1088 krb5_set_error_message(context, ret,
1089 N_("KDC has wrong realm name in "
1090 "the certificate", ""));
1093 if (ret)
1094 return ret;
1096 return ret;
1099 static krb5_error_code
1100 pk_rd_pa_reply_enckey(krb5_context context,
1101 int type,
1102 const heim_octet_string *indata,
1103 const heim_oid *dataType,
1104 const char *realm,
1105 krb5_pk_init_ctx ctx,
1106 krb5_enctype etype,
1107 unsigned nonce,
1108 const krb5_data *req_buffer,
1109 PA_DATA *pa,
1110 krb5_keyblock **key)
1112 krb5_error_code ret;
1113 struct krb5_pk_cert *host = NULL;
1114 krb5_data content;
1115 heim_octet_string unwrapped;
1116 heim_oid contentType = { 0, NULL };
1117 int flags = HX509_CMS_UE_DONT_REQUIRE_KU_ENCIPHERMENT;
1119 if (der_heim_oid_cmp(&asn1_oid_id_pkcs7_envelopedData, dataType)) {
1120 krb5_set_error_message(context, EINVAL,
1121 N_("PKINIT: Invalid content type", ""));
1122 return EINVAL;
1125 if (ctx->type == PKINIT_WIN2K)
1126 flags |= HX509_CMS_UE_ALLOW_WEAK;
1128 ret = hx509_cms_unenvelope(context->hx509ctx,
1129 ctx->id->certs,
1130 flags,
1131 indata->data,
1132 indata->length,
1133 NULL,
1135 &contentType,
1136 &content);
1137 if (ret) {
1138 pk_copy_error(context, context->hx509ctx, ret,
1139 "Failed to unenvelope CMS data in PK-INIT reply");
1140 return ret;
1142 der_free_oid(&contentType);
1144 /* win2k uses ContentInfo */
1145 if (type == PKINIT_WIN2K) {
1146 heim_oid type2;
1148 ret = hx509_cms_unwrap_ContentInfo(&content, &type2, &unwrapped, NULL);
1149 if (ret) {
1150 /* windows LH with interesting CMS packets */
1151 size_t ph = 1 + der_length_len(content.length);
1152 unsigned char *ptr = malloc(content.length + ph);
1153 size_t l;
1155 memcpy(ptr + ph, content.data, content.length);
1157 ret = der_put_length_and_tag (ptr + ph - 1, ph, content.length,
1158 ASN1_C_UNIV, CONS, UT_Sequence, &l);
1159 if (ret) {
1160 free(ptr);
1161 return ret;
1163 free(content.data);
1164 content.data = ptr;
1165 content.length += ph;
1167 ret = hx509_cms_unwrap_ContentInfo(&content, &type2, &unwrapped, NULL);
1168 if (ret)
1169 goto out;
1171 if (der_heim_oid_cmp(&type2, &asn1_oid_id_pkcs7_signedData)) {
1172 ret = EINVAL; /* XXX */
1173 krb5_set_error_message(context, ret,
1174 N_("PKINIT: Invalid content type", ""));
1175 der_free_oid(&type2);
1176 der_free_octet_string(&unwrapped);
1177 goto out;
1179 der_free_oid(&type2);
1180 krb5_data_free(&content);
1181 ret = krb5_data_copy(&content, unwrapped.data, unwrapped.length);
1182 der_free_octet_string(&unwrapped);
1183 if (ret) {
1184 krb5_set_error_message(context, ret,
1185 N_("malloc: out of memory", ""));
1186 goto out;
1190 ret = pk_verify_sign(context,
1191 content.data,
1192 content.length,
1193 ctx->id,
1194 &contentType,
1195 &unwrapped,
1196 &host);
1197 if (ret == 0) {
1198 krb5_data_free(&content);
1199 ret = krb5_data_copy(&content, unwrapped.data, unwrapped.length);
1200 der_free_octet_string(&unwrapped);
1202 if (ret)
1203 goto out;
1205 heim_assert(host || (ctx->id->flags & PKINIT_NO_KDC_ANCHOR),
1206 "KDC signature must be verified unless PKINIT_NO_KDC_ANCHOR set");
1208 if (host) {
1209 /* make sure that it is the kdc's certificate */
1210 ret = pk_verify_host(context, realm, ctx, host);
1211 if (ret)
1212 goto out;
1214 ctx->kdc_verified = 1;
1217 #if 0
1218 if (type == PKINIT_WIN2K) {
1219 if (der_heim_oid_cmp(&contentType, &asn1_oid_id_pkcs7_data) != 0) {
1220 ret = KRB5KRB_AP_ERR_MSG_TYPE;
1221 krb5_set_error_message(context, ret, "PKINIT: reply key, wrong oid");
1222 goto out;
1224 } else {
1225 if (der_heim_oid_cmp(&contentType, &asn1_oid_id_pkrkeydata) != 0) {
1226 ret = KRB5KRB_AP_ERR_MSG_TYPE;
1227 krb5_set_error_message(context, ret, "PKINIT: reply key, wrong oid");
1228 goto out;
1231 #endif
1233 switch(type) {
1234 case PKINIT_WIN2K:
1235 ret = get_reply_key(context, &content, req_buffer, key);
1236 if (ret != 0 && ctx->require_binding == 0)
1237 ret = get_reply_key_win(context, &content, nonce, key);
1238 break;
1239 case PKINIT_27:
1240 ret = get_reply_key(context, &content, req_buffer, key);
1241 break;
1243 if (ret)
1244 goto out;
1246 /* XXX compare given etype with key->etype */
1248 out:
1249 if (host)
1250 _krb5_pk_cert_free(host);
1251 der_free_oid(&contentType);
1252 krb5_data_free(&content);
1254 return ret;
1258 * RFC 8062 section 7:
1260 * The client then decrypts the KDC contribution key and verifies that
1261 * the ticket session key in the returned ticket is the combined key of
1262 * the KDC contribution key and the reply key.
1264 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1265 _krb5_pk_kx_confirm(krb5_context context,
1266 krb5_pk_init_ctx ctx,
1267 krb5_keyblock *reply_key,
1268 krb5_keyblock *session_key,
1269 PA_DATA *pa_pkinit_kx)
1271 krb5_error_code ret;
1272 EncryptedData ed;
1273 krb5_keyblock ck, sk_verify;
1274 krb5_crypto ck_crypto = NULL;
1275 krb5_crypto rk_crypto = NULL;
1276 size_t len;
1277 krb5_data data;
1278 krb5_data p1 = { sizeof("PKINIT") - 1, "PKINIT" };
1279 krb5_data p2 = { sizeof("KEYEXCHANGE") - 1, "KEYEXCHANGE" };
1281 heim_assert(ctx != NULL, "PKINIT context is non-NULL");
1282 heim_assert(reply_key != NULL, "reply key is non-NULL");
1283 heim_assert(session_key != NULL, "session key is non-NULL");
1285 /* PA-PKINIT-KX is optional unless anonymous */
1286 if (pa_pkinit_kx == NULL)
1287 return ctx->anonymous ? KRB5_KDCREP_MODIFIED : 0;
1289 memset(&ed, 0, sizeof(ed));
1290 krb5_keyblock_zero(&ck);
1291 krb5_keyblock_zero(&sk_verify);
1292 krb5_data_zero(&data);
1294 ret = decode_EncryptedData(pa_pkinit_kx->padata_value.data,
1295 pa_pkinit_kx->padata_value.length,
1296 &ed, &len);
1297 if (ret)
1298 goto out;
1300 if (len != pa_pkinit_kx->padata_value.length) {
1301 ret = KRB5_KDCREP_MODIFIED;
1302 goto out;
1305 ret = krb5_crypto_init(context, reply_key, 0, &rk_crypto);
1306 if (ret)
1307 goto out;
1309 ret = krb5_decrypt_EncryptedData(context, rk_crypto,
1310 KRB5_KU_PA_PKINIT_KX,
1311 &ed, &data);
1312 if (ret)
1313 goto out;
1315 ret = decode_EncryptionKey(data.data, data.length,
1316 &ck, &len);
1317 if (ret)
1318 goto out;
1320 ret = krb5_crypto_init(context, &ck, 0, &ck_crypto);
1321 if (ret)
1322 goto out;
1324 ret = krb5_crypto_fx_cf2(context, ck_crypto, rk_crypto,
1325 &p1, &p2, session_key->keytype,
1326 &sk_verify);
1327 if (ret)
1328 goto out;
1330 if (sk_verify.keytype != session_key->keytype ||
1331 krb5_data_ct_cmp(&sk_verify.keyvalue, &session_key->keyvalue) != 0) {
1332 ret = KRB5_KDCREP_MODIFIED;
1333 goto out;
1336 out:
1337 free_EncryptedData(&ed);
1338 krb5_free_keyblock_contents(context, &ck);
1339 krb5_free_keyblock_contents(context, &sk_verify);
1340 if (ck_crypto)
1341 krb5_crypto_destroy(context, ck_crypto);
1342 if (rk_crypto)
1343 krb5_crypto_destroy(context, rk_crypto);
1344 krb5_data_free(&data);
1346 return ret;
1349 static krb5_error_code
1350 pk_rd_pa_reply_dh(krb5_context context,
1351 const heim_octet_string *indata,
1352 const heim_oid *dataType,
1353 const char *realm,
1354 krb5_pk_init_ctx ctx,
1355 krb5_enctype etype,
1356 const DHNonce *c_n,
1357 const DHNonce *k_n,
1358 unsigned nonce,
1359 PA_DATA *pa,
1360 krb5_keyblock **key)
1362 const unsigned char *p;
1363 unsigned char *dh_gen_key = NULL;
1364 struct krb5_pk_cert *host = NULL;
1365 BIGNUM *kdc_dh_pubkey = NULL;
1366 KDCDHKeyInfo kdc_dh_info;
1367 heim_oid contentType = { 0, NULL };
1368 krb5_data content;
1369 krb5_error_code ret;
1370 int dh_gen_keylen = 0;
1371 size_t size;
1373 krb5_data_zero(&content);
1374 memset(&kdc_dh_info, 0, sizeof(kdc_dh_info));
1376 if (der_heim_oid_cmp(&asn1_oid_id_pkcs7_signedData, dataType)) {
1377 krb5_set_error_message(context, EINVAL,
1378 N_("PKINIT: Invalid content type", ""));
1379 return EINVAL;
1382 ret = pk_verify_sign(context,
1383 indata->data,
1384 indata->length,
1385 ctx->id,
1386 &contentType,
1387 &content,
1388 &host);
1389 if (ret)
1390 goto out;
1392 heim_assert(host || (ctx->id->flags & PKINIT_NO_KDC_ANCHOR),
1393 "KDC signature must be verified unless PKINIT_NO_KDC_ANCHOR set");
1395 if (host) {
1396 /* make sure that it is the kdc's certificate */
1397 ret = pk_verify_host(context, realm, ctx, host);
1398 if (ret)
1399 goto out;
1401 ctx->kdc_verified = 1;
1404 if (der_heim_oid_cmp(&contentType, &asn1_oid_id_pkdhkeydata)) {
1405 ret = KRB5KRB_AP_ERR_MSG_TYPE;
1406 krb5_set_error_message(context, ret,
1407 N_("pkinit - dh reply contains wrong oid", ""));
1408 goto out;
1411 ret = decode_KDCDHKeyInfo(content.data,
1412 content.length,
1413 &kdc_dh_info,
1414 &size);
1416 if (ret) {
1417 krb5_set_error_message(context, ret,
1418 N_("pkinit - failed to decode "
1419 "KDC DH Key Info", ""));
1420 goto out;
1423 if (kdc_dh_info.nonce != nonce) {
1424 ret = KRB5KRB_AP_ERR_MODIFIED;
1425 krb5_set_error_message(context, ret,
1426 N_("PKINIT: DH nonce is wrong", ""));
1427 goto out;
1430 if (kdc_dh_info.dhKeyExpiration) {
1431 if (k_n == NULL) {
1432 ret = KRB5KRB_ERR_GENERIC;
1433 krb5_set_error_message(context, ret,
1434 N_("pkinit; got key expiration "
1435 "without server nonce", ""));
1436 goto out;
1438 if (c_n == NULL) {
1439 ret = KRB5KRB_ERR_GENERIC;
1440 krb5_set_error_message(context, ret,
1441 N_("pkinit; got DH reuse but no "
1442 "client nonce", ""));
1443 goto out;
1445 } else {
1446 if (k_n) {
1447 ret = KRB5KRB_ERR_GENERIC;
1448 krb5_set_error_message(context, ret,
1449 N_("pkinit: got server nonce "
1450 "without key expiration", ""));
1451 goto out;
1453 c_n = NULL;
1457 p = kdc_dh_info.subjectPublicKey.data;
1458 size = (kdc_dh_info.subjectPublicKey.length + 7) / 8;
1460 if (ctx->keyex == USE_DH) {
1461 DHPublicKey k;
1462 ret = decode_DHPublicKey(p, size, &k, NULL);
1463 if (ret) {
1464 krb5_set_error_message(context, ret,
1465 N_("pkinit: can't decode "
1466 "without key expiration", ""));
1467 goto out;
1470 kdc_dh_pubkey = integer_to_BN(context, "DHPublicKey", &k);
1471 free_DHPublicKey(&k);
1472 if (kdc_dh_pubkey == NULL) {
1473 ret = ENOMEM;
1474 goto out;
1478 size = DH_size(ctx->u.dh);
1480 dh_gen_key = malloc(size);
1481 if (dh_gen_key == NULL) {
1482 ret = krb5_enomem(context);
1483 goto out;
1486 dh_gen_keylen = DH_compute_key(dh_gen_key, kdc_dh_pubkey, ctx->u.dh);
1487 if (dh_gen_keylen == -1) {
1488 ret = KRB5KRB_ERR_GENERIC;
1489 dh_gen_keylen = 0;
1490 krb5_set_error_message(context, ret,
1491 N_("PKINIT: Can't compute Diffie-Hellman key", ""));
1492 goto out;
1494 if (dh_gen_keylen < (int)size) {
1495 size -= dh_gen_keylen;
1496 memmove(dh_gen_key + size, dh_gen_key, dh_gen_keylen);
1497 memset(dh_gen_key, 0, size);
1500 } else {
1501 ret = _krb5_pk_rd_pa_reply_ecdh_compute_key(context, ctx, p,
1502 size, &dh_gen_key,
1503 &dh_gen_keylen);
1504 if (ret)
1505 goto out;
1508 if (dh_gen_keylen <= 0) {
1509 ret = EINVAL;
1510 krb5_set_error_message(context, ret,
1511 N_("PKINIT: resulting DH key <= 0", ""));
1512 dh_gen_keylen = 0;
1513 goto out;
1516 *key = malloc (sizeof (**key));
1517 if (*key == NULL) {
1518 ret = krb5_enomem(context);
1519 goto out;
1522 ret = _krb5_pk_octetstring2key(context,
1523 etype,
1524 dh_gen_key, dh_gen_keylen,
1525 c_n, k_n,
1526 *key);
1527 if (ret) {
1528 krb5_set_error_message(context, ret,
1529 N_("PKINIT: can't create key from DH key", ""));
1530 free(*key);
1531 *key = NULL;
1532 goto out;
1535 out:
1536 if (kdc_dh_pubkey)
1537 BN_free(kdc_dh_pubkey);
1538 if (dh_gen_key) {
1539 memset(dh_gen_key, 0, dh_gen_keylen);
1540 free(dh_gen_key);
1542 if (host)
1543 _krb5_pk_cert_free(host);
1544 if (content.data)
1545 krb5_data_free(&content);
1546 der_free_oid(&contentType);
1547 free_KDCDHKeyInfo(&kdc_dh_info);
1549 return ret;
1552 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1553 _krb5_pk_rd_pa_reply(krb5_context context,
1554 const char *realm,
1555 void *c,
1556 krb5_enctype etype,
1557 unsigned nonce,
1558 const krb5_data *req_buffer,
1559 PA_DATA *pa,
1560 krb5_keyblock **key)
1562 krb5_pk_init_ctx ctx = c;
1563 krb5_error_code ret;
1564 size_t size;
1566 /* Check for IETF PK-INIT first */
1567 if (ctx->type == PKINIT_27) {
1568 PA_PK_AS_REP rep;
1569 heim_octet_string os, data;
1570 heim_oid oid;
1572 if (pa->padata_type != KRB5_PADATA_PK_AS_REP) {
1573 krb5_set_error_message(context, EINVAL,
1574 N_("PKINIT: wrong padata recv", ""));
1575 return EINVAL;
1578 ret = decode_PA_PK_AS_REP(pa->padata_value.data,
1579 pa->padata_value.length,
1580 &rep,
1581 &size);
1582 if (ret) {
1583 krb5_set_error_message(context, ret,
1584 N_("Failed to decode pkinit AS rep", ""));
1585 return ret;
1588 switch (rep.element) {
1589 case choice_PA_PK_AS_REP_dhInfo:
1590 _krb5_debug(context, 5, "krb5_get_init_creds: using pkinit dh");
1591 os = rep.u.dhInfo.dhSignedData;
1592 break;
1593 case choice_PA_PK_AS_REP_encKeyPack:
1594 _krb5_debug(context, 5, "krb5_get_init_creds: using kinit enc reply key");
1595 os = rep.u.encKeyPack;
1596 break;
1597 default: {
1598 PA_PK_AS_REP_BTMM btmm;
1599 free_PA_PK_AS_REP(&rep);
1600 memset(&rep, 0, sizeof(rep));
1602 _krb5_debug(context, 5, "krb5_get_init_creds: using BTMM kinit enc reply key");
1604 ret = decode_PA_PK_AS_REP_BTMM(pa->padata_value.data,
1605 pa->padata_value.length,
1606 &btmm,
1607 &size);
1608 if (ret) {
1609 krb5_set_error_message(context, EINVAL,
1610 N_("PKINIT: -27 reply "
1611 "invalid content type", ""));
1612 return EINVAL;
1615 if (btmm.dhSignedData || btmm.encKeyPack == NULL) {
1616 free_PA_PK_AS_REP_BTMM(&btmm);
1617 ret = EINVAL;
1618 krb5_set_error_message(context, ret,
1619 N_("DH mode not supported for BTMM mode", ""));
1620 return ret;
1624 * Transform to IETF style PK-INIT reply so that free works below
1627 rep.element = choice_PA_PK_AS_REP_encKeyPack;
1628 rep.u.encKeyPack.data = btmm.encKeyPack->data;
1629 rep.u.encKeyPack.length = btmm.encKeyPack->length;
1630 btmm.encKeyPack->data = NULL;
1631 btmm.encKeyPack->length = 0;
1632 free_PA_PK_AS_REP_BTMM(&btmm);
1633 os = rep.u.encKeyPack;
1637 ret = hx509_cms_unwrap_ContentInfo(&os, &oid, &data, NULL);
1638 if (ret) {
1639 free_PA_PK_AS_REP(&rep);
1640 krb5_set_error_message(context, ret,
1641 N_("PKINIT: failed to unwrap CI", ""));
1642 return ret;
1645 switch (rep.element) {
1646 case choice_PA_PK_AS_REP_dhInfo:
1647 ret = pk_rd_pa_reply_dh(context, &data, &oid, realm, ctx, etype,
1648 ctx->clientDHNonce,
1649 rep.u.dhInfo.serverDHNonce,
1650 nonce, pa, key);
1651 break;
1652 case choice_PA_PK_AS_REP_encKeyPack:
1653 ret = pk_rd_pa_reply_enckey(context, PKINIT_27, &data, &oid, realm,
1654 ctx, etype, nonce, req_buffer, pa, key);
1655 break;
1656 default:
1657 krb5_abortx(context, "pk-init as-rep case not possible to happen");
1659 der_free_octet_string(&data);
1660 der_free_oid(&oid);
1661 free_PA_PK_AS_REP(&rep);
1663 } else if (ctx->type == PKINIT_WIN2K) {
1664 PA_PK_AS_REP_Win2k w2krep;
1666 /* Check for Windows encoding of the AS-REP pa data */
1668 #if 0 /* should this be ? */
1669 if (pa->padata_type != KRB5_PADATA_PK_AS_REP) {
1670 krb5_set_error_message(context, EINVAL,
1671 "PKINIT: wrong padata recv");
1672 return EINVAL;
1674 #endif
1676 memset(&w2krep, 0, sizeof(w2krep));
1678 ret = decode_PA_PK_AS_REP_Win2k(pa->padata_value.data,
1679 pa->padata_value.length,
1680 &w2krep,
1681 &size);
1682 if (ret) {
1683 krb5_set_error_message(context, ret,
1684 N_("PKINIT: Failed decoding windows "
1685 "pkinit reply %d", ""), (int)ret);
1686 return ret;
1689 krb5_clear_error_message(context);
1691 switch (w2krep.element) {
1692 case choice_PA_PK_AS_REP_Win2k_encKeyPack: {
1693 heim_octet_string data;
1694 heim_oid oid;
1696 ret = hx509_cms_unwrap_ContentInfo(&w2krep.u.encKeyPack,
1697 &oid, &data, NULL);
1698 free_PA_PK_AS_REP_Win2k(&w2krep);
1699 if (ret) {
1700 krb5_set_error_message(context, ret,
1701 N_("PKINIT: failed to unwrap CI", ""));
1702 return ret;
1705 ret = pk_rd_pa_reply_enckey(context, PKINIT_WIN2K, &data, &oid, realm,
1706 ctx, etype, nonce, req_buffer, pa, key);
1707 der_free_octet_string(&data);
1708 der_free_oid(&oid);
1710 break;
1712 default:
1713 free_PA_PK_AS_REP_Win2k(&w2krep);
1714 ret = EINVAL;
1715 krb5_set_error_message(context, ret,
1716 N_("PKINIT: win2k reply invalid "
1717 "content type", ""));
1718 break;
1721 } else {
1722 ret = EINVAL;
1723 krb5_set_error_message(context, ret,
1724 N_("PKINIT: unknown reply type", ""));
1727 return ret;
1730 struct prompter {
1731 krb5_context context;
1732 krb5_prompter_fct prompter;
1733 void *prompter_data;
1736 static int
1737 hx_pass_prompter(void *data, const hx509_prompt *prompter)
1739 krb5_error_code ret;
1740 krb5_prompt prompt;
1741 krb5_data password_data;
1742 struct prompter *p = data;
1744 password_data.data = prompter->reply.data;
1745 password_data.length = prompter->reply.length;
1747 prompt.prompt = prompter->prompt;
1748 prompt.hidden = hx509_prompt_hidden(prompter->type);
1749 prompt.reply = &password_data;
1751 switch (prompter->type) {
1752 case HX509_PROMPT_TYPE_INFO:
1753 prompt.type = KRB5_PROMPT_TYPE_INFO;
1754 break;
1755 case HX509_PROMPT_TYPE_PASSWORD:
1756 case HX509_PROMPT_TYPE_QUESTION:
1757 default:
1758 prompt.type = KRB5_PROMPT_TYPE_PASSWORD;
1759 break;
1762 ret = (*p->prompter)(p->context, p->prompter_data, NULL, NULL, 1, &prompt);
1763 if (ret) {
1764 memset (prompter->reply.data, 0, prompter->reply.length);
1765 return 1;
1767 return 0;
1770 static krb5_error_code
1771 _krb5_pk_set_user_id(krb5_context context,
1772 krb5_principal principal,
1773 krb5_pk_init_ctx ctx,
1774 struct hx509_certs_data *certs)
1776 hx509_certs c = hx509_certs_ref(certs);
1777 hx509_query *q = NULL;
1778 int ret;
1780 if (ctx->id->certs)
1781 hx509_certs_free(&ctx->id->certs);
1782 if (ctx->id->cert) {
1783 hx509_cert_free(ctx->id->cert);
1784 ctx->id->cert = NULL;
1787 ctx->id->certs = c;
1788 ctx->anonymous = 0;
1790 ret = hx509_query_alloc(context->hx509ctx, &q);
1791 if (ret) {
1792 pk_copy_error(context, context->hx509ctx, ret,
1793 "Allocate query to find signing certificate");
1794 return ret;
1797 hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY);
1798 hx509_query_match_option(q, HX509_QUERY_OPTION_KU_DIGITALSIGNATURE);
1800 if (principal && strncmp("LKDC:SHA1.", krb5_principal_get_realm(context, principal), 9) == 0) {
1801 ctx->id->flags |= PKINIT_BTMM;
1804 ret = find_cert(context, ctx->id, q, &ctx->id->cert);
1805 hx509_query_free(context->hx509ctx, q);
1807 if (ret == 0 && _krb5_have_debug(context, 2)) {
1808 hx509_name name;
1809 char *str, *sn;
1810 heim_integer i;
1812 ret = hx509_cert_get_subject(ctx->id->cert, &name);
1813 if (ret)
1814 goto out;
1816 ret = hx509_name_to_string(name, &str);
1817 hx509_name_free(&name);
1818 if (ret)
1819 goto out;
1821 ret = hx509_cert_get_serialnumber(ctx->id->cert, &i);
1822 if (ret) {
1823 free(str);
1824 goto out;
1827 ret = der_print_hex_heim_integer(&i, &sn);
1828 der_free_heim_integer(&i);
1829 if (ret) {
1830 free(str);
1831 goto out;
1834 _krb5_debug(context, 2, "using cert: subject: %s sn: %s", str, sn);
1835 free(str);
1836 free(sn);
1838 out:
1840 return ret;
1843 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1844 _krb5_pk_load_id(krb5_context context,
1845 struct krb5_pk_identity **ret_id,
1846 const char *user_id,
1847 const char *anchor_id,
1848 char * const *chain_list,
1849 char * const *revoke_list,
1850 krb5_prompter_fct prompter,
1851 void *prompter_data,
1852 char *password)
1854 struct krb5_pk_identity *id = NULL;
1855 struct prompter p;
1856 krb5_error_code ret;
1858 *ret_id = NULL;
1860 /* load cert */
1862 id = calloc(1, sizeof(*id));
1863 if (id == NULL)
1864 return krb5_enomem(context);
1866 if (user_id) {
1867 hx509_lock lock;
1869 ret = hx509_lock_init(context->hx509ctx, &lock);
1870 if (ret) {
1871 pk_copy_error(context, context->hx509ctx, ret, "Failed init lock");
1872 goto out;
1875 if (password && password[0])
1876 hx509_lock_add_password(lock, password);
1878 if (prompter) {
1879 p.context = context;
1880 p.prompter = prompter;
1881 p.prompter_data = prompter_data;
1883 ret = hx509_lock_set_prompter(lock, hx_pass_prompter, &p);
1884 if (ret) {
1885 hx509_lock_free(lock);
1886 goto out;
1890 ret = hx509_certs_init(context->hx509ctx, user_id, 0, lock, &id->certs);
1891 hx509_lock_free(lock);
1892 if (ret) {
1893 pk_copy_error(context, context->hx509ctx, ret,
1894 "Failed to init cert certs");
1895 goto out;
1897 } else {
1898 id->certs = NULL;
1901 ret = hx509_certs_init(context->hx509ctx, anchor_id, 0, NULL, &id->anchors);
1902 if (ret) {
1903 pk_copy_error(context, context->hx509ctx, ret,
1904 "Failed to init anchors");
1905 goto out;
1908 ret = hx509_certs_init(context->hx509ctx, "MEMORY:pkinit-cert-chain",
1909 0, NULL, &id->certpool);
1910 if (ret) {
1911 pk_copy_error(context, context->hx509ctx, ret,
1912 "Failed to init chain");
1913 goto out;
1916 while (chain_list && *chain_list) {
1917 ret = hx509_certs_append(context->hx509ctx, id->certpool,
1918 NULL, *chain_list);
1919 if (ret) {
1920 pk_copy_error(context, context->hx509ctx, ret,
1921 "Failed to load chain %s",
1922 *chain_list);
1923 goto out;
1925 chain_list++;
1928 if (revoke_list) {
1929 ret = hx509_revoke_init(context->hx509ctx, &id->revokectx);
1930 if (ret) {
1931 pk_copy_error(context, context->hx509ctx, ret,
1932 "Failed to init revoke list");
1933 goto out;
1936 while (*revoke_list) {
1937 ret = hx509_revoke_add_crl(context->hx509ctx,
1938 id->revokectx,
1939 *revoke_list);
1940 if (ret) {
1941 pk_copy_error(context, context->hx509ctx, ret,
1942 "Failed to load revoke list");
1943 goto out;
1945 revoke_list++;
1947 } else
1948 hx509_context_set_missing_revoke(context->hx509ctx, 1);
1950 ret = hx509_verify_init_ctx(context->hx509ctx, &id->verify_ctx);
1951 if (ret) {
1952 pk_copy_error(context, context->hx509ctx, ret,
1953 "Failed to init verify context");
1954 goto out;
1957 hx509_verify_attach_anchors(id->verify_ctx, id->anchors);
1958 hx509_verify_attach_revoke(id->verify_ctx, id->revokectx);
1960 out:
1961 if (ret) {
1962 hx509_verify_destroy_ctx(id->verify_ctx);
1963 hx509_certs_free(&id->certs);
1964 hx509_certs_free(&id->anchors);
1965 hx509_certs_free(&id->certpool);
1966 hx509_revoke_free(&id->revokectx);
1967 free(id);
1968 } else
1969 *ret_id = id;
1971 return ret;
1978 static void
1979 pk_copy_error(krb5_context context,
1980 hx509_context hx509ctx,
1981 int hxret,
1982 const char *fmt,
1983 ...)
1985 va_list va;
1986 char *s, *f;
1987 int ret;
1989 va_start(va, fmt);
1990 ret = vasprintf(&f, fmt, va);
1991 va_end(va);
1992 if (ret == -1 || f == NULL) {
1993 krb5_clear_error_message(context);
1994 return;
1997 s = hx509_get_error_string(hx509ctx, hxret);
1998 if (s == NULL) {
1999 krb5_clear_error_message(context);
2000 free(f);
2001 return;
2003 krb5_set_error_message(context, hxret, "%s: %s", f, s);
2004 free(s);
2005 free(f);
2008 static int
2009 parse_integer(krb5_context context, char **p, const char *file, int lineno,
2010 const char *name, heim_integer *integer)
2012 int ret;
2013 char *p1;
2014 p1 = strsep(p, " \t");
2015 if (p1 == NULL) {
2016 krb5_set_error_message(context, EINVAL,
2017 N_("moduli file %s missing %s on line %d", ""),
2018 file, name, lineno);
2019 return EINVAL;
2021 ret = der_parse_hex_heim_integer(p1, integer);
2022 if (ret) {
2023 krb5_set_error_message(context, ret,
2024 N_("moduli file %s failed parsing %s "
2025 "on line %d", ""),
2026 file, name, lineno);
2027 return ret;
2030 return 0;
2033 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2034 _krb5_parse_moduli_line(krb5_context context,
2035 const char *file,
2036 int lineno,
2037 char *p,
2038 struct krb5_dh_moduli **m)
2040 struct krb5_dh_moduli *m1;
2041 char *p1;
2042 int ret;
2044 *m = NULL;
2046 m1 = calloc(1, sizeof(*m1));
2047 if (m1 == NULL)
2048 return krb5_enomem(context);
2050 while (isspace((unsigned char)*p))
2051 p++;
2052 if (*p == '#') {
2053 free(m1);
2054 return 0;
2056 ret = EINVAL;
2058 p1 = strsep(&p, " \t");
2059 if (p1 == NULL) {
2060 krb5_set_error_message(context, ret,
2061 N_("moduli file %s missing name on line %d", ""),
2062 file, lineno);
2063 goto out;
2065 m1->name = strdup(p1);
2066 if (m1->name == NULL) {
2067 ret = krb5_enomem(context);
2068 goto out;
2071 p1 = strsep(&p, " \t");
2072 if (p1 == NULL) {
2073 krb5_set_error_message(context, ret,
2074 N_("moduli file %s missing bits on line %d", ""),
2075 file, lineno);
2076 goto out;
2079 m1->bits = atoi(p1);
2080 if (m1->bits == 0) {
2081 krb5_set_error_message(context, ret,
2082 N_("moduli file %s has un-parsable "
2083 "bits on line %d", ""), file, lineno);
2084 goto out;
2087 ret = parse_integer(context, &p, file, lineno, "p", &m1->p);
2088 if (ret)
2089 goto out;
2090 ret = parse_integer(context, &p, file, lineno, "g", &m1->g);
2091 if (ret)
2092 goto out;
2093 ret = parse_integer(context, &p, file, lineno, "q", &m1->q);
2094 if (ret) {
2095 m1->q.negative = 0;
2096 m1->q.length = 0;
2097 m1->q.data = 0;
2098 krb5_clear_error_message(context);
2101 *m = m1;
2103 return 0;
2104 out:
2105 free(m1->name);
2106 der_free_heim_integer(&m1->p);
2107 der_free_heim_integer(&m1->g);
2108 der_free_heim_integer(&m1->q);
2109 free(m1);
2110 return ret;
2113 static void
2114 free_moduli_element(struct krb5_dh_moduli *element)
2116 free(element->name);
2117 der_free_heim_integer(&element->p);
2118 der_free_heim_integer(&element->g);
2119 der_free_heim_integer(&element->q);
2120 free(element);
2123 KRB5_LIB_FUNCTION void KRB5_LIB_CALL
2124 _krb5_free_moduli(struct krb5_dh_moduli **moduli)
2126 int i;
2127 for (i = 0; moduli[i] != NULL; i++)
2128 free_moduli_element(moduli[i]);
2129 free(moduli);
2132 static const char *default_moduli_RFC2412_MODP_group2 =
2133 /* name */
2134 "RFC2412-MODP-group2 "
2135 /* bits */
2136 "1024 "
2137 /* p */
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 "
2144 /* g */
2145 "02 "
2146 /* q */
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 =
2155 /* name */
2156 "rfc3526-MODP-group14 "
2157 /* bits */
2158 "2048 "
2159 /* p */
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 "
2171 /* g */
2172 "02 "
2173 /* q */
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;
2193 char buf[4096];
2194 FILE *f;
2195 int lineno = 0, n = 0;
2197 *moduli = NULL;
2199 m = calloc(1, sizeof(m[0]) * 3);
2200 if (m == NULL)
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]);
2205 if (ret) {
2206 _krb5_free_moduli(m);
2207 return ret;
2209 n++;
2211 strlcpy(buf, default_moduli_RFC2412_MODP_group2, sizeof(buf));
2212 ret = _krb5_parse_moduli_line(context, "builtin", 1, buf, &m[1]);
2213 if (ret) {
2214 _krb5_free_moduli(m);
2215 return ret;
2217 n++;
2220 if (file == NULL)
2221 file = MODULI_FILE;
2224 char *exp_file;
2226 if (_krb5_expand_path_tokens(context, file, 1, &exp_file) == 0) {
2227 f = fopen(exp_file, "r");
2228 krb5_xfree(exp_file);
2229 } else {
2230 f = NULL;
2234 if (f == NULL) {
2235 *moduli = m;
2236 return 0;
2238 rk_cloexec_file(f);
2240 while(fgets(buf, sizeof(buf), f) != NULL) {
2241 struct krb5_dh_moduli *element;
2243 buf[strcspn(buf, "\n")] = '\0';
2244 lineno++;
2246 ret = _krb5_parse_moduli_line(context, file, lineno, buf, &element);
2247 if (ret)
2248 break;
2249 if (element == NULL)
2250 continue;
2252 m2 = realloc(m, (n + 2) * sizeof(m[0]));
2253 if (m2 == NULL) {
2254 free_moduli_element(element);
2255 ret = krb5_enomem(context);
2256 break;
2258 m = m2;
2260 m[n] = element;
2261 m[n + 1] = NULL;
2262 n++;
2264 if (ret) {
2265 _krb5_free_moduli(m);
2266 m = NULL;
2269 *moduli = m;
2271 (void) fclose(f);
2272 return ret;
2275 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2276 _krb5_dh_group_ok(krb5_context context, unsigned long bits,
2277 heim_integer *p, heim_integer *g, heim_integer *q,
2278 struct krb5_dh_moduli **moduli,
2279 char **name)
2281 int i;
2283 if (name)
2284 *name = NULL;
2286 for (i = 0; moduli[i] != NULL; i++) {
2287 if (der_heim_integer_cmp(&moduli[i]->g, g) == 0 &&
2288 der_heim_integer_cmp(&moduli[i]->p, p) == 0 &&
2289 (q == NULL || moduli[i]->q.length == 0 ||
2290 der_heim_integer_cmp(&moduli[i]->q, q) == 0))
2292 if (bits && bits > moduli[i]->bits) {
2293 krb5_set_error_message(context,
2294 KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED,
2295 N_("PKINIT: DH group parameter %s "
2296 "not accepted, not enough bits "
2297 "generated", ""),
2298 moduli[i]->name);
2299 return KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED;
2301 if (name)
2302 *name = strdup(moduli[i]->name);
2303 return 0;
2306 krb5_set_error_message(context,
2307 KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED,
2308 N_("PKINIT: DH group parameter not ok", ""));
2309 return KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED;
2311 #endif /* PKINIT */
2313 KRB5_LIB_FUNCTION void KRB5_LIB_CALL
2314 _krb5_get_init_creds_opt_free_pkinit(krb5_get_init_creds_opt *opt)
2316 #ifdef PKINIT
2317 krb5_pk_init_ctx ctx;
2319 if (opt->opt_private == NULL || opt->opt_private->pk_init_ctx == NULL)
2320 return;
2321 ctx = opt->opt_private->pk_init_ctx;
2322 switch (ctx->keyex) {
2323 case USE_DH:
2324 if (ctx->u.dh)
2325 DH_free(ctx->u.dh);
2326 break;
2327 case USE_RSA:
2328 break;
2329 case USE_ECDH:
2330 if (ctx->u.eckey)
2331 _krb5_pk_eckey_free(ctx->u.eckey);
2332 break;
2334 if (ctx->id) {
2335 hx509_verify_destroy_ctx(ctx->id->verify_ctx);
2336 hx509_certs_free(&ctx->id->certs);
2337 hx509_cert_free(ctx->id->cert);
2338 hx509_certs_free(&ctx->id->anchors);
2339 hx509_certs_free(&ctx->id->certpool);
2341 if (ctx->clientDHNonce) {
2342 krb5_free_data(NULL, ctx->clientDHNonce);
2343 ctx->clientDHNonce = NULL;
2345 if (ctx->m)
2346 _krb5_free_moduli(ctx->m);
2347 free(ctx->id);
2348 ctx->id = NULL;
2350 free(opt->opt_private->pk_init_ctx);
2351 opt->opt_private->pk_init_ctx = NULL;
2352 #endif
2355 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2356 krb5_get_init_creds_opt_set_pkinit(krb5_context context,
2357 krb5_get_init_creds_opt *opt,
2358 krb5_principal principal,
2359 const char *user_id,
2360 const char *x509_anchors,
2361 char * const * pool,
2362 char * const * pki_revoke,
2363 int flags,
2364 krb5_prompter_fct prompter,
2365 void *prompter_data,
2366 char *password)
2368 #ifdef PKINIT
2369 krb5_error_code ret;
2370 char **freeme1 = NULL;
2371 char **freeme2 = NULL;
2372 char *anchors = NULL;
2374 if (opt->opt_private == NULL) {
2375 krb5_set_error_message(context, EINVAL,
2376 N_("PKINIT: on non extendable opt", ""));
2377 return EINVAL;
2380 opt->opt_private->pk_init_ctx =
2381 calloc(1, sizeof(*opt->opt_private->pk_init_ctx));
2382 if (opt->opt_private->pk_init_ctx == NULL)
2383 return krb5_enomem(context);
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 */
2390 if (pool == NULL)
2391 pool = freeme1 = krb5_config_get_strings(context, NULL, "appdefaults",
2392 "pkinit_pool", NULL);
2394 if (pki_revoke == NULL)
2395 pki_revoke = freeme2 = krb5_config_get_strings(context, NULL,
2396 "appdefaults",
2397 "pkinit_revoke", NULL);
2399 if (x509_anchors == NULL) {
2400 krb5_appdefault_string(context, "kinit",
2401 krb5_principal_get_realm(context, principal),
2402 "pkinit_anchors", NULL, &anchors);
2403 x509_anchors = anchors;
2406 if (flags & KRB5_GIC_OPT_PKINIT_ANONYMOUS)
2407 opt->opt_private->pk_init_ctx->anonymous = 1;
2409 if ((flags & KRB5_GIC_OPT_PKINIT_NO_KDC_ANCHOR) == 0 &&
2410 x509_anchors == NULL) {
2411 krb5_set_error_message(context, HEIM_PKINIT_NO_VALID_CA,
2412 N_("PKINIT: No anchor given", ""));
2413 return HEIM_PKINIT_NO_VALID_CA;
2416 ret = _krb5_pk_load_id(context,
2417 &opt->opt_private->pk_init_ctx->id,
2418 user_id,
2419 x509_anchors,
2420 pool,
2421 pki_revoke,
2422 prompter,
2423 prompter_data,
2424 password);
2425 krb5_config_free_strings(freeme2);
2426 krb5_config_free_strings(freeme1);
2427 free(anchors);
2428 if (ret) {
2429 free(opt->opt_private->pk_init_ctx);
2430 opt->opt_private->pk_init_ctx = NULL;
2431 return ret;
2433 if (flags & KRB5_GIC_OPT_PKINIT_BTMM)
2434 opt->opt_private->pk_init_ctx->id->flags |= PKINIT_BTMM;
2435 if (principal && krb5_principal_is_lkdc(context, principal))
2436 opt->opt_private->pk_init_ctx->id->flags |= PKINIT_BTMM;
2437 if (flags & KRB5_GIC_OPT_PKINIT_NO_KDC_ANCHOR)
2438 opt->opt_private->pk_init_ctx->id->flags |= PKINIT_NO_KDC_ANCHOR;
2440 if (opt->opt_private->pk_init_ctx->id->certs) {
2441 ret = _krb5_pk_set_user_id(context,
2442 principal,
2443 opt->opt_private->pk_init_ctx,
2444 opt->opt_private->pk_init_ctx->id->certs);
2445 if (ret) {
2446 free(opt->opt_private->pk_init_ctx);
2447 opt->opt_private->pk_init_ctx = NULL;
2448 return ret;
2450 } else
2451 opt->opt_private->pk_init_ctx->id->cert = NULL;
2453 if ((flags & KRB5_GIC_OPT_PKINIT_USE_ENCKEY) == 0) {
2454 hx509_context hx509ctx = context->hx509ctx;
2455 hx509_cert cert = opt->opt_private->pk_init_ctx->id->cert;
2457 opt->opt_private->pk_init_ctx->keyex = USE_DH;
2460 * If its a ECDSA certs, lets select ECDSA as the keyex algorithm.
2462 if (cert) {
2463 AlgorithmIdentifier alg;
2465 ret = hx509_cert_get_SPKI_AlgorithmIdentifier(hx509ctx, cert, &alg);
2466 if (ret == 0) {
2467 if (der_heim_oid_cmp(&alg.algorithm, &asn1_oid_id_ecPublicKey) == 0)
2468 opt->opt_private->pk_init_ctx->keyex = USE_ECDH;
2469 free_AlgorithmIdentifier(&alg);
2473 } else {
2474 opt->opt_private->pk_init_ctx->keyex = USE_RSA;
2476 if (opt->opt_private->pk_init_ctx->id->certs == NULL) {
2477 krb5_set_error_message(context, EINVAL,
2478 N_("No anonymous pkinit support in RSA mode", ""));
2479 return EINVAL;
2483 return 0;
2484 #else
2485 krb5_set_error_message(context, EINVAL,
2486 N_("no support for PKINIT compiled in", ""));
2487 return EINVAL;
2488 #endif
2491 krb5_error_code KRB5_LIB_FUNCTION
2492 krb5_get_init_creds_opt_set_pkinit_user_certs(krb5_context context,
2493 krb5_get_init_creds_opt *opt,
2494 struct hx509_certs_data *certs)
2496 #ifdef PKINIT
2497 if (opt->opt_private == NULL) {
2498 krb5_set_error_message(context, EINVAL,
2499 N_("PKINIT: on non extendable opt", ""));
2500 return EINVAL;
2502 if (opt->opt_private->pk_init_ctx == NULL) {
2503 krb5_set_error_message(context, EINVAL,
2504 N_("PKINIT: on pkinit context", ""));
2505 return EINVAL;
2508 return _krb5_pk_set_user_id(context, NULL, opt->opt_private->pk_init_ctx, certs);
2509 #else
2510 krb5_set_error_message(context, EINVAL,
2511 N_("no support for PKINIT compiled in", ""));
2512 return EINVAL;
2513 #endif
2516 #ifdef PKINIT
2518 static int
2519 get_ms_san(hx509_context context, hx509_cert cert, char **upn)
2521 hx509_octet_string_list list;
2522 int ret;
2524 *upn = NULL;
2526 ret = hx509_cert_find_subjectAltName_otherName(context,
2527 cert,
2528 &asn1_oid_id_pkinit_ms_san,
2529 &list);
2530 if (ret)
2531 return 0;
2533 if (list.len > 0 && list.val[0].length > 0)
2534 ret = decode_MS_UPN_SAN(list.val[0].data, list.val[0].length,
2535 upn, NULL);
2536 else
2537 ret = 1;
2538 hx509_free_octet_string_list(&list);
2540 return ret;
2543 static int
2544 find_ms_san(hx509_context context, hx509_cert cert, void *ctx)
2546 char *upn;
2547 int ret;
2549 ret = get_ms_san(context, cert, &upn);
2550 if (ret == 0)
2551 free(upn);
2552 return ret;
2557 #endif
2560 * Private since it need to be redesigned using krb5_get_init_creds()
2563 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2564 krb5_pk_enterprise_cert(krb5_context context,
2565 const char *user_id,
2566 krb5_const_realm realm,
2567 krb5_principal *principal,
2568 struct hx509_certs_data **res)
2570 #ifdef PKINIT
2571 krb5_error_code ret;
2572 hx509_certs certs, result;
2573 hx509_cert cert = NULL;
2574 hx509_query *q;
2575 char *name;
2577 *principal = NULL;
2578 if (res)
2579 *res = NULL;
2581 if (user_id == NULL) {
2582 krb5_set_error_message(context, ENOENT, "no user id");
2583 return ENOENT;
2586 ret = hx509_certs_init(context->hx509ctx, user_id, 0, NULL, &certs);
2587 if (ret) {
2588 pk_copy_error(context, context->hx509ctx, ret,
2589 "Failed to init cert certs");
2590 goto out;
2593 ret = hx509_query_alloc(context->hx509ctx, &q);
2594 if (ret) {
2595 krb5_set_error_message(context, ret, "out of memory");
2596 hx509_certs_free(&certs);
2597 goto out;
2600 hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY);
2601 hx509_query_match_option(q, HX509_QUERY_OPTION_KU_DIGITALSIGNATURE);
2602 hx509_query_match_eku(q, &asn1_oid_id_pkinit_ms_eku);
2603 hx509_query_match_cmp_func(q, find_ms_san, NULL);
2605 ret = hx509_certs_filter(context->hx509ctx, certs, q, &result);
2606 hx509_query_free(context->hx509ctx, q);
2607 hx509_certs_free(&certs);
2608 if (ret) {
2609 pk_copy_error(context, context->hx509ctx, ret,
2610 "Failed to find PKINIT certificate");
2611 return ret;
2614 ret = hx509_get_one_cert(context->hx509ctx, result, &cert);
2615 hx509_certs_free(&result);
2616 if (ret) {
2617 pk_copy_error(context, context->hx509ctx, ret,
2618 "Failed to get one cert");
2619 goto out;
2622 ret = get_ms_san(context->hx509ctx, cert, &name);
2623 if (ret) {
2624 pk_copy_error(context, context->hx509ctx, ret,
2625 "Failed to get MS SAN");
2626 goto out;
2629 ret = krb5_make_principal(context, principal, realm, name, NULL);
2630 free(name);
2631 if (ret)
2632 goto out;
2634 krb5_principal_set_type(context, *principal, KRB5_NT_ENTERPRISE_PRINCIPAL);
2636 if (res) {
2637 ret = hx509_certs_init(context->hx509ctx, "MEMORY:", 0, NULL, res);
2638 if (ret)
2639 goto out;
2641 ret = hx509_certs_add(context->hx509ctx, *res, cert);
2642 if (ret) {
2643 hx509_certs_free(res);
2644 goto out;
2648 out:
2649 hx509_cert_free(cert);
2651 return ret;
2652 #else
2653 krb5_set_error_message(context, EINVAL,
2654 N_("no support for PKINIT compiled in", ""));
2655 return EINVAL;
2656 #endif
2659 KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
2660 _krb5_pk_is_kdc_verified(krb5_context context,
2661 krb5_get_init_creds_opt *opt)
2663 if (opt == NULL ||
2664 opt->opt_private == NULL ||
2665 opt->opt_private->pk_init_ctx == NULL)
2666 return FALSE;
2668 return opt->opt_private->pk_init_ctx->kdc_verified;