xref: /netbsd-src/crypto/external/bsd/heimdal/dist/lib/krb5/pkinit.c (revision 6a493d6bc668897c91594964a732d38505b70cbb)
1 /*	$NetBSD: pkinit.c,v 1.1.1.1 2011/04/13 18:15:37 elric Exp $	*/
2 
3 /*
4  * Copyright (c) 2003 - 2007 Kungliga Tekniska Högskolan
5  * (Royal Institute of Technology, Stockholm, Sweden).
6  * All rights reserved.
7  *
8  * Portions Copyright (c) 2009 Apple Inc. All rights reserved.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  *
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  *
17  * 2. Redistributions in binary form must reproduce the above copyright
18  *    notice, this list of conditions and the following disclaimer in the
19  *    documentation and/or other materials provided with the distribution.
20  *
21  * 3. Neither the name of the Institute nor the names of its contributors
22  *    may be used to endorse or promote products derived from this software
23  *    without specific prior written permission.
24  *
25  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
26  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
29  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35  * SUCH DAMAGE.
36  */
37 
38 #include "krb5_locl.h"
39 
40 struct krb5_dh_moduli {
41     char *name;
42     unsigned long bits;
43     heim_integer p;
44     heim_integer g;
45     heim_integer q;
46 };
47 
48 #ifdef PKINIT
49 
50 #include <krb5/cms_asn1.h>
51 #include <krb5/pkcs8_asn1.h>
52 #include <krb5/pkcs9_asn1.h>
53 #include <krb5/pkcs12_asn1.h>
54 #include <krb5/pkinit_asn1.h>
55 #include <krb5/asn1_err.h>
56 
57 #include <krb5/der.h>
58 
59 struct krb5_pk_cert {
60     hx509_cert cert;
61 };
62 
63 struct krb5_pk_init_ctx_data {
64     struct krb5_pk_identity *id;
65     enum { USE_RSA, USE_DH, USE_ECDH } keyex;
66     union {
67 	DH *dh;
68 #ifdef HAVE_OPENSSL
69 	EC_KEY *eckey;
70 #endif
71     } u;
72     krb5_data *clientDHNonce;
73     struct krb5_dh_moduli **m;
74     hx509_peer_info peer;
75     enum krb5_pk_type type;
76     unsigned int require_binding:1;
77     unsigned int require_eku:1;
78     unsigned int require_krbtgt_otherName:1;
79     unsigned int require_hostname_match:1;
80     unsigned int trustedCertifiers:1;
81     unsigned int anonymous:1;
82 };
83 
84 static void
85 pk_copy_error(krb5_context context,
86 	      hx509_context hx509ctx,
87 	      int hxret,
88 	      const char *fmt,
89 	      ...)
90     __attribute__ ((format (printf, 4, 5)));
91 
92 /*
93  *
94  */
95 
96 KRB5_LIB_FUNCTION void KRB5_LIB_CALL
97 _krb5_pk_cert_free(struct krb5_pk_cert *cert)
98 {
99     if (cert->cert) {
100 	hx509_cert_free(cert->cert);
101     }
102     free(cert);
103 }
104 
105 static krb5_error_code
106 BN_to_integer(krb5_context context, BIGNUM *bn, heim_integer *integer)
107 {
108     integer->length = BN_num_bytes(bn);
109     integer->data = malloc(integer->length);
110     if (integer->data == NULL) {
111 	krb5_clear_error_message(context);
112 	return ENOMEM;
113     }
114     BN_bn2bin(bn, integer->data);
115     integer->negative = BN_is_negative(bn);
116     return 0;
117 }
118 
119 static BIGNUM *
120 integer_to_BN(krb5_context context, const char *field, const heim_integer *f)
121 {
122     BIGNUM *bn;
123 
124     bn = BN_bin2bn((const unsigned char *)f->data, f->length, NULL);
125     if (bn == NULL) {
126 	krb5_set_error_message(context, ENOMEM,
127 			       N_("PKINIT: parsing BN failed %s", ""), field);
128 	return NULL;
129     }
130     BN_set_negative(bn, f->negative);
131     return bn;
132 }
133 
134 static krb5_error_code
135 select_dh_group(krb5_context context, DH *dh, unsigned long bits,
136 		struct krb5_dh_moduli **moduli)
137 {
138     const struct krb5_dh_moduli *m;
139 
140     if (bits == 0) {
141 	m = moduli[1]; /* XXX */
142 	if (m == NULL)
143 	    m = moduli[0]; /* XXX */
144     } else {
145 	int i;
146 	for (i = 0; moduli[i] != NULL; i++) {
147 	    if (bits < moduli[i]->bits)
148 		break;
149 	}
150 	if (moduli[i] == NULL) {
151 	    krb5_set_error_message(context, EINVAL,
152 				   N_("Did not find a DH group parameter "
153 				      "matching requirement of %lu bits", ""),
154 				   bits);
155 	    return EINVAL;
156 	}
157 	m = moduli[i];
158     }
159 
160     dh->p = integer_to_BN(context, "p", &m->p);
161     if (dh->p == NULL)
162 	return ENOMEM;
163     dh->g = integer_to_BN(context, "g", &m->g);
164     if (dh->g == NULL)
165 	return ENOMEM;
166     dh->q = integer_to_BN(context, "q", &m->q);
167     if (dh->q == NULL)
168 	return ENOMEM;
169 
170     return 0;
171 }
172 
173 struct certfind {
174     const char *type;
175     const heim_oid *oid;
176 };
177 
178 /*
179  * Try searchin the key by to use by first looking for for PK-INIT
180  * EKU, then the Microsoft smart card EKU and last, no special EKU at all.
181  */
182 
183 static krb5_error_code
184 find_cert(krb5_context context, struct krb5_pk_identity *id,
185 	  hx509_query *q, hx509_cert *cert)
186 {
187     struct certfind cf[4] = {
188 	{ "MobileMe EKU" },
189 	{ "PKINIT EKU" },
190 	{ "MS EKU" },
191 	{ "any (or no)" }
192     };
193     int i, ret, start = 1;
194     unsigned oids[] = { 1, 2, 840, 113635, 100, 3, 2, 1 };
195     const heim_oid mobileMe = { sizeof(oids)/sizeof(oids[0]), oids };
196 
197 
198     if (id->flags & PKINIT_BTMM)
199 	start = 0;
200 
201     cf[0].oid = &mobileMe;
202     cf[1].oid = &asn1_oid_id_pkekuoid;
203     cf[2].oid = &asn1_oid_id_pkinit_ms_eku;
204     cf[3].oid = NULL;
205 
206     for (i = start; i < sizeof(cf)/sizeof(cf[0]); i++) {
207 	ret = hx509_query_match_eku(q, cf[i].oid);
208 	if (ret) {
209 	    pk_copy_error(context, context->hx509ctx, ret,
210 			  "Failed setting %s OID", cf[i].type);
211 	    return ret;
212 	}
213 
214 	ret = hx509_certs_find(context->hx509ctx, id->certs, q, cert);
215 	if (ret == 0)
216 	    break;
217 	pk_copy_error(context, context->hx509ctx, ret,
218 		      "Failed finding certificate with %s OID", cf[i].type);
219     }
220     return ret;
221 }
222 
223 
224 static krb5_error_code
225 create_signature(krb5_context context,
226 		 const heim_oid *eContentType,
227 		 krb5_data *eContent,
228 		 struct krb5_pk_identity *id,
229 		 hx509_peer_info peer,
230 		 krb5_data *sd_data)
231 {
232     int ret, flags = 0;
233 
234     if (id->cert == NULL)
235 	flags |= HX509_CMS_SIGNATURE_NO_SIGNER;
236 
237     ret = hx509_cms_create_signed_1(context->hx509ctx,
238 				    flags,
239 				    eContentType,
240 				    eContent->data,
241 				    eContent->length,
242 				    NULL,
243 				    id->cert,
244 				    peer,
245 				    NULL,
246 				    id->certs,
247 				    sd_data);
248     if (ret) {
249 	pk_copy_error(context, context->hx509ctx, ret,
250 		      "Create CMS signedData");
251 	return ret;
252     }
253 
254     return 0;
255 }
256 
257 static int
258 cert2epi(hx509_context context, void *ctx, hx509_cert c)
259 {
260     ExternalPrincipalIdentifiers *ids = ctx;
261     ExternalPrincipalIdentifier id;
262     hx509_name subject = NULL;
263     void *p;
264     int ret;
265 
266     if (ids->len > 10)
267 	return 0;
268 
269     memset(&id, 0, sizeof(id));
270 
271     ret = hx509_cert_get_subject(c, &subject);
272     if (ret)
273 	return ret;
274 
275     if (hx509_name_is_null_p(subject) != 0) {
276 
277 	id.subjectName = calloc(1, sizeof(*id.subjectName));
278 	if (id.subjectName == NULL) {
279 	    hx509_name_free(&subject);
280 	    free_ExternalPrincipalIdentifier(&id);
281 	    return ENOMEM;
282 	}
283 
284 	ret = hx509_name_binary(subject, id.subjectName);
285 	if (ret) {
286 	    hx509_name_free(&subject);
287 	    free_ExternalPrincipalIdentifier(&id);
288 	    return ret;
289 	}
290     }
291     hx509_name_free(&subject);
292 
293 
294     id.issuerAndSerialNumber = calloc(1, sizeof(*id.issuerAndSerialNumber));
295     if (id.issuerAndSerialNumber == NULL) {
296 	free_ExternalPrincipalIdentifier(&id);
297 	return ENOMEM;
298     }
299 
300     {
301 	IssuerAndSerialNumber iasn;
302 	hx509_name issuer;
303 	size_t size;
304 
305 	memset(&iasn, 0, sizeof(iasn));
306 
307 	ret = hx509_cert_get_issuer(c, &issuer);
308 	if (ret) {
309 	    free_ExternalPrincipalIdentifier(&id);
310 	    return ret;
311 	}
312 
313 	ret = hx509_name_to_Name(issuer, &iasn.issuer);
314 	hx509_name_free(&issuer);
315 	if (ret) {
316 	    free_ExternalPrincipalIdentifier(&id);
317 	    return ret;
318 	}
319 
320 	ret = hx509_cert_get_serialnumber(c, &iasn.serialNumber);
321 	if (ret) {
322 	    free_IssuerAndSerialNumber(&iasn);
323 	    free_ExternalPrincipalIdentifier(&id);
324 	    return ret;
325 	}
326 
327 	ASN1_MALLOC_ENCODE(IssuerAndSerialNumber,
328 			   id.issuerAndSerialNumber->data,
329 			   id.issuerAndSerialNumber->length,
330 			   &iasn, &size, ret);
331 	free_IssuerAndSerialNumber(&iasn);
332 	if (ret)
333 	    return ret;
334 	if (id.issuerAndSerialNumber->length != size)
335 	    abort();
336     }
337 
338     id.subjectKeyIdentifier = NULL;
339 
340     p = realloc(ids->val, sizeof(ids->val[0]) * (ids->len + 1));
341     if (p == NULL) {
342 	free_ExternalPrincipalIdentifier(&id);
343 	return ENOMEM;
344     }
345 
346     ids->val = p;
347     ids->val[ids->len] = id;
348     ids->len++;
349 
350     return 0;
351 }
352 
353 static krb5_error_code
354 build_edi(krb5_context context,
355 	  hx509_context hx509ctx,
356 	  hx509_certs certs,
357 	  ExternalPrincipalIdentifiers *ids)
358 {
359     return hx509_certs_iter_f(hx509ctx, certs, cert2epi, ids);
360 }
361 
362 static krb5_error_code
363 build_auth_pack(krb5_context context,
364 		unsigned nonce,
365 		krb5_pk_init_ctx ctx,
366 		const KDC_REQ_BODY *body,
367 		AuthPack *a)
368 {
369     size_t buf_size, len;
370     krb5_error_code ret;
371     void *buf;
372     krb5_timestamp sec;
373     int32_t usec;
374     Checksum checksum;
375 
376     krb5_clear_error_message(context);
377 
378     memset(&checksum, 0, sizeof(checksum));
379 
380     krb5_us_timeofday(context, &sec, &usec);
381     a->pkAuthenticator.ctime = sec;
382     a->pkAuthenticator.nonce = nonce;
383 
384     ASN1_MALLOC_ENCODE(KDC_REQ_BODY, buf, buf_size, body, &len, ret);
385     if (ret)
386 	return ret;
387     if (buf_size != len)
388 	krb5_abortx(context, "internal error in ASN.1 encoder");
389 
390     ret = krb5_create_checksum(context,
391 			       NULL,
392 			       0,
393 			       CKSUMTYPE_SHA1,
394 			       buf,
395 			       len,
396 			       &checksum);
397     free(buf);
398     if (ret)
399 	return ret;
400 
401     ALLOC(a->pkAuthenticator.paChecksum, 1);
402     if (a->pkAuthenticator.paChecksum == NULL) {
403 	krb5_set_error_message(context, ENOMEM,
404 			       N_("malloc: out of memory", ""));
405 	return ENOMEM;
406     }
407 
408     ret = krb5_data_copy(a->pkAuthenticator.paChecksum,
409 			 checksum.checksum.data, checksum.checksum.length);
410     free_Checksum(&checksum);
411     if (ret)
412 	return ret;
413 
414     if (ctx->keyex == USE_DH || ctx->keyex == USE_ECDH) {
415 	const char *moduli_file;
416 	unsigned long dh_min_bits;
417 	krb5_data dhbuf;
418 	size_t size;
419 
420 	krb5_data_zero(&dhbuf);
421 
422 
423 
424 	moduli_file = krb5_config_get_string(context, NULL,
425 					     "libdefaults",
426 					     "moduli",
427 					     NULL);
428 
429 	dh_min_bits =
430 	    krb5_config_get_int_default(context, NULL, 0,
431 					"libdefaults",
432 					"pkinit_dh_min_bits",
433 					NULL);
434 
435 	ret = _krb5_parse_moduli(context, moduli_file, &ctx->m);
436 	if (ret)
437 	    return ret;
438 
439 	ctx->u.dh = DH_new();
440 	if (ctx->u.dh == NULL) {
441 	    krb5_set_error_message(context, ENOMEM,
442 				   N_("malloc: out of memory", ""));
443 	    return ENOMEM;
444 	}
445 
446 	ret = select_dh_group(context, ctx->u.dh, dh_min_bits, ctx->m);
447 	if (ret)
448 	    return ret;
449 
450 	if (DH_generate_key(ctx->u.dh) != 1) {
451 	    krb5_set_error_message(context, ENOMEM,
452 				   N_("pkinit: failed to generate DH key", ""));
453 	    return ENOMEM;
454 	}
455 
456 
457 	if (1 /* support_cached_dh */) {
458 	    ALLOC(a->clientDHNonce, 1);
459 	    if (a->clientDHNonce == NULL) {
460 		krb5_clear_error_message(context);
461 		return ENOMEM;
462 	    }
463 	    ret = krb5_data_alloc(a->clientDHNonce, 40);
464 	    if (a->clientDHNonce == NULL) {
465 		krb5_clear_error_message(context);
466 		return ret;
467 	    }
468 	    RAND_bytes(a->clientDHNonce->data, a->clientDHNonce->length);
469 	    ret = krb5_copy_data(context, a->clientDHNonce,
470 				 &ctx->clientDHNonce);
471 	    if (ret)
472 		return ret;
473 	}
474 
475 	ALLOC(a->clientPublicValue, 1);
476 	if (a->clientPublicValue == NULL)
477 	    return ENOMEM;
478 
479 	if (ctx->keyex == USE_DH) {
480 	    DH *dh = ctx->u.dh;
481 	    DomainParameters dp;
482 	    heim_integer dh_pub_key;
483 
484 	    ret = der_copy_oid(&asn1_oid_id_dhpublicnumber,
485 			       &a->clientPublicValue->algorithm.algorithm);
486 	    if (ret)
487 		return ret;
488 
489 	    memset(&dp, 0, sizeof(dp));
490 
491 	    ret = BN_to_integer(context, dh->p, &dp.p);
492 	    if (ret) {
493 		free_DomainParameters(&dp);
494 		return ret;
495 	    }
496 	    ret = BN_to_integer(context, dh->g, &dp.g);
497 	    if (ret) {
498 		free_DomainParameters(&dp);
499 		return ret;
500 	    }
501 	    ret = BN_to_integer(context, dh->q, &dp.q);
502 	    if (ret) {
503 		free_DomainParameters(&dp);
504 		return ret;
505 	    }
506 	    dp.j = NULL;
507 	    dp.validationParms = NULL;
508 
509 	    a->clientPublicValue->algorithm.parameters =
510 		malloc(sizeof(*a->clientPublicValue->algorithm.parameters));
511 	    if (a->clientPublicValue->algorithm.parameters == NULL) {
512 		free_DomainParameters(&dp);
513 		return ret;
514 	    }
515 
516 	    ASN1_MALLOC_ENCODE(DomainParameters,
517 			       a->clientPublicValue->algorithm.parameters->data,
518 			       a->clientPublicValue->algorithm.parameters->length,
519 			       &dp, &size, ret);
520 	    free_DomainParameters(&dp);
521 	    if (ret)
522 		return ret;
523 	    if (size != a->clientPublicValue->algorithm.parameters->length)
524 		krb5_abortx(context, "Internal ASN1 encoder error");
525 
526 	    ret = BN_to_integer(context, dh->pub_key, &dh_pub_key);
527 	    if (ret)
528 		return ret;
529 
530 	    ASN1_MALLOC_ENCODE(DHPublicKey, dhbuf.data, dhbuf.length,
531 			       &dh_pub_key, &size, ret);
532 	    der_free_heim_integer(&dh_pub_key);
533 	    if (ret)
534 		return ret;
535 	    if (size != dhbuf.length)
536 		krb5_abortx(context, "asn1 internal error");
537 	} else if (ctx->keyex == USE_ECDH) {
538 #ifdef HAVE_OPENSSL
539 	    ECParameters ecp;
540 	    unsigned char *p;
541 	    int len;
542 
543 	    /* copy in public key, XXX find the best curve that the server support or use the clients curve if possible */
544 
545 	    ecp.element = choice_ECParameters_namedCurve;
546 	    ret = der_copy_oid(&asn1_oid_id_ec_group_secp256r1,
547 			       &ecp.u.namedCurve);
548 	    if (ret)
549 		return ret;
550 
551 	    ALLOC(a->clientPublicValue->algorithm.parameters, 1);
552 	    if (a->clientPublicValue->algorithm.parameters == NULL) {
553 		free_ECParameters(&ecp);
554 		return ENOMEM;
555 	    }
556 	    ASN1_MALLOC_ENCODE(ECParameters, p, len, &ecp, &size, ret);
557 	    free_ECParameters(&ecp);
558 	    if (ret)
559 		return ret;
560 	    if (size != len)
561 		krb5_abortx(context, "asn1 internal error");
562 
563 	    a->clientPublicValue->algorithm.parameters->data = p;
564 	    a->clientPublicValue->algorithm.parameters->length = size;
565 
566 	    /* copy in public key */
567 
568 	    ret = der_copy_oid(&asn1_oid_id_ecPublicKey,
569 			       &a->clientPublicValue->algorithm.algorithm);
570 	    if (ret)
571 		return ret;
572 
573 	    ctx->u.eckey = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
574 	    if (ctx->u.eckey == NULL)
575 		return ENOMEM;
576 
577 	    ret = EC_KEY_generate_key(ctx->u.eckey);
578 	    if (ret != 1)
579 		return EINVAL;
580 
581 	    /* encode onto dhkey */
582 
583 	    len = i2o_ECPublicKey(ctx->u.eckey, NULL);
584 	    if (len <= 0)
585 		abort();
586 
587 	    dhbuf.data = malloc(len);
588 	    if (dhbuf.data == NULL)
589 		abort();
590 	    dhbuf.length = len;
591 	    p = dhbuf.data;
592 
593 	    len = i2o_ECPublicKey(ctx->u.eckey, &p);
594 	    if (len <= 0)
595 		abort();
596 
597 	    /* XXX verify that this is right with RFC3279 */
598 #else
599 	    return EINVAL;
600 #endif
601 	} else
602 	    krb5_abortx(context, "internal error");
603 	a->clientPublicValue->subjectPublicKey.length = dhbuf.length * 8;
604 	a->clientPublicValue->subjectPublicKey.data = dhbuf.data;
605     }
606 
607     {
608 	a->supportedCMSTypes = calloc(1, sizeof(*a->supportedCMSTypes));
609 	if (a->supportedCMSTypes == NULL)
610 	    return ENOMEM;
611 
612 	ret = hx509_crypto_available(context->hx509ctx, HX509_SELECT_ALL, NULL,
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;
654     size_t size;
655     krb5_data buf, sd_buf;
656     int pa_type;
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 (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 	int 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 < 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;
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 	    hx509_cert_free(cert);
2624 	    goto out;
2625 	}
2626 
2627 	ret = hx509_certs_add(context->hx509ctx, *res, cert);
2628 	if (ret) {
2629 	    hx509_certs_free(res);
2630 	    goto out;
2631 	}
2632     }
2633 
2634  out:
2635     hx509_cert_free(cert);
2636 
2637     return ret;
2638 #else
2639     krb5_set_error_message(context, EINVAL,
2640 			   N_("no support for PKINIT compiled in", ""));
2641     return EINVAL;
2642 #endif
2643 }
2644