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