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