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