xref: /freebsd/crypto/heimdal/lib/krb5/pkinit.c (revision 884a2a699669ec61e2366e3e358342dbc94be24a)
1 /*
2  * Copyright (c) 2003 - 2007 Kungliga Tekniska H�gskolan
3  * (Royal Institute of Technology, Stockholm, Sweden).
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  *
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * 3. Neither the name of the Institute nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33 
34 #include "krb5_locl.h"
35 
36 RCSID("$Id: pkinit.c 22433 2008-01-13 14:11:46Z lha $");
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 <heim_asn1.h>
49 #include <rfc2459_asn1.h>
50 #include <cms_asn1.h>
51 #include <pkcs8_asn1.h>
52 #include <pkcs9_asn1.h>
53 #include <pkcs12_asn1.h>
54 #include <pkinit_asn1.h>
55 #include <asn1_err.h>
56 
57 #include <der.h>
58 
59 #include <hx509.h>
60 
61 enum {
62     COMPAT_WIN2K = 1,
63     COMPAT_IETF = 2
64 };
65 
66 struct krb5_pk_identity {
67     hx509_context hx509ctx;
68     hx509_verify_ctx verify_ctx;
69     hx509_certs certs;
70     hx509_certs anchors;
71     hx509_certs certpool;
72     hx509_revoke_ctx revokectx;
73 };
74 
75 struct krb5_pk_cert {
76     hx509_cert cert;
77 };
78 
79 struct krb5_pk_init_ctx_data {
80     struct krb5_pk_identity *id;
81     DH *dh;
82     krb5_data *clientDHNonce;
83     struct krb5_dh_moduli **m;
84     hx509_peer_info peer;
85     int type;
86     unsigned int require_binding:1;
87     unsigned int require_eku:1;
88     unsigned int require_krbtgt_otherName:1;
89     unsigned int require_hostname_match:1;
90     unsigned int trustedCertifiers:1;
91 };
92 
93 static void
94 _krb5_pk_copy_error(krb5_context context,
95 		    hx509_context hx509ctx,
96 		    int hxret,
97 		    const char *fmt,
98 		    ...)
99     __attribute__ ((format (printf, 4, 5)));
100 
101 /*
102  *
103  */
104 
105 void KRB5_LIB_FUNCTION
106 _krb5_pk_cert_free(struct krb5_pk_cert *cert)
107 {
108     if (cert->cert) {
109 	hx509_cert_free(cert->cert);
110     }
111     free(cert);
112 }
113 
114 static krb5_error_code
115 BN_to_integer(krb5_context context, BIGNUM *bn, heim_integer *integer)
116 {
117     integer->length = BN_num_bytes(bn);
118     integer->data = malloc(integer->length);
119     if (integer->data == NULL) {
120 	krb5_clear_error_string(context);
121 	return ENOMEM;
122     }
123     BN_bn2bin(bn, integer->data);
124     integer->negative = BN_is_negative(bn);
125     return 0;
126 }
127 
128 static BIGNUM *
129 integer_to_BN(krb5_context context, const char *field, const heim_integer *f)
130 {
131     BIGNUM *bn;
132 
133     bn = BN_bin2bn((const unsigned char *)f->data, f->length, NULL);
134     if (bn == NULL) {
135 	krb5_set_error_string(context, "PKINIT: parsing BN failed %s", field);
136 	return NULL;
137     }
138     BN_set_negative(bn, f->negative);
139     return bn;
140 }
141 
142 
143 static krb5_error_code
144 _krb5_pk_create_sign(krb5_context context,
145 		     const heim_oid *eContentType,
146 		     krb5_data *eContent,
147 		     struct krb5_pk_identity *id,
148 		     hx509_peer_info peer,
149 		     krb5_data *sd_data)
150 {
151     hx509_cert cert;
152     hx509_query *q;
153     int ret;
154 
155     ret = hx509_query_alloc(id->hx509ctx, &q);
156     if (ret) {
157 	_krb5_pk_copy_error(context, id->hx509ctx, ret,
158 			    "Allocate query to find signing certificate");
159 	return ret;
160     }
161 
162     hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY);
163     hx509_query_match_option(q, HX509_QUERY_OPTION_KU_DIGITALSIGNATURE);
164 
165     ret = hx509_certs_find(id->hx509ctx, id->certs, q, &cert);
166     hx509_query_free(id->hx509ctx, q);
167     if (ret) {
168 	_krb5_pk_copy_error(context, id->hx509ctx, ret,
169 			    "Find certificate to signed CMS data");
170 	return ret;
171     }
172 
173     ret = hx509_cms_create_signed_1(id->hx509ctx,
174 				    0,
175 				    eContentType,
176 				    eContent->data,
177 				    eContent->length,
178 				    NULL,
179 				    cert,
180 				    peer,
181 				    NULL,
182 				    id->certs,
183 				    sd_data);
184     if (ret)
185 	_krb5_pk_copy_error(context, id->hx509ctx, ret, "create CMS signedData");
186     hx509_cert_free(cert);
187 
188     return ret;
189 }
190 
191 static int
192 cert2epi(hx509_context context, void *ctx, hx509_cert c)
193 {
194     ExternalPrincipalIdentifiers *ids = ctx;
195     ExternalPrincipalIdentifier id;
196     hx509_name subject = NULL;
197     void *p;
198     int ret;
199 
200     memset(&id, 0, sizeof(id));
201 
202     ret = hx509_cert_get_subject(c, &subject);
203     if (ret)
204 	return ret;
205 
206     if (hx509_name_is_null_p(subject) != 0) {
207 
208 	id.subjectName = calloc(1, sizeof(*id.subjectName));
209 	if (id.subjectName == NULL) {
210 	    hx509_name_free(&subject);
211 	    free_ExternalPrincipalIdentifier(&id);
212 	    return ENOMEM;
213 	}
214 
215 	ret = hx509_name_binary(subject, id.subjectName);
216 	if (ret) {
217 	    hx509_name_free(&subject);
218 	    free_ExternalPrincipalIdentifier(&id);
219 	    return ret;
220 	}
221     }
222     hx509_name_free(&subject);
223 
224 
225     id.issuerAndSerialNumber = calloc(1, sizeof(*id.issuerAndSerialNumber));
226     if (id.issuerAndSerialNumber == NULL) {
227 	free_ExternalPrincipalIdentifier(&id);
228 	return ENOMEM;
229     }
230 
231     {
232 	IssuerAndSerialNumber iasn;
233 	hx509_name issuer;
234 	size_t size;
235 
236 	memset(&iasn, 0, sizeof(iasn));
237 
238 	ret = hx509_cert_get_issuer(c, &issuer);
239 	if (ret) {
240 	    free_ExternalPrincipalIdentifier(&id);
241 	    return ret;
242 	}
243 
244 	ret = hx509_name_to_Name(issuer, &iasn.issuer);
245 	hx509_name_free(&issuer);
246 	if (ret) {
247 	    free_ExternalPrincipalIdentifier(&id);
248 	    return ret;
249 	}
250 
251 	ret = hx509_cert_get_serialnumber(c, &iasn.serialNumber);
252 	if (ret) {
253 	    free_IssuerAndSerialNumber(&iasn);
254 	    free_ExternalPrincipalIdentifier(&id);
255 	    return ret;
256 	}
257 
258 	ASN1_MALLOC_ENCODE(IssuerAndSerialNumber,
259 			   id.issuerAndSerialNumber->data,
260 			   id.issuerAndSerialNumber->length,
261 			   &iasn, &size, ret);
262 	free_IssuerAndSerialNumber(&iasn);
263 	if (ret)
264 	    return ret;
265 	if (id.issuerAndSerialNumber->length != size)
266 	    abort();
267     }
268 
269     id.subjectKeyIdentifier = NULL;
270 
271     p = realloc(ids->val, sizeof(ids->val[0]) * (ids->len + 1));
272     if (p == NULL) {
273 	free_ExternalPrincipalIdentifier(&id);
274 	return ENOMEM;
275     }
276 
277     ids->val = p;
278     ids->val[ids->len] = id;
279     ids->len++;
280 
281     return 0;
282 }
283 
284 static krb5_error_code
285 build_edi(krb5_context context,
286 	  hx509_context hx509ctx,
287 	  hx509_certs certs,
288 	  ExternalPrincipalIdentifiers *ids)
289 {
290     return hx509_certs_iter(hx509ctx, certs, cert2epi, ids);
291 }
292 
293 static krb5_error_code
294 build_auth_pack(krb5_context context,
295 		unsigned nonce,
296 		krb5_pk_init_ctx ctx,
297 		DH *dh,
298 		const KDC_REQ_BODY *body,
299 		AuthPack *a)
300 {
301     size_t buf_size, len;
302     krb5_error_code ret;
303     void *buf;
304     krb5_timestamp sec;
305     int32_t usec;
306     Checksum checksum;
307 
308     krb5_clear_error_string(context);
309 
310     memset(&checksum, 0, sizeof(checksum));
311 
312     krb5_us_timeofday(context, &sec, &usec);
313     a->pkAuthenticator.ctime = sec;
314     a->pkAuthenticator.nonce = nonce;
315 
316     ASN1_MALLOC_ENCODE(KDC_REQ_BODY, buf, buf_size, body, &len, ret);
317     if (ret)
318 	return ret;
319     if (buf_size != len)
320 	krb5_abortx(context, "internal error in ASN.1 encoder");
321 
322     ret = krb5_create_checksum(context,
323 			       NULL,
324 			       0,
325 			       CKSUMTYPE_SHA1,
326 			       buf,
327 			       len,
328 			       &checksum);
329     free(buf);
330     if (ret)
331 	return ret;
332 
333     ALLOC(a->pkAuthenticator.paChecksum, 1);
334     if (a->pkAuthenticator.paChecksum == NULL) {
335 	krb5_set_error_string(context, "malloc: out of memory");
336 	return ENOMEM;
337     }
338 
339     ret = krb5_data_copy(a->pkAuthenticator.paChecksum,
340 			 checksum.checksum.data, checksum.checksum.length);
341     free_Checksum(&checksum);
342     if (ret)
343 	return ret;
344 
345     if (dh) {
346 	DomainParameters dp;
347 	heim_integer dh_pub_key;
348 	krb5_data dhbuf;
349 	size_t size;
350 
351 	if (1 /* support_cached_dh */) {
352 	    ALLOC(a->clientDHNonce, 1);
353 	    if (a->clientDHNonce == NULL) {
354 		krb5_clear_error_string(context);
355 		return ENOMEM;
356 	    }
357 	    ret = krb5_data_alloc(a->clientDHNonce, 40);
358 	    if (a->clientDHNonce == NULL) {
359 		krb5_clear_error_string(context);
360 		return ENOMEM;
361 	    }
362 	    memset(a->clientDHNonce->data, 0, a->clientDHNonce->length);
363 	    ret = krb5_copy_data(context, a->clientDHNonce,
364 				 &ctx->clientDHNonce);
365 	    if (ret)
366 		return ret;
367 	}
368 
369 	ALLOC(a->clientPublicValue, 1);
370 	if (a->clientPublicValue == NULL)
371 	    return ENOMEM;
372 	ret = der_copy_oid(oid_id_dhpublicnumber(),
373 			   &a->clientPublicValue->algorithm.algorithm);
374 	if (ret)
375 	    return ret;
376 
377 	memset(&dp, 0, sizeof(dp));
378 
379 	ret = BN_to_integer(context, dh->p, &dp.p);
380 	if (ret) {
381 	    free_DomainParameters(&dp);
382 	    return ret;
383 	}
384 	ret = BN_to_integer(context, dh->g, &dp.g);
385 	if (ret) {
386 	    free_DomainParameters(&dp);
387 	    return ret;
388 	}
389 	ret = BN_to_integer(context, dh->q, &dp.q);
390 	if (ret) {
391 	    free_DomainParameters(&dp);
392 	    return ret;
393 	}
394 	dp.j = NULL;
395 	dp.validationParms = NULL;
396 
397 	a->clientPublicValue->algorithm.parameters =
398 	    malloc(sizeof(*a->clientPublicValue->algorithm.parameters));
399 	if (a->clientPublicValue->algorithm.parameters == NULL) {
400 	    free_DomainParameters(&dp);
401 	    return ret;
402 	}
403 
404 	ASN1_MALLOC_ENCODE(DomainParameters,
405 			   a->clientPublicValue->algorithm.parameters->data,
406 			   a->clientPublicValue->algorithm.parameters->length,
407 			   &dp, &size, ret);
408 	free_DomainParameters(&dp);
409 	if (ret)
410 	    return ret;
411 	if (size != a->clientPublicValue->algorithm.parameters->length)
412 	    krb5_abortx(context, "Internal ASN1 encoder error");
413 
414 	ret = BN_to_integer(context, dh->pub_key, &dh_pub_key);
415 	if (ret)
416 	    return ret;
417 
418 	ASN1_MALLOC_ENCODE(DHPublicKey, dhbuf.data, dhbuf.length,
419 			   &dh_pub_key, &size, ret);
420 	der_free_heim_integer(&dh_pub_key);
421 	if (ret)
422 	    return ret;
423 	if (size != dhbuf.length)
424 	    krb5_abortx(context, "asn1 internal error");
425 
426 	a->clientPublicValue->subjectPublicKey.length = dhbuf.length * 8;
427 	a->clientPublicValue->subjectPublicKey.data = dhbuf.data;
428     }
429 
430     {
431 	a->supportedCMSTypes = calloc(1, sizeof(*a->supportedCMSTypes));
432 	if (a->supportedCMSTypes == NULL)
433 	    return ENOMEM;
434 
435 	ret = hx509_crypto_available(ctx->id->hx509ctx, HX509_SELECT_ALL, NULL,
436 				     &a->supportedCMSTypes->val,
437 				     &a->supportedCMSTypes->len);
438 	if (ret)
439 	    return ret;
440     }
441 
442     return ret;
443 }
444 
445 krb5_error_code KRB5_LIB_FUNCTION
446 _krb5_pk_mk_ContentInfo(krb5_context context,
447 			const krb5_data *buf,
448 			const heim_oid *oid,
449 			struct ContentInfo *content_info)
450 {
451     krb5_error_code ret;
452 
453     ret = der_copy_oid(oid, &content_info->contentType);
454     if (ret)
455 	return ret;
456     ALLOC(content_info->content, 1);
457     if (content_info->content == NULL)
458 	return ENOMEM;
459     content_info->content->data = malloc(buf->length);
460     if (content_info->content->data == NULL)
461 	return ENOMEM;
462     memcpy(content_info->content->data, buf->data, buf->length);
463     content_info->content->length = buf->length;
464     return 0;
465 }
466 
467 static krb5_error_code
468 pk_mk_padata(krb5_context context,
469 	     krb5_pk_init_ctx ctx,
470 	     const KDC_REQ_BODY *req_body,
471 	     unsigned nonce,
472 	     METHOD_DATA *md)
473 {
474     struct ContentInfo content_info;
475     krb5_error_code ret;
476     const heim_oid *oid;
477     size_t size;
478     krb5_data buf, sd_buf;
479     int pa_type;
480 
481     krb5_data_zero(&buf);
482     krb5_data_zero(&sd_buf);
483     memset(&content_info, 0, sizeof(content_info));
484 
485     if (ctx->type == COMPAT_WIN2K) {
486 	AuthPack_Win2k ap;
487 	krb5_timestamp sec;
488 	int32_t usec;
489 
490 	memset(&ap, 0, sizeof(ap));
491 
492 	/* fill in PKAuthenticator */
493 	ret = copy_PrincipalName(req_body->sname, &ap.pkAuthenticator.kdcName);
494 	if (ret) {
495 	    free_AuthPack_Win2k(&ap);
496 	    krb5_clear_error_string(context);
497 	    goto out;
498 	}
499 	ret = copy_Realm(&req_body->realm, &ap.pkAuthenticator.kdcRealm);
500 	if (ret) {
501 	    free_AuthPack_Win2k(&ap);
502 	    krb5_clear_error_string(context);
503 	    goto out;
504 	}
505 
506 	krb5_us_timeofday(context, &sec, &usec);
507 	ap.pkAuthenticator.ctime = sec;
508 	ap.pkAuthenticator.cusec = usec;
509 	ap.pkAuthenticator.nonce = nonce;
510 
511 	ASN1_MALLOC_ENCODE(AuthPack_Win2k, buf.data, buf.length,
512 			   &ap, &size, ret);
513 	free_AuthPack_Win2k(&ap);
514 	if (ret) {
515 	    krb5_set_error_string(context, "AuthPack_Win2k: %d", ret);
516 	    goto out;
517 	}
518 	if (buf.length != size)
519 	    krb5_abortx(context, "internal ASN1 encoder error");
520 
521 	oid = oid_id_pkcs7_data();
522     } else if (ctx->type == COMPAT_IETF) {
523 	AuthPack ap;
524 
525 	memset(&ap, 0, sizeof(ap));
526 
527 	ret = build_auth_pack(context, nonce, ctx, ctx->dh, req_body, &ap);
528 	if (ret) {
529 	    free_AuthPack(&ap);
530 	    goto out;
531 	}
532 
533 	ASN1_MALLOC_ENCODE(AuthPack, buf.data, buf.length, &ap, &size, ret);
534 	free_AuthPack(&ap);
535 	if (ret) {
536 	    krb5_set_error_string(context, "AuthPack: %d", ret);
537 	    goto out;
538 	}
539 	if (buf.length != size)
540 	    krb5_abortx(context, "internal ASN1 encoder error");
541 
542 	oid = oid_id_pkauthdata();
543     } else
544 	krb5_abortx(context, "internal pkinit error");
545 
546     ret = _krb5_pk_create_sign(context,
547 			       oid,
548 			       &buf,
549 			       ctx->id,
550 			       ctx->peer,
551 			       &sd_buf);
552     krb5_data_free(&buf);
553     if (ret)
554 	goto out;
555 
556     ret = hx509_cms_wrap_ContentInfo(oid_id_pkcs7_signedData(), &sd_buf, &buf);
557     krb5_data_free(&sd_buf);
558     if (ret) {
559 	krb5_set_error_string(context,
560 			      "ContentInfo wrapping of signedData failed");
561 	goto out;
562     }
563 
564     if (ctx->type == COMPAT_WIN2K) {
565 	PA_PK_AS_REQ_Win2k winreq;
566 
567 	pa_type = KRB5_PADATA_PK_AS_REQ_WIN;
568 
569 	memset(&winreq, 0, sizeof(winreq));
570 
571 	winreq.signed_auth_pack = buf;
572 
573 	ASN1_MALLOC_ENCODE(PA_PK_AS_REQ_Win2k, buf.data, buf.length,
574 			   &winreq, &size, ret);
575 	free_PA_PK_AS_REQ_Win2k(&winreq);
576 
577     } else if (ctx->type == COMPAT_IETF) {
578 	PA_PK_AS_REQ req;
579 
580 	pa_type = KRB5_PADATA_PK_AS_REQ;
581 
582 	memset(&req, 0, sizeof(req));
583 	req.signedAuthPack = buf;
584 
585 	if (ctx->trustedCertifiers) {
586 
587 	    req.trustedCertifiers = calloc(1, sizeof(*req.trustedCertifiers));
588 	    if (req.trustedCertifiers == NULL) {
589 		krb5_set_error_string(context, "malloc: out of memory");
590 		free_PA_PK_AS_REQ(&req);
591 		goto out;
592 	    }
593 	    ret = build_edi(context, ctx->id->hx509ctx,
594 			    ctx->id->anchors, req.trustedCertifiers);
595 	    if (ret) {
596 		krb5_set_error_string(context, "pk-init: failed to build trustedCertifiers");
597 		free_PA_PK_AS_REQ(&req);
598 		goto out;
599 	    }
600 	}
601 	req.kdcPkId = NULL;
602 
603 	ASN1_MALLOC_ENCODE(PA_PK_AS_REQ, buf.data, buf.length,
604 			   &req, &size, ret);
605 
606 	free_PA_PK_AS_REQ(&req);
607 
608     } else
609 	krb5_abortx(context, "internal pkinit error");
610     if (ret) {
611 	krb5_set_error_string(context, "PA-PK-AS-REQ %d", ret);
612 	goto out;
613     }
614     if (buf.length != size)
615 	krb5_abortx(context, "Internal ASN1 encoder error");
616 
617     ret = krb5_padata_add(context, md, pa_type, buf.data, buf.length);
618     if (ret)
619 	free(buf.data);
620 
621     if (ret == 0 && ctx->type == COMPAT_WIN2K)
622 	krb5_padata_add(context, md, KRB5_PADATA_PK_AS_09_BINDING, NULL, 0);
623 
624 out:
625     free_ContentInfo(&content_info);
626 
627     return ret;
628 }
629 
630 
631 krb5_error_code KRB5_LIB_FUNCTION
632 _krb5_pk_mk_padata(krb5_context context,
633 		   void *c,
634 		   const KDC_REQ_BODY *req_body,
635 		   unsigned nonce,
636 		   METHOD_DATA *md)
637 {
638     krb5_pk_init_ctx ctx = c;
639     int win2k_compat;
640 
641     win2k_compat = krb5_config_get_bool_default(context, NULL,
642 						FALSE,
643 						"realms",
644 						req_body->realm,
645 						"pkinit_win2k",
646 						NULL);
647 
648     if (win2k_compat) {
649 	ctx->require_binding =
650 	    krb5_config_get_bool_default(context, NULL,
651 					 FALSE,
652 					 "realms",
653 					 req_body->realm,
654 					 "pkinit_win2k_require_binding",
655 					 NULL);
656 	ctx->type = COMPAT_WIN2K;
657     } else
658 	ctx->type = COMPAT_IETF;
659 
660     ctx->require_eku =
661 	krb5_config_get_bool_default(context, NULL,
662 				     TRUE,
663 				     "realms",
664 				     req_body->realm,
665 				     "pkinit_require_eku",
666 				     NULL);
667     ctx->require_krbtgt_otherName =
668 	krb5_config_get_bool_default(context, NULL,
669 				     TRUE,
670 				     "realms",
671 				     req_body->realm,
672 				     "pkinit_require_krbtgt_otherName",
673 				     NULL);
674 
675     ctx->require_hostname_match =
676 	krb5_config_get_bool_default(context, NULL,
677 				     FALSE,
678 				     "realms",
679 				     req_body->realm,
680 				     "pkinit_require_hostname_match",
681 				     NULL);
682 
683     ctx->trustedCertifiers =
684 	krb5_config_get_bool_default(context, NULL,
685 				     TRUE,
686 				     "realms",
687 				     req_body->realm,
688 				     "pkinit_trustedCertifiers",
689 				     NULL);
690 
691     return pk_mk_padata(context, ctx, req_body, nonce, md);
692 }
693 
694 krb5_error_code KRB5_LIB_FUNCTION
695 _krb5_pk_verify_sign(krb5_context context,
696 		     const void *data,
697 		     size_t length,
698 		     struct krb5_pk_identity *id,
699 		     heim_oid *contentType,
700 		     krb5_data *content,
701 		     struct krb5_pk_cert **signer)
702 {
703     hx509_certs signer_certs;
704     int ret;
705 
706     *signer = NULL;
707 
708     ret = hx509_cms_verify_signed(id->hx509ctx,
709 				  id->verify_ctx,
710 				  data,
711 				  length,
712 				  NULL,
713 				  id->certpool,
714 				  contentType,
715 				  content,
716 				  &signer_certs);
717     if (ret) {
718 	_krb5_pk_copy_error(context, id->hx509ctx, ret,
719 			    "CMS verify signed failed");
720 	return ret;
721     }
722 
723     *signer = calloc(1, sizeof(**signer));
724     if (*signer == NULL) {
725 	krb5_clear_error_string(context);
726 	ret = ENOMEM;
727 	goto out;
728     }
729 
730     ret = hx509_get_one_cert(id->hx509ctx, signer_certs, &(*signer)->cert);
731     if (ret) {
732 	_krb5_pk_copy_error(context, id->hx509ctx, ret,
733 			    "Failed to get on of the signer certs");
734 	goto out;
735     }
736 
737 out:
738     hx509_certs_free(&signer_certs);
739     if (ret) {
740 	if (*signer) {
741 	    hx509_cert_free((*signer)->cert);
742 	    free(*signer);
743 	    *signer = NULL;
744 	}
745     }
746 
747     return ret;
748 }
749 
750 static krb5_error_code
751 get_reply_key_win(krb5_context context,
752 		  const krb5_data *content,
753 		  unsigned nonce,
754 		  krb5_keyblock **key)
755 {
756     ReplyKeyPack_Win2k key_pack;
757     krb5_error_code ret;
758     size_t size;
759 
760     ret = decode_ReplyKeyPack_Win2k(content->data,
761 				    content->length,
762 				    &key_pack,
763 				    &size);
764     if (ret) {
765 	krb5_set_error_string(context, "PKINIT decoding reply key failed");
766 	free_ReplyKeyPack_Win2k(&key_pack);
767 	return ret;
768     }
769 
770     if (key_pack.nonce != nonce) {
771 	krb5_set_error_string(context, "PKINIT enckey nonce is wrong");
772 	free_ReplyKeyPack_Win2k(&key_pack);
773 	return KRB5KRB_AP_ERR_MODIFIED;
774     }
775 
776     *key = malloc (sizeof (**key));
777     if (*key == NULL) {
778 	krb5_set_error_string(context, "PKINIT failed allocating reply key");
779 	free_ReplyKeyPack_Win2k(&key_pack);
780 	krb5_set_error_string(context, "malloc: out of memory");
781 	return ENOMEM;
782     }
783 
784     ret = copy_EncryptionKey(&key_pack.replyKey, *key);
785     free_ReplyKeyPack_Win2k(&key_pack);
786     if (ret) {
787 	krb5_set_error_string(context, "PKINIT failed copying reply key");
788 	free(*key);
789 	*key = NULL;
790     }
791 
792     return ret;
793 }
794 
795 static krb5_error_code
796 get_reply_key(krb5_context context,
797 	      const krb5_data *content,
798 	      const krb5_data *req_buffer,
799 	      krb5_keyblock **key)
800 {
801     ReplyKeyPack key_pack;
802     krb5_error_code ret;
803     size_t size;
804 
805     ret = decode_ReplyKeyPack(content->data,
806 			      content->length,
807 			      &key_pack,
808 			      &size);
809     if (ret) {
810 	krb5_set_error_string(context, "PKINIT decoding reply key failed");
811 	free_ReplyKeyPack(&key_pack);
812 	return ret;
813     }
814 
815     {
816 	krb5_crypto crypto;
817 
818 	/*
819 	 * XXX Verify kp.replyKey is a allowed enctype in the
820 	 * configuration file
821 	 */
822 
823 	ret = krb5_crypto_init(context, &key_pack.replyKey, 0, &crypto);
824 	if (ret) {
825 	    free_ReplyKeyPack(&key_pack);
826 	    return ret;
827 	}
828 
829 	ret = krb5_verify_checksum(context, crypto, 6,
830 				   req_buffer->data, req_buffer->length,
831 				   &key_pack.asChecksum);
832 	krb5_crypto_destroy(context, crypto);
833 	if (ret) {
834 	    free_ReplyKeyPack(&key_pack);
835 	    return ret;
836 	}
837     }
838 
839     *key = malloc (sizeof (**key));
840     if (*key == NULL) {
841 	krb5_set_error_string(context, "PKINIT failed allocating reply key");
842 	free_ReplyKeyPack(&key_pack);
843 	krb5_set_error_string(context, "malloc: out of memory");
844 	return ENOMEM;
845     }
846 
847     ret = copy_EncryptionKey(&key_pack.replyKey, *key);
848     free_ReplyKeyPack(&key_pack);
849     if (ret) {
850 	krb5_set_error_string(context, "PKINIT failed copying reply key");
851 	free(*key);
852 	*key = NULL;
853     }
854 
855     return ret;
856 }
857 
858 
859 static krb5_error_code
860 pk_verify_host(krb5_context context,
861 	       const char *realm,
862 	       const krb5_krbhst_info *hi,
863 	       struct krb5_pk_init_ctx_data *ctx,
864 	       struct krb5_pk_cert *host)
865 {
866     krb5_error_code ret = 0;
867 
868     if (ctx->require_eku) {
869 	ret = hx509_cert_check_eku(ctx->id->hx509ctx, host->cert,
870 				   oid_id_pkkdcekuoid(), 0);
871 	if (ret) {
872 	    krb5_set_error_string(context, "No PK-INIT KDC EKU in kdc certificate");
873 	    return ret;
874 	}
875     }
876     if (ctx->require_krbtgt_otherName) {
877 	hx509_octet_string_list list;
878 	int i;
879 
880 	ret = hx509_cert_find_subjectAltName_otherName(ctx->id->hx509ctx,
881 						       host->cert,
882 						       oid_id_pkinit_san(),
883 						       &list);
884 	if (ret) {
885 	    krb5_set_error_string(context, "Failed to find the PK-INIT "
886 				  "subjectAltName in the KDC certificate");
887 
888 	    return ret;
889 	}
890 
891 	for (i = 0; i < list.len; i++) {
892 	    KRB5PrincipalName r;
893 
894 	    ret = decode_KRB5PrincipalName(list.val[i].data,
895 					   list.val[i].length,
896 					   &r,
897 					   NULL);
898 	    if (ret) {
899 		krb5_set_error_string(context, "Failed to decode the PK-INIT "
900 				      "subjectAltName in the KDC certificate");
901 
902 		break;
903 	    }
904 
905 	    if (r.principalName.name_string.len != 2 ||
906 		strcmp(r.principalName.name_string.val[0], KRB5_TGS_NAME) != 0 ||
907 		strcmp(r.principalName.name_string.val[1], realm) != 0 ||
908 		strcmp(r.realm, realm) != 0)
909 	    {
910 		krb5_set_error_string(context, "KDC have wrong realm name in "
911 				      "the certificate");
912 		ret = KRB5_KDC_ERR_INVALID_CERTIFICATE;
913 	    }
914 
915 	    free_KRB5PrincipalName(&r);
916 	    if (ret)
917 		break;
918 	}
919 	hx509_free_octet_string_list(&list);
920     }
921     if (ret)
922 	return ret;
923 
924     if (hi) {
925 	ret = hx509_verify_hostname(ctx->id->hx509ctx, host->cert,
926 				    ctx->require_hostname_match,
927 				    HX509_HN_HOSTNAME,
928 				    hi->hostname,
929 				    hi->ai->ai_addr, hi->ai->ai_addrlen);
930 
931 	if (ret)
932 	    krb5_set_error_string(context, "Address mismatch in "
933 				  "the KDC certificate");
934     }
935     return ret;
936 }
937 
938 static krb5_error_code
939 pk_rd_pa_reply_enckey(krb5_context context,
940 		      int type,
941 		      const heim_octet_string *indata,
942 		      const heim_oid *dataType,
943 		      const char *realm,
944 		      krb5_pk_init_ctx ctx,
945 		      krb5_enctype etype,
946 		      const krb5_krbhst_info *hi,
947 	       	      unsigned nonce,
948 		      const krb5_data *req_buffer,
949 	       	      PA_DATA *pa,
950 	       	      krb5_keyblock **key)
951 {
952     krb5_error_code ret;
953     struct krb5_pk_cert *host = NULL;
954     krb5_data content;
955     heim_oid contentType = { 0, NULL };
956 
957     if (der_heim_oid_cmp(oid_id_pkcs7_envelopedData(), dataType)) {
958 	krb5_set_error_string(context, "PKINIT: Invalid content type");
959 	return EINVAL;
960     }
961 
962     ret = hx509_cms_unenvelope(ctx->id->hx509ctx,
963 			       ctx->id->certs,
964 			       HX509_CMS_UE_DONT_REQUIRE_KU_ENCIPHERMENT,
965 			       indata->data,
966 			       indata->length,
967 			       NULL,
968 			       &contentType,
969 			       &content);
970     if (ret) {
971 	_krb5_pk_copy_error(context, ctx->id->hx509ctx, ret,
972 			    "Failed to unenvelope CMS data in PK-INIT reply");
973 	return ret;
974     }
975     der_free_oid(&contentType);
976 
977 #if 0 /* windows LH with interesting CMS packets, leaks memory */
978     {
979 	size_t ph = 1 + der_length_len (length);
980 	unsigned char *ptr = malloc(length + ph);
981 	size_t l;
982 
983 	memcpy(ptr + ph, p, length);
984 
985 	ret = der_put_length_and_tag (ptr + ph - 1, ph, length,
986 				      ASN1_C_UNIV, CONS, UT_Sequence, &l);
987 	if (ret)
988 	    return ret;
989 	ptr += ph - l;
990 	length += l;
991 	p = ptr;
992     }
993 #endif
994 
995     /* win2k uses ContentInfo */
996     if (type == COMPAT_WIN2K) {
997 	heim_oid type;
998 	heim_octet_string out;
999 
1000 	ret = hx509_cms_unwrap_ContentInfo(&content, &type, &out, NULL);
1001 	if (der_heim_oid_cmp(&type, oid_id_pkcs7_signedData())) {
1002 	    ret = EINVAL; /* XXX */
1003 	    krb5_set_error_string(context, "PKINIT: Invalid content type");
1004 	    der_free_oid(&type);
1005 	    der_free_octet_string(&out);
1006 	    goto out;
1007 	}
1008 	der_free_oid(&type);
1009 	krb5_data_free(&content);
1010 	ret = krb5_data_copy(&content, out.data, out.length);
1011 	der_free_octet_string(&out);
1012 	if (ret) {
1013 	    krb5_set_error_string(context, "PKINIT: out of memory");
1014 	    goto out;
1015 	}
1016     }
1017 
1018     ret = _krb5_pk_verify_sign(context,
1019 			       content.data,
1020 			       content.length,
1021 			       ctx->id,
1022 			       &contentType,
1023 			       &content,
1024 			       &host);
1025     if (ret)
1026 	goto out;
1027 
1028     /* make sure that it is the kdc's certificate */
1029     ret = pk_verify_host(context, realm, hi, ctx, host);
1030     if (ret) {
1031 	goto out;
1032     }
1033 
1034 #if 0
1035     if (type == COMPAT_WIN2K) {
1036 	if (der_heim_oid_cmp(&contentType, oid_id_pkcs7_data()) != 0) {
1037 	    krb5_set_error_string(context, "PKINIT: reply key, wrong oid");
1038 	    ret = KRB5KRB_AP_ERR_MSG_TYPE;
1039 	    goto out;
1040 	}
1041     } else {
1042 	if (der_heim_oid_cmp(&contentType, oid_id_pkrkeydata()) != 0) {
1043 	    krb5_set_error_string(context, "PKINIT: reply key, wrong oid");
1044 	    ret = KRB5KRB_AP_ERR_MSG_TYPE;
1045 	    goto out;
1046 	}
1047     }
1048 #endif
1049 
1050     switch(type) {
1051     case COMPAT_WIN2K:
1052 	ret = get_reply_key(context, &content, req_buffer, key);
1053 	if (ret != 0 && ctx->require_binding == 0)
1054 	    ret = get_reply_key_win(context, &content, nonce, key);
1055 	break;
1056     case COMPAT_IETF:
1057 	ret = get_reply_key(context, &content, req_buffer, key);
1058 	break;
1059     }
1060     if (ret)
1061 	goto out;
1062 
1063     /* XXX compare given etype with key->etype */
1064 
1065  out:
1066     if (host)
1067 	_krb5_pk_cert_free(host);
1068     der_free_oid(&contentType);
1069     krb5_data_free(&content);
1070 
1071     return ret;
1072 }
1073 
1074 static krb5_error_code
1075 pk_rd_pa_reply_dh(krb5_context context,
1076 		  const heim_octet_string *indata,
1077 		  const heim_oid *dataType,
1078 		  const char *realm,
1079 		  krb5_pk_init_ctx ctx,
1080 		  krb5_enctype etype,
1081 		  const krb5_krbhst_info *hi,
1082 		  const DHNonce *c_n,
1083 		  const DHNonce *k_n,
1084                   unsigned nonce,
1085                   PA_DATA *pa,
1086                   krb5_keyblock **key)
1087 {
1088     unsigned char *p, *dh_gen_key = NULL;
1089     struct krb5_pk_cert *host = NULL;
1090     BIGNUM *kdc_dh_pubkey = NULL;
1091     KDCDHKeyInfo kdc_dh_info;
1092     heim_oid contentType = { 0, NULL };
1093     krb5_data content;
1094     krb5_error_code ret;
1095     int dh_gen_keylen;
1096     size_t size;
1097 
1098     krb5_data_zero(&content);
1099     memset(&kdc_dh_info, 0, sizeof(kdc_dh_info));
1100 
1101     if (der_heim_oid_cmp(oid_id_pkcs7_signedData(), dataType)) {
1102 	krb5_set_error_string(context, "PKINIT: Invalid content type");
1103 	return EINVAL;
1104     }
1105 
1106     ret = _krb5_pk_verify_sign(context,
1107 			       indata->data,
1108 			       indata->length,
1109 			       ctx->id,
1110 			       &contentType,
1111 			       &content,
1112 			       &host);
1113     if (ret)
1114 	goto out;
1115 
1116     /* make sure that it is the kdc's certificate */
1117     ret = pk_verify_host(context, realm, hi, ctx, host);
1118     if (ret)
1119 	goto out;
1120 
1121     if (der_heim_oid_cmp(&contentType, oid_id_pkdhkeydata())) {
1122 	krb5_set_error_string(context, "pkinit - dh reply contains wrong oid");
1123 	ret = KRB5KRB_AP_ERR_MSG_TYPE;
1124 	goto out;
1125     }
1126 
1127     ret = decode_KDCDHKeyInfo(content.data,
1128 			      content.length,
1129 			      &kdc_dh_info,
1130 			      &size);
1131 
1132     if (ret) {
1133 	krb5_set_error_string(context, "pkinit - "
1134 			      "failed to decode KDC DH Key Info");
1135 	goto out;
1136     }
1137 
1138     if (kdc_dh_info.nonce != nonce) {
1139 	krb5_set_error_string(context, "PKINIT: DH nonce is wrong");
1140 	ret = KRB5KRB_AP_ERR_MODIFIED;
1141 	goto out;
1142     }
1143 
1144     if (kdc_dh_info.dhKeyExpiration) {
1145 	if (k_n == NULL) {
1146 	    krb5_set_error_string(context, "pkinit; got key expiration "
1147 				  "without server nonce");
1148 	    ret = KRB5KRB_ERR_GENERIC;
1149 	    goto out;
1150 	}
1151 	if (c_n == NULL) {
1152 	    krb5_set_error_string(context, "pkinit; got DH reuse but no "
1153 				  "client nonce");
1154 	    ret = KRB5KRB_ERR_GENERIC;
1155 	    goto out;
1156 	}
1157     } else {
1158 	if (k_n) {
1159 	    krb5_set_error_string(context, "pkinit: got server nonce "
1160 				  "without key expiration");
1161 	    ret = KRB5KRB_ERR_GENERIC;
1162 	    goto out;
1163 	}
1164 	c_n = NULL;
1165     }
1166 
1167 
1168     p = kdc_dh_info.subjectPublicKey.data;
1169     size = (kdc_dh_info.subjectPublicKey.length + 7) / 8;
1170 
1171     {
1172 	DHPublicKey k;
1173 	ret = decode_DHPublicKey(p, size, &k, NULL);
1174 	if (ret) {
1175 	    krb5_set_error_string(context, "pkinit: can't decode "
1176 				  "without key expiration");
1177 	    goto out;
1178 	}
1179 
1180 	kdc_dh_pubkey = integer_to_BN(context, "DHPublicKey", &k);
1181 	free_DHPublicKey(&k);
1182 	if (kdc_dh_pubkey == NULL) {
1183 	    ret = KRB5KRB_ERR_GENERIC;
1184 	    goto out;
1185 	}
1186     }
1187 
1188     dh_gen_keylen = DH_size(ctx->dh);
1189     size = BN_num_bytes(ctx->dh->p);
1190     if (size < dh_gen_keylen)
1191 	size = dh_gen_keylen;
1192 
1193     dh_gen_key = malloc(size);
1194     if (dh_gen_key == NULL) {
1195 	krb5_set_error_string(context, "malloc: out of memory");
1196 	ret = ENOMEM;
1197 	goto out;
1198     }
1199     memset(dh_gen_key, 0, size - dh_gen_keylen);
1200 
1201     dh_gen_keylen = DH_compute_key(dh_gen_key + (size - dh_gen_keylen),
1202 				   kdc_dh_pubkey, ctx->dh);
1203     if (dh_gen_keylen == -1) {
1204 	krb5_set_error_string(context,
1205 			      "PKINIT: Can't compute Diffie-Hellman key");
1206 	ret = KRB5KRB_ERR_GENERIC;
1207 	goto out;
1208     }
1209 
1210     *key = malloc (sizeof (**key));
1211     if (*key == NULL) {
1212 	krb5_set_error_string(context, "malloc: out of memory");
1213 	ret = ENOMEM;
1214 	goto out;
1215     }
1216 
1217     ret = _krb5_pk_octetstring2key(context,
1218 				   etype,
1219 				   dh_gen_key, dh_gen_keylen,
1220 				   c_n, k_n,
1221 				   *key);
1222     if (ret) {
1223 	krb5_set_error_string(context,
1224 			      "PKINIT: can't create key from DH key");
1225 	free(*key);
1226 	*key = NULL;
1227 	goto out;
1228     }
1229 
1230  out:
1231     if (kdc_dh_pubkey)
1232 	BN_free(kdc_dh_pubkey);
1233     if (dh_gen_key) {
1234 	memset(dh_gen_key, 0, DH_size(ctx->dh));
1235 	free(dh_gen_key);
1236     }
1237     if (host)
1238 	_krb5_pk_cert_free(host);
1239     if (content.data)
1240 	krb5_data_free(&content);
1241     der_free_oid(&contentType);
1242     free_KDCDHKeyInfo(&kdc_dh_info);
1243 
1244     return ret;
1245 }
1246 
1247 krb5_error_code KRB5_LIB_FUNCTION
1248 _krb5_pk_rd_pa_reply(krb5_context context,
1249 		     const char *realm,
1250 		     void *c,
1251 		     krb5_enctype etype,
1252 		     const krb5_krbhst_info *hi,
1253 		     unsigned nonce,
1254 		     const krb5_data *req_buffer,
1255 		     PA_DATA *pa,
1256 		     krb5_keyblock **key)
1257 {
1258     krb5_pk_init_ctx ctx = c;
1259     krb5_error_code ret;
1260     size_t size;
1261 
1262     /* Check for IETF PK-INIT first */
1263     if (ctx->type == COMPAT_IETF) {
1264 	PA_PK_AS_REP rep;
1265 	heim_octet_string os, data;
1266 	heim_oid oid;
1267 
1268 	if (pa->padata_type != KRB5_PADATA_PK_AS_REP) {
1269 	    krb5_set_error_string(context, "PKINIT: wrong padata recv");
1270 	    return EINVAL;
1271 	}
1272 
1273 	ret = decode_PA_PK_AS_REP(pa->padata_value.data,
1274 				  pa->padata_value.length,
1275 				  &rep,
1276 				  &size);
1277 	if (ret) {
1278 	    krb5_set_error_string(context, "Failed to decode pkinit AS rep");
1279 	    return ret;
1280 	}
1281 
1282 	switch (rep.element) {
1283 	case choice_PA_PK_AS_REP_dhInfo:
1284 	    os = rep.u.dhInfo.dhSignedData;
1285 	    break;
1286 	case choice_PA_PK_AS_REP_encKeyPack:
1287 	    os = rep.u.encKeyPack;
1288 	    break;
1289 	default:
1290 	    free_PA_PK_AS_REP(&rep);
1291 	    krb5_set_error_string(context, "PKINIT: -27 reply "
1292 				  "invalid content type");
1293 	    return EINVAL;
1294 	}
1295 
1296 	ret = hx509_cms_unwrap_ContentInfo(&os, &oid, &data, NULL);
1297 	if (ret) {
1298 	    free_PA_PK_AS_REP(&rep);
1299 	    krb5_set_error_string(context, "PKINIT: failed to unwrap CI");
1300 	    return ret;
1301 	}
1302 
1303 	switch (rep.element) {
1304 	case choice_PA_PK_AS_REP_dhInfo:
1305 	    ret = pk_rd_pa_reply_dh(context, &data, &oid, realm, ctx, etype, hi,
1306 				    ctx->clientDHNonce,
1307 				    rep.u.dhInfo.serverDHNonce,
1308 				    nonce, pa, key);
1309 	    break;
1310 	case choice_PA_PK_AS_REP_encKeyPack:
1311 	    ret = pk_rd_pa_reply_enckey(context, COMPAT_IETF, &data, &oid, realm,
1312 					ctx, etype, hi, nonce, req_buffer, pa, key);
1313 	    break;
1314 	default:
1315 	    krb5_abortx(context, "pk-init as-rep case not possible to happen");
1316 	}
1317 	der_free_octet_string(&data);
1318 	der_free_oid(&oid);
1319 	free_PA_PK_AS_REP(&rep);
1320 
1321     } else if (ctx->type == COMPAT_WIN2K) {
1322 	PA_PK_AS_REP_Win2k w2krep;
1323 
1324 	/* Check for Windows encoding of the AS-REP pa data */
1325 
1326 #if 0 /* should this be ? */
1327 	if (pa->padata_type != KRB5_PADATA_PK_AS_REP) {
1328 	    krb5_set_error_string(context, "PKINIT: wrong padata recv");
1329 	    return EINVAL;
1330 	}
1331 #endif
1332 
1333 	memset(&w2krep, 0, sizeof(w2krep));
1334 
1335 	ret = decode_PA_PK_AS_REP_Win2k(pa->padata_value.data,
1336 					pa->padata_value.length,
1337 					&w2krep,
1338 					&size);
1339 	if (ret) {
1340 	    krb5_set_error_string(context, "PKINIT: Failed decoding windows "
1341 				  "pkinit reply %d", ret);
1342 	    return ret;
1343 	}
1344 
1345 	krb5_clear_error_string(context);
1346 
1347 	switch (w2krep.element) {
1348 	case choice_PA_PK_AS_REP_Win2k_encKeyPack: {
1349 	    heim_octet_string data;
1350 	    heim_oid oid;
1351 
1352 	    ret = hx509_cms_unwrap_ContentInfo(&w2krep.u.encKeyPack,
1353 					       &oid, &data, NULL);
1354 	    free_PA_PK_AS_REP_Win2k(&w2krep);
1355 	    if (ret) {
1356 		krb5_set_error_string(context, "PKINIT: failed to unwrap CI");
1357 		return ret;
1358 	    }
1359 
1360 	    ret = pk_rd_pa_reply_enckey(context, COMPAT_WIN2K, &data, &oid, realm,
1361 					ctx, etype, hi, nonce, req_buffer, pa, key);
1362 	    der_free_octet_string(&data);
1363 	    der_free_oid(&oid);
1364 
1365 	    break;
1366 	}
1367 	default:
1368 	    free_PA_PK_AS_REP_Win2k(&w2krep);
1369 	    krb5_set_error_string(context, "PKINIT: win2k reply invalid "
1370 				  "content type");
1371 	    ret = EINVAL;
1372 	    break;
1373 	}
1374 
1375     } else {
1376 	krb5_set_error_string(context, "PKINIT: unknown reply type");
1377 	ret = EINVAL;
1378     }
1379 
1380     return ret;
1381 }
1382 
1383 struct prompter {
1384     krb5_context context;
1385     krb5_prompter_fct prompter;
1386     void *prompter_data;
1387 };
1388 
1389 static int
1390 hx_pass_prompter(void *data, const hx509_prompt *prompter)
1391 {
1392     krb5_error_code ret;
1393     krb5_prompt prompt;
1394     krb5_data password_data;
1395     struct prompter *p = data;
1396 
1397     password_data.data   = prompter->reply.data;
1398     password_data.length = prompter->reply.length;
1399 
1400     prompt.prompt = prompter->prompt;
1401     prompt.hidden = hx509_prompt_hidden(prompter->type);
1402     prompt.reply  = &password_data;
1403 
1404     switch (prompter->type) {
1405     case HX509_PROMPT_TYPE_INFO:
1406 	prompt.type   = KRB5_PROMPT_TYPE_INFO;
1407 	break;
1408     case HX509_PROMPT_TYPE_PASSWORD:
1409     case HX509_PROMPT_TYPE_QUESTION:
1410     default:
1411 	prompt.type   = KRB5_PROMPT_TYPE_PASSWORD;
1412 	break;
1413     }
1414 
1415     ret = (*p->prompter)(p->context, p->prompter_data, NULL, NULL, 1, &prompt);
1416     if (ret) {
1417 	memset (prompter->reply.data, 0, prompter->reply.length);
1418 	return 1;
1419     }
1420     return 0;
1421 }
1422 
1423 
1424 void KRB5_LIB_FUNCTION
1425 _krb5_pk_allow_proxy_certificate(struct krb5_pk_identity *id,
1426 				 int boolean)
1427 {
1428     hx509_verify_set_proxy_certificate(id->verify_ctx, boolean);
1429 }
1430 
1431 
1432 krb5_error_code KRB5_LIB_FUNCTION
1433 _krb5_pk_load_id(krb5_context context,
1434 		 struct krb5_pk_identity **ret_id,
1435 		 const char *user_id,
1436 		 const char *anchor_id,
1437 		 char * const *chain_list,
1438 		 char * const *revoke_list,
1439 		 krb5_prompter_fct prompter,
1440 		 void *prompter_data,
1441 		 char *password)
1442 {
1443     struct krb5_pk_identity *id = NULL;
1444     hx509_lock lock = NULL;
1445     struct prompter p;
1446     int ret;
1447 
1448     *ret_id = NULL;
1449 
1450     if (anchor_id == NULL) {
1451 	krb5_set_error_string(context, "PKINIT: No anchor given");
1452 	return HEIM_PKINIT_NO_VALID_CA;
1453     }
1454 
1455     if (user_id == NULL) {
1456 	krb5_set_error_string(context,
1457 			      "PKINIT: No user certificate given");
1458 	return HEIM_PKINIT_NO_PRIVATE_KEY;
1459     }
1460 
1461     /* load cert */
1462 
1463     id = calloc(1, sizeof(*id));
1464     if (id == NULL) {
1465 	krb5_set_error_string(context, "malloc: out of memory");
1466 	return ENOMEM;
1467     }
1468 
1469     ret = hx509_context_init(&id->hx509ctx);
1470     if (ret)
1471 	goto out;
1472 
1473     ret = hx509_lock_init(id->hx509ctx, &lock);
1474     if (password && password[0])
1475 	hx509_lock_add_password(lock, password);
1476 
1477     if (prompter) {
1478 	p.context = context;
1479 	p.prompter = prompter;
1480 	p.prompter_data = prompter_data;
1481 
1482 	ret = hx509_lock_set_prompter(lock, hx_pass_prompter, &p);
1483 	if (ret)
1484 	    goto out;
1485     }
1486 
1487     ret = hx509_certs_init(id->hx509ctx, user_id, 0, lock, &id->certs);
1488     if (ret) {
1489 	_krb5_pk_copy_error(context, id->hx509ctx, ret,
1490 			    "Failed to init cert certs");
1491 	goto out;
1492     }
1493 
1494     ret = hx509_certs_init(id->hx509ctx, anchor_id, 0, NULL, &id->anchors);
1495     if (ret) {
1496 	_krb5_pk_copy_error(context, id->hx509ctx, ret,
1497 			    "Failed to init anchors");
1498 	goto out;
1499     }
1500 
1501     ret = hx509_certs_init(id->hx509ctx, "MEMORY:pkinit-cert-chain",
1502 			   0, NULL, &id->certpool);
1503     if (ret) {
1504 	_krb5_pk_copy_error(context, id->hx509ctx, ret,
1505 			    "Failed to init chain");
1506 	goto out;
1507     }
1508 
1509     while (chain_list && *chain_list) {
1510 	ret = hx509_certs_append(id->hx509ctx, id->certpool,
1511 				 NULL, *chain_list);
1512 	if (ret) {
1513 	    _krb5_pk_copy_error(context, id->hx509ctx, ret,
1514 				"Failed to laod chain %s",
1515 				*chain_list);
1516 	    goto out;
1517 	}
1518 	chain_list++;
1519     }
1520 
1521     if (revoke_list) {
1522 	ret = hx509_revoke_init(id->hx509ctx, &id->revokectx);
1523 	if (ret) {
1524 	    _krb5_pk_copy_error(context, id->hx509ctx, ret,
1525 				"Failed init revoke list");
1526 	    goto out;
1527 	}
1528 
1529 	while (*revoke_list) {
1530 	    ret = hx509_revoke_add_crl(id->hx509ctx,
1531 				       id->revokectx,
1532 				       *revoke_list);
1533 	    if (ret) {
1534 		_krb5_pk_copy_error(context, id->hx509ctx, ret,
1535 				    "Failed load revoke list");
1536 		goto out;
1537 	    }
1538 	    revoke_list++;
1539 	}
1540     } else
1541 	hx509_context_set_missing_revoke(id->hx509ctx, 1);
1542 
1543     ret = hx509_verify_init_ctx(id->hx509ctx, &id->verify_ctx);
1544     if (ret) {
1545 	_krb5_pk_copy_error(context, id->hx509ctx, ret,
1546 			    "Failed init verify context");
1547 	goto out;
1548     }
1549 
1550     hx509_verify_attach_anchors(id->verify_ctx, id->anchors);
1551     hx509_verify_attach_revoke(id->verify_ctx, id->revokectx);
1552 
1553 out:
1554     if (ret) {
1555 	hx509_verify_destroy_ctx(id->verify_ctx);
1556 	hx509_certs_free(&id->certs);
1557 	hx509_certs_free(&id->anchors);
1558 	hx509_certs_free(&id->certpool);
1559 	hx509_revoke_free(&id->revokectx);
1560 	hx509_context_free(&id->hx509ctx);
1561 	free(id);
1562     } else
1563 	*ret_id = id;
1564 
1565     hx509_lock_free(lock);
1566 
1567     return ret;
1568 }
1569 
1570 static krb5_error_code
1571 select_dh_group(krb5_context context, DH *dh, unsigned long bits,
1572 		struct krb5_dh_moduli **moduli)
1573 {
1574     const struct krb5_dh_moduli *m;
1575 
1576     if (bits == 0) {
1577 	m = moduli[1]; /* XXX */
1578 	if (m == NULL)
1579 	    m = moduli[0]; /* XXX */
1580     } else {
1581 	int i;
1582 	for (i = 0; moduli[i] != NULL; i++) {
1583 	    if (bits < moduli[i]->bits)
1584 		break;
1585 	}
1586 	if (moduli[i] == NULL) {
1587 	    krb5_set_error_string(context,
1588 				  "Did not find a DH group parameter "
1589 				  "matching requirement of %lu bits",
1590 				  bits);
1591 	    return EINVAL;
1592 	}
1593 	m = moduli[i];
1594     }
1595 
1596     dh->p = integer_to_BN(context, "p", &m->p);
1597     if (dh->p == NULL)
1598 	return ENOMEM;
1599     dh->g = integer_to_BN(context, "g", &m->g);
1600     if (dh->g == NULL)
1601 	return ENOMEM;
1602     dh->q = integer_to_BN(context, "q", &m->q);
1603     if (dh->q == NULL)
1604 	return ENOMEM;
1605 
1606     return 0;
1607 }
1608 
1609 #endif /* PKINIT */
1610 
1611 static int
1612 parse_integer(krb5_context context, char **p, const char *file, int lineno,
1613 	      const char *name, heim_integer *integer)
1614 {
1615     int ret;
1616     char *p1;
1617     p1 = strsep(p, " \t");
1618     if (p1 == NULL) {
1619 	krb5_set_error_string(context, "moduli file %s missing %s on line %d",
1620 			      file, name, lineno);
1621 	return EINVAL;
1622     }
1623     ret = der_parse_hex_heim_integer(p1, integer);
1624     if (ret) {
1625 	krb5_set_error_string(context, "moduli file %s failed parsing %s "
1626 			      "on line %d",
1627 			      file, name, lineno);
1628 	return ret;
1629     }
1630 
1631     return 0;
1632 }
1633 
1634 krb5_error_code
1635 _krb5_parse_moduli_line(krb5_context context,
1636 			const char *file,
1637 			int lineno,
1638 			char *p,
1639 			struct krb5_dh_moduli **m)
1640 {
1641     struct krb5_dh_moduli *m1;
1642     char *p1;
1643     int ret;
1644 
1645     *m = NULL;
1646 
1647     m1 = calloc(1, sizeof(*m1));
1648     if (m1 == NULL) {
1649 	krb5_set_error_string(context, "malloc - out of memory");
1650 	return ENOMEM;
1651     }
1652 
1653     while (isspace((unsigned char)*p))
1654 	p++;
1655     if (*p  == '#')
1656 	return 0;
1657     ret = EINVAL;
1658 
1659     p1 = strsep(&p, " \t");
1660     if (p1 == NULL) {
1661 	krb5_set_error_string(context, "moduli file %s missing name "
1662 			      "on line %d", file, lineno);
1663 	goto out;
1664     }
1665     m1->name = strdup(p1);
1666     if (p1 == NULL) {
1667 	krb5_set_error_string(context, "malloc - out of memeory");
1668 	ret = ENOMEM;
1669 	goto out;
1670     }
1671 
1672     p1 = strsep(&p, " \t");
1673     if (p1 == NULL) {
1674 	krb5_set_error_string(context, "moduli file %s missing bits on line %d",
1675 			      file, lineno);
1676 	goto out;
1677     }
1678 
1679     m1->bits = atoi(p1);
1680     if (m1->bits == 0) {
1681 	krb5_set_error_string(context, "moduli file %s have un-parsable "
1682 			      "bits on line %d", file, lineno);
1683 	goto out;
1684     }
1685 
1686     ret = parse_integer(context, &p, file, lineno, "p", &m1->p);
1687     if (ret)
1688 	goto out;
1689     ret = parse_integer(context, &p, file, lineno, "g", &m1->g);
1690     if (ret)
1691 	goto out;
1692     ret = parse_integer(context, &p, file, lineno, "q", &m1->q);
1693     if (ret)
1694 	goto out;
1695 
1696     *m = m1;
1697 
1698     return 0;
1699 out:
1700     free(m1->name);
1701     der_free_heim_integer(&m1->p);
1702     der_free_heim_integer(&m1->g);
1703     der_free_heim_integer(&m1->q);
1704     free(m1);
1705     return ret;
1706 }
1707 
1708 void
1709 _krb5_free_moduli(struct krb5_dh_moduli **moduli)
1710 {
1711     int i;
1712     for (i = 0; moduli[i] != NULL; i++) {
1713 	free(moduli[i]->name);
1714 	der_free_heim_integer(&moduli[i]->p);
1715 	der_free_heim_integer(&moduli[i]->g);
1716 	der_free_heim_integer(&moduli[i]->q);
1717 	free(moduli[i]);
1718     }
1719     free(moduli);
1720 }
1721 
1722 static const char *default_moduli_RFC2412_MODP_group2 =
1723     /* name */
1724     "RFC2412-MODP-group2 "
1725     /* bits */
1726     "1024 "
1727     /* p */
1728     "FFFFFFFF" "FFFFFFFF" "C90FDAA2" "2168C234" "C4C6628B" "80DC1CD1"
1729     "29024E08" "8A67CC74" "020BBEA6" "3B139B22" "514A0879" "8E3404DD"
1730     "EF9519B3" "CD3A431B" "302B0A6D" "F25F1437" "4FE1356D" "6D51C245"
1731     "E485B576" "625E7EC6" "F44C42E9" "A637ED6B" "0BFF5CB6" "F406B7ED"
1732     "EE386BFB" "5A899FA5" "AE9F2411" "7C4B1FE6" "49286651" "ECE65381"
1733     "FFFFFFFF" "FFFFFFFF "
1734     /* g */
1735     "02 "
1736     /* q */
1737     "7FFFFFFF" "FFFFFFFF" "E487ED51" "10B4611A" "62633145" "C06E0E68"
1738     "94812704" "4533E63A" "0105DF53" "1D89CD91" "28A5043C" "C71A026E"
1739     "F7CA8CD9" "E69D218D" "98158536" "F92F8A1B" "A7F09AB6" "B6A8E122"
1740     "F242DABB" "312F3F63" "7A262174" "D31BF6B5" "85FFAE5B" "7A035BF6"
1741     "F71C35FD" "AD44CFD2" "D74F9208" "BE258FF3" "24943328" "F67329C0"
1742     "FFFFFFFF" "FFFFFFFF";
1743 
1744 static const char *default_moduli_rfc3526_MODP_group14 =
1745     /* name */
1746     "rfc3526-MODP-group14 "
1747     /* bits */
1748     "1760 "
1749     /* p */
1750     "FFFFFFFF" "FFFFFFFF" "C90FDAA2" "2168C234" "C4C6628B" "80DC1CD1"
1751     "29024E08" "8A67CC74" "020BBEA6" "3B139B22" "514A0879" "8E3404DD"
1752     "EF9519B3" "CD3A431B" "302B0A6D" "F25F1437" "4FE1356D" "6D51C245"
1753     "E485B576" "625E7EC6" "F44C42E9" "A637ED6B" "0BFF5CB6" "F406B7ED"
1754     "EE386BFB" "5A899FA5" "AE9F2411" "7C4B1FE6" "49286651" "ECE45B3D"
1755     "C2007CB8" "A163BF05" "98DA4836" "1C55D39A" "69163FA8" "FD24CF5F"
1756     "83655D23" "DCA3AD96" "1C62F356" "208552BB" "9ED52907" "7096966D"
1757     "670C354E" "4ABC9804" "F1746C08" "CA18217C" "32905E46" "2E36CE3B"
1758     "E39E772C" "180E8603" "9B2783A2" "EC07A28F" "B5C55DF0" "6F4C52C9"
1759     "DE2BCBF6" "95581718" "3995497C" "EA956AE5" "15D22618" "98FA0510"
1760     "15728E5A" "8AACAA68" "FFFFFFFF" "FFFFFFFF "
1761     /* g */
1762     "02 "
1763     /* q */
1764     "7FFFFFFF" "FFFFFFFF" "E487ED51" "10B4611A" "62633145" "C06E0E68"
1765     "94812704" "4533E63A" "0105DF53" "1D89CD91" "28A5043C" "C71A026E"
1766     "F7CA8CD9" "E69D218D" "98158536" "F92F8A1B" "A7F09AB6" "B6A8E122"
1767     "F242DABB" "312F3F63" "7A262174" "D31BF6B5" "85FFAE5B" "7A035BF6"
1768     "F71C35FD" "AD44CFD2" "D74F9208" "BE258FF3" "24943328" "F6722D9E"
1769     "E1003E5C" "50B1DF82" "CC6D241B" "0E2AE9CD" "348B1FD4" "7E9267AF"
1770     "C1B2AE91" "EE51D6CB" "0E3179AB" "1042A95D" "CF6A9483" "B84B4B36"
1771     "B3861AA7" "255E4C02" "78BA3604" "650C10BE" "19482F23" "171B671D"
1772     "F1CF3B96" "0C074301" "CD93C1D1" "7603D147" "DAE2AEF8" "37A62964"
1773     "EF15E5FB" "4AAC0B8C" "1CCAA4BE" "754AB572" "8AE9130C" "4C7D0288"
1774     "0AB9472D" "45565534" "7FFFFFFF" "FFFFFFFF";
1775 
1776 krb5_error_code
1777 _krb5_parse_moduli(krb5_context context, const char *file,
1778 		   struct krb5_dh_moduli ***moduli)
1779 {
1780     /* name bits P G Q */
1781     krb5_error_code ret;
1782     struct krb5_dh_moduli **m = NULL, **m2;
1783     char buf[4096];
1784     FILE *f;
1785     int lineno = 0, n = 0;
1786 
1787     *moduli = NULL;
1788 
1789     m = calloc(1, sizeof(m[0]) * 3);
1790     if (m == NULL) {
1791 	krb5_set_error_string(context, "malloc: out of memory");
1792 	return ENOMEM;
1793     }
1794 
1795     strlcpy(buf, default_moduli_rfc3526_MODP_group14, sizeof(buf));
1796     ret = _krb5_parse_moduli_line(context, "builtin", 1, buf,  &m[0]);
1797     if (ret) {
1798 	_krb5_free_moduli(m);
1799 	return ret;
1800     }
1801     n++;
1802 
1803     strlcpy(buf, default_moduli_RFC2412_MODP_group2, sizeof(buf));
1804     ret = _krb5_parse_moduli_line(context, "builtin", 1, buf,  &m[1]);
1805     if (ret) {
1806 	_krb5_free_moduli(m);
1807 	return ret;
1808     }
1809     n++;
1810 
1811 
1812     if (file == NULL)
1813 	file = MODULI_FILE;
1814 
1815     f = fopen(file, "r");
1816     if (f == NULL) {
1817 	*moduli = m;
1818 	return 0;
1819     }
1820 
1821     while(fgets(buf, sizeof(buf), f) != NULL) {
1822 	struct krb5_dh_moduli *element;
1823 
1824 	buf[strcspn(buf, "\n")] = '\0';
1825 	lineno++;
1826 
1827 	m2 = realloc(m, (n + 2) * sizeof(m[0]));
1828 	if (m2 == NULL) {
1829 	    krb5_set_error_string(context, "malloc: out of memory");
1830 	    _krb5_free_moduli(m);
1831 	    return ENOMEM;
1832 	}
1833 	m = m2;
1834 
1835 	m[n] = NULL;
1836 
1837 	ret = _krb5_parse_moduli_line(context, file, lineno, buf,  &element);
1838 	if (ret) {
1839 	    _krb5_free_moduli(m);
1840 	    return ret;
1841 	}
1842 	if (element == NULL)
1843 	    continue;
1844 
1845 	m[n] = element;
1846 	m[n + 1] = NULL;
1847 	n++;
1848     }
1849     *moduli = m;
1850     return 0;
1851 }
1852 
1853 krb5_error_code
1854 _krb5_dh_group_ok(krb5_context context, unsigned long bits,
1855 		  heim_integer *p, heim_integer *g, heim_integer *q,
1856 		  struct krb5_dh_moduli **moduli,
1857 		  char **name)
1858 {
1859     int i;
1860 
1861     if (name)
1862 	*name = NULL;
1863 
1864     for (i = 0; moduli[i] != NULL; i++) {
1865 	if (der_heim_integer_cmp(&moduli[i]->g, g) == 0 &&
1866 	    der_heim_integer_cmp(&moduli[i]->p, p) == 0 &&
1867 	    (q == NULL || der_heim_integer_cmp(&moduli[i]->q, q) == 0))
1868 	{
1869 	    if (bits && bits > moduli[i]->bits) {
1870 		krb5_set_error_string(context, "PKINIT: DH group parameter %s "
1871 				      "no accepted, not enough bits generated",
1872 				      moduli[i]->name);
1873 		return KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED;
1874 	    }
1875 	    if (name)
1876 		*name = strdup(moduli[i]->name);
1877 	    return 0;
1878 	}
1879     }
1880     krb5_set_error_string(context, "PKINIT: DH group parameter no ok");
1881     return KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED;
1882 }
1883 
1884 void KRB5_LIB_FUNCTION
1885 _krb5_get_init_creds_opt_free_pkinit(krb5_get_init_creds_opt *opt)
1886 {
1887 #ifdef PKINIT
1888     krb5_pk_init_ctx ctx;
1889 
1890     if (opt->opt_private == NULL || opt->opt_private->pk_init_ctx == NULL)
1891 	return;
1892     ctx = opt->opt_private->pk_init_ctx;
1893     if (ctx->dh)
1894 	DH_free(ctx->dh);
1895 	ctx->dh = NULL;
1896     if (ctx->id) {
1897 	hx509_verify_destroy_ctx(ctx->id->verify_ctx);
1898 	hx509_certs_free(&ctx->id->certs);
1899 	hx509_certs_free(&ctx->id->anchors);
1900 	hx509_certs_free(&ctx->id->certpool);
1901 	hx509_context_free(&ctx->id->hx509ctx);
1902 
1903 	if (ctx->clientDHNonce) {
1904 	    krb5_free_data(NULL, ctx->clientDHNonce);
1905 	    ctx->clientDHNonce = NULL;
1906 	}
1907 	if (ctx->m)
1908 	    _krb5_free_moduli(ctx->m);
1909 	free(ctx->id);
1910 	ctx->id = NULL;
1911     }
1912     free(opt->opt_private->pk_init_ctx);
1913     opt->opt_private->pk_init_ctx = NULL;
1914 #endif
1915 }
1916 
1917 krb5_error_code KRB5_LIB_FUNCTION
1918 krb5_get_init_creds_opt_set_pkinit(krb5_context context,
1919 				   krb5_get_init_creds_opt *opt,
1920 				   krb5_principal principal,
1921 				   const char *user_id,
1922 				   const char *x509_anchors,
1923 				   char * const * pool,
1924 				   char * const * pki_revoke,
1925 				   int flags,
1926 				   krb5_prompter_fct prompter,
1927 				   void *prompter_data,
1928 				   char *password)
1929 {
1930 #ifdef PKINIT
1931     krb5_error_code ret;
1932     char *anchors = NULL;
1933 
1934     if (opt->opt_private == NULL) {
1935 	krb5_set_error_string(context, "PKINIT: on non extendable opt");
1936 	return EINVAL;
1937     }
1938 
1939     opt->opt_private->pk_init_ctx =
1940 	calloc(1, sizeof(*opt->opt_private->pk_init_ctx));
1941     if (opt->opt_private->pk_init_ctx == NULL) {
1942 	krb5_set_error_string(context, "malloc: out of memory");
1943 	return ENOMEM;
1944     }
1945     opt->opt_private->pk_init_ctx->dh = NULL;
1946     opt->opt_private->pk_init_ctx->id = NULL;
1947     opt->opt_private->pk_init_ctx->clientDHNonce = NULL;
1948     opt->opt_private->pk_init_ctx->require_binding = 0;
1949     opt->opt_private->pk_init_ctx->require_eku = 1;
1950     opt->opt_private->pk_init_ctx->require_krbtgt_otherName = 1;
1951     opt->opt_private->pk_init_ctx->peer = NULL;
1952 
1953     /* XXX implement krb5_appdefault_strings  */
1954     if (pool == NULL)
1955 	pool = krb5_config_get_strings(context, NULL,
1956 				       "appdefaults",
1957 				       "pkinit_pool",
1958 				       NULL);
1959 
1960     if (pki_revoke == NULL)
1961 	pki_revoke = krb5_config_get_strings(context, NULL,
1962 					     "appdefaults",
1963 					     "pkinit_revoke",
1964 					     NULL);
1965 
1966     if (x509_anchors == NULL) {
1967 	krb5_appdefault_string(context, "kinit",
1968 			       krb5_principal_get_realm(context, principal),
1969 			       "pkinit_anchors", NULL, &anchors);
1970 	x509_anchors = anchors;
1971     }
1972 
1973     ret = _krb5_pk_load_id(context,
1974 			   &opt->opt_private->pk_init_ctx->id,
1975 			   user_id,
1976 			   x509_anchors,
1977 			   pool,
1978 			   pki_revoke,
1979 			   prompter,
1980 			   prompter_data,
1981 			   password);
1982     if (ret) {
1983 	free(opt->opt_private->pk_init_ctx);
1984 	opt->opt_private->pk_init_ctx = NULL;
1985 	return ret;
1986     }
1987 
1988     if ((flags & 2) == 0) {
1989 	const char *moduli_file;
1990 	unsigned long dh_min_bits;
1991 
1992 	moduli_file = krb5_config_get_string(context, NULL,
1993 					     "libdefaults",
1994 					     "moduli",
1995 					     NULL);
1996 
1997 	dh_min_bits =
1998 	    krb5_config_get_int_default(context, NULL, 0,
1999 					"libdefaults",
2000 					"pkinit_dh_min_bits",
2001 					NULL);
2002 
2003 	ret = _krb5_parse_moduli(context, moduli_file,
2004 				 &opt->opt_private->pk_init_ctx->m);
2005 	if (ret) {
2006 	    _krb5_get_init_creds_opt_free_pkinit(opt);
2007 	    return ret;
2008 	}
2009 
2010 	opt->opt_private->pk_init_ctx->dh = DH_new();
2011 	if (opt->opt_private->pk_init_ctx->dh == NULL) {
2012 	    krb5_set_error_string(context, "malloc: out of memory");
2013 	    _krb5_get_init_creds_opt_free_pkinit(opt);
2014 	    return ENOMEM;
2015 	}
2016 
2017 	ret = select_dh_group(context, opt->opt_private->pk_init_ctx->dh,
2018 			      dh_min_bits,
2019 			      opt->opt_private->pk_init_ctx->m);
2020 	if (ret) {
2021 	    _krb5_get_init_creds_opt_free_pkinit(opt);
2022 	    return ret;
2023 	}
2024 
2025 	if (DH_generate_key(opt->opt_private->pk_init_ctx->dh) != 1) {
2026 	    krb5_set_error_string(context, "pkinit: failed to generate DH key");
2027 	    _krb5_get_init_creds_opt_free_pkinit(opt);
2028 	    return ENOMEM;
2029 	}
2030     }
2031 
2032     return 0;
2033 #else
2034     krb5_set_error_string(context, "no support for PKINIT compiled in");
2035     return EINVAL;
2036 #endif
2037 }
2038 
2039 /*
2040  *
2041  */
2042 
2043 static void
2044 _krb5_pk_copy_error(krb5_context context,
2045 		    hx509_context hx509ctx,
2046 		    int hxret,
2047 		    const char *fmt,
2048 		    ...)
2049 {
2050     va_list va;
2051     char *s, *f;
2052 
2053     va_start(va, fmt);
2054     vasprintf(&f, fmt, va);
2055     va_end(va);
2056     if (f == NULL) {
2057 	krb5_clear_error_string(context);
2058 	return;
2059     }
2060 
2061     s = hx509_get_error_string(hx509ctx, hxret);
2062     if (s == NULL) {
2063 	krb5_clear_error_string(context);
2064 	free(f);
2065 	return;
2066     }
2067     krb5_set_error_string(context, "%s: %s", f, s);
2068     free(s);
2069     free(f);
2070 }
2071