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