check that we don't pass negative numbers of memset [CID-169]
[heimdal.git] / lib / krb5 / pkinit.c
blob37395353940ca6c08bc00f0f78b171e2bed5b441
1 /*
2 * Copyright (c) 2003 - 2007 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
17 * 3. Neither the name of the Institute nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
34 #include "krb5_locl.h"
36 struct krb5_dh_moduli {
37 char *name;
38 unsigned long bits;
39 heim_integer p;
40 heim_integer g;
41 heim_integer q;
44 #ifdef PKINIT
46 #include <cms_asn1.h>
47 #include <pkcs8_asn1.h>
48 #include <pkcs9_asn1.h>
49 #include <pkcs12_asn1.h>
50 #include <pkinit_asn1.h>
51 #include <asn1_err.h>
53 #include <der.h>
55 struct krb5_pk_cert {
56 hx509_cert cert;
59 struct krb5_pk_init_ctx_data {
60 struct krb5_pk_identity *id;
61 enum { USE_RSA, USE_DH, USE_ECDH } keyex;
62 union {
63 DH *dh;
64 #ifdef HAVE_OPENSSL
65 EC_KEY *eckey;
66 #endif
67 } u;
68 krb5_data *clientDHNonce;
69 struct krb5_dh_moduli **m;
70 hx509_peer_info peer;
71 enum krb5_pk_type type;
72 unsigned int require_binding:1;
73 unsigned int require_eku:1;
74 unsigned int require_krbtgt_otherName:1;
75 unsigned int require_hostname_match:1;
76 unsigned int trustedCertifiers:1;
79 static void
80 pk_copy_error(krb5_context context,
81 hx509_context hx509ctx,
82 int hxret,
83 const char *fmt,
84 ...)
85 __attribute__ ((format (printf, 4, 5)));
91 void KRB5_LIB_FUNCTION
92 _krb5_pk_cert_free(struct krb5_pk_cert *cert)
94 if (cert->cert) {
95 hx509_cert_free(cert->cert);
97 free(cert);
100 static krb5_error_code
101 BN_to_integer(krb5_context context, BIGNUM *bn, heim_integer *integer)
103 integer->length = BN_num_bytes(bn);
104 integer->data = malloc(integer->length);
105 if (integer->data == NULL) {
106 krb5_clear_error_message(context);
107 return ENOMEM;
109 BN_bn2bin(bn, integer->data);
110 integer->negative = BN_is_negative(bn);
111 return 0;
114 static BIGNUM *
115 integer_to_BN(krb5_context context, const char *field, const heim_integer *f)
117 BIGNUM *bn;
119 bn = BN_bin2bn((const unsigned char *)f->data, f->length, NULL);
120 if (bn == NULL) {
121 krb5_set_error_message(context, ENOMEM,
122 N_("PKINIT: parsing BN failed %s", ""), field);
123 return NULL;
125 BN_set_negative(bn, f->negative);
126 return bn;
129 static krb5_error_code
130 select_dh_group(krb5_context context, DH *dh, unsigned long bits,
131 struct krb5_dh_moduli **moduli)
133 const struct krb5_dh_moduli *m;
135 if (bits == 0) {
136 m = moduli[1]; /* XXX */
137 if (m == NULL)
138 m = moduli[0]; /* XXX */
139 } else {
140 int i;
141 for (i = 0; moduli[i] != NULL; i++) {
142 if (bits < moduli[i]->bits)
143 break;
145 if (moduli[i] == NULL) {
146 krb5_set_error_message(context, EINVAL,
147 N_("Did not find a DH group parameter "
148 "matching requirement of %lu bits", ""),
149 bits);
150 return EINVAL;
152 m = moduli[i];
155 dh->p = integer_to_BN(context, "p", &m->p);
156 if (dh->p == NULL)
157 return ENOMEM;
158 dh->g = integer_to_BN(context, "g", &m->g);
159 if (dh->g == NULL)
160 return ENOMEM;
161 dh->q = integer_to_BN(context, "q", &m->q);
162 if (dh->q == NULL)
163 return ENOMEM;
165 return 0;
168 struct certfind {
169 const char *type;
170 const heim_oid *oid;
174 * Try searchin the key by to use by first looking for for PK-INIT
175 * EKU, then the Microsoft smart card EKU and last, no special EKU at all.
178 static krb5_error_code
179 find_cert(krb5_context context, struct krb5_pk_identity *id,
180 hx509_query *q, hx509_cert *cert)
182 struct certfind cf[3] = {
183 { "PKINIT EKU" },
184 { "MS EKU" },
185 { "any (or no)" }
187 int i, ret;
189 cf[0].oid = &asn1_oid_id_pkekuoid;
190 cf[1].oid = &asn1_oid_id_pkinit_ms_eku;
191 cf[2].oid = NULL;
193 for (i = 0; i < sizeof(cf)/sizeof(cf[0]); i++) {
194 ret = hx509_query_match_eku(q, cf[i].oid);
195 if (ret) {
196 pk_copy_error(context, id->hx509ctx, ret,
197 "Failed setting %s OID", cf[i].type);
198 return ret;
201 ret = hx509_certs_find(id->hx509ctx, id->certs, q, cert);
202 if (ret == 0)
203 break;
204 pk_copy_error(context, id->hx509ctx, ret,
205 "Failed finding certificate with %s OID", cf[i].type);
207 return ret;
211 static krb5_error_code
212 create_signature(krb5_context context,
213 const heim_oid *eContentType,
214 krb5_data *eContent,
215 struct krb5_pk_identity *id,
216 hx509_peer_info peer,
217 krb5_data *sd_data)
219 int ret, flags = 0;
221 if (id->cert == NULL)
222 flags |= HX509_CMS_SIGNATURE_NO_SIGNER;
224 ret = hx509_cms_create_signed_1(id->hx509ctx,
225 flags,
226 eContentType,
227 eContent->data,
228 eContent->length,
229 NULL,
230 id->cert,
231 peer,
232 NULL,
233 id->certs,
234 sd_data);
235 if (ret) {
236 pk_copy_error(context, id->hx509ctx, ret,
237 "Create CMS signedData");
238 return ret;
241 return 0;
244 static int
245 cert2epi(hx509_context context, void *ctx, hx509_cert c)
247 ExternalPrincipalIdentifiers *ids = ctx;
248 ExternalPrincipalIdentifier id;
249 hx509_name subject = NULL;
250 void *p;
251 int ret;
253 if (ids->len > 10)
254 return 0;
256 memset(&id, 0, sizeof(id));
258 ret = hx509_cert_get_subject(c, &subject);
259 if (ret)
260 return ret;
262 if (hx509_name_is_null_p(subject) != 0) {
264 id.subjectName = calloc(1, sizeof(*id.subjectName));
265 if (id.subjectName == NULL) {
266 hx509_name_free(&subject);
267 free_ExternalPrincipalIdentifier(&id);
268 return ENOMEM;
271 ret = hx509_name_binary(subject, id.subjectName);
272 if (ret) {
273 hx509_name_free(&subject);
274 free_ExternalPrincipalIdentifier(&id);
275 return ret;
278 hx509_name_free(&subject);
281 id.issuerAndSerialNumber = calloc(1, sizeof(*id.issuerAndSerialNumber));
282 if (id.issuerAndSerialNumber == NULL) {
283 free_ExternalPrincipalIdentifier(&id);
284 return ENOMEM;
288 IssuerAndSerialNumber iasn;
289 hx509_name issuer;
290 size_t size;
292 memset(&iasn, 0, sizeof(iasn));
294 ret = hx509_cert_get_issuer(c, &issuer);
295 if (ret) {
296 free_ExternalPrincipalIdentifier(&id);
297 return ret;
300 ret = hx509_name_to_Name(issuer, &iasn.issuer);
301 hx509_name_free(&issuer);
302 if (ret) {
303 free_ExternalPrincipalIdentifier(&id);
304 return ret;
307 ret = hx509_cert_get_serialnumber(c, &iasn.serialNumber);
308 if (ret) {
309 free_IssuerAndSerialNumber(&iasn);
310 free_ExternalPrincipalIdentifier(&id);
311 return ret;
314 ASN1_MALLOC_ENCODE(IssuerAndSerialNumber,
315 id.issuerAndSerialNumber->data,
316 id.issuerAndSerialNumber->length,
317 &iasn, &size, ret);
318 free_IssuerAndSerialNumber(&iasn);
319 if (ret)
320 return ret;
321 if (id.issuerAndSerialNumber->length != size)
322 abort();
325 id.subjectKeyIdentifier = NULL;
327 p = realloc(ids->val, sizeof(ids->val[0]) * (ids->len + 1));
328 if (p == NULL) {
329 free_ExternalPrincipalIdentifier(&id);
330 return ENOMEM;
333 ids->val = p;
334 ids->val[ids->len] = id;
335 ids->len++;
337 return 0;
340 static krb5_error_code
341 build_edi(krb5_context context,
342 hx509_context hx509ctx,
343 hx509_certs certs,
344 ExternalPrincipalIdentifiers *ids)
346 return hx509_certs_iter(hx509ctx, certs, cert2epi, ids);
349 static krb5_error_code
350 build_auth_pack(krb5_context context,
351 unsigned nonce,
352 krb5_pk_init_ctx ctx,
353 const KDC_REQ_BODY *body,
354 AuthPack *a)
356 size_t buf_size, len;
357 krb5_error_code ret;
358 void *buf;
359 krb5_timestamp sec;
360 int32_t usec;
361 Checksum checksum;
363 krb5_clear_error_message(context);
365 memset(&checksum, 0, sizeof(checksum));
367 krb5_us_timeofday(context, &sec, &usec);
368 a->pkAuthenticator.ctime = sec;
369 a->pkAuthenticator.nonce = nonce;
371 ASN1_MALLOC_ENCODE(KDC_REQ_BODY, buf, buf_size, body, &len, ret);
372 if (ret)
373 return ret;
374 if (buf_size != len)
375 krb5_abortx(context, "internal error in ASN.1 encoder");
377 ret = krb5_create_checksum(context,
378 NULL,
380 CKSUMTYPE_SHA1,
381 buf,
382 len,
383 &checksum);
384 free(buf);
385 if (ret)
386 return ret;
388 ALLOC(a->pkAuthenticator.paChecksum, 1);
389 if (a->pkAuthenticator.paChecksum == NULL) {
390 krb5_set_error_message(context, ENOMEM,
391 N_("malloc: out of memory", ""));
392 return ENOMEM;
395 ret = krb5_data_copy(a->pkAuthenticator.paChecksum,
396 checksum.checksum.data, checksum.checksum.length);
397 free_Checksum(&checksum);
398 if (ret)
399 return ret;
401 if (ctx->keyex == USE_DH || ctx->keyex == USE_ECDH) {
402 const char *moduli_file;
403 unsigned long dh_min_bits;
404 krb5_data dhbuf;
405 size_t size;
407 krb5_data_zero(&dhbuf);
411 moduli_file = krb5_config_get_string(context, NULL,
412 "libdefaults",
413 "moduli",
414 NULL);
416 dh_min_bits =
417 krb5_config_get_int_default(context, NULL, 0,
418 "libdefaults",
419 "pkinit_dh_min_bits",
420 NULL);
422 ret = _krb5_parse_moduli(context, moduli_file, &ctx->m);
423 if (ret)
424 return ret;
426 ctx->u.dh = DH_new();
427 if (ctx->u.dh == NULL) {
428 krb5_set_error_message(context, ENOMEM,
429 N_("malloc: out of memory", ""));
430 return ENOMEM;
433 ret = select_dh_group(context, ctx->u.dh, dh_min_bits, ctx->m);
434 if (ret)
435 return ret;
437 if (DH_generate_key(ctx->u.dh) != 1) {
438 krb5_set_error_message(context, ENOMEM,
439 N_("pkinit: failed to generate DH key", ""));
440 return ENOMEM;
444 if (1 /* support_cached_dh */) {
445 ALLOC(a->clientDHNonce, 1);
446 if (a->clientDHNonce == NULL) {
447 krb5_clear_error_message(context);
448 return ENOMEM;
450 ret = krb5_data_alloc(a->clientDHNonce, 40);
451 if (a->clientDHNonce == NULL) {
452 krb5_clear_error_message(context);
453 return ret;
455 RAND_bytes(a->clientDHNonce->data, a->clientDHNonce->length);
456 ret = krb5_copy_data(context, a->clientDHNonce,
457 &ctx->clientDHNonce);
458 if (ret)
459 return ret;
462 ALLOC(a->clientPublicValue, 1);
463 if (a->clientPublicValue == NULL)
464 return ENOMEM;
466 if (ctx->keyex == USE_DH) {
467 DH *dh = ctx->u.dh;
468 DomainParameters dp;
469 heim_integer dh_pub_key;
471 ret = der_copy_oid(&asn1_oid_id_dhpublicnumber,
472 &a->clientPublicValue->algorithm.algorithm);
473 if (ret)
474 return ret;
476 memset(&dp, 0, sizeof(dp));
478 ret = BN_to_integer(context, dh->p, &dp.p);
479 if (ret) {
480 free_DomainParameters(&dp);
481 return ret;
483 ret = BN_to_integer(context, dh->g, &dp.g);
484 if (ret) {
485 free_DomainParameters(&dp);
486 return ret;
488 ret = BN_to_integer(context, dh->q, &dp.q);
489 if (ret) {
490 free_DomainParameters(&dp);
491 return ret;
493 dp.j = NULL;
494 dp.validationParms = NULL;
496 a->clientPublicValue->algorithm.parameters =
497 malloc(sizeof(*a->clientPublicValue->algorithm.parameters));
498 if (a->clientPublicValue->algorithm.parameters == NULL) {
499 free_DomainParameters(&dp);
500 return ret;
503 ASN1_MALLOC_ENCODE(DomainParameters,
504 a->clientPublicValue->algorithm.parameters->data,
505 a->clientPublicValue->algorithm.parameters->length,
506 &dp, &size, ret);
507 free_DomainParameters(&dp);
508 if (ret)
509 return ret;
510 if (size != a->clientPublicValue->algorithm.parameters->length)
511 krb5_abortx(context, "Internal ASN1 encoder error");
513 ret = BN_to_integer(context, dh->pub_key, &dh_pub_key);
514 if (ret)
515 return ret;
517 ASN1_MALLOC_ENCODE(DHPublicKey, dhbuf.data, dhbuf.length,
518 &dh_pub_key, &size, ret);
519 der_free_heim_integer(&dh_pub_key);
520 if (ret)
521 return ret;
522 if (size != dhbuf.length)
523 krb5_abortx(context, "asn1 internal error");
524 } else if (ctx->keyex == USE_ECDH) {
525 #ifdef HAVE_OPENSSL
526 ECParameters ecp;
527 unsigned char *p;
528 int len;
530 /* copy in public key, XXX find the best curve that the server support or use the clients curve if possible */
532 ecp.element = choice_ECParameters_namedCurve;
533 ret = der_copy_oid(&asn1_oid_id_ec_group_secp256r1,
534 &ecp.u.namedCurve);
535 if (ret)
536 return ret;
538 ALLOC(a->clientPublicValue->algorithm.parameters, 1);
539 if (a->clientPublicValue->algorithm.parameters == NULL) {
540 free_ECParameters(&ecp);
541 return ENOMEM;
543 ASN1_MALLOC_ENCODE(ECParameters, p, len, &ecp, &size, ret);
544 free_ECParameters(&ecp);
545 if (ret)
546 return ret;
547 if (size != len)
548 krb5_abortx(context, "asn1 internal error");
550 a->clientPublicValue->algorithm.parameters->data = p;
551 a->clientPublicValue->algorithm.parameters->length = size;
553 /* copy in public key */
555 ret = der_copy_oid(&asn1_oid_id_ecPublicKey,
556 &a->clientPublicValue->algorithm.algorithm);
557 if (ret)
558 return ret;
560 ctx->u.eckey = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
561 if (ctx->u.eckey == NULL)
562 return ENOMEM;
564 ret = EC_KEY_generate_key(ctx->u.eckey);
565 if (ret != 1)
566 return EINVAL;
568 /* encode onto dhkey */
570 len = i2o_ECPublicKey(ctx->u.eckey, NULL);
571 if (len <= 0)
572 abort();
574 dhbuf.data = malloc(len);
575 if (dhbuf.data == NULL)
576 abort();
577 dhbuf.length = len;
578 p = dhbuf.data;
580 len = i2o_ECPublicKey(ctx->u.eckey, &p);
581 if (len <= 0)
582 abort();
584 /* XXX verify that this is right with RFC3279 */
585 #else
586 return EINVAL;
587 #endif
588 } else
589 krb5_abortx(context, "internal error");
590 a->clientPublicValue->subjectPublicKey.length = dhbuf.length * 8;
591 a->clientPublicValue->subjectPublicKey.data = dhbuf.data;
595 a->supportedCMSTypes = calloc(1, sizeof(*a->supportedCMSTypes));
596 if (a->supportedCMSTypes == NULL)
597 return ENOMEM;
599 ret = hx509_crypto_available(ctx->id->hx509ctx, HX509_SELECT_ALL, NULL,
600 &a->supportedCMSTypes->val,
601 &a->supportedCMSTypes->len);
602 if (ret)
603 return ret;
606 return ret;
609 krb5_error_code KRB5_LIB_FUNCTION
610 _krb5_pk_mk_ContentInfo(krb5_context context,
611 const krb5_data *buf,
612 const heim_oid *oid,
613 struct ContentInfo *content_info)
615 krb5_error_code ret;
617 ret = der_copy_oid(oid, &content_info->contentType);
618 if (ret)
619 return ret;
620 ALLOC(content_info->content, 1);
621 if (content_info->content == NULL)
622 return ENOMEM;
623 content_info->content->data = malloc(buf->length);
624 if (content_info->content->data == NULL)
625 return ENOMEM;
626 memcpy(content_info->content->data, buf->data, buf->length);
627 content_info->content->length = buf->length;
628 return 0;
631 static krb5_error_code
632 pk_mk_padata(krb5_context context,
633 krb5_pk_init_ctx ctx,
634 const KDC_REQ_BODY *req_body,
635 unsigned nonce,
636 METHOD_DATA *md)
638 struct ContentInfo content_info;
639 krb5_error_code ret;
640 const heim_oid *oid;
641 size_t size;
642 krb5_data buf, sd_buf;
643 int pa_type;
645 krb5_data_zero(&buf);
646 krb5_data_zero(&sd_buf);
647 memset(&content_info, 0, sizeof(content_info));
649 if (ctx->type == PKINIT_WIN2K) {
650 AuthPack_Win2k ap;
651 krb5_timestamp sec;
652 int32_t usec;
654 memset(&ap, 0, sizeof(ap));
656 /* fill in PKAuthenticator */
657 ret = copy_PrincipalName(req_body->sname, &ap.pkAuthenticator.kdcName);
658 if (ret) {
659 free_AuthPack_Win2k(&ap);
660 krb5_clear_error_message(context);
661 goto out;
663 ret = copy_Realm(&req_body->realm, &ap.pkAuthenticator.kdcRealm);
664 if (ret) {
665 free_AuthPack_Win2k(&ap);
666 krb5_clear_error_message(context);
667 goto out;
670 krb5_us_timeofday(context, &sec, &usec);
671 ap.pkAuthenticator.ctime = sec;
672 ap.pkAuthenticator.cusec = usec;
673 ap.pkAuthenticator.nonce = nonce;
675 ASN1_MALLOC_ENCODE(AuthPack_Win2k, buf.data, buf.length,
676 &ap, &size, ret);
677 free_AuthPack_Win2k(&ap);
678 if (ret) {
679 krb5_set_error_message(context, ret,
680 N_("Failed encoding AuthPackWin: %d", ""),
681 (int)ret);
682 goto out;
684 if (buf.length != size)
685 krb5_abortx(context, "internal ASN1 encoder error");
687 oid = &asn1_oid_id_pkcs7_data;
688 } else if (ctx->type == PKINIT_27) {
689 AuthPack ap;
691 memset(&ap, 0, sizeof(ap));
693 ret = build_auth_pack(context, nonce, ctx, req_body, &ap);
694 if (ret) {
695 free_AuthPack(&ap);
696 goto out;
699 ASN1_MALLOC_ENCODE(AuthPack, buf.data, buf.length, &ap, &size, ret);
700 free_AuthPack(&ap);
701 if (ret) {
702 krb5_set_error_message(context, ret,
703 N_("Failed encoding AuthPack: %d", ""),
704 (int)ret);
705 goto out;
707 if (buf.length != size)
708 krb5_abortx(context, "internal ASN1 encoder error");
710 oid = &asn1_oid_id_pkauthdata;
711 } else
712 krb5_abortx(context, "internal pkinit error");
714 ret = create_signature(context, oid, &buf, ctx->id,
715 ctx->peer, &sd_buf);
716 krb5_data_free(&buf);
717 if (ret)
718 goto out;
720 ret = hx509_cms_wrap_ContentInfo(&asn1_oid_id_pkcs7_signedData, &sd_buf, &buf);
721 krb5_data_free(&sd_buf);
722 if (ret) {
723 krb5_set_error_message(context, ret,
724 N_("ContentInfo wrapping of signedData failed",""));
725 goto out;
728 if (ctx->type == PKINIT_WIN2K) {
729 PA_PK_AS_REQ_Win2k winreq;
731 pa_type = KRB5_PADATA_PK_AS_REQ_WIN;
733 memset(&winreq, 0, sizeof(winreq));
735 winreq.signed_auth_pack = buf;
737 ASN1_MALLOC_ENCODE(PA_PK_AS_REQ_Win2k, buf.data, buf.length,
738 &winreq, &size, ret);
739 free_PA_PK_AS_REQ_Win2k(&winreq);
741 } else if (ctx->type == PKINIT_27) {
742 PA_PK_AS_REQ req;
744 pa_type = KRB5_PADATA_PK_AS_REQ;
746 memset(&req, 0, sizeof(req));
747 req.signedAuthPack = buf;
749 if (ctx->trustedCertifiers) {
751 req.trustedCertifiers = calloc(1, sizeof(*req.trustedCertifiers));
752 if (req.trustedCertifiers == NULL) {
753 ret = ENOMEM;
754 krb5_set_error_message(context, ret,
755 N_("malloc: out of memory", ""));
756 free_PA_PK_AS_REQ(&req);
757 goto out;
759 ret = build_edi(context, ctx->id->hx509ctx,
760 ctx->id->anchors, req.trustedCertifiers);
761 if (ret) {
762 krb5_set_error_message(context, ret,
763 N_("pk-init: failed to build "
764 "trustedCertifiers", ""));
765 free_PA_PK_AS_REQ(&req);
766 goto out;
769 req.kdcPkId = NULL;
771 ASN1_MALLOC_ENCODE(PA_PK_AS_REQ, buf.data, buf.length,
772 &req, &size, ret);
774 free_PA_PK_AS_REQ(&req);
776 } else
777 krb5_abortx(context, "internal pkinit error");
778 if (ret) {
779 krb5_set_error_message(context, ret, "PA-PK-AS-REQ %d", (int)ret);
780 goto out;
782 if (buf.length != size)
783 krb5_abortx(context, "Internal ASN1 encoder error");
785 ret = krb5_padata_add(context, md, pa_type, buf.data, buf.length);
786 if (ret)
787 free(buf.data);
789 if (ret == 0)
790 krb5_padata_add(context, md, KRB5_PADATA_PK_AS_09_BINDING, NULL, 0);
792 out:
793 free_ContentInfo(&content_info);
795 return ret;
799 krb5_error_code KRB5_LIB_FUNCTION
800 _krb5_pk_mk_padata(krb5_context context,
801 void *c,
802 const KDC_REQ_BODY *req_body,
803 unsigned nonce,
804 METHOD_DATA *md)
806 krb5_pk_init_ctx ctx = c;
807 int win2k_compat;
809 win2k_compat = krb5_config_get_bool_default(context, NULL,
810 FALSE,
811 "realms",
812 req_body->realm,
813 "pkinit_win2k",
814 NULL);
816 if (win2k_compat) {
817 ctx->require_binding =
818 krb5_config_get_bool_default(context, NULL,
819 FALSE,
820 "realms",
821 req_body->realm,
822 "pkinit_win2k_require_binding",
823 NULL);
824 ctx->type = PKINIT_WIN2K;
825 } else
826 ctx->type = PKINIT_27;
828 ctx->require_eku =
829 krb5_config_get_bool_default(context, NULL,
830 TRUE,
831 "realms",
832 req_body->realm,
833 "pkinit_require_eku",
834 NULL);
835 ctx->require_krbtgt_otherName =
836 krb5_config_get_bool_default(context, NULL,
837 TRUE,
838 "realms",
839 req_body->realm,
840 "pkinit_require_krbtgt_otherName",
841 NULL);
843 ctx->require_hostname_match =
844 krb5_config_get_bool_default(context, NULL,
845 FALSE,
846 "realms",
847 req_body->realm,
848 "pkinit_require_hostname_match",
849 NULL);
851 ctx->trustedCertifiers =
852 krb5_config_get_bool_default(context, NULL,
853 TRUE,
854 "realms",
855 req_body->realm,
856 "pkinit_trustedCertifiers",
857 NULL);
859 return pk_mk_padata(context, ctx, req_body, nonce, md);
862 static krb5_error_code
863 pk_verify_sign(krb5_context context,
864 const void *data,
865 size_t length,
866 struct krb5_pk_identity *id,
867 heim_oid *contentType,
868 krb5_data *content,
869 struct krb5_pk_cert **signer)
871 hx509_certs signer_certs;
872 int ret;
874 *signer = NULL;
876 ret = hx509_cms_verify_signed(id->hx509ctx,
877 id->verify_ctx,
878 HX509_CMS_VS_ALLOW_DATA_OID_MISMATCH|HX509_CMS_VS_NO_KU_CHECK,
879 data,
880 length,
881 NULL,
882 id->certpool,
883 contentType,
884 content,
885 &signer_certs);
886 if (ret) {
887 pk_copy_error(context, id->hx509ctx, ret,
888 "CMS verify signed failed");
889 return ret;
892 *signer = calloc(1, sizeof(**signer));
893 if (*signer == NULL) {
894 krb5_clear_error_message(context);
895 ret = ENOMEM;
896 goto out;
899 ret = hx509_get_one_cert(id->hx509ctx, signer_certs, &(*signer)->cert);
900 if (ret) {
901 pk_copy_error(context, id->hx509ctx, ret,
902 "Failed to get on of the signer certs");
903 goto out;
906 out:
907 hx509_certs_free(&signer_certs);
908 if (ret) {
909 if (*signer) {
910 hx509_cert_free((*signer)->cert);
911 free(*signer);
912 *signer = NULL;
916 return ret;
919 static krb5_error_code
920 get_reply_key_win(krb5_context context,
921 const krb5_data *content,
922 unsigned nonce,
923 krb5_keyblock **key)
925 ReplyKeyPack_Win2k key_pack;
926 krb5_error_code ret;
927 size_t size;
929 ret = decode_ReplyKeyPack_Win2k(content->data,
930 content->length,
931 &key_pack,
932 &size);
933 if (ret) {
934 krb5_set_error_message(context, ret,
935 N_("PKINIT decoding reply key failed", ""));
936 free_ReplyKeyPack_Win2k(&key_pack);
937 return ret;
940 if (key_pack.nonce != nonce) {
941 krb5_set_error_message(context, ret,
942 N_("PKINIT enckey nonce is wrong", ""));
943 free_ReplyKeyPack_Win2k(&key_pack);
944 return KRB5KRB_AP_ERR_MODIFIED;
947 *key = malloc (sizeof (**key));
948 if (*key == NULL) {
949 free_ReplyKeyPack_Win2k(&key_pack);
950 krb5_set_error_message(context, ENOMEM,
951 N_("malloc: out of memory", ""));
952 return ENOMEM;
955 ret = copy_EncryptionKey(&key_pack.replyKey, *key);
956 free_ReplyKeyPack_Win2k(&key_pack);
957 if (ret) {
958 krb5_set_error_message(context, ret,
959 N_("PKINIT failed copying reply key", ""));
960 free(*key);
961 *key = NULL;
964 return ret;
967 static krb5_error_code
968 get_reply_key(krb5_context context,
969 const krb5_data *content,
970 const krb5_data *req_buffer,
971 krb5_keyblock **key)
973 ReplyKeyPack key_pack;
974 krb5_error_code ret;
975 size_t size;
977 ret = decode_ReplyKeyPack(content->data,
978 content->length,
979 &key_pack,
980 &size);
981 if (ret) {
982 krb5_set_error_message(context, ret,
983 N_("PKINIT decoding reply key failed", ""));
984 free_ReplyKeyPack(&key_pack);
985 return ret;
989 krb5_crypto crypto;
992 * XXX Verify kp.replyKey is a allowed enctype in the
993 * configuration file
996 ret = krb5_crypto_init(context, &key_pack.replyKey, 0, &crypto);
997 if (ret) {
998 free_ReplyKeyPack(&key_pack);
999 return ret;
1002 ret = krb5_verify_checksum(context, crypto, 6,
1003 req_buffer->data, req_buffer->length,
1004 &key_pack.asChecksum);
1005 krb5_crypto_destroy(context, crypto);
1006 if (ret) {
1007 free_ReplyKeyPack(&key_pack);
1008 return ret;
1012 *key = malloc (sizeof (**key));
1013 if (*key == NULL) {
1014 free_ReplyKeyPack(&key_pack);
1015 krb5_set_error_message(context, ENOMEM,
1016 N_("malloc: out of memory", ""));
1017 return ENOMEM;
1020 ret = copy_EncryptionKey(&key_pack.replyKey, *key);
1021 free_ReplyKeyPack(&key_pack);
1022 if (ret) {
1023 krb5_set_error_message(context, ret,
1024 N_("PKINIT failed copying reply key", ""));
1025 free(*key);
1026 *key = NULL;
1029 return ret;
1033 static krb5_error_code
1034 pk_verify_host(krb5_context context,
1035 const char *realm,
1036 const krb5_krbhst_info *hi,
1037 struct krb5_pk_init_ctx_data *ctx,
1038 struct krb5_pk_cert *host)
1040 krb5_error_code ret = 0;
1042 if (ctx->require_eku) {
1043 ret = hx509_cert_check_eku(ctx->id->hx509ctx, host->cert,
1044 &asn1_oid_id_pkkdcekuoid, 0);
1045 if (ret) {
1046 krb5_set_error_message(context, ret,
1047 N_("No PK-INIT KDC EKU in kdc certificate", ""));
1048 return ret;
1051 if (ctx->require_krbtgt_otherName) {
1052 hx509_octet_string_list list;
1053 int i;
1055 ret = hx509_cert_find_subjectAltName_otherName(ctx->id->hx509ctx,
1056 host->cert,
1057 &asn1_oid_id_pkinit_san,
1058 &list);
1059 if (ret) {
1060 krb5_set_error_message(context, ret,
1061 N_("Failed to find the PK-INIT "
1062 "subjectAltName in the KDC "
1063 "certificate", ""));
1065 return ret;
1068 for (i = 0; i < list.len; i++) {
1069 KRB5PrincipalName r;
1071 ret = decode_KRB5PrincipalName(list.val[i].data,
1072 list.val[i].length,
1074 NULL);
1075 if (ret) {
1076 krb5_set_error_message(context, ret,
1077 N_("Failed to decode the PK-INIT "
1078 "subjectAltName in the "
1079 "KDC certificate", ""));
1081 break;
1084 if (r.principalName.name_string.len != 2 ||
1085 strcmp(r.principalName.name_string.val[0], KRB5_TGS_NAME) != 0 ||
1086 strcmp(r.principalName.name_string.val[1], realm) != 0 ||
1087 strcmp(r.realm, realm) != 0)
1089 ret = KRB5_KDC_ERR_INVALID_CERTIFICATE;
1090 krb5_set_error_message(context, ret,
1091 N_("KDC have wrong realm name in "
1092 "the certificate", ""));
1095 free_KRB5PrincipalName(&r);
1096 if (ret)
1097 break;
1099 hx509_free_octet_string_list(&list);
1101 if (ret)
1102 return ret;
1104 if (hi) {
1105 ret = hx509_verify_hostname(ctx->id->hx509ctx, host->cert,
1106 ctx->require_hostname_match,
1107 HX509_HN_HOSTNAME,
1108 hi->hostname,
1109 hi->ai->ai_addr, hi->ai->ai_addrlen);
1111 if (ret)
1112 krb5_set_error_message(context, ret,
1113 N_("Address mismatch in "
1114 "the KDC certificate", ""));
1116 return ret;
1119 static krb5_error_code
1120 pk_rd_pa_reply_enckey(krb5_context context,
1121 int type,
1122 const heim_octet_string *indata,
1123 const heim_oid *dataType,
1124 const char *realm,
1125 krb5_pk_init_ctx ctx,
1126 krb5_enctype etype,
1127 const krb5_krbhst_info *hi,
1128 unsigned nonce,
1129 const krb5_data *req_buffer,
1130 PA_DATA *pa,
1131 krb5_keyblock **key)
1133 krb5_error_code ret;
1134 struct krb5_pk_cert *host = NULL;
1135 krb5_data content;
1136 heim_oid contentType = { 0, NULL };
1137 int flags = HX509_CMS_UE_DONT_REQUIRE_KU_ENCIPHERMENT;
1139 if (der_heim_oid_cmp(&asn1_oid_id_pkcs7_envelopedData, dataType)) {
1140 krb5_set_error_message(context, EINVAL,
1141 N_("PKINIT: Invalid content type", ""));
1142 return EINVAL;
1145 if (ctx->type == PKINIT_WIN2K)
1146 flags |= HX509_CMS_UE_ALLOW_WEAK;
1148 ret = hx509_cms_unenvelope(ctx->id->hx509ctx,
1149 ctx->id->certs,
1150 flags,
1151 indata->data,
1152 indata->length,
1153 NULL,
1155 &contentType,
1156 &content);
1157 if (ret) {
1158 pk_copy_error(context, ctx->id->hx509ctx, ret,
1159 "Failed to unenvelope CMS data in PK-INIT reply");
1160 return ret;
1162 der_free_oid(&contentType);
1164 #if 0 /* windows LH with interesting CMS packets, leaks memory */
1166 size_t ph = 1 + der_length_len (length);
1167 unsigned char *ptr = malloc(length + ph);
1168 size_t l;
1170 memcpy(ptr + ph, p, length);
1172 ret = der_put_length_and_tag (ptr + ph - 1, ph, length,
1173 ASN1_C_UNIV, CONS, UT_Sequence, &l);
1174 if (ret)
1175 return ret;
1176 ptr += ph - l;
1177 length += l;
1178 p = ptr;
1180 #endif
1182 /* win2k uses ContentInfo */
1183 if (type == PKINIT_WIN2K) {
1184 heim_oid type;
1185 heim_octet_string out;
1187 ret = hx509_cms_unwrap_ContentInfo(&content, &type, &out, NULL);
1188 if (ret)
1189 goto out;
1190 if (der_heim_oid_cmp(&type, &asn1_oid_id_pkcs7_signedData)) {
1191 ret = EINVAL; /* XXX */
1192 krb5_set_error_message(context, ret,
1193 N_("PKINIT: Invalid content type", ""));
1194 der_free_oid(&type);
1195 der_free_octet_string(&out);
1196 goto out;
1198 der_free_oid(&type);
1199 krb5_data_free(&content);
1200 ret = krb5_data_copy(&content, out.data, out.length);
1201 der_free_octet_string(&out);
1202 if (ret) {
1203 krb5_set_error_message(context, ret,
1204 N_("malloc: out of memory", ""));
1205 goto out;
1209 ret = pk_verify_sign(context,
1210 content.data,
1211 content.length,
1212 ctx->id,
1213 &contentType,
1214 &content,
1215 &host);
1216 if (ret)
1217 goto out;
1219 /* make sure that it is the kdc's certificate */
1220 ret = pk_verify_host(context, realm, hi, ctx, host);
1221 if (ret) {
1222 goto out;
1225 #if 0
1226 if (type == PKINIT_WIN2K) {
1227 if (der_heim_oid_cmp(&contentType, &asn1_oid_id_pkcs7_data) != 0) {
1228 ret = KRB5KRB_AP_ERR_MSG_TYPE;
1229 krb5_set_error_message(context, ret, "PKINIT: reply key, wrong oid");
1230 goto out;
1232 } else {
1233 if (der_heim_oid_cmp(&contentType, &asn1_oid_id_pkrkeydata) != 0) {
1234 ret = KRB5KRB_AP_ERR_MSG_TYPE;
1235 krb5_set_error_message(context, ret, "PKINIT: reply key, wrong oid");
1236 goto out;
1239 #endif
1241 switch(type) {
1242 case PKINIT_WIN2K:
1243 ret = get_reply_key(context, &content, req_buffer, key);
1244 if (ret != 0 && ctx->require_binding == 0)
1245 ret = get_reply_key_win(context, &content, nonce, key);
1246 break;
1247 case PKINIT_27:
1248 ret = get_reply_key(context, &content, req_buffer, key);
1249 break;
1251 if (ret)
1252 goto out;
1254 /* XXX compare given etype with key->etype */
1256 out:
1257 if (host)
1258 _krb5_pk_cert_free(host);
1259 der_free_oid(&contentType);
1260 krb5_data_free(&content);
1262 return ret;
1265 static krb5_error_code
1266 pk_rd_pa_reply_dh(krb5_context context,
1267 const heim_octet_string *indata,
1268 const heim_oid *dataType,
1269 const char *realm,
1270 krb5_pk_init_ctx ctx,
1271 krb5_enctype etype,
1272 const krb5_krbhst_info *hi,
1273 const DHNonce *c_n,
1274 const DHNonce *k_n,
1275 unsigned nonce,
1276 PA_DATA *pa,
1277 krb5_keyblock **key)
1279 const unsigned char *p;
1280 unsigned char *dh_gen_key = NULL;
1281 struct krb5_pk_cert *host = NULL;
1282 BIGNUM *kdc_dh_pubkey = NULL;
1283 KDCDHKeyInfo kdc_dh_info;
1284 heim_oid contentType = { 0, NULL };
1285 krb5_data content;
1286 krb5_error_code ret;
1287 int dh_gen_keylen = 0;
1288 size_t size;
1290 krb5_data_zero(&content);
1291 memset(&kdc_dh_info, 0, sizeof(kdc_dh_info));
1293 if (der_heim_oid_cmp(&asn1_oid_id_pkcs7_signedData, dataType)) {
1294 krb5_set_error_message(context, EINVAL,
1295 N_("PKINIT: Invalid content type", ""));
1296 return EINVAL;
1299 ret = pk_verify_sign(context,
1300 indata->data,
1301 indata->length,
1302 ctx->id,
1303 &contentType,
1304 &content,
1305 &host);
1306 if (ret)
1307 goto out;
1309 /* make sure that it is the kdc's certificate */
1310 ret = pk_verify_host(context, realm, hi, ctx, host);
1311 if (ret)
1312 goto out;
1314 if (der_heim_oid_cmp(&contentType, &asn1_oid_id_pkdhkeydata)) {
1315 ret = KRB5KRB_AP_ERR_MSG_TYPE;
1316 krb5_set_error_message(context, ret,
1317 N_("pkinit - dh reply contains wrong oid", ""));
1318 goto out;
1321 ret = decode_KDCDHKeyInfo(content.data,
1322 content.length,
1323 &kdc_dh_info,
1324 &size);
1326 if (ret) {
1327 krb5_set_error_message(context, ret,
1328 N_("pkinit - failed to decode "
1329 "KDC DH Key Info", ""));
1330 goto out;
1333 if (kdc_dh_info.nonce != nonce) {
1334 ret = KRB5KRB_AP_ERR_MODIFIED;
1335 krb5_set_error_message(context, ret,
1336 N_("PKINIT: DH nonce is wrong", ""));
1337 goto out;
1340 if (kdc_dh_info.dhKeyExpiration) {
1341 if (k_n == NULL) {
1342 ret = KRB5KRB_ERR_GENERIC;
1343 krb5_set_error_message(context, ret,
1344 N_("pkinit; got key expiration "
1345 "without server nonce", ""));
1346 goto out;
1348 if (c_n == NULL) {
1349 ret = KRB5KRB_ERR_GENERIC;
1350 krb5_set_error_message(context, ret,
1351 N_("pkinit; got DH reuse but no "
1352 "client nonce", ""));
1353 goto out;
1355 } else {
1356 if (k_n) {
1357 ret = KRB5KRB_ERR_GENERIC;
1358 krb5_set_error_message(context, ret,
1359 N_("pkinit: got server nonce "
1360 "without key expiration", ""));
1361 goto out;
1363 c_n = NULL;
1367 p = kdc_dh_info.subjectPublicKey.data;
1368 size = (kdc_dh_info.subjectPublicKey.length + 7) / 8;
1370 if (ctx->keyex == USE_DH) {
1371 DHPublicKey k;
1372 ret = decode_DHPublicKey(p, size, &k, NULL);
1373 if (ret) {
1374 krb5_set_error_message(context, ret,
1375 N_("pkinit: can't decode "
1376 "without key expiration", ""));
1377 goto out;
1380 kdc_dh_pubkey = integer_to_BN(context, "DHPublicKey", &k);
1381 free_DHPublicKey(&k);
1382 if (kdc_dh_pubkey == NULL) {
1383 ret = ENOMEM;
1384 goto out;
1388 dh_gen_keylen = DH_size(ctx->u.dh);
1389 size = BN_num_bytes(ctx->u.dh->p);
1390 if (size < dh_gen_keylen)
1391 size = dh_gen_keylen;
1393 dh_gen_key = malloc(size);
1394 if (dh_gen_key == NULL) {
1395 ret = ENOMEM;
1396 krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
1397 goto out;
1399 memset(dh_gen_key, 0, size - dh_gen_keylen);
1401 dh_gen_keylen = DH_compute_key(dh_gen_key + (size - dh_gen_keylen),
1402 kdc_dh_pubkey, ctx->u.dh);
1403 if (dh_gen_keylen == -1) {
1404 ret = KRB5KRB_ERR_GENERIC;
1405 dh_gen_keylen = 0;
1406 krb5_set_error_message(context, ret,
1407 N_("PKINIT: Can't compute Diffie-Hellman key", ""));
1408 goto out;
1410 } else {
1411 #ifdef HAVE_OPENSSL
1412 const EC_GROUP *group;
1413 EC_KEY *public = NULL;
1415 group = EC_KEY_get0_group(ctx->u.eckey);
1417 public = EC_KEY_new();
1418 if (public == NULL) {
1419 ret = ENOMEM;
1420 goto out;
1422 if (EC_KEY_set_group(public, group) != 1) {
1423 EC_KEY_free(public);
1424 ret = ENOMEM;
1425 goto out;
1428 if (o2i_ECPublicKey(&public, &p, size) == NULL) {
1429 EC_KEY_free(public);
1430 ret = KRB5KRB_ERR_GENERIC;
1431 krb5_set_error_message(context, ret,
1432 N_("PKINIT: Can't parse ECDH public key", ""));
1433 goto out;
1436 size = (EC_GROUP_get_degree(group) + 7) / 8;
1437 dh_gen_key = malloc(size);
1438 if (dh_gen_key == NULL) {
1439 EC_KEY_free(public);
1440 ret = ENOMEM;
1441 krb5_set_error_message(context, ret,
1442 N_("malloc: out of memory", ""));
1443 goto out;
1445 dh_gen_keylen = ECDH_compute_key(dh_gen_key, size,
1446 EC_KEY_get0_public_key(public), ctx->u.eckey, NULL);
1447 EC_KEY_free(public);
1448 if (dh_gen_keylen == -1) {
1449 ret = KRB5KRB_ERR_GENERIC;
1450 dh_gen_keylen = 0;
1451 krb5_set_error_message(context, ret,
1452 N_("PKINIT: Can't compute ECDH public key", ""));
1453 goto out;
1455 #else
1456 ret = EINVAL;
1457 #endif
1460 if (dh_gen_keylen >= 0) {
1461 ret = EINVAL;
1462 goto out;
1465 *key = malloc (sizeof (**key));
1466 if (*key == NULL) {
1467 ret = ENOMEM;
1468 krb5_set_error_message(context, ret,
1469 N_("malloc: out of memory", ""));
1470 goto out;
1473 ret = _krb5_pk_octetstring2key(context,
1474 etype,
1475 dh_gen_key, dh_gen_keylen,
1476 c_n, k_n,
1477 *key);
1478 if (ret) {
1479 krb5_set_error_message(context, ret,
1480 N_("PKINIT: can't create key from DH key", ""));
1481 free(*key);
1482 *key = NULL;
1483 goto out;
1486 out:
1487 if (kdc_dh_pubkey)
1488 BN_free(kdc_dh_pubkey);
1489 if (dh_gen_key) {
1490 memset(dh_gen_key, 0, dh_gen_keylen);
1491 free(dh_gen_key);
1493 if (host)
1494 _krb5_pk_cert_free(host);
1495 if (content.data)
1496 krb5_data_free(&content);
1497 der_free_oid(&contentType);
1498 free_KDCDHKeyInfo(&kdc_dh_info);
1500 return ret;
1503 krb5_error_code KRB5_LIB_FUNCTION
1504 _krb5_pk_rd_pa_reply(krb5_context context,
1505 const char *realm,
1506 void *c,
1507 krb5_enctype etype,
1508 const krb5_krbhst_info *hi,
1509 unsigned nonce,
1510 const krb5_data *req_buffer,
1511 PA_DATA *pa,
1512 krb5_keyblock **key)
1514 krb5_pk_init_ctx ctx = c;
1515 krb5_error_code ret;
1516 size_t size;
1518 /* Check for IETF PK-INIT first */
1519 if (ctx->type == PKINIT_27) {
1520 PA_PK_AS_REP rep;
1521 heim_octet_string os, data;
1522 heim_oid oid;
1524 if (pa->padata_type != KRB5_PADATA_PK_AS_REP) {
1525 krb5_set_error_message(context, EINVAL,
1526 N_("PKINIT: wrong padata recv", ""));
1527 return EINVAL;
1530 ret = decode_PA_PK_AS_REP(pa->padata_value.data,
1531 pa->padata_value.length,
1532 &rep,
1533 &size);
1534 if (ret) {
1535 krb5_set_error_message(context, ret,
1536 N_("Failed to decode pkinit AS rep", ""));
1537 return ret;
1540 switch (rep.element) {
1541 case choice_PA_PK_AS_REP_dhInfo:
1542 os = rep.u.dhInfo.dhSignedData;
1543 break;
1544 case choice_PA_PK_AS_REP_encKeyPack:
1545 os = rep.u.encKeyPack;
1546 break;
1547 default: {
1548 PA_PK_AS_REP_BTMM btmm;
1549 free_PA_PK_AS_REP(&rep);
1550 memset(&rep, 0, sizeof(rep));
1552 ret = decode_PA_PK_AS_REP_BTMM(pa->padata_value.data,
1553 pa->padata_value.length,
1554 &btmm,
1555 &size);
1556 if (ret) {
1557 krb5_set_error_message(context, EINVAL,
1558 N_("PKINIT: -27 reply "
1559 "invalid content type", ""));
1560 return EINVAL;
1563 if (btmm.dhSignedData || btmm.encKeyPack == NULL) {
1564 free_PA_PK_AS_REP_BTMM(&btmm);
1565 ret = EINVAL;
1566 krb5_set_error_message(context, ret,
1567 N_("DH mode not supported for BTMM mode", ""));
1568 return ret;
1572 * Transform to IETF style PK-INIT reply so that free works below
1575 rep.element = choice_PA_PK_AS_REP_encKeyPack;
1576 rep.u.encKeyPack.data = btmm.encKeyPack->data;
1577 rep.u.encKeyPack.length = btmm.encKeyPack->length;
1578 btmm.encKeyPack->data = NULL;
1579 btmm.encKeyPack->length = 0;
1580 free_PA_PK_AS_REP_BTMM(&btmm);
1581 os = rep.u.encKeyPack;
1585 ret = hx509_cms_unwrap_ContentInfo(&os, &oid, &data, NULL);
1586 if (ret) {
1587 free_PA_PK_AS_REP(&rep);
1588 krb5_set_error_message(context, ret,
1589 N_("PKINIT: failed to unwrap CI", ""));
1590 return ret;
1593 switch (rep.element) {
1594 case choice_PA_PK_AS_REP_dhInfo:
1595 ret = pk_rd_pa_reply_dh(context, &data, &oid, realm, ctx, etype, hi,
1596 ctx->clientDHNonce,
1597 rep.u.dhInfo.serverDHNonce,
1598 nonce, pa, key);
1599 break;
1600 case choice_PA_PK_AS_REP_encKeyPack:
1601 ret = pk_rd_pa_reply_enckey(context, PKINIT_27, &data, &oid, realm,
1602 ctx, etype, hi, nonce, req_buffer, pa, key);
1603 break;
1604 default:
1605 krb5_abortx(context, "pk-init as-rep case not possible to happen");
1607 der_free_octet_string(&data);
1608 der_free_oid(&oid);
1609 free_PA_PK_AS_REP(&rep);
1611 } else if (ctx->type == PKINIT_WIN2K) {
1612 PA_PK_AS_REP_Win2k w2krep;
1614 /* Check for Windows encoding of the AS-REP pa data */
1616 #if 0 /* should this be ? */
1617 if (pa->padata_type != KRB5_PADATA_PK_AS_REP) {
1618 krb5_set_error_message(context, EINVAL,
1619 "PKINIT: wrong padata recv");
1620 return EINVAL;
1622 #endif
1624 memset(&w2krep, 0, sizeof(w2krep));
1626 ret = decode_PA_PK_AS_REP_Win2k(pa->padata_value.data,
1627 pa->padata_value.length,
1628 &w2krep,
1629 &size);
1630 if (ret) {
1631 krb5_set_error_message(context, ret,
1632 N_("PKINIT: Failed decoding windows "
1633 "pkinit reply %d", ""), (int)ret);
1634 return ret;
1637 krb5_clear_error_message(context);
1639 switch (w2krep.element) {
1640 case choice_PA_PK_AS_REP_Win2k_encKeyPack: {
1641 heim_octet_string data;
1642 heim_oid oid;
1644 ret = hx509_cms_unwrap_ContentInfo(&w2krep.u.encKeyPack,
1645 &oid, &data, NULL);
1646 free_PA_PK_AS_REP_Win2k(&w2krep);
1647 if (ret) {
1648 krb5_set_error_message(context, ret,
1649 N_("PKINIT: failed to unwrap CI", ""));
1650 return ret;
1653 ret = pk_rd_pa_reply_enckey(context, PKINIT_WIN2K, &data, &oid, realm,
1654 ctx, etype, hi, nonce, req_buffer, pa, key);
1655 der_free_octet_string(&data);
1656 der_free_oid(&oid);
1658 break;
1660 default:
1661 free_PA_PK_AS_REP_Win2k(&w2krep);
1662 ret = EINVAL;
1663 krb5_set_error_message(context, ret,
1664 N_("PKINIT: win2k reply invalid "
1665 "content type", ""));
1666 break;
1669 } else {
1670 ret = EINVAL;
1671 krb5_set_error_message(context, ret,
1672 N_("PKINIT: unknown reply type", ""));
1675 return ret;
1678 struct prompter {
1679 krb5_context context;
1680 krb5_prompter_fct prompter;
1681 void *prompter_data;
1684 static int
1685 hx_pass_prompter(void *data, const hx509_prompt *prompter)
1687 krb5_error_code ret;
1688 krb5_prompt prompt;
1689 krb5_data password_data;
1690 struct prompter *p = data;
1692 password_data.data = prompter->reply.data;
1693 password_data.length = prompter->reply.length;
1695 prompt.prompt = prompter->prompt;
1696 prompt.hidden = hx509_prompt_hidden(prompter->type);
1697 prompt.reply = &password_data;
1699 switch (prompter->type) {
1700 case HX509_PROMPT_TYPE_INFO:
1701 prompt.type = KRB5_PROMPT_TYPE_INFO;
1702 break;
1703 case HX509_PROMPT_TYPE_PASSWORD:
1704 case HX509_PROMPT_TYPE_QUESTION:
1705 default:
1706 prompt.type = KRB5_PROMPT_TYPE_PASSWORD;
1707 break;
1710 ret = (*p->prompter)(p->context, p->prompter_data, NULL, NULL, 1, &prompt);
1711 if (ret) {
1712 memset (prompter->reply.data, 0, prompter->reply.length);
1713 return 1;
1715 return 0;
1718 krb5_error_code KRB5_LIB_FUNCTION
1719 _krb5_pk_load_id(krb5_context context,
1720 struct krb5_pk_identity **ret_id,
1721 int flags,
1722 const char *user_id,
1723 const char *anchor_id,
1724 char * const *chain_list,
1725 char * const *revoke_list,
1726 krb5_prompter_fct prompter,
1727 void *prompter_data,
1728 char *password)
1730 struct krb5_pk_identity *id = NULL;
1731 hx509_lock lock = NULL;
1732 struct prompter p;
1733 int ret;
1735 *ret_id = NULL;
1737 if (anchor_id == NULL) {
1738 krb5_set_error_message(context, HEIM_PKINIT_NO_VALID_CA,
1739 N_("PKINIT: No anchor given", ""));
1740 return HEIM_PKINIT_NO_VALID_CA;
1743 if (user_id == NULL && (flags & 4) == 0) {
1744 krb5_set_error_message(context, HEIM_PKINIT_NO_PRIVATE_KEY,
1745 N_("PKINIT: No user certificate given", ""));
1746 return HEIM_PKINIT_NO_PRIVATE_KEY;
1749 /* load cert */
1751 id = calloc(1, sizeof(*id));
1752 if (id == NULL) {
1753 krb5_set_error_message(context, ENOMEM,
1754 N_("malloc: out of memory", ""));
1755 return ENOMEM;
1758 ret = hx509_context_init(&id->hx509ctx);
1759 if (ret)
1760 goto out;
1762 ret = hx509_lock_init(id->hx509ctx, &lock);
1763 if (ret) {
1764 pk_copy_error(context, id->hx509ctx, ret, "Failed init lock");
1765 goto out;
1768 if (password && password[0])
1769 hx509_lock_add_password(lock, password);
1771 if (prompter) {
1772 p.context = context;
1773 p.prompter = prompter;
1774 p.prompter_data = prompter_data;
1776 ret = hx509_lock_set_prompter(lock, hx_pass_prompter, &p);
1777 if (ret)
1778 goto out;
1781 if (user_id) {
1782 ret = hx509_certs_init(id->hx509ctx, user_id, 0, lock, &id->certs);
1783 if (ret) {
1784 pk_copy_error(context, id->hx509ctx, ret,
1785 "Failed to init cert certs");
1786 goto out;
1788 } else {
1789 id->certs = NULL;
1792 ret = hx509_certs_init(id->hx509ctx, anchor_id, 0, NULL, &id->anchors);
1793 if (ret) {
1794 pk_copy_error(context, id->hx509ctx, ret,
1795 "Failed to init anchors");
1796 goto out;
1799 ret = hx509_certs_init(id->hx509ctx, "MEMORY:pkinit-cert-chain",
1800 0, NULL, &id->certpool);
1801 if (ret) {
1802 pk_copy_error(context, id->hx509ctx, ret,
1803 "Failed to init chain");
1804 goto out;
1807 while (chain_list && *chain_list) {
1808 ret = hx509_certs_append(id->hx509ctx, id->certpool,
1809 NULL, *chain_list);
1810 if (ret) {
1811 pk_copy_error(context, id->hx509ctx, ret,
1812 "Failed to laod chain %s",
1813 *chain_list);
1814 goto out;
1816 chain_list++;
1819 if (revoke_list) {
1820 ret = hx509_revoke_init(id->hx509ctx, &id->revokectx);
1821 if (ret) {
1822 pk_copy_error(context, id->hx509ctx, ret,
1823 "Failed init revoke list");
1824 goto out;
1827 while (*revoke_list) {
1828 ret = hx509_revoke_add_crl(id->hx509ctx,
1829 id->revokectx,
1830 *revoke_list);
1831 if (ret) {
1832 pk_copy_error(context, id->hx509ctx, ret,
1833 "Failed load revoke list");
1834 goto out;
1836 revoke_list++;
1838 } else
1839 hx509_context_set_missing_revoke(id->hx509ctx, 1);
1841 ret = hx509_verify_init_ctx(id->hx509ctx, &id->verify_ctx);
1842 if (ret) {
1843 pk_copy_error(context, id->hx509ctx, ret,
1844 "Failed init verify context");
1845 goto out;
1848 hx509_verify_attach_anchors(id->verify_ctx, id->anchors);
1849 hx509_verify_attach_revoke(id->verify_ctx, id->revokectx);
1851 out:
1852 if (ret) {
1853 hx509_verify_destroy_ctx(id->verify_ctx);
1854 hx509_certs_free(&id->certs);
1855 hx509_certs_free(&id->anchors);
1856 hx509_certs_free(&id->certpool);
1857 hx509_revoke_free(&id->revokectx);
1858 hx509_context_free(&id->hx509ctx);
1859 free(id);
1860 } else
1861 *ret_id = id;
1863 if (lock)
1864 hx509_lock_free(lock);
1866 return ret;
1873 static void
1874 pk_copy_error(krb5_context context,
1875 hx509_context hx509ctx,
1876 int hxret,
1877 const char *fmt,
1878 ...)
1880 va_list va;
1881 char *s, *f;
1883 va_start(va, fmt);
1884 vasprintf(&f, fmt, va);
1885 va_end(va);
1886 if (f == NULL) {
1887 krb5_clear_error_message(context);
1888 return;
1891 s = hx509_get_error_string(hx509ctx, hxret);
1892 if (s == NULL) {
1893 krb5_clear_error_message(context);
1894 free(f);
1895 return;
1897 krb5_set_error_message(context, hxret, "%s: %s", f, s);
1898 free(s);
1899 free(f);
1902 static int
1903 parse_integer(krb5_context context, char **p, const char *file, int lineno,
1904 const char *name, heim_integer *integer)
1906 int ret;
1907 char *p1;
1908 p1 = strsep(p, " \t");
1909 if (p1 == NULL) {
1910 krb5_set_error_message(context, EINVAL,
1911 N_("moduli file %s missing %s on line %d", ""),
1912 file, name, lineno);
1913 return EINVAL;
1915 ret = der_parse_hex_heim_integer(p1, integer);
1916 if (ret) {
1917 krb5_set_error_message(context, ret,
1918 N_("moduli file %s failed parsing %s "
1919 "on line %d", ""),
1920 file, name, lineno);
1921 return ret;
1924 return 0;
1927 krb5_error_code
1928 _krb5_parse_moduli_line(krb5_context context,
1929 const char *file,
1930 int lineno,
1931 char *p,
1932 struct krb5_dh_moduli **m)
1934 struct krb5_dh_moduli *m1;
1935 char *p1;
1936 int ret;
1938 *m = NULL;
1940 m1 = calloc(1, sizeof(*m1));
1941 if (m1 == NULL) {
1942 krb5_set_error_message(context, ENOMEM,
1943 N_("malloc: out of memory", ""));
1944 return ENOMEM;
1947 while (isspace((unsigned char)*p))
1948 p++;
1949 if (*p == '#') {
1950 free(m1);
1951 return 0;
1953 ret = EINVAL;
1955 p1 = strsep(&p, " \t");
1956 if (p1 == NULL) {
1957 krb5_set_error_message(context, ret,
1958 N_("moduli file %s missing name on line %d", ""),
1959 file, lineno);
1960 goto out;
1962 m1->name = strdup(p1);
1963 if (m1->name == NULL) {
1964 ret = ENOMEM;
1965 krb5_set_error_message(context, ret, N_("malloc: out of memeory", ""));
1966 goto out;
1969 p1 = strsep(&p, " \t");
1970 if (p1 == NULL) {
1971 krb5_set_error_message(context, ret,
1972 N_("moduli file %s missing bits on line %d", ""),
1973 file, lineno);
1974 goto out;
1977 m1->bits = atoi(p1);
1978 if (m1->bits == 0) {
1979 krb5_set_error_message(context, ret,
1980 N_("moduli file %s have un-parsable "
1981 "bits on line %d", ""), file, lineno);
1982 goto out;
1985 ret = parse_integer(context, &p, file, lineno, "p", &m1->p);
1986 if (ret)
1987 goto out;
1988 ret = parse_integer(context, &p, file, lineno, "g", &m1->g);
1989 if (ret)
1990 goto out;
1991 ret = parse_integer(context, &p, file, lineno, "q", &m1->q);
1992 if (ret)
1993 goto out;
1995 *m = m1;
1997 return 0;
1998 out:
1999 free(m1->name);
2000 der_free_heim_integer(&m1->p);
2001 der_free_heim_integer(&m1->g);
2002 der_free_heim_integer(&m1->q);
2003 free(m1);
2004 return ret;
2007 void
2008 _krb5_free_moduli(struct krb5_dh_moduli **moduli)
2010 int i;
2011 for (i = 0; moduli[i] != NULL; i++) {
2012 free(moduli[i]->name);
2013 der_free_heim_integer(&moduli[i]->p);
2014 der_free_heim_integer(&moduli[i]->g);
2015 der_free_heim_integer(&moduli[i]->q);
2016 free(moduli[i]);
2018 free(moduli);
2021 static const char *default_moduli_RFC2412_MODP_group2 =
2022 /* name */
2023 "RFC2412-MODP-group2 "
2024 /* bits */
2025 "1024 "
2026 /* p */
2027 "FFFFFFFF" "FFFFFFFF" "C90FDAA2" "2168C234" "C4C6628B" "80DC1CD1"
2028 "29024E08" "8A67CC74" "020BBEA6" "3B139B22" "514A0879" "8E3404DD"
2029 "EF9519B3" "CD3A431B" "302B0A6D" "F25F1437" "4FE1356D" "6D51C245"
2030 "E485B576" "625E7EC6" "F44C42E9" "A637ED6B" "0BFF5CB6" "F406B7ED"
2031 "EE386BFB" "5A899FA5" "AE9F2411" "7C4B1FE6" "49286651" "ECE65381"
2032 "FFFFFFFF" "FFFFFFFF "
2033 /* g */
2034 "02 "
2035 /* q */
2036 "7FFFFFFF" "FFFFFFFF" "E487ED51" "10B4611A" "62633145" "C06E0E68"
2037 "94812704" "4533E63A" "0105DF53" "1D89CD91" "28A5043C" "C71A026E"
2038 "F7CA8CD9" "E69D218D" "98158536" "F92F8A1B" "A7F09AB6" "B6A8E122"
2039 "F242DABB" "312F3F63" "7A262174" "D31BF6B5" "85FFAE5B" "7A035BF6"
2040 "F71C35FD" "AD44CFD2" "D74F9208" "BE258FF3" "24943328" "F67329C0"
2041 "FFFFFFFF" "FFFFFFFF";
2043 static const char *default_moduli_rfc3526_MODP_group14 =
2044 /* name */
2045 "rfc3526-MODP-group14 "
2046 /* bits */
2047 "1760 "
2048 /* p */
2049 "FFFFFFFF" "FFFFFFFF" "C90FDAA2" "2168C234" "C4C6628B" "80DC1CD1"
2050 "29024E08" "8A67CC74" "020BBEA6" "3B139B22" "514A0879" "8E3404DD"
2051 "EF9519B3" "CD3A431B" "302B0A6D" "F25F1437" "4FE1356D" "6D51C245"
2052 "E485B576" "625E7EC6" "F44C42E9" "A637ED6B" "0BFF5CB6" "F406B7ED"
2053 "EE386BFB" "5A899FA5" "AE9F2411" "7C4B1FE6" "49286651" "ECE45B3D"
2054 "C2007CB8" "A163BF05" "98DA4836" "1C55D39A" "69163FA8" "FD24CF5F"
2055 "83655D23" "DCA3AD96" "1C62F356" "208552BB" "9ED52907" "7096966D"
2056 "670C354E" "4ABC9804" "F1746C08" "CA18217C" "32905E46" "2E36CE3B"
2057 "E39E772C" "180E8603" "9B2783A2" "EC07A28F" "B5C55DF0" "6F4C52C9"
2058 "DE2BCBF6" "95581718" "3995497C" "EA956AE5" "15D22618" "98FA0510"
2059 "15728E5A" "8AACAA68" "FFFFFFFF" "FFFFFFFF "
2060 /* g */
2061 "02 "
2062 /* q */
2063 "7FFFFFFF" "FFFFFFFF" "E487ED51" "10B4611A" "62633145" "C06E0E68"
2064 "94812704" "4533E63A" "0105DF53" "1D89CD91" "28A5043C" "C71A026E"
2065 "F7CA8CD9" "E69D218D" "98158536" "F92F8A1B" "A7F09AB6" "B6A8E122"
2066 "F242DABB" "312F3F63" "7A262174" "D31BF6B5" "85FFAE5B" "7A035BF6"
2067 "F71C35FD" "AD44CFD2" "D74F9208" "BE258FF3" "24943328" "F6722D9E"
2068 "E1003E5C" "50B1DF82" "CC6D241B" "0E2AE9CD" "348B1FD4" "7E9267AF"
2069 "C1B2AE91" "EE51D6CB" "0E3179AB" "1042A95D" "CF6A9483" "B84B4B36"
2070 "B3861AA7" "255E4C02" "78BA3604" "650C10BE" "19482F23" "171B671D"
2071 "F1CF3B96" "0C074301" "CD93C1D1" "7603D147" "DAE2AEF8" "37A62964"
2072 "EF15E5FB" "4AAC0B8C" "1CCAA4BE" "754AB572" "8AE9130C" "4C7D0288"
2073 "0AB9472D" "45565534" "7FFFFFFF" "FFFFFFFF";
2075 krb5_error_code
2076 _krb5_parse_moduli(krb5_context context, const char *file,
2077 struct krb5_dh_moduli ***moduli)
2079 /* name bits P G Q */
2080 krb5_error_code ret;
2081 struct krb5_dh_moduli **m = NULL, **m2;
2082 char buf[4096];
2083 FILE *f;
2084 int lineno = 0, n = 0;
2086 *moduli = NULL;
2088 m = calloc(1, sizeof(m[0]) * 3);
2089 if (m == NULL) {
2090 krb5_set_error_message(context, ENOMEM,
2091 N_("malloc: out of memory", ""));
2092 return ENOMEM;
2095 strlcpy(buf, default_moduli_rfc3526_MODP_group14, sizeof(buf));
2096 ret = _krb5_parse_moduli_line(context, "builtin", 1, buf, &m[0]);
2097 if (ret) {
2098 _krb5_free_moduli(m);
2099 return ret;
2101 n++;
2103 strlcpy(buf, default_moduli_RFC2412_MODP_group2, sizeof(buf));
2104 ret = _krb5_parse_moduli_line(context, "builtin", 1, buf, &m[1]);
2105 if (ret) {
2106 _krb5_free_moduli(m);
2107 return ret;
2109 n++;
2112 if (file == NULL)
2113 file = MODULI_FILE;
2115 f = fopen(file, "r");
2116 if (f == NULL) {
2117 *moduli = m;
2118 return 0;
2120 rk_cloexec_file(f);
2122 while(fgets(buf, sizeof(buf), f) != NULL) {
2123 struct krb5_dh_moduli *element;
2125 buf[strcspn(buf, "\n")] = '\0';
2126 lineno++;
2128 m2 = realloc(m, (n + 2) * sizeof(m[0]));
2129 if (m2 == NULL) {
2130 _krb5_free_moduli(m);
2131 krb5_set_error_message(context, ENOMEM,
2132 N_("malloc: out of memory", ""));
2133 return ENOMEM;
2135 m = m2;
2137 m[n] = NULL;
2139 ret = _krb5_parse_moduli_line(context, file, lineno, buf, &element);
2140 if (ret) {
2141 _krb5_free_moduli(m);
2142 return ret;
2144 if (element == NULL)
2145 continue;
2147 m[n] = element;
2148 m[n + 1] = NULL;
2149 n++;
2151 *moduli = m;
2152 return 0;
2155 krb5_error_code
2156 _krb5_dh_group_ok(krb5_context context, unsigned long bits,
2157 heim_integer *p, heim_integer *g, heim_integer *q,
2158 struct krb5_dh_moduli **moduli,
2159 char **name)
2161 int i;
2163 if (name)
2164 *name = NULL;
2166 for (i = 0; moduli[i] != NULL; i++) {
2167 if (der_heim_integer_cmp(&moduli[i]->g, g) == 0 &&
2168 der_heim_integer_cmp(&moduli[i]->p, p) == 0 &&
2169 (q == NULL || der_heim_integer_cmp(&moduli[i]->q, q) == 0))
2171 if (bits && bits > moduli[i]->bits) {
2172 krb5_set_error_message(context,
2173 KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED,
2174 N_("PKINIT: DH group parameter %s "
2175 "no accepted, not enough bits "
2176 "generated", ""),
2177 moduli[i]->name);
2178 return KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED;
2180 if (name)
2181 *name = strdup(moduli[i]->name);
2182 return 0;
2185 krb5_set_error_message(context,
2186 KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED,
2187 N_("PKINIT: DH group parameter no ok", ""));
2188 return KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED;
2190 #endif /* PKINIT */
2192 void KRB5_LIB_FUNCTION
2193 _krb5_get_init_creds_opt_free_pkinit(krb5_get_init_creds_opt *opt)
2195 #ifdef PKINIT
2196 krb5_pk_init_ctx ctx;
2198 if (opt->opt_private == NULL || opt->opt_private->pk_init_ctx == NULL)
2199 return;
2200 ctx = opt->opt_private->pk_init_ctx;
2201 switch (ctx->keyex) {
2202 case USE_DH:
2203 DH_free(ctx->u.dh);
2204 break;
2205 case USE_RSA:
2206 break;
2207 case USE_ECDH:
2208 #ifdef HAVE_OPENSSL
2209 EC_KEY_free(ctx->u.eckey);
2210 #endif
2211 break;
2213 if (ctx->id) {
2214 hx509_verify_destroy_ctx(ctx->id->verify_ctx);
2215 hx509_certs_free(&ctx->id->certs);
2216 hx509_cert_free(ctx->id->cert);
2217 hx509_certs_free(&ctx->id->anchors);
2218 hx509_certs_free(&ctx->id->certpool);
2219 hx509_context_free(&ctx->id->hx509ctx);
2221 if (ctx->clientDHNonce) {
2222 krb5_free_data(NULL, ctx->clientDHNonce);
2223 ctx->clientDHNonce = NULL;
2225 if (ctx->m)
2226 _krb5_free_moduli(ctx->m);
2227 free(ctx->id);
2228 ctx->id = NULL;
2230 free(opt->opt_private->pk_init_ctx);
2231 opt->opt_private->pk_init_ctx = NULL;
2232 #endif
2235 krb5_error_code KRB5_LIB_FUNCTION
2236 krb5_get_init_creds_opt_set_pkinit(krb5_context context,
2237 krb5_get_init_creds_opt *opt,
2238 krb5_principal principal,
2239 const char *user_id,
2240 const char *x509_anchors,
2241 char * const * pool,
2242 char * const * pki_revoke,
2243 int flags,
2244 krb5_prompter_fct prompter,
2245 void *prompter_data,
2246 char *password)
2248 #ifdef PKINIT
2249 krb5_error_code ret;
2250 char *anchors = NULL;
2252 if (opt->opt_private == NULL) {
2253 krb5_set_error_message(context, EINVAL,
2254 N_("PKINIT: on non extendable opt", ""));
2255 return EINVAL;
2258 opt->opt_private->pk_init_ctx =
2259 calloc(1, sizeof(*opt->opt_private->pk_init_ctx));
2260 if (opt->opt_private->pk_init_ctx == NULL) {
2261 krb5_set_error_message(context, ENOMEM,
2262 N_("malloc: out of memory", ""));
2263 return ENOMEM;
2265 opt->opt_private->pk_init_ctx->require_binding = 0;
2266 opt->opt_private->pk_init_ctx->require_eku = 1;
2267 opt->opt_private->pk_init_ctx->require_krbtgt_otherName = 1;
2268 opt->opt_private->pk_init_ctx->peer = NULL;
2270 /* XXX implement krb5_appdefault_strings */
2271 if (pool == NULL)
2272 pool = krb5_config_get_strings(context, NULL,
2273 "appdefaults",
2274 "pkinit_pool",
2275 NULL);
2277 if (pki_revoke == NULL)
2278 pki_revoke = krb5_config_get_strings(context, NULL,
2279 "appdefaults",
2280 "pkinit_revoke",
2281 NULL);
2283 if (x509_anchors == NULL) {
2284 krb5_appdefault_string(context, "kinit",
2285 krb5_principal_get_realm(context, principal),
2286 "pkinit_anchors", NULL, &anchors);
2287 x509_anchors = anchors;
2290 ret = _krb5_pk_load_id(context,
2291 &opt->opt_private->pk_init_ctx->id,
2292 flags,
2293 user_id,
2294 x509_anchors,
2295 pool,
2296 pki_revoke,
2297 prompter,
2298 prompter_data,
2299 password);
2300 if (ret) {
2301 free(opt->opt_private->pk_init_ctx);
2302 opt->opt_private->pk_init_ctx = NULL;
2303 return ret;
2306 if (opt->opt_private->pk_init_ctx->id->certs) {
2307 hx509_query *q = NULL;
2308 hx509_cert cert = NULL;
2309 hx509_context hx509ctx = opt->opt_private->pk_init_ctx->id->hx509ctx;
2311 ret = hx509_query_alloc(hx509ctx, &q);
2312 if (ret) {
2313 pk_copy_error(context, hx509ctx, ret,
2314 "Allocate query to find signing certificate");
2315 return ret;
2318 hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY);
2319 hx509_query_match_option(q, HX509_QUERY_OPTION_KU_DIGITALSIGNATURE);
2321 ret = find_cert(context, opt->opt_private->pk_init_ctx->id, q, &cert);
2322 hx509_query_free(hx509ctx, q);
2323 if (ret)
2324 return ret;
2326 opt->opt_private->pk_init_ctx->id->cert = cert;
2327 } else
2328 opt->opt_private->pk_init_ctx->id->cert = NULL;
2330 if ((flags & 2) == 0) {
2331 hx509_context hx509ctx = opt->opt_private->pk_init_ctx->id->hx509ctx;
2332 hx509_cert cert = opt->opt_private->pk_init_ctx->id->cert;
2334 opt->opt_private->pk_init_ctx->keyex = USE_DH;
2337 * If its a ECDSA certs, lets select ECDSA as the keyex algorithm.
2339 if (cert) {
2340 AlgorithmIdentifier alg;
2342 ret = hx509_cert_get_SPKI_AlgorithmIdentifier(hx509ctx, cert, &alg);
2343 if (ret == 0) {
2344 if (der_heim_oid_cmp(&alg.algorithm, &asn1_oid_id_ecPublicKey) == 0)
2345 opt->opt_private->pk_init_ctx->keyex = USE_ECDH;
2346 free_AlgorithmIdentifier(&alg);
2350 } else {
2351 opt->opt_private->pk_init_ctx->keyex = USE_RSA;
2353 if (opt->opt_private->pk_init_ctx->id->certs == NULL) {
2354 krb5_set_error_message(context, EINVAL,
2355 N_("No anonymous pkinit support in RSA mode", ""));
2356 return EINVAL;
2360 return 0;
2361 #else
2362 krb5_set_error_message(context, EINVAL,
2363 N_("no support for PKINIT compiled in", ""));
2364 return EINVAL;
2365 #endif
2368 #ifdef PKINIT
2370 static int
2371 get_ms_san(hx509_context context, hx509_cert cert, char **upn)
2373 hx509_octet_string_list list;
2374 int ret;
2376 *upn = NULL;
2378 ret = hx509_cert_find_subjectAltName_otherName(context,
2379 cert,
2380 &asn1_oid_id_pkinit_ms_san,
2381 &list);
2382 if (ret)
2383 return 0;
2385 if (list.len > 0 && list.val[0].length > 0)
2386 ret = decode_MS_UPN_SAN(list.val[0].data, list.val[0].length,
2387 upn, NULL);
2388 else
2389 ret = 1;
2390 hx509_free_octet_string_list(&list);
2392 return ret;
2395 static int
2396 find_ms_san(hx509_context context, hx509_cert cert, void *ctx)
2398 char *upn;
2399 int ret;
2401 ret = get_ms_san(context, cert, &upn);
2402 if (ret == 0)
2403 free(upn);
2404 return ret;
2409 #endif
2412 * Private since it need to be redesigned using krb5_get_init_creds()
2415 krb5_error_code KRB5_LIB_FUNCTION
2416 _krb5_pk_enterprise_cert(krb5_context context,
2417 const char *user_id,
2418 krb5_const_realm realm,
2419 krb5_principal *principal)
2421 #ifdef PKINIT
2422 krb5_error_code ret;
2423 hx509_context hx509ctx;
2424 hx509_certs certs, result;
2425 hx509_cert cert;
2426 hx509_query *q;
2427 char *name;
2429 *principal = NULL;
2431 if (user_id == NULL)
2432 return ENOENT;
2434 ret = hx509_context_init(&hx509ctx);
2435 if (ret)
2436 return ret;
2438 ret = hx509_certs_init(hx509ctx, user_id, 0, NULL, &certs);
2439 if (ret) {
2440 pk_copy_error(context, hx509ctx, ret,
2441 "Failed to init cert certs");
2442 return ret;
2445 ret = hx509_query_alloc(hx509ctx, &q);
2446 if (ret) {
2447 hx509_certs_free(&certs);
2448 return ret;
2451 hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY);
2452 hx509_query_match_option(q, HX509_QUERY_OPTION_KU_DIGITALSIGNATURE);
2453 hx509_query_match_eku(q, &asn1_oid_id_pkinit_ms_eku);
2454 hx509_query_match_cmp_func(q, find_ms_san, NULL);
2456 ret = hx509_certs_filter(hx509ctx, certs, q, &result);
2457 hx509_query_free(hx509ctx, q);
2458 hx509_certs_free(&certs);
2459 if (ret)
2460 return ret;
2462 ret = hx509_get_one_cert(hx509ctx, result, &cert);
2463 hx509_certs_free(&result);
2464 if (ret)
2465 return ret;
2467 ret = get_ms_san(hx509ctx, cert, &name);
2468 if (ret)
2469 return ret;
2471 ret = krb5_make_principal(context, principal, realm, name, NULL);
2472 free(name);
2473 hx509_context_free(&hx509ctx);
2474 if (ret)
2475 return ret;
2477 krb5_principal_set_type(context, *principal, KRB5_NT_ENTERPRISE_PRINCIPAL);
2479 return ret;
2480 #else
2481 krb5_set_error_message(context, EINVAL,
2482 N_("no support for PKINIT compiled in", ""));
2483 return EINVAL;
2484 #endif