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