c8587770f4ca3096c114ccd58f1a91ab71b228ce
[sfrench/samba-autobuild/.git] / source4 / 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 21684 2007-07-23 23:09: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 = hx509_cms_wrap_ContentInfo(oid_id_pkcs7_signedData(), &sd_buf, &buf);
558     krb5_data_free(&sd_buf);
559     if (ret) {
560         krb5_set_error_string(context,
561                               "ContentInfo wrapping of signedData failed");
562         goto out;
563     }
564
565     if (ctx->type == COMPAT_WIN2K) {
566         PA_PK_AS_REQ_Win2k winreq;
567
568         pa_type = KRB5_PADATA_PK_AS_REQ_WIN;
569
570         memset(&winreq, 0, sizeof(winreq));
571
572         winreq.signed_auth_pack = buf;
573
574         ASN1_MALLOC_ENCODE(PA_PK_AS_REQ_Win2k, buf.data, buf.length,
575                            &winreq, &size, ret);
576         free_PA_PK_AS_REQ_Win2k(&winreq);
577
578     } else if (ctx->type == COMPAT_IETF) {
579         PA_PK_AS_REQ req;
580
581         pa_type = KRB5_PADATA_PK_AS_REQ;
582
583         memset(&req, 0, sizeof(req));
584         req.signedAuthPack = buf;       
585
586         if (ctx->trustedCertifiers) {
587
588             req.trustedCertifiers = calloc(1, sizeof(*req.trustedCertifiers));
589             if (req.trustedCertifiers == NULL) {
590                 krb5_set_error_string(context, "malloc: out of memory");
591                 free_PA_PK_AS_REQ(&req);
592                 goto out;
593             }
594             ret = build_edi(context, ctx->id->hx509ctx, 
595                             ctx->id->anchors, req.trustedCertifiers);
596             if (ret) {
597                 krb5_set_error_string(context, "pk-init: failed to build trustedCertifiers");
598                 free_PA_PK_AS_REQ(&req);
599                 goto out;
600             }
601         }
602         req.kdcPkId = NULL;
603
604         ASN1_MALLOC_ENCODE(PA_PK_AS_REQ, buf.data, buf.length,
605                            &req, &size, ret);
606
607         free_PA_PK_AS_REQ(&req);
608
609     } else
610         krb5_abortx(context, "internal pkinit error");
611     if (ret) {
612         krb5_set_error_string(context, "PA-PK-AS-REQ %d", ret);
613         goto out;
614     }
615     if (buf.length != size)
616         krb5_abortx(context, "Internal ASN1 encoder error");
617
618     ret = krb5_padata_add(context, md, pa_type, buf.data, buf.length);
619     if (ret)
620         free(buf.data);
621
622     if (ret == 0 && ctx->type == COMPAT_WIN2K)
623         krb5_padata_add(context, md, KRB5_PADATA_PK_AS_09_BINDING, NULL, 0);
624
625 out:
626     free_ContentInfo(&content_info);
627
628     return ret;
629 }
630
631
632 krb5_error_code KRB5_LIB_FUNCTION 
633 _krb5_pk_mk_padata(krb5_context context,
634                    void *c,
635                    const KDC_REQ_BODY *req_body,
636                    unsigned nonce,
637                    METHOD_DATA *md)
638 {
639     krb5_pk_init_ctx ctx = c;
640     int win2k_compat;
641
642     win2k_compat = krb5_config_get_bool_default(context, NULL,
643                                                 FALSE,
644                                                 "realms",
645                                                 req_body->realm,
646                                                 "pkinit_win2k",
647                                                 NULL);
648
649     if (win2k_compat) {
650         ctx->require_binding = 
651             krb5_config_get_bool_default(context, NULL,
652                                          FALSE,
653                                          "realms",
654                                          req_body->realm,
655                                          "pkinit_win2k_require_binding",
656                                          NULL);
657         ctx->type = COMPAT_WIN2K;
658     } else
659         ctx->type = COMPAT_IETF;
660
661     ctx->require_eku = 
662         krb5_config_get_bool_default(context, NULL,
663                                      TRUE,
664                                      "realms",
665                                      req_body->realm,
666                                      "pkinit_require_eku",
667                                      NULL);
668     ctx->require_krbtgt_otherName = 
669         krb5_config_get_bool_default(context, NULL,
670                                      TRUE,
671                                      "realms",
672                                      req_body->realm,
673                                      "pkinit_require_krbtgt_otherName",
674                                      NULL);
675
676     ctx->require_hostname_match = 
677         krb5_config_get_bool_default(context, NULL,
678                                      FALSE,
679                                      "realms",
680                                      req_body->realm,
681                                      "pkinit_require_hostname_match",
682                                      NULL);
683
684     ctx->trustedCertifiers = 
685         krb5_config_get_bool_default(context, NULL,
686                                      TRUE,
687                                      "realms",
688                                      req_body->realm,
689                                      "pkinit_trustedCertifiers",
690                                      NULL);
691
692     return pk_mk_padata(context, ctx, req_body, nonce, md);
693 }
694
695 krb5_error_code KRB5_LIB_FUNCTION
696 _krb5_pk_verify_sign(krb5_context context,
697                      const void *data,
698                      size_t length,
699                      struct krb5_pk_identity *id,
700                      heim_oid *contentType,
701                      krb5_data *content,
702                      struct krb5_pk_cert **signer)
703 {
704     hx509_certs signer_certs;
705     int ret;
706
707     *signer = NULL;
708
709     ret = hx509_cms_verify_signed(id->hx509ctx,
710                                   id->verify_ctx,
711                                   data,
712                                   length,
713                                   NULL,
714                                   id->certpool,
715                                   contentType,
716                                   content,
717                                   &signer_certs);
718     if (ret) {
719         _krb5_pk_copy_error(context, id->hx509ctx, ret,
720                             "CMS verify signed failed");
721         return ret;
722     }
723
724     *signer = calloc(1, sizeof(**signer));
725     if (*signer == NULL) {
726         krb5_clear_error_string(context);
727         ret = ENOMEM;
728         goto out;
729     }
730         
731     ret = hx509_get_one_cert(id->hx509ctx, signer_certs, &(*signer)->cert);
732     if (ret) {
733         _krb5_pk_copy_error(context, id->hx509ctx, ret,
734                             "Failed to get on of the signer certs");
735         goto out;
736     }
737
738 out:
739     hx509_certs_free(&signer_certs);
740     if (ret) {
741         if (*signer) {
742             hx509_cert_free((*signer)->cert);
743             free(*signer);
744             *signer = NULL;
745         }
746     }
747
748     return ret;
749 }
750
751 static krb5_error_code
752 get_reply_key_win(krb5_context context,
753                   const krb5_data *content,
754                   unsigned nonce,
755                   krb5_keyblock **key)
756 {
757     ReplyKeyPack_Win2k key_pack;
758     krb5_error_code ret;
759     size_t size;
760
761     ret = decode_ReplyKeyPack_Win2k(content->data,
762                                     content->length,
763                                     &key_pack,
764                                     &size);
765     if (ret) {
766         krb5_set_error_string(context, "PKINIT decoding reply key failed");
767         free_ReplyKeyPack_Win2k(&key_pack);
768         return ret;
769     }
770      
771     if (key_pack.nonce != nonce) {
772         krb5_set_error_string(context, "PKINIT enckey nonce is wrong");
773         free_ReplyKeyPack_Win2k(&key_pack);
774         return KRB5KRB_AP_ERR_MODIFIED;
775     }
776
777     *key = malloc (sizeof (**key));
778     if (*key == NULL) {
779         krb5_set_error_string(context, "PKINIT failed allocating reply key");
780         free_ReplyKeyPack_Win2k(&key_pack);
781         krb5_set_error_string(context, "malloc: out of memory");
782         return ENOMEM;
783     }
784
785     ret = copy_EncryptionKey(&key_pack.replyKey, *key);
786     free_ReplyKeyPack_Win2k(&key_pack);
787     if (ret) {
788         krb5_set_error_string(context, "PKINIT failed copying reply key");
789         free(*key);
790         *key = NULL;
791     }
792
793     return ret;
794 }
795
796 static krb5_error_code
797 get_reply_key(krb5_context context,
798               const krb5_data *content,
799               const krb5_data *req_buffer,
800               krb5_keyblock **key)
801 {
802     ReplyKeyPack key_pack;
803     krb5_error_code ret;
804     size_t size;
805
806     ret = decode_ReplyKeyPack(content->data,
807                               content->length,
808                               &key_pack,
809                               &size);
810     if (ret) {
811         krb5_set_error_string(context, "PKINIT decoding reply key failed");
812         free_ReplyKeyPack(&key_pack);
813         return ret;
814     }
815     
816     {
817         krb5_crypto crypto;
818
819         /* 
820          * XXX Verify kp.replyKey is a allowed enctype in the
821          * configuration file
822          */
823
824         ret = krb5_crypto_init(context, &key_pack.replyKey, 0, &crypto);
825         if (ret) {
826             free_ReplyKeyPack(&key_pack);
827             return ret;
828         }
829
830         ret = krb5_verify_checksum(context, crypto, 6,
831                                    req_buffer->data, req_buffer->length,
832                                    &key_pack.asChecksum);
833         krb5_crypto_destroy(context, crypto);
834         if (ret) {
835             free_ReplyKeyPack(&key_pack);
836             return ret;
837         }
838     }
839
840     *key = malloc (sizeof (**key));
841     if (*key == NULL) {
842         krb5_set_error_string(context, "PKINIT failed allocating reply key");
843         free_ReplyKeyPack(&key_pack);
844         krb5_set_error_string(context, "malloc: out of memory");
845         return ENOMEM;
846     }
847
848     ret = copy_EncryptionKey(&key_pack.replyKey, *key);
849     free_ReplyKeyPack(&key_pack);
850     if (ret) {
851         krb5_set_error_string(context, "PKINIT failed copying reply key");
852         free(*key);
853         *key = NULL;
854     }
855
856     return ret;
857 }
858
859
860 static krb5_error_code
861 pk_verify_host(krb5_context context,
862                const char *realm,
863                const krb5_krbhst_info *hi,
864                struct krb5_pk_init_ctx_data *ctx,
865                struct krb5_pk_cert *host)
866 {
867     krb5_error_code ret = 0;
868
869     if (ctx->require_eku) {
870         ret = hx509_cert_check_eku(ctx->id->hx509ctx, host->cert,
871                                    oid_id_pkkdcekuoid(), 0);
872         if (ret) {
873             krb5_set_error_string(context, "No PK-INIT KDC EKU in kdc certificate");
874             return ret;
875         }
876     }
877     if (ctx->require_krbtgt_otherName) {
878         hx509_octet_string_list list;
879         int i;
880
881         ret = hx509_cert_find_subjectAltName_otherName(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 }