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