xref: /freebsd/crypto/heimdal/lib/krb5/pkinit.c (revision cb14a3fe5122c879eae1fb480ed7ce82a699ddb6)
1 /*
2  * Copyright (c) 2003 - 2007 Kungliga Tekniska Högskolan
3  * (Royal Institute of Technology, Stockholm, Sweden).
4  * All rights reserved.
5  *
6  * Portions Copyright (c) 2009 Apple Inc. All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  *
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.
18  *
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.
22  *
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.
34  */
35 
36 #include "krb5_locl.h"
37 
38 struct krb5_dh_moduli {
39     char *name;
40     unsigned long bits;
41     heim_integer p;
42     heim_integer g;
43     heim_integer q;
44 };
45 
46 #ifdef PKINIT
47 
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>
54 
55 #include <der.h>
56 
57 struct krb5_pk_cert {
58     hx509_cert cert;
59 };
60 
61 struct krb5_pk_init_ctx_data {
62     struct krb5_pk_identity *id;
63     enum { USE_RSA, USE_DH, USE_ECDH } keyex;
64     union {
65 	DH *dh;
66 #ifdef HAVE_OPENSSL
67 	EC_KEY *eckey;
68 #endif
69     } u;
70     krb5_data *clientDHNonce;
71     struct krb5_dh_moduli **m;
72     hx509_peer_info peer;
73     enum krb5_pk_type type;
74     unsigned int require_binding:1;
75     unsigned int require_eku:1;
76     unsigned int require_krbtgt_otherName:1;
77     unsigned int require_hostname_match:1;
78     unsigned int trustedCertifiers:1;
79     unsigned int anonymous:1;
80 };
81 
82 static void
83 pk_copy_error(krb5_context context,
84 	      hx509_context hx509ctx,
85 	      int hxret,
86 	      const char *fmt,
87 	      ...)
88     __attribute__ ((format (printf, 4, 5)));
89 
90 /*
91  *
92  */
93 
94 KRB5_LIB_FUNCTION void KRB5_LIB_CALL
95 _krb5_pk_cert_free(struct krb5_pk_cert *cert)
96 {
97     if (cert->cert) {
98 	hx509_cert_free(cert->cert);
99     }
100     free(cert);
101 }
102 
103 static krb5_error_code
104 BN_to_integer(krb5_context context, const BIGNUM *bn, heim_integer *integer)
105 {
106     integer->length = BN_num_bytes(bn);
107     integer->data = malloc(integer->length);
108     if (integer->data == NULL) {
109 	krb5_clear_error_message(context);
110 	return ENOMEM;
111     }
112     BN_bn2bin(bn, integer->data);
113     integer->negative = BN_is_negative(bn);
114     return 0;
115 }
116 
117 static BIGNUM *
118 integer_to_BN(krb5_context context, const char *field, const heim_integer *f)
119 {
120     BIGNUM *bn;
121 
122     bn = BN_bin2bn((const unsigned char *)f->data, f->length, NULL);
123     if (bn == NULL) {
124 	krb5_set_error_message(context, ENOMEM,
125 			       N_("PKINIT: parsing BN failed %s", ""), field);
126 	return NULL;
127     }
128     BN_set_negative(bn, f->negative);
129     return bn;
130 }
131 
132 static krb5_error_code
133 select_dh_group(krb5_context context, DH *dh, unsigned long bits,
134 		struct krb5_dh_moduli **moduli)
135 {
136     const struct krb5_dh_moduli *m;
137     BIGNUM *p, *g, *q;
138 
139     if (bits == 0) {
140 	m = moduli[1]; /* XXX */
141 	if (m == NULL)
142 	    m = moduli[0]; /* XXX */
143     } else {
144 	int i;
145 	for (i = 0; moduli[i] != NULL; i++) {
146 	    if (bits < moduli[i]->bits)
147 		break;
148 	}
149 	if (moduli[i] == NULL) {
150 	    krb5_set_error_message(context, EINVAL,
151 				   N_("Did not find a DH group parameter "
152 				      "matching requirement of %lu bits", ""),
153 				   bits);
154 	    return EINVAL;
155 	}
156 	m = moduli[i];
157     }
158 
159     p = integer_to_BN(context, "p", &m->p);
160     g = integer_to_BN(context, "g", &m->g);
161     q = integer_to_BN(context, "q", &m->q);
162     if (p == NULL || g == NULL || q == NULL) {
163 	BN_free(p);
164 	BN_free(g);
165 	BN_free(q);
166 	return ENOMEM;
167     }
168 
169     if (DH_set0_pqg(dh, p, q, g) != 1) {
170 	BN_free(p);
171 	BN_free(g);
172 	BN_free(q);
173 	return EINVAL;
174     }
175 
176     return 0;
177 }
178 
179 struct certfind {
180     const char *type;
181     const heim_oid *oid;
182 };
183 
184 /*
185  * Try searchin the key by to use by first looking for for PK-INIT
186  * EKU, then the Microsoft smart card EKU and last, no special EKU at all.
187  */
188 
189 static krb5_error_code
190 find_cert(krb5_context context, struct krb5_pk_identity *id,
191 	  hx509_query *q, hx509_cert *cert)
192 {
193     struct certfind cf[4] = {
194 	{ "MobileMe EKU" },
195 	{ "PKINIT EKU" },
196 	{ "MS EKU" },
197 	{ "any (or no)" }
198     };
199     int ret = HX509_CERT_NOT_FOUND;
200     size_t i, start = 1;
201     unsigned oids[] = { 1, 2, 840, 113635, 100, 3, 2, 1 };
202     const heim_oid mobileMe = { sizeof(oids)/sizeof(oids[0]), oids };
203 
204 
205     if (id->flags & PKINIT_BTMM)
206 	start = 0;
207 
208     cf[0].oid = &mobileMe;
209     cf[1].oid = &asn1_oid_id_pkekuoid;
210     cf[2].oid = &asn1_oid_id_pkinit_ms_eku;
211     cf[3].oid = NULL;
212 
213     for (i = start; i < sizeof(cf)/sizeof(cf[0]); i++) {
214 	ret = hx509_query_match_eku(q, cf[i].oid);
215 	if (ret) {
216 	    pk_copy_error(context, context->hx509ctx, ret,
217 			  "Failed setting %s OID", cf[i].type);
218 	    return ret;
219 	}
220 
221 	ret = hx509_certs_find(context->hx509ctx, id->certs, q, cert);
222 	if (ret == 0)
223 	    break;
224 	pk_copy_error(context, context->hx509ctx, ret,
225 		      "Failed finding certificate with %s OID", cf[i].type);
226     }
227     return ret;
228 }
229 
230 
231 static krb5_error_code
232 create_signature(krb5_context context,
233 		 const heim_oid *eContentType,
234 		 krb5_data *eContent,
235 		 struct krb5_pk_identity *id,
236 		 hx509_peer_info peer,
237 		 krb5_data *sd_data)
238 {
239     int ret, flags = 0;
240 
241     if (id->cert == NULL)
242 	flags |= HX509_CMS_SIGNATURE_NO_SIGNER;
243 
244     ret = hx509_cms_create_signed_1(context->hx509ctx,
245 				    flags,
246 				    eContentType,
247 				    eContent->data,
248 				    eContent->length,
249 				    NULL,
250 				    id->cert,
251 				    peer,
252 				    NULL,
253 				    id->certs,
254 				    sd_data);
255     if (ret) {
256 	pk_copy_error(context, context->hx509ctx, ret,
257 		      "Create CMS signedData");
258 	return ret;
259     }
260 
261     return 0;
262 }
263 
264 static int
265 cert2epi(hx509_context context, void *ctx, hx509_cert c)
266 {
267     ExternalPrincipalIdentifiers *ids = ctx;
268     ExternalPrincipalIdentifier id;
269     hx509_name subject = NULL;
270     void *p;
271     int ret;
272 
273     if (ids->len > 10)
274 	return 0;
275 
276     memset(&id, 0, sizeof(id));
277 
278     ret = hx509_cert_get_subject(c, &subject);
279     if (ret)
280 	return ret;
281 
282     if (hx509_name_is_null_p(subject) != 0) {
283 
284 	id.subjectName = calloc(1, sizeof(*id.subjectName));
285 	if (id.subjectName == NULL) {
286 	    hx509_name_free(&subject);
287 	    free_ExternalPrincipalIdentifier(&id);
288 	    return ENOMEM;
289 	}
290 
291 	ret = hx509_name_binary(subject, id.subjectName);
292 	if (ret) {
293 	    hx509_name_free(&subject);
294 	    free_ExternalPrincipalIdentifier(&id);
295 	    return ret;
296 	}
297     }
298     hx509_name_free(&subject);
299 
300 
301     id.issuerAndSerialNumber = calloc(1, sizeof(*id.issuerAndSerialNumber));
302     if (id.issuerAndSerialNumber == NULL) {
303 	free_ExternalPrincipalIdentifier(&id);
304 	return ENOMEM;
305     }
306 
307     {
308 	IssuerAndSerialNumber iasn;
309 	hx509_name issuer;
310 	size_t size = 0;
311 
312 	memset(&iasn, 0, sizeof(iasn));
313 
314 	ret = hx509_cert_get_issuer(c, &issuer);
315 	if (ret) {
316 	    free_ExternalPrincipalIdentifier(&id);
317 	    return ret;
318 	}
319 
320 	ret = hx509_name_to_Name(issuer, &iasn.issuer);
321 	hx509_name_free(&issuer);
322 	if (ret) {
323 	    free_ExternalPrincipalIdentifier(&id);
324 	    return ret;
325 	}
326 
327 	ret = hx509_cert_get_serialnumber(c, &iasn.serialNumber);
328 	if (ret) {
329 	    free_IssuerAndSerialNumber(&iasn);
330 	    free_ExternalPrincipalIdentifier(&id);
331 	    return ret;
332 	}
333 
334 	ASN1_MALLOC_ENCODE(IssuerAndSerialNumber,
335 			   id.issuerAndSerialNumber->data,
336 			   id.issuerAndSerialNumber->length,
337 			   &iasn, &size, ret);
338 	free_IssuerAndSerialNumber(&iasn);
339 	if (ret)
340 	    return ret;
341 	if (id.issuerAndSerialNumber->length != size)
342 	    abort();
343     }
344 
345     id.subjectKeyIdentifier = NULL;
346 
347     p = realloc(ids->val, sizeof(ids->val[0]) * (ids->len + 1));
348     if (p == NULL) {
349 	free_ExternalPrincipalIdentifier(&id);
350 	return ENOMEM;
351     }
352 
353     ids->val = p;
354     ids->val[ids->len] = id;
355     ids->len++;
356 
357     return 0;
358 }
359 
360 static krb5_error_code
361 build_edi(krb5_context context,
362 	  hx509_context hx509ctx,
363 	  hx509_certs certs,
364 	  ExternalPrincipalIdentifiers *ids)
365 {
366     return hx509_certs_iter_f(hx509ctx, certs, cert2epi, ids);
367 }
368 
369 static krb5_error_code
370 build_auth_pack(krb5_context context,
371 		unsigned nonce,
372 		krb5_pk_init_ctx ctx,
373 		const KDC_REQ_BODY *body,
374 		AuthPack *a)
375 {
376     size_t buf_size, len = 0;
377     krb5_error_code ret;
378     void *buf;
379     krb5_timestamp sec;
380     int32_t usec;
381     Checksum checksum;
382 
383     krb5_clear_error_message(context);
384 
385     memset(&checksum, 0, sizeof(checksum));
386 
387     krb5_us_timeofday(context, &sec, &usec);
388     a->pkAuthenticator.ctime = sec;
389     a->pkAuthenticator.nonce = nonce;
390 
391     ASN1_MALLOC_ENCODE(KDC_REQ_BODY, buf, buf_size, body, &len, ret);
392     if (ret)
393 	return ret;
394     if (buf_size != len)
395 	krb5_abortx(context, "internal error in ASN.1 encoder");
396 
397     ret = krb5_create_checksum(context,
398 			       NULL,
399 			       0,
400 			       CKSUMTYPE_SHA1,
401 			       buf,
402 			       len,
403 			       &checksum);
404     free(buf);
405     if (ret)
406 	return ret;
407 
408     ALLOC(a->pkAuthenticator.paChecksum, 1);
409     if (a->pkAuthenticator.paChecksum == NULL) {
410 	krb5_set_error_message(context, ENOMEM,
411 			       N_("malloc: out of memory", ""));
412 	return ENOMEM;
413     }
414 
415     ret = krb5_data_copy(a->pkAuthenticator.paChecksum,
416 			 checksum.checksum.data, checksum.checksum.length);
417     free_Checksum(&checksum);
418     if (ret)
419 	return ret;
420 
421     if (ctx->keyex == USE_DH || ctx->keyex == USE_ECDH) {
422 	const char *moduli_file;
423 	unsigned long dh_min_bits;
424 	krb5_data dhbuf;
425 	size_t size = 0;
426 
427 	krb5_data_zero(&dhbuf);
428 
429 
430 
431 	moduli_file = krb5_config_get_string(context, NULL,
432 					     "libdefaults",
433 					     "moduli",
434 					     NULL);
435 
436 	dh_min_bits =
437 	    krb5_config_get_int_default(context, NULL, 0,
438 					"libdefaults",
439 					"pkinit_dh_min_bits",
440 					NULL);
441 
442 	ret = _krb5_parse_moduli(context, moduli_file, &ctx->m);
443 	if (ret)
444 	    return ret;
445 
446 	ctx->u.dh = DH_new();
447 	if (ctx->u.dh == NULL) {
448 	    krb5_set_error_message(context, ENOMEM,
449 				   N_("malloc: out of memory", ""));
450 	    return ENOMEM;
451 	}
452 
453 	ret = select_dh_group(context, ctx->u.dh, dh_min_bits, ctx->m);
454 	if (ret)
455 	    return ret;
456 
457 	if (DH_generate_key(ctx->u.dh) != 1) {
458 	    krb5_set_error_message(context, ENOMEM,
459 				   N_("pkinit: failed to generate DH key", ""));
460 	    return ENOMEM;
461 	}
462 
463 
464 	if (1 /* support_cached_dh */) {
465 	    ALLOC(a->clientDHNonce, 1);
466 	    if (a->clientDHNonce == NULL) {
467 		krb5_clear_error_message(context);
468 		return ENOMEM;
469 	    }
470 	    ret = krb5_data_alloc(a->clientDHNonce, 40);
471 	    if (a->clientDHNonce == NULL) {
472 		krb5_clear_error_message(context);
473 		return ret;
474 	    }
475 	    RAND_bytes(a->clientDHNonce->data, a->clientDHNonce->length);
476 	    ret = krb5_copy_data(context, a->clientDHNonce,
477 				 &ctx->clientDHNonce);
478 	    if (ret)
479 		return ret;
480 	}
481 
482 	ALLOC(a->clientPublicValue, 1);
483 	if (a->clientPublicValue == NULL)
484 	    return ENOMEM;
485 
486 	if (ctx->keyex == USE_DH) {
487 	    DH *dh = ctx->u.dh;
488 	    const BIGNUM *p, *g, *q, *pub_key;
489 	    DomainParameters dp;
490 	    heim_integer dh_pub_key;
491 
492 	    ret = der_copy_oid(&asn1_oid_id_dhpublicnumber,
493 			       &a->clientPublicValue->algorithm.algorithm);
494 	    if (ret)
495 		return ret;
496 
497 	    memset(&dp, 0, sizeof(dp));
498 
499 	    DH_get0_pqg(dh, &p, &q, &g);
500 	    ret = BN_to_integer(context, p, &dp.p);
501 	    if (ret) {
502 		free_DomainParameters(&dp);
503 		return ret;
504 	    }
505 	    ret = BN_to_integer(context, g, &dp.g);
506 	    if (ret) {
507 		free_DomainParameters(&dp);
508 		return ret;
509 	    }
510 	    ret = BN_to_integer(context, q, &dp.q);
511 	    if (ret) {
512 		free_DomainParameters(&dp);
513 		return ret;
514 	    }
515 	    dp.j = NULL;
516 	    dp.validationParms = NULL;
517 
518 	    a->clientPublicValue->algorithm.parameters =
519 		malloc(sizeof(*a->clientPublicValue->algorithm.parameters));
520 	    if (a->clientPublicValue->algorithm.parameters == NULL) {
521 		free_DomainParameters(&dp);
522 		return ret;
523 	    }
524 
525 	    ASN1_MALLOC_ENCODE(DomainParameters,
526 			       a->clientPublicValue->algorithm.parameters->data,
527 			       a->clientPublicValue->algorithm.parameters->length,
528 			       &dp, &size, ret);
529 	    free_DomainParameters(&dp);
530 	    if (ret)
531 		return ret;
532 	    if (size != a->clientPublicValue->algorithm.parameters->length)
533 		krb5_abortx(context, "Internal ASN1 encoder error");
534 
535 	    DH_get0_key(dh, &pub_key, NULL);
536 	    ret = BN_to_integer(context, pub_key, &dh_pub_key);
537 	    if (ret)
538 		return ret;
539 
540 	    ASN1_MALLOC_ENCODE(DHPublicKey, dhbuf.data, dhbuf.length,
541 			       &dh_pub_key, &size, ret);
542 	    der_free_heim_integer(&dh_pub_key);
543 	    if (ret)
544 		return ret;
545 	    if (size != dhbuf.length)
546 		krb5_abortx(context, "asn1 internal error");
547 	} else if (ctx->keyex == USE_ECDH) {
548 #ifdef HAVE_OPENSSL
549 	    ECParameters ecp;
550 	    unsigned char *p;
551 	    int xlen;
552 
553 	    /* copy in public key, XXX find the best curve that the server support or use the clients curve if possible */
554 
555 	    ecp.element = choice_ECParameters_namedCurve;
556 	    ret = der_copy_oid(&asn1_oid_id_ec_group_secp256r1,
557 			       &ecp.u.namedCurve);
558 	    if (ret)
559 		return ret;
560 
561 	    ALLOC(a->clientPublicValue->algorithm.parameters, 1);
562 	    if (a->clientPublicValue->algorithm.parameters == NULL) {
563 		free_ECParameters(&ecp);
564 		return ENOMEM;
565 	    }
566 	    ASN1_MALLOC_ENCODE(ECParameters, p, xlen, &ecp, &size, ret);
567 	    free_ECParameters(&ecp);
568 	    if (ret)
569 		return ret;
570 	    if ((int)size != xlen)
571 		krb5_abortx(context, "asn1 internal error");
572 
573 	    a->clientPublicValue->algorithm.parameters->data = p;
574 	    a->clientPublicValue->algorithm.parameters->length = size;
575 
576 	    /* copy in public key */
577 
578 	    ret = der_copy_oid(&asn1_oid_id_ecPublicKey,
579 			       &a->clientPublicValue->algorithm.algorithm);
580 	    if (ret)
581 		return ret;
582 
583 	    ctx->u.eckey = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
584 	    if (ctx->u.eckey == NULL)
585 		return ENOMEM;
586 
587 	    ret = EC_KEY_generate_key(ctx->u.eckey);
588 	    if (ret != 1)
589 		return EINVAL;
590 
591 	    /* encode onto dhkey */
592 
593 	    xlen = i2o_ECPublicKey(ctx->u.eckey, NULL);
594 	    if (xlen <= 0)
595 		abort();
596 
597 	    dhbuf.data = malloc(xlen);
598 	    if (dhbuf.data == NULL)
599 		abort();
600 	    dhbuf.length = xlen;
601 	    p = dhbuf.data;
602 
603 	    xlen = i2o_ECPublicKey(ctx->u.eckey, &p);
604 	    if (xlen <= 0)
605 		abort();
606 
607 	    /* XXX verify that this is right with RFC3279 */
608 #else
609 	    return EINVAL;
610 #endif
611 	} else
612 	    krb5_abortx(context, "internal error");
613 	a->clientPublicValue->subjectPublicKey.length = dhbuf.length * 8;
614 	a->clientPublicValue->subjectPublicKey.data = dhbuf.data;
615     }
616 
617     {
618 	a->supportedCMSTypes = calloc(1, sizeof(*a->supportedCMSTypes));
619 	if (a->supportedCMSTypes == NULL)
620 	    return ENOMEM;
621 
622 	ret = hx509_crypto_available(context->hx509ctx, HX509_SELECT_ALL,
623 				     ctx->id->cert,
624 				     &a->supportedCMSTypes->val,
625 				     &a->supportedCMSTypes->len);
626 	if (ret)
627 	    return ret;
628     }
629 
630     return ret;
631 }
632 
633 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
634 _krb5_pk_mk_ContentInfo(krb5_context context,
635 			const krb5_data *buf,
636 			const heim_oid *oid,
637 			struct ContentInfo *content_info)
638 {
639     krb5_error_code ret;
640 
641     ret = der_copy_oid(oid, &content_info->contentType);
642     if (ret)
643 	return ret;
644     ALLOC(content_info->content, 1);
645     if (content_info->content == NULL)
646 	return ENOMEM;
647     content_info->content->data = malloc(buf->length);
648     if (content_info->content->data == NULL)
649 	return ENOMEM;
650     memcpy(content_info->content->data, buf->data, buf->length);
651     content_info->content->length = buf->length;
652     return 0;
653 }
654 
655 static krb5_error_code
656 pk_mk_padata(krb5_context context,
657 	     krb5_pk_init_ctx ctx,
658 	     const KDC_REQ_BODY *req_body,
659 	     unsigned nonce,
660 	     METHOD_DATA *md)
661 {
662     struct ContentInfo content_info;
663     krb5_error_code ret;
664     const heim_oid *oid = NULL;
665     size_t size = 0;
666     krb5_data buf, sd_buf;
667     int pa_type = -1;
668 
669     krb5_data_zero(&buf);
670     krb5_data_zero(&sd_buf);
671     memset(&content_info, 0, sizeof(content_info));
672 
673     if (ctx->type == PKINIT_WIN2K) {
674 	AuthPack_Win2k ap;
675 	krb5_timestamp sec;
676 	int32_t usec;
677 
678 	memset(&ap, 0, sizeof(ap));
679 
680 	/* fill in PKAuthenticator */
681 	ret = copy_PrincipalName(req_body->sname, &ap.pkAuthenticator.kdcName);
682 	if (ret) {
683 	    free_AuthPack_Win2k(&ap);
684 	    krb5_clear_error_message(context);
685 	    goto out;
686 	}
687 	ret = copy_Realm(&req_body->realm, &ap.pkAuthenticator.kdcRealm);
688 	if (ret) {
689 	    free_AuthPack_Win2k(&ap);
690 	    krb5_clear_error_message(context);
691 	    goto out;
692 	}
693 
694 	krb5_us_timeofday(context, &sec, &usec);
695 	ap.pkAuthenticator.ctime = sec;
696 	ap.pkAuthenticator.cusec = usec;
697 	ap.pkAuthenticator.nonce = nonce;
698 
699 	ASN1_MALLOC_ENCODE(AuthPack_Win2k, buf.data, buf.length,
700 			   &ap, &size, ret);
701 	free_AuthPack_Win2k(&ap);
702 	if (ret) {
703 	    krb5_set_error_message(context, ret,
704 				   N_("Failed encoding AuthPackWin: %d", ""),
705 				   (int)ret);
706 	    goto out;
707 	}
708 	if (buf.length != size)
709 	    krb5_abortx(context, "internal ASN1 encoder error");
710 
711 	oid = &asn1_oid_id_pkcs7_data;
712     } else if (ctx->type == PKINIT_27) {
713 	AuthPack ap;
714 
715 	memset(&ap, 0, sizeof(ap));
716 
717 	ret = build_auth_pack(context, nonce, ctx, req_body, &ap);
718 	if (ret) {
719 	    free_AuthPack(&ap);
720 	    goto out;
721 	}
722 
723 	ASN1_MALLOC_ENCODE(AuthPack, buf.data, buf.length, &ap, &size, ret);
724 	free_AuthPack(&ap);
725 	if (ret) {
726 	    krb5_set_error_message(context, ret,
727 				   N_("Failed encoding AuthPack: %d", ""),
728 				   (int)ret);
729 	    goto out;
730 	}
731 	if (buf.length != size)
732 	    krb5_abortx(context, "internal ASN1 encoder error");
733 
734 	oid = &asn1_oid_id_pkauthdata;
735     } else
736 	krb5_abortx(context, "internal pkinit error");
737 
738     ret = create_signature(context, oid, &buf, ctx->id,
739 			   ctx->peer, &sd_buf);
740     krb5_data_free(&buf);
741     if (ret)
742 	goto out;
743 
744     ret = hx509_cms_wrap_ContentInfo(&asn1_oid_id_pkcs7_signedData, &sd_buf, &buf);
745     krb5_data_free(&sd_buf);
746     if (ret) {
747 	krb5_set_error_message(context, ret,
748 			       N_("ContentInfo wrapping of signedData failed",""));
749 	goto out;
750     }
751 
752     if (ctx->type == PKINIT_WIN2K) {
753 	PA_PK_AS_REQ_Win2k winreq;
754 
755 	pa_type = KRB5_PADATA_PK_AS_REQ_WIN;
756 
757 	memset(&winreq, 0, sizeof(winreq));
758 
759 	winreq.signed_auth_pack = buf;
760 
761 	ASN1_MALLOC_ENCODE(PA_PK_AS_REQ_Win2k, buf.data, buf.length,
762 			   &winreq, &size, ret);
763 	free_PA_PK_AS_REQ_Win2k(&winreq);
764 
765     } else if (ctx->type == PKINIT_27) {
766 	PA_PK_AS_REQ req;
767 
768 	pa_type = KRB5_PADATA_PK_AS_REQ;
769 
770 	memset(&req, 0, sizeof(req));
771 	req.signedAuthPack = buf;
772 
773 	if (ctx->trustedCertifiers) {
774 
775 	    req.trustedCertifiers = calloc(1, sizeof(*req.trustedCertifiers));
776 	    if (req.trustedCertifiers == NULL) {
777 		ret = ENOMEM;
778 		krb5_set_error_message(context, ret,
779 				       N_("malloc: out of memory", ""));
780 		free_PA_PK_AS_REQ(&req);
781 		goto out;
782 	    }
783 	    ret = build_edi(context, context->hx509ctx,
784 			    ctx->id->anchors, req.trustedCertifiers);
785 	    if (ret) {
786 		krb5_set_error_message(context, ret,
787 				       N_("pk-init: failed to build "
788 					  "trustedCertifiers", ""));
789 		free_PA_PK_AS_REQ(&req);
790 		goto out;
791 	    }
792 	}
793 	req.kdcPkId = NULL;
794 
795 	ASN1_MALLOC_ENCODE(PA_PK_AS_REQ, buf.data, buf.length,
796 			   &req, &size, ret);
797 
798 	free_PA_PK_AS_REQ(&req);
799 
800     } else
801 	krb5_abortx(context, "internal pkinit error");
802     if (ret) {
803 	krb5_set_error_message(context, ret, "PA-PK-AS-REQ %d", (int)ret);
804 	goto out;
805     }
806     if (buf.length != size)
807 	krb5_abortx(context, "Internal ASN1 encoder error");
808 
809     ret = krb5_padata_add(context, md, pa_type, buf.data, buf.length);
810     if (ret)
811 	free(buf.data);
812 
813     if (ret == 0)
814     	krb5_padata_add(context, md, KRB5_PADATA_PK_AS_09_BINDING, NULL, 0);
815 
816  out:
817     free_ContentInfo(&content_info);
818 
819     return ret;
820 }
821 
822 
823 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
824 _krb5_pk_mk_padata(krb5_context context,
825 		   void *c,
826 		   int ic_flags,
827 		   int win2k,
828 		   const KDC_REQ_BODY *req_body,
829 		   unsigned nonce,
830 		   METHOD_DATA *md)
831 {
832     krb5_pk_init_ctx ctx = c;
833     int win2k_compat;
834 
835     if (ctx->id->certs == NULL && ctx->anonymous == 0) {
836 	krb5_set_error_message(context, HEIM_PKINIT_NO_PRIVATE_KEY,
837 			       N_("PKINIT: No user certificate given", ""));
838 	return HEIM_PKINIT_NO_PRIVATE_KEY;
839     }
840 
841     win2k_compat = krb5_config_get_bool_default(context, NULL,
842 						win2k,
843 						"realms",
844 						req_body->realm,
845 						"pkinit_win2k",
846 						NULL);
847 
848     if (win2k_compat) {
849 	ctx->require_binding =
850 	    krb5_config_get_bool_default(context, NULL,
851 					 TRUE,
852 					 "realms",
853 					 req_body->realm,
854 					 "pkinit_win2k_require_binding",
855 					 NULL);
856 	ctx->type = PKINIT_WIN2K;
857     } else
858 	ctx->type = PKINIT_27;
859 
860     ctx->require_eku =
861 	krb5_config_get_bool_default(context, NULL,
862 				     TRUE,
863 				     "realms",
864 				     req_body->realm,
865 				     "pkinit_require_eku",
866 				     NULL);
867     if (ic_flags & KRB5_INIT_CREDS_NO_C_NO_EKU_CHECK)
868 	ctx->require_eku = 0;
869     if (ctx->id->flags & PKINIT_BTMM)
870 	ctx->require_eku = 0;
871 
872     ctx->require_krbtgt_otherName =
873 	krb5_config_get_bool_default(context, NULL,
874 				     TRUE,
875 				     "realms",
876 				     req_body->realm,
877 				     "pkinit_require_krbtgt_otherName",
878 				     NULL);
879 
880     ctx->require_hostname_match =
881 	krb5_config_get_bool_default(context, NULL,
882 				     FALSE,
883 				     "realms",
884 				     req_body->realm,
885 				     "pkinit_require_hostname_match",
886 				     NULL);
887 
888     ctx->trustedCertifiers =
889 	krb5_config_get_bool_default(context, NULL,
890 				     TRUE,
891 				     "realms",
892 				     req_body->realm,
893 				     "pkinit_trustedCertifiers",
894 				     NULL);
895 
896     return pk_mk_padata(context, ctx, req_body, nonce, md);
897 }
898 
899 static krb5_error_code
900 pk_verify_sign(krb5_context context,
901 	       const void *data,
902 	       size_t length,
903 	       struct krb5_pk_identity *id,
904 	       heim_oid *contentType,
905 	       krb5_data *content,
906 	       struct krb5_pk_cert **signer)
907 {
908     hx509_certs signer_certs;
909     int ret, flags = 0;
910 
911     /* BTMM is broken in Leo and SnowLeo */
912     if (id->flags & PKINIT_BTMM) {
913 	flags |= HX509_CMS_VS_ALLOW_DATA_OID_MISMATCH;
914 	flags |= HX509_CMS_VS_NO_KU_CHECK;
915 	flags |= HX509_CMS_VS_NO_VALIDATE;
916     }
917 
918     *signer = NULL;
919 
920     ret = hx509_cms_verify_signed(context->hx509ctx,
921 				  id->verify_ctx,
922 				  flags,
923 				  data,
924 				  length,
925 				  NULL,
926 				  id->certpool,
927 				  contentType,
928 				  content,
929 				  &signer_certs);
930     if (ret) {
931 	pk_copy_error(context, context->hx509ctx, ret,
932 		      "CMS verify signed failed");
933 	return ret;
934     }
935 
936     *signer = calloc(1, sizeof(**signer));
937     if (*signer == NULL) {
938 	krb5_clear_error_message(context);
939 	ret = ENOMEM;
940 	goto out;
941     }
942 
943     ret = hx509_get_one_cert(context->hx509ctx, signer_certs, &(*signer)->cert);
944     if (ret) {
945 	pk_copy_error(context, context->hx509ctx, ret,
946 		      "Failed to get on of the signer certs");
947 	goto out;
948     }
949 
950  out:
951     hx509_certs_free(&signer_certs);
952     if (ret) {
953 	if (*signer) {
954 	    hx509_cert_free((*signer)->cert);
955 	    free(*signer);
956 	    *signer = NULL;
957 	}
958     }
959 
960     return ret;
961 }
962 
963 static krb5_error_code
964 get_reply_key_win(krb5_context context,
965 		  const krb5_data *content,
966 		  unsigned nonce,
967 		  krb5_keyblock **key)
968 {
969     ReplyKeyPack_Win2k key_pack;
970     krb5_error_code ret;
971     size_t size;
972 
973     ret = decode_ReplyKeyPack_Win2k(content->data,
974 				    content->length,
975 				    &key_pack,
976 				    &size);
977     if (ret) {
978 	krb5_set_error_message(context, ret,
979 			       N_("PKINIT decoding reply key failed", ""));
980 	free_ReplyKeyPack_Win2k(&key_pack);
981 	return ret;
982     }
983 
984     if ((unsigned)key_pack.nonce != nonce) {
985 	krb5_set_error_message(context, ret,
986 			       N_("PKINIT enckey nonce is wrong", ""));
987 	free_ReplyKeyPack_Win2k(&key_pack);
988 	return KRB5KRB_AP_ERR_MODIFIED;
989     }
990 
991     *key = malloc (sizeof (**key));
992     if (*key == NULL) {
993 	free_ReplyKeyPack_Win2k(&key_pack);
994 	krb5_set_error_message(context, ENOMEM,
995 			       N_("malloc: out of memory", ""));
996 	return ENOMEM;
997     }
998 
999     ret = copy_EncryptionKey(&key_pack.replyKey, *key);
1000     free_ReplyKeyPack_Win2k(&key_pack);
1001     if (ret) {
1002 	krb5_set_error_message(context, ret,
1003 			       N_("PKINIT failed copying reply key", ""));
1004 	free(*key);
1005 	*key = NULL;
1006     }
1007 
1008     return ret;
1009 }
1010 
1011 static krb5_error_code
1012 get_reply_key(krb5_context context,
1013 	      const krb5_data *content,
1014 	      const krb5_data *req_buffer,
1015 	      krb5_keyblock **key)
1016 {
1017     ReplyKeyPack key_pack;
1018     krb5_error_code ret;
1019     size_t size;
1020 
1021     ret = decode_ReplyKeyPack(content->data,
1022 			      content->length,
1023 			      &key_pack,
1024 			      &size);
1025     if (ret) {
1026 	krb5_set_error_message(context, ret,
1027 			       N_("PKINIT decoding reply key failed", ""));
1028 	free_ReplyKeyPack(&key_pack);
1029 	return ret;
1030     }
1031 
1032     {
1033 	krb5_crypto crypto;
1034 
1035 	/*
1036 	 * XXX Verify kp.replyKey is a allowed enctype in the
1037 	 * configuration file
1038 	 */
1039 
1040 	ret = krb5_crypto_init(context, &key_pack.replyKey, 0, &crypto);
1041 	if (ret) {
1042 	    free_ReplyKeyPack(&key_pack);
1043 	    return ret;
1044 	}
1045 
1046 	ret = krb5_verify_checksum(context, crypto, 6,
1047 				   req_buffer->data, req_buffer->length,
1048 				   &key_pack.asChecksum);
1049 	krb5_crypto_destroy(context, crypto);
1050 	if (ret) {
1051 	    free_ReplyKeyPack(&key_pack);
1052 	    return ret;
1053 	}
1054     }
1055 
1056     *key = malloc (sizeof (**key));
1057     if (*key == NULL) {
1058 	free_ReplyKeyPack(&key_pack);
1059 	krb5_set_error_message(context, ENOMEM,
1060 			       N_("malloc: out of memory", ""));
1061 	return ENOMEM;
1062     }
1063 
1064     ret = copy_EncryptionKey(&key_pack.replyKey, *key);
1065     free_ReplyKeyPack(&key_pack);
1066     if (ret) {
1067 	krb5_set_error_message(context, ret,
1068 			       N_("PKINIT failed copying reply key", ""));
1069 	free(*key);
1070 	*key = NULL;
1071     }
1072 
1073     return ret;
1074 }
1075 
1076 
1077 static krb5_error_code
1078 pk_verify_host(krb5_context context,
1079 	       const char *realm,
1080 	       const krb5_krbhst_info *hi,
1081 	       struct krb5_pk_init_ctx_data *ctx,
1082 	       struct krb5_pk_cert *host)
1083 {
1084     krb5_error_code ret = 0;
1085 
1086     if (ctx->require_eku) {
1087 	ret = hx509_cert_check_eku(context->hx509ctx, host->cert,
1088 				   &asn1_oid_id_pkkdcekuoid, 0);
1089 	if (ret) {
1090 	    krb5_set_error_message(context, ret,
1091 				   N_("No PK-INIT KDC EKU in kdc certificate", ""));
1092 	    return ret;
1093 	}
1094     }
1095     if (ctx->require_krbtgt_otherName) {
1096 	hx509_octet_string_list list;
1097 	size_t i;
1098 
1099 	ret = hx509_cert_find_subjectAltName_otherName(context->hx509ctx,
1100 						       host->cert,
1101 						       &asn1_oid_id_pkinit_san,
1102 						       &list);
1103 	if (ret) {
1104 	    krb5_set_error_message(context, ret,
1105 				   N_("Failed to find the PK-INIT "
1106 				      "subjectAltName in the KDC "
1107 				      "certificate", ""));
1108 
1109 	    return ret;
1110 	}
1111 
1112 	for (i = 0; i < list.len; i++) {
1113 	    KRB5PrincipalName r;
1114 
1115 	    ret = decode_KRB5PrincipalName(list.val[i].data,
1116 					   list.val[i].length,
1117 					   &r,
1118 					   NULL);
1119 	    if (ret) {
1120 		krb5_set_error_message(context, ret,
1121 				       N_("Failed to decode the PK-INIT "
1122 					  "subjectAltName in the "
1123 					  "KDC certificate", ""));
1124 
1125 		break;
1126 	    }
1127 
1128 	    if (r.principalName.name_string.len != 2 ||
1129 		strcmp(r.principalName.name_string.val[0], KRB5_TGS_NAME) != 0 ||
1130 		strcmp(r.principalName.name_string.val[1], realm) != 0 ||
1131 		strcmp(r.realm, realm) != 0)
1132 		{
1133 		    ret = KRB5_KDC_ERR_INVALID_CERTIFICATE;
1134 		    krb5_set_error_message(context, ret,
1135 					   N_("KDC have wrong realm name in "
1136 					      "the certificate", ""));
1137 		}
1138 
1139 	    free_KRB5PrincipalName(&r);
1140 	    if (ret)
1141 		break;
1142 	}
1143 	hx509_free_octet_string_list(&list);
1144     }
1145     if (ret)
1146 	return ret;
1147 
1148     if (hi) {
1149 	ret = hx509_verify_hostname(context->hx509ctx, host->cert,
1150 				    ctx->require_hostname_match,
1151 				    HX509_HN_HOSTNAME,
1152 				    hi->hostname,
1153 				    hi->ai->ai_addr, hi->ai->ai_addrlen);
1154 
1155 	if (ret)
1156 	    krb5_set_error_message(context, ret,
1157 				   N_("Address mismatch in "
1158 				      "the KDC certificate", ""));
1159     }
1160     return ret;
1161 }
1162 
1163 static krb5_error_code
1164 pk_rd_pa_reply_enckey(krb5_context context,
1165 		      int type,
1166 		      const heim_octet_string *indata,
1167 		      const heim_oid *dataType,
1168 		      const char *realm,
1169 		      krb5_pk_init_ctx ctx,
1170 		      krb5_enctype etype,
1171 		      const krb5_krbhst_info *hi,
1172 	       	      unsigned nonce,
1173 		      const krb5_data *req_buffer,
1174 	       	      PA_DATA *pa,
1175 	       	      krb5_keyblock **key)
1176 {
1177     krb5_error_code ret;
1178     struct krb5_pk_cert *host = NULL;
1179     krb5_data content;
1180     heim_oid contentType = { 0, NULL };
1181     int flags = HX509_CMS_UE_DONT_REQUIRE_KU_ENCIPHERMENT;
1182 
1183     if (der_heim_oid_cmp(&asn1_oid_id_pkcs7_envelopedData, dataType)) {
1184 	krb5_set_error_message(context, EINVAL,
1185 			       N_("PKINIT: Invalid content type", ""));
1186 	return EINVAL;
1187     }
1188 
1189     if (ctx->type == PKINIT_WIN2K)
1190 	flags |= HX509_CMS_UE_ALLOW_WEAK;
1191 
1192     ret = hx509_cms_unenvelope(context->hx509ctx,
1193 			       ctx->id->certs,
1194 			       flags,
1195 			       indata->data,
1196 			       indata->length,
1197 			       NULL,
1198 			       0,
1199 			       &contentType,
1200 			       &content);
1201     if (ret) {
1202 	pk_copy_error(context, context->hx509ctx, ret,
1203 		      "Failed to unenvelope CMS data in PK-INIT reply");
1204 	return ret;
1205     }
1206     der_free_oid(&contentType);
1207 
1208     /* win2k uses ContentInfo */
1209     if (type == PKINIT_WIN2K) {
1210 	heim_oid type2;
1211 	heim_octet_string out;
1212 
1213 	ret = hx509_cms_unwrap_ContentInfo(&content, &type2, &out, NULL);
1214 	if (ret) {
1215 	    /* windows LH with interesting CMS packets */
1216 	    size_t ph = 1 + der_length_len(content.length);
1217 	    unsigned char *ptr = malloc(content.length + ph);
1218 	    size_t l;
1219 
1220 	    memcpy(ptr + ph, content.data, content.length);
1221 
1222 	    ret = der_put_length_and_tag (ptr + ph - 1, ph, content.length,
1223 					  ASN1_C_UNIV, CONS, UT_Sequence, &l);
1224 	    if (ret)
1225 		return ret;
1226 	    free(content.data);
1227 	    content.data = ptr;
1228 	    content.length += ph;
1229 
1230 	    ret = hx509_cms_unwrap_ContentInfo(&content, &type2, &out, NULL);
1231 	    if (ret)
1232 		goto out;
1233 	}
1234 	if (der_heim_oid_cmp(&type2, &asn1_oid_id_pkcs7_signedData)) {
1235 	    ret = EINVAL; /* XXX */
1236 	    krb5_set_error_message(context, ret,
1237 				   N_("PKINIT: Invalid content type", ""));
1238 	    der_free_oid(&type2);
1239 	    der_free_octet_string(&out);
1240 	    goto out;
1241 	}
1242 	der_free_oid(&type2);
1243 	krb5_data_free(&content);
1244 	ret = krb5_data_copy(&content, out.data, out.length);
1245 	der_free_octet_string(&out);
1246 	if (ret) {
1247 	    krb5_set_error_message(context, ret,
1248 				   N_("malloc: out of memory", ""));
1249 	    goto out;
1250 	}
1251     }
1252 
1253     ret = pk_verify_sign(context,
1254 			 content.data,
1255 			 content.length,
1256 			 ctx->id,
1257 			 &contentType,
1258 			 &content,
1259 			 &host);
1260     if (ret)
1261 	goto out;
1262 
1263     /* make sure that it is the kdc's certificate */
1264     ret = pk_verify_host(context, realm, hi, ctx, host);
1265     if (ret) {
1266 	goto out;
1267     }
1268 
1269 #if 0
1270     if (type == PKINIT_WIN2K) {
1271 	if (der_heim_oid_cmp(&contentType, &asn1_oid_id_pkcs7_data) != 0) {
1272 	    ret = KRB5KRB_AP_ERR_MSG_TYPE;
1273 	    krb5_set_error_message(context, ret, "PKINIT: reply key, wrong oid");
1274 	    goto out;
1275 	}
1276     } else {
1277 	if (der_heim_oid_cmp(&contentType, &asn1_oid_id_pkrkeydata) != 0) {
1278 	    ret = KRB5KRB_AP_ERR_MSG_TYPE;
1279 	    krb5_set_error_message(context, ret, "PKINIT: reply key, wrong oid");
1280 	    goto out;
1281 	}
1282     }
1283 #endif
1284 
1285     switch(type) {
1286     case PKINIT_WIN2K:
1287 	ret = get_reply_key(context, &content, req_buffer, key);
1288 	if (ret != 0 && ctx->require_binding == 0)
1289 	    ret = get_reply_key_win(context, &content, nonce, key);
1290 	break;
1291     case PKINIT_27:
1292 	ret = get_reply_key(context, &content, req_buffer, key);
1293 	break;
1294     }
1295     if (ret)
1296 	goto out;
1297 
1298     /* XXX compare given etype with key->etype */
1299 
1300  out:
1301     if (host)
1302 	_krb5_pk_cert_free(host);
1303     der_free_oid(&contentType);
1304     krb5_data_free(&content);
1305 
1306     return ret;
1307 }
1308 
1309 /*
1310  * RFC 8062 section 7:
1311  *
1312  *  The client then decrypts the KDC contribution key and verifies that
1313  *  the ticket session key in the returned ticket is the combined key of
1314  *  the KDC contribution key and the reply key.
1315  */
1316 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1317 _krb5_pk_kx_confirm(krb5_context context,
1318 		    krb5_pk_init_ctx ctx,
1319 		    krb5_keyblock *reply_key,
1320 		    krb5_keyblock *session_key,
1321 		    PA_DATA *pa_pkinit_kx)
1322 {
1323     krb5_error_code ret;
1324     EncryptedData ed;
1325     krb5_keyblock ck, sk_verify;
1326     krb5_crypto ck_crypto = NULL;
1327     krb5_crypto rk_crypto = NULL;
1328     size_t len;
1329     krb5_data data;
1330     krb5_data p1 = { sizeof("PKINIT") - 1, "PKINIT" };
1331     krb5_data p2 = { sizeof("KEYEXCHANGE") - 1, "KEYEXCHANGE" };
1332 
1333     heim_assert(ctx != NULL, "PKINIT context is non-NULL");
1334     heim_assert(reply_key != NULL, "reply key is non-NULL");
1335     heim_assert(session_key != NULL, "session key is non-NULL");
1336 
1337     /* PA-PKINIT-KX is optional unless anonymous */
1338     if (pa_pkinit_kx == NULL)
1339 	return ctx->anonymous ? KRB5_KDCREP_MODIFIED : 0;
1340 
1341     memset(&ed, 0, sizeof(ed));
1342     krb5_keyblock_zero(&ck);
1343     krb5_keyblock_zero(&sk_verify);
1344     krb5_data_zero(&data);
1345 
1346     ret = decode_EncryptedData(pa_pkinit_kx->padata_value.data,
1347 			       pa_pkinit_kx->padata_value.length,
1348 			       &ed, &len);
1349     if (ret)
1350 	goto out;
1351 
1352     if (len != pa_pkinit_kx->padata_value.length) {
1353 	ret = KRB5_KDCREP_MODIFIED;
1354 	goto out;
1355     }
1356 
1357     ret = krb5_crypto_init(context, reply_key, 0, &rk_crypto);
1358     if (ret)
1359 	goto out;
1360 
1361     ret = krb5_decrypt_EncryptedData(context, rk_crypto,
1362 				     KRB5_KU_PA_PKINIT_KX,
1363 				     &ed, &data);
1364     if (ret)
1365 	goto out;
1366 
1367     ret = decode_EncryptionKey(data.data, data.length,
1368 			       &ck, &len);
1369     if (ret)
1370 	goto out;
1371 
1372     ret = krb5_crypto_init(context, &ck, 0, &ck_crypto);
1373     if (ret)
1374 	goto out;
1375 
1376     ret = krb5_crypto_fx_cf2(context, ck_crypto, rk_crypto,
1377 			     &p1, &p2, session_key->keytype,
1378 			     &sk_verify);
1379     if (ret)
1380 	goto out;
1381 
1382     if (sk_verify.keytype != session_key->keytype ||
1383 	krb5_data_ct_cmp(&sk_verify.keyvalue, &session_key->keyvalue) != 0) {
1384 	ret = KRB5_KDCREP_MODIFIED;
1385 	goto out;
1386     }
1387 
1388 out:
1389     free_EncryptedData(&ed);
1390     krb5_free_keyblock_contents(context, &ck);
1391     krb5_free_keyblock_contents(context, &sk_verify);
1392     if (ck_crypto)
1393 	krb5_crypto_destroy(context, ck_crypto);
1394     if (rk_crypto)
1395 	krb5_crypto_destroy(context, rk_crypto);
1396     krb5_data_free(&data);
1397 
1398     return ret;
1399 }
1400 
1401 static krb5_error_code
1402 pk_rd_pa_reply_dh(krb5_context context,
1403 		  const heim_octet_string *indata,
1404 		  const heim_oid *dataType,
1405 		  const char *realm,
1406 		  krb5_pk_init_ctx ctx,
1407 		  krb5_enctype etype,
1408 		  const krb5_krbhst_info *hi,
1409 		  const DHNonce *c_n,
1410 		  const DHNonce *k_n,
1411                   unsigned nonce,
1412                   PA_DATA *pa,
1413                   krb5_keyblock **key)
1414 {
1415     const unsigned char *p;
1416     unsigned char *dh_gen_key = NULL;
1417     struct krb5_pk_cert *host = NULL;
1418     BIGNUM *kdc_dh_pubkey = NULL;
1419     KDCDHKeyInfo kdc_dh_info;
1420     heim_oid contentType = { 0, NULL };
1421     krb5_data content;
1422     krb5_error_code ret;
1423     int dh_gen_keylen = 0;
1424     size_t size;
1425 
1426     krb5_data_zero(&content);
1427     memset(&kdc_dh_info, 0, sizeof(kdc_dh_info));
1428 
1429     if (der_heim_oid_cmp(&asn1_oid_id_pkcs7_signedData, dataType)) {
1430 	krb5_set_error_message(context, EINVAL,
1431 			       N_("PKINIT: Invalid content type", ""));
1432 	return EINVAL;
1433     }
1434 
1435     ret = pk_verify_sign(context,
1436 			 indata->data,
1437 			 indata->length,
1438 			 ctx->id,
1439 			 &contentType,
1440 			 &content,
1441 			 &host);
1442     if (ret)
1443 	goto out;
1444 
1445     /* make sure that it is the kdc's certificate */
1446     ret = pk_verify_host(context, realm, hi, ctx, host);
1447     if (ret)
1448 	goto out;
1449 
1450     if (der_heim_oid_cmp(&contentType, &asn1_oid_id_pkdhkeydata)) {
1451 	ret = KRB5KRB_AP_ERR_MSG_TYPE;
1452 	krb5_set_error_message(context, ret,
1453 			       N_("pkinit - dh reply contains wrong oid", ""));
1454 	goto out;
1455     }
1456 
1457     ret = decode_KDCDHKeyInfo(content.data,
1458 			      content.length,
1459 			      &kdc_dh_info,
1460 			      &size);
1461 
1462     if (ret) {
1463 	krb5_set_error_message(context, ret,
1464 			       N_("pkinit - failed to decode "
1465 				  "KDC DH Key Info", ""));
1466 	goto out;
1467     }
1468 
1469     if (kdc_dh_info.nonce != nonce) {
1470 	ret = KRB5KRB_AP_ERR_MODIFIED;
1471 	krb5_set_error_message(context, ret,
1472 			       N_("PKINIT: DH nonce is wrong", ""));
1473 	goto out;
1474     }
1475 
1476     if (kdc_dh_info.dhKeyExpiration) {
1477 	if (k_n == NULL) {
1478 	    ret = KRB5KRB_ERR_GENERIC;
1479 	    krb5_set_error_message(context, ret,
1480 				   N_("pkinit; got key expiration "
1481 				      "without server nonce", ""));
1482 	    goto out;
1483 	}
1484 	if (c_n == NULL) {
1485 	    ret = KRB5KRB_ERR_GENERIC;
1486 	    krb5_set_error_message(context, ret,
1487 				   N_("pkinit; got DH reuse but no "
1488 				      "client nonce", ""));
1489 	    goto out;
1490 	}
1491     } else {
1492 	if (k_n) {
1493 	    ret = KRB5KRB_ERR_GENERIC;
1494 	    krb5_set_error_message(context, ret,
1495 				   N_("pkinit: got server nonce "
1496 				      "without key expiration", ""));
1497 	    goto out;
1498 	}
1499 	c_n = NULL;
1500     }
1501 
1502 
1503     p = kdc_dh_info.subjectPublicKey.data;
1504     size = (kdc_dh_info.subjectPublicKey.length + 7) / 8;
1505 
1506     if (ctx->keyex == USE_DH) {
1507 	DHPublicKey k;
1508 	ret = decode_DHPublicKey(p, size, &k, NULL);
1509 	if (ret) {
1510 	    krb5_set_error_message(context, ret,
1511 				   N_("pkinit: can't decode "
1512 				      "without key expiration", ""));
1513 	    goto out;
1514 	}
1515 
1516 	kdc_dh_pubkey = integer_to_BN(context, "DHPublicKey", &k);
1517 	free_DHPublicKey(&k);
1518 	if (kdc_dh_pubkey == NULL) {
1519 	    ret = ENOMEM;
1520 	    goto out;
1521 	}
1522 
1523 
1524 	size = DH_size(ctx->u.dh);
1525 
1526 	dh_gen_key = malloc(size);
1527 	if (dh_gen_key == NULL) {
1528 	    ret = ENOMEM;
1529 	    krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
1530 	    goto out;
1531 	}
1532 
1533 	dh_gen_keylen = DH_compute_key(dh_gen_key, kdc_dh_pubkey, ctx->u.dh);
1534 	if (dh_gen_keylen == -1) {
1535 	    ret = KRB5KRB_ERR_GENERIC;
1536 	    dh_gen_keylen = 0;
1537 	    krb5_set_error_message(context, ret,
1538 				   N_("PKINIT: Can't compute Diffie-Hellman key", ""));
1539 	    goto out;
1540 	}
1541 	if (dh_gen_keylen < (int)size) {
1542 	    size -= dh_gen_keylen;
1543 	    memmove(dh_gen_key + size, dh_gen_key, dh_gen_keylen);
1544 	    memset(dh_gen_key, 0, size);
1545 	}
1546 
1547     } else {
1548 #ifdef HAVE_OPENSSL
1549 	const EC_GROUP *group;
1550 	EC_KEY *public = NULL;
1551 
1552 	group = EC_KEY_get0_group(ctx->u.eckey);
1553 
1554 	public = EC_KEY_new();
1555 	if (public == NULL) {
1556 	    ret = ENOMEM;
1557 	    goto out;
1558 	}
1559 	if (EC_KEY_set_group(public, group) != 1) {
1560 	    EC_KEY_free(public);
1561 	    ret = ENOMEM;
1562 	    goto out;
1563 	}
1564 
1565 	if (o2i_ECPublicKey(&public, &p, size) == NULL) {
1566 	    EC_KEY_free(public);
1567 	    ret = KRB5KRB_ERR_GENERIC;
1568 	    krb5_set_error_message(context, ret,
1569 				   N_("PKINIT: Can't parse ECDH public key", ""));
1570 	    goto out;
1571 	}
1572 
1573 	size = (EC_GROUP_get_degree(group) + 7) / 8;
1574 	dh_gen_key = malloc(size);
1575 	if (dh_gen_key == NULL) {
1576 	    EC_KEY_free(public);
1577 	    ret = ENOMEM;
1578 	    krb5_set_error_message(context, ret,
1579 				   N_("malloc: out of memory", ""));
1580 	    goto out;
1581 	}
1582 	dh_gen_keylen = ECDH_compute_key(dh_gen_key, size,
1583 					 EC_KEY_get0_public_key(public), ctx->u.eckey, NULL);
1584 	EC_KEY_free(public);
1585 	if (dh_gen_keylen == -1) {
1586 	    ret = KRB5KRB_ERR_GENERIC;
1587 	    dh_gen_keylen = 0;
1588 	    krb5_set_error_message(context, ret,
1589 				   N_("PKINIT: Can't compute ECDH public key", ""));
1590 	    goto out;
1591 	}
1592 #else
1593 	ret = EINVAL;
1594 #endif
1595     }
1596 
1597     if (dh_gen_keylen <= 0) {
1598 	ret = EINVAL;
1599 	krb5_set_error_message(context, ret,
1600 			       N_("PKINIT: resulting DH key <= 0", ""));
1601 	dh_gen_keylen = 0;
1602 	goto out;
1603     }
1604 
1605     *key = malloc (sizeof (**key));
1606     if (*key == NULL) {
1607 	ret = ENOMEM;
1608 	krb5_set_error_message(context, ret,
1609 			       N_("malloc: out of memory", ""));
1610 	goto out;
1611     }
1612 
1613     ret = _krb5_pk_octetstring2key(context,
1614 				   etype,
1615 				   dh_gen_key, dh_gen_keylen,
1616 				   c_n, k_n,
1617 				   *key);
1618     if (ret) {
1619 	krb5_set_error_message(context, ret,
1620 			       N_("PKINIT: can't create key from DH key", ""));
1621 	free(*key);
1622 	*key = NULL;
1623 	goto out;
1624     }
1625 
1626  out:
1627     if (kdc_dh_pubkey)
1628 	BN_free(kdc_dh_pubkey);
1629     if (dh_gen_key) {
1630 	memset(dh_gen_key, 0, dh_gen_keylen);
1631 	free(dh_gen_key);
1632     }
1633     if (host)
1634 	_krb5_pk_cert_free(host);
1635     if (content.data)
1636 	krb5_data_free(&content);
1637     der_free_oid(&contentType);
1638     free_KDCDHKeyInfo(&kdc_dh_info);
1639 
1640     return ret;
1641 }
1642 
1643 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1644 _krb5_pk_rd_pa_reply(krb5_context context,
1645 		     const char *realm,
1646 		     void *c,
1647 		     krb5_enctype etype,
1648 		     const krb5_krbhst_info *hi,
1649 		     unsigned nonce,
1650 		     const krb5_data *req_buffer,
1651 		     PA_DATA *pa,
1652 		     krb5_keyblock **key)
1653 {
1654     krb5_pk_init_ctx ctx = c;
1655     krb5_error_code ret;
1656     size_t size;
1657 
1658     /* Check for IETF PK-INIT first */
1659     if (ctx->type == PKINIT_27) {
1660 	PA_PK_AS_REP rep;
1661 	heim_octet_string os, data;
1662 	heim_oid oid;
1663 
1664 	if (pa->padata_type != KRB5_PADATA_PK_AS_REP) {
1665 	    krb5_set_error_message(context, EINVAL,
1666 				   N_("PKINIT: wrong padata recv", ""));
1667 	    return EINVAL;
1668 	}
1669 
1670 	ret = decode_PA_PK_AS_REP(pa->padata_value.data,
1671 				  pa->padata_value.length,
1672 				  &rep,
1673 				  &size);
1674 	if (ret) {
1675 	    krb5_set_error_message(context, ret,
1676 				   N_("Failed to decode pkinit AS rep", ""));
1677 	    return ret;
1678 	}
1679 
1680 	switch (rep.element) {
1681 	case choice_PA_PK_AS_REP_dhInfo:
1682 	    _krb5_debug(context, 5, "krb5_get_init_creds: using pkinit dh");
1683 	    os = rep.u.dhInfo.dhSignedData;
1684 	    break;
1685 	case choice_PA_PK_AS_REP_encKeyPack:
1686 	    _krb5_debug(context, 5, "krb5_get_init_creds: using kinit enc reply key");
1687 	    os = rep.u.encKeyPack;
1688 	    break;
1689 	default: {
1690 	    PA_PK_AS_REP_BTMM btmm;
1691 	    free_PA_PK_AS_REP(&rep);
1692 	    memset(&rep, 0, sizeof(rep));
1693 
1694 	    _krb5_debug(context, 5, "krb5_get_init_creds: using BTMM kinit enc reply key");
1695 
1696 	    ret = decode_PA_PK_AS_REP_BTMM(pa->padata_value.data,
1697 					   pa->padata_value.length,
1698 					   &btmm,
1699 					   &size);
1700 	    if (ret) {
1701 		krb5_set_error_message(context, EINVAL,
1702 				       N_("PKINIT: -27 reply "
1703 					  "invalid content type", ""));
1704 		return EINVAL;
1705 	    }
1706 
1707 	    if (btmm.dhSignedData || btmm.encKeyPack == NULL) {
1708 		free_PA_PK_AS_REP_BTMM(&btmm);
1709 		ret = EINVAL;
1710 		krb5_set_error_message(context, ret,
1711 				       N_("DH mode not supported for BTMM mode", ""));
1712 		return ret;
1713 	    }
1714 
1715 	    /*
1716 	     * Transform to IETF style PK-INIT reply so that free works below
1717 	     */
1718 
1719 	    rep.element = choice_PA_PK_AS_REP_encKeyPack;
1720 	    rep.u.encKeyPack.data = btmm.encKeyPack->data;
1721 	    rep.u.encKeyPack.length = btmm.encKeyPack->length;
1722 	    btmm.encKeyPack->data = NULL;
1723 	    btmm.encKeyPack->length = 0;
1724 	    free_PA_PK_AS_REP_BTMM(&btmm);
1725 	    os = rep.u.encKeyPack;
1726 	}
1727 	}
1728 
1729 	ret = hx509_cms_unwrap_ContentInfo(&os, &oid, &data, NULL);
1730 	if (ret) {
1731 	    free_PA_PK_AS_REP(&rep);
1732 	    krb5_set_error_message(context, ret,
1733 				   N_("PKINIT: failed to unwrap CI", ""));
1734 	    return ret;
1735 	}
1736 
1737 	switch (rep.element) {
1738 	case choice_PA_PK_AS_REP_dhInfo:
1739 	    ret = pk_rd_pa_reply_dh(context, &data, &oid, realm, ctx, etype, hi,
1740 				    ctx->clientDHNonce,
1741 				    rep.u.dhInfo.serverDHNonce,
1742 				    nonce, pa, key);
1743 	    break;
1744 	case choice_PA_PK_AS_REP_encKeyPack:
1745 	    ret = pk_rd_pa_reply_enckey(context, PKINIT_27, &data, &oid, realm,
1746 					ctx, etype, hi, nonce, req_buffer, pa, key);
1747 	    break;
1748 	default:
1749 	    krb5_abortx(context, "pk-init as-rep case not possible to happen");
1750 	}
1751 	der_free_octet_string(&data);
1752 	der_free_oid(&oid);
1753 	free_PA_PK_AS_REP(&rep);
1754 
1755     } else if (ctx->type == PKINIT_WIN2K) {
1756 	PA_PK_AS_REP_Win2k w2krep;
1757 
1758 	/* Check for Windows encoding of the AS-REP pa data */
1759 
1760 #if 0 /* should this be ? */
1761 	if (pa->padata_type != KRB5_PADATA_PK_AS_REP) {
1762 	    krb5_set_error_message(context, EINVAL,
1763 				   "PKINIT: wrong padata recv");
1764 	    return EINVAL;
1765 	}
1766 #endif
1767 
1768 	memset(&w2krep, 0, sizeof(w2krep));
1769 
1770 	ret = decode_PA_PK_AS_REP_Win2k(pa->padata_value.data,
1771 					pa->padata_value.length,
1772 					&w2krep,
1773 					&size);
1774 	if (ret) {
1775 	    krb5_set_error_message(context, ret,
1776 				   N_("PKINIT: Failed decoding windows "
1777 				      "pkinit reply %d", ""), (int)ret);
1778 	    return ret;
1779 	}
1780 
1781 	krb5_clear_error_message(context);
1782 
1783 	switch (w2krep.element) {
1784 	case choice_PA_PK_AS_REP_Win2k_encKeyPack: {
1785 	    heim_octet_string data;
1786 	    heim_oid oid;
1787 
1788 	    ret = hx509_cms_unwrap_ContentInfo(&w2krep.u.encKeyPack,
1789 					       &oid, &data, NULL);
1790 	    free_PA_PK_AS_REP_Win2k(&w2krep);
1791 	    if (ret) {
1792 		krb5_set_error_message(context, ret,
1793 				       N_("PKINIT: failed to unwrap CI", ""));
1794 		return ret;
1795 	    }
1796 
1797 	    ret = pk_rd_pa_reply_enckey(context, PKINIT_WIN2K, &data, &oid, realm,
1798 					ctx, etype, hi, nonce, req_buffer, pa, key);
1799 	    der_free_octet_string(&data);
1800 	    der_free_oid(&oid);
1801 
1802 	    break;
1803 	}
1804 	default:
1805 	    free_PA_PK_AS_REP_Win2k(&w2krep);
1806 	    ret = EINVAL;
1807 	    krb5_set_error_message(context, ret,
1808 				   N_("PKINIT: win2k reply invalid "
1809 				      "content type", ""));
1810 	    break;
1811 	}
1812 
1813     } else {
1814 	ret = EINVAL;
1815 	krb5_set_error_message(context, ret,
1816 			       N_("PKINIT: unknown reply type", ""));
1817     }
1818 
1819     return ret;
1820 }
1821 
1822 struct prompter {
1823     krb5_context context;
1824     krb5_prompter_fct prompter;
1825     void *prompter_data;
1826 };
1827 
1828 static int
1829 hx_pass_prompter(void *data, const hx509_prompt *prompter)
1830 {
1831     krb5_error_code ret;
1832     krb5_prompt prompt;
1833     krb5_data password_data;
1834     struct prompter *p = data;
1835 
1836     password_data.data   = prompter->reply.data;
1837     password_data.length = prompter->reply.length;
1838 
1839     prompt.prompt = prompter->prompt;
1840     prompt.hidden = hx509_prompt_hidden(prompter->type);
1841     prompt.reply  = &password_data;
1842 
1843     switch (prompter->type) {
1844     case HX509_PROMPT_TYPE_INFO:
1845 	prompt.type   = KRB5_PROMPT_TYPE_INFO;
1846 	break;
1847     case HX509_PROMPT_TYPE_PASSWORD:
1848     case HX509_PROMPT_TYPE_QUESTION:
1849     default:
1850 	prompt.type   = KRB5_PROMPT_TYPE_PASSWORD;
1851 	break;
1852     }
1853 
1854     ret = (*p->prompter)(p->context, p->prompter_data, NULL, NULL, 1, &prompt);
1855     if (ret) {
1856 	memset (prompter->reply.data, 0, prompter->reply.length);
1857 	return 1;
1858     }
1859     return 0;
1860 }
1861 
1862 static krb5_error_code
1863 _krb5_pk_set_user_id(krb5_context context,
1864 		     krb5_principal principal,
1865 		     krb5_pk_init_ctx ctx,
1866 		     struct hx509_certs_data *certs)
1867 {
1868     hx509_certs c = hx509_certs_ref(certs);
1869     hx509_query *q = NULL;
1870     int ret;
1871 
1872     if (ctx->id->certs)
1873 	hx509_certs_free(&ctx->id->certs);
1874     if (ctx->id->cert) {
1875 	hx509_cert_free(ctx->id->cert);
1876 	ctx->id->cert = NULL;
1877     }
1878 
1879     ctx->id->certs = c;
1880     ctx->anonymous = 0;
1881 
1882     ret = hx509_query_alloc(context->hx509ctx, &q);
1883     if (ret) {
1884 	pk_copy_error(context, context->hx509ctx, ret,
1885 		      "Allocate query to find signing certificate");
1886 	return ret;
1887     }
1888 
1889     hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY);
1890     hx509_query_match_option(q, HX509_QUERY_OPTION_KU_DIGITALSIGNATURE);
1891 
1892     if (principal && strncmp("LKDC:SHA1.", krb5_principal_get_realm(context, principal), 9) == 0) {
1893 	ctx->id->flags |= PKINIT_BTMM;
1894     }
1895 
1896     ret = find_cert(context, ctx->id, q, &ctx->id->cert);
1897     hx509_query_free(context->hx509ctx, q);
1898 
1899     if (ret == 0 && _krb5_have_debug(context, 2)) {
1900 	hx509_name name;
1901 	char *str, *sn;
1902 	heim_integer i;
1903 
1904 	ret = hx509_cert_get_subject(ctx->id->cert, &name);
1905 	if (ret)
1906 	    goto out;
1907 
1908 	ret = hx509_name_to_string(name, &str);
1909 	hx509_name_free(&name);
1910 	if (ret)
1911 	    goto out;
1912 
1913 	ret = hx509_cert_get_serialnumber(ctx->id->cert, &i);
1914 	if (ret) {
1915 	    free(str);
1916 	    goto out;
1917 	}
1918 
1919 	ret = der_print_hex_heim_integer(&i, &sn);
1920 	der_free_heim_integer(&i);
1921 	if (ret) {
1922 	    free(name);
1923 	    goto out;
1924 	}
1925 
1926 	_krb5_debug(context, 2, "using cert: subject: %s sn: %s", str, sn);
1927 	free(str);
1928 	free(sn);
1929     }
1930  out:
1931 
1932     return ret;
1933 }
1934 
1935 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1936 _krb5_pk_load_id(krb5_context context,
1937 		 struct krb5_pk_identity **ret_id,
1938 		 const char *user_id,
1939 		 const char *anchor_id,
1940 		 char * const *chain_list,
1941 		 char * const *revoke_list,
1942 		 krb5_prompter_fct prompter,
1943 		 void *prompter_data,
1944 		 char *password)
1945 {
1946     struct krb5_pk_identity *id = NULL;
1947     struct prompter p;
1948     int ret;
1949 
1950     *ret_id = NULL;
1951 
1952     if (anchor_id == NULL) {
1953 	krb5_set_error_message(context, HEIM_PKINIT_NO_VALID_CA,
1954 			       N_("PKINIT: No anchor given", ""));
1955 	return HEIM_PKINIT_NO_VALID_CA;
1956     }
1957 
1958     /* load cert */
1959 
1960     id = calloc(1, sizeof(*id));
1961     if (id == NULL) {
1962 	krb5_set_error_message(context, ENOMEM,
1963 			       N_("malloc: out of memory", ""));
1964 	return ENOMEM;
1965     }
1966 
1967     if (user_id) {
1968 	hx509_lock lock;
1969 
1970 	ret = hx509_lock_init(context->hx509ctx, &lock);
1971 	if (ret) {
1972 	    pk_copy_error(context, context->hx509ctx, ret, "Failed init lock");
1973 	    goto out;
1974 	}
1975 
1976 	if (password && password[0])
1977 	    hx509_lock_add_password(lock, password);
1978 
1979 	if (prompter) {
1980 	    p.context = context;
1981 	    p.prompter = prompter;
1982 	    p.prompter_data = prompter_data;
1983 
1984 	    ret = hx509_lock_set_prompter(lock, hx_pass_prompter, &p);
1985 	    if (ret) {
1986 		hx509_lock_free(lock);
1987 		goto out;
1988 	    }
1989 	}
1990 
1991 	ret = hx509_certs_init(context->hx509ctx, user_id, 0, lock, &id->certs);
1992         hx509_lock_free(lock);
1993 	if (ret) {
1994 	    pk_copy_error(context, context->hx509ctx, ret,
1995 			  "Failed to init cert certs");
1996 	    goto out;
1997 	}
1998     } else {
1999 	id->certs = NULL;
2000     }
2001 
2002     ret = hx509_certs_init(context->hx509ctx, anchor_id, 0, NULL, &id->anchors);
2003     if (ret) {
2004 	pk_copy_error(context, context->hx509ctx, ret,
2005 		      "Failed to init anchors");
2006 	goto out;
2007     }
2008 
2009     ret = hx509_certs_init(context->hx509ctx, "MEMORY:pkinit-cert-chain",
2010 			   0, NULL, &id->certpool);
2011     if (ret) {
2012 	pk_copy_error(context, context->hx509ctx, ret,
2013 		      "Failed to init chain");
2014 	goto out;
2015     }
2016 
2017     while (chain_list && *chain_list) {
2018 	ret = hx509_certs_append(context->hx509ctx, id->certpool,
2019 				 NULL, *chain_list);
2020 	if (ret) {
2021 	    pk_copy_error(context, context->hx509ctx, ret,
2022 			  "Failed to laod chain %s",
2023 			  *chain_list);
2024 	    goto out;
2025 	}
2026 	chain_list++;
2027     }
2028 
2029     if (revoke_list) {
2030 	ret = hx509_revoke_init(context->hx509ctx, &id->revokectx);
2031 	if (ret) {
2032 	    pk_copy_error(context, context->hx509ctx, ret,
2033 			  "Failed init revoke list");
2034 	    goto out;
2035 	}
2036 
2037 	while (*revoke_list) {
2038 	    ret = hx509_revoke_add_crl(context->hx509ctx,
2039 				       id->revokectx,
2040 				       *revoke_list);
2041 	    if (ret) {
2042 		pk_copy_error(context, context->hx509ctx, ret,
2043 			      "Failed load revoke list");
2044 		goto out;
2045 	    }
2046 	    revoke_list++;
2047 	}
2048     } else
2049 	hx509_context_set_missing_revoke(context->hx509ctx, 1);
2050 
2051     ret = hx509_verify_init_ctx(context->hx509ctx, &id->verify_ctx);
2052     if (ret) {
2053 	pk_copy_error(context, context->hx509ctx, ret,
2054 		      "Failed init verify context");
2055 	goto out;
2056     }
2057 
2058     hx509_verify_attach_anchors(id->verify_ctx, id->anchors);
2059     hx509_verify_attach_revoke(id->verify_ctx, id->revokectx);
2060 
2061  out:
2062     if (ret) {
2063 	hx509_verify_destroy_ctx(id->verify_ctx);
2064 	hx509_certs_free(&id->certs);
2065 	hx509_certs_free(&id->anchors);
2066 	hx509_certs_free(&id->certpool);
2067 	hx509_revoke_free(&id->revokectx);
2068 	free(id);
2069     } else
2070 	*ret_id = id;
2071 
2072     return ret;
2073 }
2074 
2075 /*
2076  *
2077  */
2078 
2079 static void
2080 pk_copy_error(krb5_context context,
2081 	      hx509_context hx509ctx,
2082 	      int hxret,
2083 	      const char *fmt,
2084 	      ...)
2085 {
2086     va_list va;
2087     char *s, *f;
2088     int ret;
2089 
2090     va_start(va, fmt);
2091     ret = vasprintf(&f, fmt, va);
2092     va_end(va);
2093     if (ret == -1 || f == NULL) {
2094 	krb5_clear_error_message(context);
2095 	return;
2096     }
2097 
2098     s = hx509_get_error_string(hx509ctx, hxret);
2099     if (s == NULL) {
2100 	krb5_clear_error_message(context);
2101 	free(f);
2102 	return;
2103     }
2104     krb5_set_error_message(context, hxret, "%s: %s", f, s);
2105     free(s);
2106     free(f);
2107 }
2108 
2109 static int
2110 parse_integer(krb5_context context, char **p, const char *file, int lineno,
2111 	      const char *name, heim_integer *integer)
2112 {
2113     int ret;
2114     char *p1;
2115     p1 = strsep(p, " \t");
2116     if (p1 == NULL) {
2117 	krb5_set_error_message(context, EINVAL,
2118 			       N_("moduli file %s missing %s on line %d", ""),
2119 			       file, name, lineno);
2120 	return EINVAL;
2121     }
2122     ret = der_parse_hex_heim_integer(p1, integer);
2123     if (ret) {
2124 	krb5_set_error_message(context, ret,
2125 			       N_("moduli file %s failed parsing %s "
2126 				  "on line %d", ""),
2127 			       file, name, lineno);
2128 	return ret;
2129     }
2130 
2131     return 0;
2132 }
2133 
2134 krb5_error_code
2135 _krb5_parse_moduli_line(krb5_context context,
2136 			const char *file,
2137 			int lineno,
2138 			char *p,
2139 			struct krb5_dh_moduli **m)
2140 {
2141     struct krb5_dh_moduli *m1;
2142     char *p1;
2143     int ret;
2144 
2145     *m = NULL;
2146 
2147     m1 = calloc(1, sizeof(*m1));
2148     if (m1 == NULL) {
2149 	krb5_set_error_message(context, ENOMEM,
2150 			       N_("malloc: out of memory", ""));
2151 	return ENOMEM;
2152     }
2153 
2154     while (isspace((unsigned char)*p))
2155 	p++;
2156     if (*p  == '#') {
2157         free(m1);
2158 	return 0;
2159     }
2160     ret = EINVAL;
2161 
2162     p1 = strsep(&p, " \t");
2163     if (p1 == NULL) {
2164 	krb5_set_error_message(context, ret,
2165 			       N_("moduli file %s missing name on line %d", ""),
2166 			       file, lineno);
2167 	goto out;
2168     }
2169     m1->name = strdup(p1);
2170     if (m1->name == NULL) {
2171 	ret = ENOMEM;
2172 	krb5_set_error_message(context, ret, N_("malloc: out of memeory", ""));
2173 	goto out;
2174     }
2175 
2176     p1 = strsep(&p, " \t");
2177     if (p1 == NULL) {
2178 	krb5_set_error_message(context, ret,
2179 			       N_("moduli file %s missing bits on line %d", ""),
2180 			       file, lineno);
2181 	goto out;
2182     }
2183 
2184     m1->bits = atoi(p1);
2185     if (m1->bits == 0) {
2186 	krb5_set_error_message(context, ret,
2187 			       N_("moduli file %s have un-parsable "
2188 				  "bits on line %d", ""), file, lineno);
2189 	goto out;
2190     }
2191 
2192     ret = parse_integer(context, &p, file, lineno, "p", &m1->p);
2193     if (ret)
2194 	goto out;
2195     ret = parse_integer(context, &p, file, lineno, "g", &m1->g);
2196     if (ret)
2197 	goto out;
2198     ret = parse_integer(context, &p, file, lineno, "q", &m1->q);
2199     if (ret)
2200 	goto out;
2201 
2202     *m = m1;
2203 
2204     return 0;
2205  out:
2206     free(m1->name);
2207     der_free_heim_integer(&m1->p);
2208     der_free_heim_integer(&m1->g);
2209     der_free_heim_integer(&m1->q);
2210     free(m1);
2211     return ret;
2212 }
2213 
2214 void
2215 _krb5_free_moduli(struct krb5_dh_moduli **moduli)
2216 {
2217     int i;
2218     for (i = 0; moduli[i] != NULL; i++) {
2219 	free(moduli[i]->name);
2220 	der_free_heim_integer(&moduli[i]->p);
2221 	der_free_heim_integer(&moduli[i]->g);
2222 	der_free_heim_integer(&moduli[i]->q);
2223 	free(moduli[i]);
2224     }
2225     free(moduli);
2226 }
2227 
2228 static const char *default_moduli_RFC2412_MODP_group2 =
2229     /* name */
2230     "RFC2412-MODP-group2 "
2231     /* bits */
2232     "1024 "
2233     /* p */
2234     "FFFFFFFF" "FFFFFFFF" "C90FDAA2" "2168C234" "C4C6628B" "80DC1CD1"
2235     "29024E08" "8A67CC74" "020BBEA6" "3B139B22" "514A0879" "8E3404DD"
2236     "EF9519B3" "CD3A431B" "302B0A6D" "F25F1437" "4FE1356D" "6D51C245"
2237     "E485B576" "625E7EC6" "F44C42E9" "A637ED6B" "0BFF5CB6" "F406B7ED"
2238     "EE386BFB" "5A899FA5" "AE9F2411" "7C4B1FE6" "49286651" "ECE65381"
2239     "FFFFFFFF" "FFFFFFFF "
2240     /* g */
2241     "02 "
2242     /* q */
2243     "7FFFFFFF" "FFFFFFFF" "E487ED51" "10B4611A" "62633145" "C06E0E68"
2244     "94812704" "4533E63A" "0105DF53" "1D89CD91" "28A5043C" "C71A026E"
2245     "F7CA8CD9" "E69D218D" "98158536" "F92F8A1B" "A7F09AB6" "B6A8E122"
2246     "F242DABB" "312F3F63" "7A262174" "D31BF6B5" "85FFAE5B" "7A035BF6"
2247     "F71C35FD" "AD44CFD2" "D74F9208" "BE258FF3" "24943328" "F67329C0"
2248     "FFFFFFFF" "FFFFFFFF";
2249 
2250 static const char *default_moduli_rfc3526_MODP_group14 =
2251     /* name */
2252     "rfc3526-MODP-group14 "
2253     /* bits */
2254     "1760 "
2255     /* p */
2256     "FFFFFFFF" "FFFFFFFF" "C90FDAA2" "2168C234" "C4C6628B" "80DC1CD1"
2257     "29024E08" "8A67CC74" "020BBEA6" "3B139B22" "514A0879" "8E3404DD"
2258     "EF9519B3" "CD3A431B" "302B0A6D" "F25F1437" "4FE1356D" "6D51C245"
2259     "E485B576" "625E7EC6" "F44C42E9" "A637ED6B" "0BFF5CB6" "F406B7ED"
2260     "EE386BFB" "5A899FA5" "AE9F2411" "7C4B1FE6" "49286651" "ECE45B3D"
2261     "C2007CB8" "A163BF05" "98DA4836" "1C55D39A" "69163FA8" "FD24CF5F"
2262     "83655D23" "DCA3AD96" "1C62F356" "208552BB" "9ED52907" "7096966D"
2263     "670C354E" "4ABC9804" "F1746C08" "CA18217C" "32905E46" "2E36CE3B"
2264     "E39E772C" "180E8603" "9B2783A2" "EC07A28F" "B5C55DF0" "6F4C52C9"
2265     "DE2BCBF6" "95581718" "3995497C" "EA956AE5" "15D22618" "98FA0510"
2266     "15728E5A" "8AACAA68" "FFFFFFFF" "FFFFFFFF "
2267     /* g */
2268     "02 "
2269     /* q */
2270     "7FFFFFFF" "FFFFFFFF" "E487ED51" "10B4611A" "62633145" "C06E0E68"
2271     "94812704" "4533E63A" "0105DF53" "1D89CD91" "28A5043C" "C71A026E"
2272     "F7CA8CD9" "E69D218D" "98158536" "F92F8A1B" "A7F09AB6" "B6A8E122"
2273     "F242DABB" "312F3F63" "7A262174" "D31BF6B5" "85FFAE5B" "7A035BF6"
2274     "F71C35FD" "AD44CFD2" "D74F9208" "BE258FF3" "24943328" "F6722D9E"
2275     "E1003E5C" "50B1DF82" "CC6D241B" "0E2AE9CD" "348B1FD4" "7E9267AF"
2276     "C1B2AE91" "EE51D6CB" "0E3179AB" "1042A95D" "CF6A9483" "B84B4B36"
2277     "B3861AA7" "255E4C02" "78BA3604" "650C10BE" "19482F23" "171B671D"
2278     "F1CF3B96" "0C074301" "CD93C1D1" "7603D147" "DAE2AEF8" "37A62964"
2279     "EF15E5FB" "4AAC0B8C" "1CCAA4BE" "754AB572" "8AE9130C" "4C7D0288"
2280     "0AB9472D" "45565534" "7FFFFFFF" "FFFFFFFF";
2281 
2282 krb5_error_code
2283 _krb5_parse_moduli(krb5_context context, const char *file,
2284 		   struct krb5_dh_moduli ***moduli)
2285 {
2286     /* name bits P G Q */
2287     krb5_error_code ret;
2288     struct krb5_dh_moduli **m = NULL, **m2;
2289     char buf[4096];
2290     FILE *f;
2291     int lineno = 0, n = 0;
2292 
2293     *moduli = NULL;
2294 
2295     m = calloc(1, sizeof(m[0]) * 3);
2296     if (m == NULL) {
2297 	krb5_set_error_message(context, ENOMEM,
2298 			       N_("malloc: out of memory", ""));
2299 	return ENOMEM;
2300     }
2301 
2302     strlcpy(buf, default_moduli_rfc3526_MODP_group14, sizeof(buf));
2303     ret = _krb5_parse_moduli_line(context, "builtin", 1, buf,  &m[0]);
2304     if (ret) {
2305 	_krb5_free_moduli(m);
2306 	return ret;
2307     }
2308     n++;
2309 
2310     strlcpy(buf, default_moduli_RFC2412_MODP_group2, sizeof(buf));
2311     ret = _krb5_parse_moduli_line(context, "builtin", 1, buf,  &m[1]);
2312     if (ret) {
2313 	_krb5_free_moduli(m);
2314 	return ret;
2315     }
2316     n++;
2317 
2318 
2319     if (file == NULL)
2320 	file = MODULI_FILE;
2321 
2322 #ifdef KRB5_USE_PATH_TOKENS
2323     {
2324         char * exp_file;
2325 
2326         if (_krb5_expand_path_tokens(context, file, &exp_file) == 0) {
2327             f = fopen(exp_file, "r");
2328             krb5_xfree(exp_file);
2329         } else {
2330             f = NULL;
2331         }
2332     }
2333 #else
2334     f = fopen(file, "r");
2335 #endif
2336 
2337     if (f == NULL) {
2338 	*moduli = m;
2339 	return 0;
2340     }
2341     rk_cloexec_file(f);
2342 
2343     while(fgets(buf, sizeof(buf), f) != NULL) {
2344 	struct krb5_dh_moduli *element;
2345 
2346 	buf[strcspn(buf, "\n")] = '\0';
2347 	lineno++;
2348 
2349 	m2 = realloc(m, (n + 2) * sizeof(m[0]));
2350 	if (m2 == NULL) {
2351 	    _krb5_free_moduli(m);
2352 	    krb5_set_error_message(context, ENOMEM,
2353 				   N_("malloc: out of memory", ""));
2354 	    return ENOMEM;
2355 	}
2356 	m = m2;
2357 
2358 	m[n] = NULL;
2359 
2360 	ret = _krb5_parse_moduli_line(context, file, lineno, buf,  &element);
2361 	if (ret) {
2362 	    _krb5_free_moduli(m);
2363 	    return ret;
2364 	}
2365 	if (element == NULL)
2366 	    continue;
2367 
2368 	m[n] = element;
2369 	m[n + 1] = NULL;
2370 	n++;
2371     }
2372     *moduli = m;
2373     return 0;
2374 }
2375 
2376 krb5_error_code
2377 _krb5_dh_group_ok(krb5_context context, unsigned long bits,
2378 		  heim_integer *p, heim_integer *g, heim_integer *q,
2379 		  struct krb5_dh_moduli **moduli,
2380 		  char **name)
2381 {
2382     int i;
2383 
2384     if (name)
2385 	*name = NULL;
2386 
2387     for (i = 0; moduli[i] != NULL; i++) {
2388 	if (der_heim_integer_cmp(&moduli[i]->g, g) == 0 &&
2389 	    der_heim_integer_cmp(&moduli[i]->p, p) == 0 &&
2390 	    (q == NULL || der_heim_integer_cmp(&moduli[i]->q, q) == 0))
2391 	    {
2392 		if (bits && bits > moduli[i]->bits) {
2393 		    krb5_set_error_message(context,
2394 					   KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED,
2395 					   N_("PKINIT: DH group parameter %s "
2396 					      "no accepted, not enough bits "
2397 					      "generated", ""),
2398 					   moduli[i]->name);
2399 		    return KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED;
2400 		}
2401 		if (name)
2402 		    *name = strdup(moduli[i]->name);
2403 		return 0;
2404 	    }
2405     }
2406     krb5_set_error_message(context,
2407 			   KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED,
2408 			   N_("PKINIT: DH group parameter no ok", ""));
2409     return KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED;
2410 }
2411 #endif /* PKINIT */
2412 
2413 KRB5_LIB_FUNCTION void KRB5_LIB_CALL
2414 _krb5_get_init_creds_opt_free_pkinit(krb5_get_init_creds_opt *opt)
2415 {
2416 #ifdef PKINIT
2417     krb5_pk_init_ctx ctx;
2418 
2419     if (opt->opt_private == NULL || opt->opt_private->pk_init_ctx == NULL)
2420 	return;
2421     ctx = opt->opt_private->pk_init_ctx;
2422     switch (ctx->keyex) {
2423     case USE_DH:
2424 	if (ctx->u.dh)
2425 	    DH_free(ctx->u.dh);
2426 	break;
2427     case USE_RSA:
2428 	break;
2429     case USE_ECDH:
2430 #ifdef HAVE_OPENSSL
2431 	if (ctx->u.eckey)
2432 	    EC_KEY_free(ctx->u.eckey);
2433 #endif
2434 	break;
2435     }
2436     if (ctx->id) {
2437 	hx509_verify_destroy_ctx(ctx->id->verify_ctx);
2438 	hx509_certs_free(&ctx->id->certs);
2439 	hx509_cert_free(ctx->id->cert);
2440 	hx509_certs_free(&ctx->id->anchors);
2441 	hx509_certs_free(&ctx->id->certpool);
2442 
2443 	if (ctx->clientDHNonce) {
2444 	    krb5_free_data(NULL, ctx->clientDHNonce);
2445 	    ctx->clientDHNonce = NULL;
2446 	}
2447 	if (ctx->m)
2448 	    _krb5_free_moduli(ctx->m);
2449 	free(ctx->id);
2450 	ctx->id = NULL;
2451     }
2452     free(opt->opt_private->pk_init_ctx);
2453     opt->opt_private->pk_init_ctx = NULL;
2454 #endif
2455 }
2456 
2457 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2458 krb5_get_init_creds_opt_set_pkinit(krb5_context context,
2459 				   krb5_get_init_creds_opt *opt,
2460 				   krb5_principal principal,
2461 				   const char *user_id,
2462 				   const char *x509_anchors,
2463 				   char * const * pool,
2464 				   char * const * pki_revoke,
2465 				   int flags,
2466 				   krb5_prompter_fct prompter,
2467 				   void *prompter_data,
2468 				   char *password)
2469 {
2470 #ifdef PKINIT
2471     krb5_error_code ret;
2472     char *anchors = NULL;
2473 
2474     if (opt->opt_private == NULL) {
2475 	krb5_set_error_message(context, EINVAL,
2476 			       N_("PKINIT: on non extendable opt", ""));
2477 	return EINVAL;
2478     }
2479 
2480     opt->opt_private->pk_init_ctx =
2481 	calloc(1, sizeof(*opt->opt_private->pk_init_ctx));
2482     if (opt->opt_private->pk_init_ctx == NULL) {
2483 	krb5_set_error_message(context, ENOMEM,
2484 			       N_("malloc: out of memory", ""));
2485 	return ENOMEM;
2486     }
2487     opt->opt_private->pk_init_ctx->require_binding = 0;
2488     opt->opt_private->pk_init_ctx->require_eku = 1;
2489     opt->opt_private->pk_init_ctx->require_krbtgt_otherName = 1;
2490     opt->opt_private->pk_init_ctx->peer = NULL;
2491 
2492     /* XXX implement krb5_appdefault_strings  */
2493     if (pool == NULL)
2494 	pool = krb5_config_get_strings(context, NULL,
2495 				       "appdefaults",
2496 				       "pkinit_pool",
2497 				       NULL);
2498 
2499     if (pki_revoke == NULL)
2500 	pki_revoke = krb5_config_get_strings(context, NULL,
2501 					     "appdefaults",
2502 					     "pkinit_revoke",
2503 					     NULL);
2504 
2505     if (x509_anchors == NULL) {
2506 	krb5_appdefault_string(context, "kinit",
2507 			       krb5_principal_get_realm(context, principal),
2508 			       "pkinit_anchors", NULL, &anchors);
2509 	x509_anchors = anchors;
2510     }
2511 
2512     if (flags & 4)
2513 	opt->opt_private->pk_init_ctx->anonymous = 1;
2514 
2515     ret = _krb5_pk_load_id(context,
2516 			   &opt->opt_private->pk_init_ctx->id,
2517 			   user_id,
2518 			   x509_anchors,
2519 			   pool,
2520 			   pki_revoke,
2521 			   prompter,
2522 			   prompter_data,
2523 			   password);
2524     if (ret) {
2525 	free(opt->opt_private->pk_init_ctx);
2526 	opt->opt_private->pk_init_ctx = NULL;
2527 	return ret;
2528     }
2529 
2530     if (opt->opt_private->pk_init_ctx->id->certs) {
2531 	_krb5_pk_set_user_id(context,
2532 			     principal,
2533 			     opt->opt_private->pk_init_ctx,
2534 			     opt->opt_private->pk_init_ctx->id->certs);
2535     } else
2536 	opt->opt_private->pk_init_ctx->id->cert = NULL;
2537 
2538     if ((flags & 2) == 0) {
2539 	hx509_context hx509ctx = context->hx509ctx;
2540 	hx509_cert cert = opt->opt_private->pk_init_ctx->id->cert;
2541 
2542 	opt->opt_private->pk_init_ctx->keyex = USE_DH;
2543 
2544 	/*
2545 	 * If its a ECDSA certs, lets select ECDSA as the keyex algorithm.
2546 	 */
2547 	if (cert) {
2548 	    AlgorithmIdentifier alg;
2549 
2550 	    ret = hx509_cert_get_SPKI_AlgorithmIdentifier(hx509ctx, cert, &alg);
2551 	    if (ret == 0) {
2552 		if (der_heim_oid_cmp(&alg.algorithm, &asn1_oid_id_ecPublicKey) == 0)
2553 		    opt->opt_private->pk_init_ctx->keyex = USE_ECDH;
2554 		free_AlgorithmIdentifier(&alg);
2555 	    }
2556 	}
2557 
2558     } else {
2559 	opt->opt_private->pk_init_ctx->keyex = USE_RSA;
2560 
2561 	if (opt->opt_private->pk_init_ctx->id->certs == NULL) {
2562 	    krb5_set_error_message(context, EINVAL,
2563 				   N_("No anonymous pkinit support in RSA mode", ""));
2564 	    return EINVAL;
2565 	}
2566     }
2567 
2568     return 0;
2569 #else
2570     krb5_set_error_message(context, EINVAL,
2571 			   N_("no support for PKINIT compiled in", ""));
2572     return EINVAL;
2573 #endif
2574 }
2575 
2576 krb5_error_code KRB5_LIB_FUNCTION
2577 krb5_get_init_creds_opt_set_pkinit_user_certs(krb5_context context,
2578 					      krb5_get_init_creds_opt *opt,
2579 					      struct hx509_certs_data *certs)
2580 {
2581 #ifdef PKINIT
2582     if (opt->opt_private == NULL) {
2583 	krb5_set_error_message(context, EINVAL,
2584 			       N_("PKINIT: on non extendable opt", ""));
2585 	return EINVAL;
2586     }
2587     if (opt->opt_private->pk_init_ctx == NULL) {
2588 	krb5_set_error_message(context, EINVAL,
2589 			       N_("PKINIT: on pkinit context", ""));
2590 	return EINVAL;
2591     }
2592 
2593     _krb5_pk_set_user_id(context, NULL, opt->opt_private->pk_init_ctx, certs);
2594 
2595     return 0;
2596 #else
2597     krb5_set_error_message(context, EINVAL,
2598 			   N_("no support for PKINIT compiled in", ""));
2599     return EINVAL;
2600 #endif
2601 }
2602 
2603 #ifdef PKINIT
2604 
2605 static int
2606 get_ms_san(hx509_context context, hx509_cert cert, char **upn)
2607 {
2608     hx509_octet_string_list list;
2609     int ret;
2610 
2611     *upn = NULL;
2612 
2613     ret = hx509_cert_find_subjectAltName_otherName(context,
2614 						   cert,
2615 						   &asn1_oid_id_pkinit_ms_san,
2616 						   &list);
2617     if (ret)
2618 	return 0;
2619 
2620     if (list.len > 0 && list.val[0].length > 0)
2621 	ret = decode_MS_UPN_SAN(list.val[0].data, list.val[0].length,
2622 				upn, NULL);
2623     else
2624 	ret = 1;
2625     hx509_free_octet_string_list(&list);
2626 
2627     return ret;
2628 }
2629 
2630 static int
2631 find_ms_san(hx509_context context, hx509_cert cert, void *ctx)
2632 {
2633     char *upn;
2634     int ret;
2635 
2636     ret = get_ms_san(context, cert, &upn);
2637     if (ret == 0)
2638 	free(upn);
2639     return ret;
2640 }
2641 
2642 
2643 
2644 #endif
2645 
2646 /*
2647  * Private since it need to be redesigned using krb5_get_init_creds()
2648  */
2649 
2650 KRB5_LIB_FUNCTION krb5_error_code  KRB5_LIB_CALL
2651 krb5_pk_enterprise_cert(krb5_context context,
2652 			const char *user_id,
2653 			krb5_const_realm realm,
2654 			krb5_principal *principal,
2655 			struct hx509_certs_data **res)
2656 {
2657 #ifdef PKINIT
2658     krb5_error_code ret;
2659     hx509_certs certs, result;
2660     hx509_cert cert = NULL;
2661     hx509_query *q;
2662     char *name;
2663 
2664     *principal = NULL;
2665     if (res)
2666 	*res = NULL;
2667 
2668     if (user_id == NULL) {
2669 	krb5_set_error_message(context, ENOENT, "no user id");
2670 	return ENOENT;
2671     }
2672 
2673     ret = hx509_certs_init(context->hx509ctx, user_id, 0, NULL, &certs);
2674     if (ret) {
2675 	pk_copy_error(context, context->hx509ctx, ret,
2676 		      "Failed to init cert certs");
2677 	goto out;
2678     }
2679 
2680     ret = hx509_query_alloc(context->hx509ctx, &q);
2681     if (ret) {
2682 	krb5_set_error_message(context, ret, "out of memory");
2683 	hx509_certs_free(&certs);
2684 	goto out;
2685     }
2686 
2687     hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY);
2688     hx509_query_match_option(q, HX509_QUERY_OPTION_KU_DIGITALSIGNATURE);
2689     hx509_query_match_eku(q, &asn1_oid_id_pkinit_ms_eku);
2690     hx509_query_match_cmp_func(q, find_ms_san, NULL);
2691 
2692     ret = hx509_certs_filter(context->hx509ctx, certs, q, &result);
2693     hx509_query_free(context->hx509ctx, q);
2694     hx509_certs_free(&certs);
2695     if (ret) {
2696 	pk_copy_error(context, context->hx509ctx, ret,
2697 		      "Failed to find PKINIT certificate");
2698 	return ret;
2699     }
2700 
2701     ret = hx509_get_one_cert(context->hx509ctx, result, &cert);
2702     hx509_certs_free(&result);
2703     if (ret) {
2704 	pk_copy_error(context, context->hx509ctx, ret,
2705 		      "Failed to get one cert");
2706 	goto out;
2707     }
2708 
2709     ret = get_ms_san(context->hx509ctx, cert, &name);
2710     if (ret) {
2711 	pk_copy_error(context, context->hx509ctx, ret,
2712 		      "Failed to get MS SAN");
2713 	goto out;
2714     }
2715 
2716     ret = krb5_make_principal(context, principal, realm, name, NULL);
2717     free(name);
2718     if (ret)
2719 	goto out;
2720 
2721     krb5_principal_set_type(context, *principal, KRB5_NT_ENTERPRISE_PRINCIPAL);
2722 
2723     if (res) {
2724 	ret = hx509_certs_init(context->hx509ctx, "MEMORY:", 0, NULL, res);
2725 	if (ret)
2726 	    goto out;
2727 
2728 	ret = hx509_certs_add(context->hx509ctx, *res, cert);
2729 	if (ret) {
2730 	    hx509_certs_free(res);
2731 	    goto out;
2732 	}
2733     }
2734 
2735  out:
2736     hx509_cert_free(cert);
2737 
2738     return ret;
2739 #else
2740     krb5_set_error_message(context, EINVAL,
2741 			   N_("no support for PKINIT compiled in", ""));
2742     return EINVAL;
2743 #endif
2744 }
2745