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