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