s4:heimdal: import lorikeet-heimdal-200908050050 (commit 8714779fa7376fd9f7761587639e...
[samba.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 struct krb5_dh_moduli {
37     char *name;
38     unsigned long bits;
39     heim_integer p;
40     heim_integer g;
41     heim_integer q;
42 };
43
44 #ifdef PKINIT
45
46 #include <cms_asn1.h>
47 #include <pkcs8_asn1.h>
48 #include <pkcs9_asn1.h>
49 #include <pkcs12_asn1.h>
50 #include <pkinit_asn1.h>
51 #include <asn1_err.h>
52
53 #include <der.h>
54
55 struct krb5_pk_cert {
56     hx509_cert cert;
57 };
58
59 struct krb5_pk_init_ctx_data {
60     struct krb5_pk_identity *id;
61     enum { USE_RSA, USE_DH, USE_ECDH } keyex;
62     union {
63         DH *dh;
64 #ifdef HAVE_OPENSSL
65         EC_KEY *eckey;
66 #endif
67     } u;
68     krb5_data *clientDHNonce;
69     struct krb5_dh_moduli **m;
70     hx509_peer_info peer;
71     enum krb5_pk_type type;
72     unsigned int require_binding:1;
73     unsigned int require_eku:1;
74     unsigned int require_krbtgt_otherName:1;
75     unsigned int require_hostname_match:1;
76     unsigned int trustedCertifiers:1;
77 };
78
79 static void
80 pk_copy_error(krb5_context context,
81               hx509_context hx509ctx,
82               int hxret,
83               const char *fmt,
84               ...)
85     __attribute__ ((format (printf, 4, 5)));
86
87 /*
88  *
89  */
90
91 void KRB5_LIB_FUNCTION
92 _krb5_pk_cert_free(struct krb5_pk_cert *cert)
93 {
94     if (cert->cert) {
95         hx509_cert_free(cert->cert);
96     }
97     free(cert);
98 }
99
100 static krb5_error_code
101 BN_to_integer(krb5_context context, BIGNUM *bn, heim_integer *integer)
102 {
103     integer->length = BN_num_bytes(bn);
104     integer->data = malloc(integer->length);
105     if (integer->data == NULL) {
106         krb5_clear_error_message(context);
107         return ENOMEM;
108     }
109     BN_bn2bin(bn, integer->data);
110     integer->negative = BN_is_negative(bn);
111     return 0;
112 }
113
114 static BIGNUM *
115 integer_to_BN(krb5_context context, const char *field, const heim_integer *f)
116 {
117     BIGNUM *bn;
118
119     bn = BN_bin2bn((const unsigned char *)f->data, f->length, NULL);
120     if (bn == NULL) {
121         krb5_set_error_message(context, ENOMEM,
122                                N_("PKINIT: parsing BN failed %s", ""), field);
123         return NULL;
124     }
125     BN_set_negative(bn, f->negative);
126     return bn;
127 }
128
129 static krb5_error_code
130 select_dh_group(krb5_context context, DH *dh, unsigned long bits,
131                 struct krb5_dh_moduli **moduli)
132 {
133     const struct krb5_dh_moduli *m;
134
135     if (bits == 0) {
136         m = moduli[1]; /* XXX */
137         if (m == NULL)
138             m = moduli[0]; /* XXX */
139     } else {
140         int i;
141         for (i = 0; moduli[i] != NULL; i++) {
142             if (bits < moduli[i]->bits)
143                 break;
144         }
145         if (moduli[i] == NULL) {
146             krb5_set_error_message(context, EINVAL,
147                                    N_("Did not find a DH group parameter "
148                                       "matching requirement of %lu bits", ""),
149                                    bits);
150             return EINVAL;
151         }
152         m = moduli[i];
153     }
154
155     dh->p = integer_to_BN(context, "p", &m->p);
156     if (dh->p == NULL)
157         return ENOMEM;
158     dh->g = integer_to_BN(context, "g", &m->g);
159     if (dh->g == NULL)
160         return ENOMEM;
161     dh->q = integer_to_BN(context, "q", &m->q);
162     if (dh->q == NULL)
163         return ENOMEM;
164
165     return 0;
166 }
167
168 struct certfind {
169     const char *type;
170     const heim_oid *oid;
171 };
172
173 /*
174  * Try searchin the key by to use by first looking for for PK-INIT
175  * EKU, then the Microsoft smart card EKU and last, no special EKU at all.
176  */
177
178 static krb5_error_code
179 find_cert(krb5_context context, struct krb5_pk_identity *id,
180           hx509_query *q, hx509_cert *cert)
181 {
182     struct certfind cf[3] = {
183         { "PKINIT EKU" },
184         { "MS EKU" },
185         { "any (or no)" }
186     };
187     int i, ret;
188
189     cf[0].oid = &asn1_oid_id_pkekuoid;
190     cf[1].oid = &asn1_oid_id_pkinit_ms_eku;
191     cf[2].oid = NULL;
192
193     for (i = 0; i < sizeof(cf)/sizeof(cf[0]); i++) {
194         ret = hx509_query_match_eku(q, cf[i].oid);
195         if (ret) {
196             pk_copy_error(context, id->hx509ctx, ret,
197                           "Failed setting %s OID", cf[i].type);
198             return ret;
199         }
200
201         ret = hx509_certs_find(id->hx509ctx, id->certs, q, cert);
202         if (ret == 0)
203             break;
204         pk_copy_error(context, id->hx509ctx, ret,
205                       "Failed finding certificate with %s OID", cf[i].type);
206     }
207     return ret;
208 }
209
210
211 static krb5_error_code
212 create_signature(krb5_context context,
213                  const heim_oid *eContentType,
214                  krb5_data *eContent,
215                  struct krb5_pk_identity *id,
216                  hx509_peer_info peer,
217                  krb5_data *sd_data)
218 {
219     int ret, flags = 0;
220
221     if (id->cert == NULL)
222         flags |= HX509_CMS_SIGNATURE_NO_SIGNER;
223
224     ret = hx509_cms_create_signed_1(id->hx509ctx,
225                                     flags,
226                                     eContentType,
227                                     eContent->data,
228                                     eContent->length,
229                                     NULL,
230                                     id->cert,
231                                     peer,
232                                     NULL,
233                                     id->certs,
234                                     sd_data);
235     if (ret) {
236         pk_copy_error(context, id->hx509ctx, ret,
237                       "Create CMS signedData");
238         return ret;
239     }
240
241     return 0;
242 }
243
244 static int
245 cert2epi(hx509_context context, void *ctx, hx509_cert c)
246 {
247     ExternalPrincipalIdentifiers *ids = ctx;
248     ExternalPrincipalIdentifier id;
249     hx509_name subject = NULL;
250     void *p;
251     int ret;
252
253     if (ids->len > 10)
254         return 0;
255
256     memset(&id, 0, sizeof(id));
257
258     ret = hx509_cert_get_subject(c, &subject);
259     if (ret)
260         return ret;
261
262     if (hx509_name_is_null_p(subject) != 0) {
263
264         id.subjectName = calloc(1, sizeof(*id.subjectName));
265         if (id.subjectName == NULL) {
266             hx509_name_free(&subject);
267             free_ExternalPrincipalIdentifier(&id);
268             return ENOMEM;
269         }
270
271         ret = hx509_name_binary(subject, id.subjectName);
272         if (ret) {
273             hx509_name_free(&subject);
274             free_ExternalPrincipalIdentifier(&id);
275             return ret;
276         }
277     }
278     hx509_name_free(&subject);
279
280
281     id.issuerAndSerialNumber = calloc(1, sizeof(*id.issuerAndSerialNumber));
282     if (id.issuerAndSerialNumber == NULL) {
283         free_ExternalPrincipalIdentifier(&id);
284         return ENOMEM;
285     }
286
287     {
288         IssuerAndSerialNumber iasn;
289         hx509_name issuer;
290         size_t size;
291         
292         memset(&iasn, 0, sizeof(iasn));
293
294         ret = hx509_cert_get_issuer(c, &issuer);
295         if (ret) {
296             free_ExternalPrincipalIdentifier(&id);
297             return ret;
298         }
299
300         ret = hx509_name_to_Name(issuer, &iasn.issuer);
301         hx509_name_free(&issuer);
302         if (ret) {
303             free_ExternalPrincipalIdentifier(&id);
304             return ret;
305         }
306         
307         ret = hx509_cert_get_serialnumber(c, &iasn.serialNumber);
308         if (ret) {
309             free_IssuerAndSerialNumber(&iasn);
310             free_ExternalPrincipalIdentifier(&id);
311             return ret;
312         }
313
314         ASN1_MALLOC_ENCODE(IssuerAndSerialNumber,
315                            id.issuerAndSerialNumber->data,
316                            id.issuerAndSerialNumber->length,
317                            &iasn, &size, ret);
318         free_IssuerAndSerialNumber(&iasn);
319         if (ret)
320             return ret;
321         if (id.issuerAndSerialNumber->length != size)
322             abort();
323     }
324
325     id.subjectKeyIdentifier = NULL;
326
327     p = realloc(ids->val, sizeof(ids->val[0]) * (ids->len + 1));
328     if (p == NULL) {
329         free_ExternalPrincipalIdentifier(&id);
330         return ENOMEM;
331     }
332
333     ids->val = p;
334     ids->val[ids->len] = id;
335     ids->len++;
336
337     return 0;
338 }
339
340 static krb5_error_code
341 build_edi(krb5_context context,
342           hx509_context hx509ctx,
343           hx509_certs certs,
344           ExternalPrincipalIdentifiers *ids)
345 {
346     return hx509_certs_iter(hx509ctx, certs, cert2epi, ids);
347 }
348
349 static krb5_error_code
350 build_auth_pack(krb5_context context,
351                 unsigned nonce,
352                 krb5_pk_init_ctx ctx,
353                 const KDC_REQ_BODY *body,
354                 AuthPack *a)
355 {
356     size_t buf_size, len;
357     krb5_error_code ret;
358     void *buf;
359     krb5_timestamp sec;
360     int32_t usec;
361     Checksum checksum;
362
363     krb5_clear_error_message(context);
364
365     memset(&checksum, 0, sizeof(checksum));
366
367     krb5_us_timeofday(context, &sec, &usec);
368     a->pkAuthenticator.ctime = sec;
369     a->pkAuthenticator.nonce = nonce;
370
371     ASN1_MALLOC_ENCODE(KDC_REQ_BODY, buf, buf_size, body, &len, ret);
372     if (ret)
373         return ret;
374     if (buf_size != len)
375         krb5_abortx(context, "internal error in ASN.1 encoder");
376
377     ret = krb5_create_checksum(context,
378                                NULL,
379                                0,
380                                CKSUMTYPE_SHA1,
381                                buf,
382                                len,
383                                &checksum);
384     free(buf);
385     if (ret)
386         return ret;
387
388     ALLOC(a->pkAuthenticator.paChecksum, 1);
389     if (a->pkAuthenticator.paChecksum == NULL) {
390         krb5_set_error_message(context, ENOMEM,
391                                N_("malloc: out of memory", ""));
392         return ENOMEM;
393     }
394
395     ret = krb5_data_copy(a->pkAuthenticator.paChecksum,
396                          checksum.checksum.data, checksum.checksum.length);
397     free_Checksum(&checksum);
398     if (ret)
399         return ret;
400
401     if (ctx->keyex == USE_DH || ctx->keyex == USE_ECDH) {
402         const char *moduli_file;
403         unsigned long dh_min_bits;
404         krb5_data dhbuf;
405         size_t size;
406
407         krb5_data_zero(&dhbuf);
408
409
410
411         moduli_file = krb5_config_get_string(context, NULL,
412                                              "libdefaults",
413                                              "moduli",
414                                              NULL);
415
416         dh_min_bits =
417             krb5_config_get_int_default(context, NULL, 0,
418                                         "libdefaults",
419                                         "pkinit_dh_min_bits",
420                                         NULL);
421
422         ret = _krb5_parse_moduli(context, moduli_file, &ctx->m);
423         if (ret)
424             return ret;
425         
426         ctx->u.dh = DH_new();
427         if (ctx->u.dh == NULL) {
428             krb5_set_error_message(context, ENOMEM,
429                                    N_("malloc: out of memory", ""));
430             return ENOMEM;
431         }
432
433         ret = select_dh_group(context, ctx->u.dh, dh_min_bits, ctx->m);
434         if (ret)
435             return ret;
436
437         if (DH_generate_key(ctx->u.dh) != 1) {
438             krb5_set_error_message(context, ENOMEM,
439                                    N_("pkinit: failed to generate DH key", ""));
440             return ENOMEM;
441         }
442
443
444         if (1 /* support_cached_dh */) {
445             ALLOC(a->clientDHNonce, 1);
446             if (a->clientDHNonce == NULL) {
447                 krb5_clear_error_message(context);
448                 return ENOMEM;
449             }
450             ret = krb5_data_alloc(a->clientDHNonce, 40);
451             if (a->clientDHNonce == NULL) {
452                 krb5_clear_error_message(context);
453                 return ret;
454             }
455             RAND_bytes(a->clientDHNonce->data, a->clientDHNonce->length);
456             ret = krb5_copy_data(context, a->clientDHNonce,
457                                  &ctx->clientDHNonce);
458             if (ret)
459                 return ret;
460         }
461
462         ALLOC(a->clientPublicValue, 1);
463         if (a->clientPublicValue == NULL)
464             return ENOMEM;
465
466         if (ctx->keyex == USE_DH) {
467             DH *dh = ctx->u.dh;
468             DomainParameters dp;
469             heim_integer dh_pub_key;
470
471             ret = der_copy_oid(&asn1_oid_id_dhpublicnumber,
472                                &a->clientPublicValue->algorithm.algorithm);
473             if (ret)
474                 return ret;
475             
476             memset(&dp, 0, sizeof(dp));
477             
478             ret = BN_to_integer(context, dh->p, &dp.p);
479             if (ret) {
480                 free_DomainParameters(&dp);
481                 return ret;
482             }
483             ret = BN_to_integer(context, dh->g, &dp.g);
484             if (ret) {
485                 free_DomainParameters(&dp);
486                 return ret;
487             }
488             ret = BN_to_integer(context, dh->q, &dp.q);
489             if (ret) {
490                 free_DomainParameters(&dp);
491                 return ret;
492             }
493             dp.j = NULL;
494             dp.validationParms = NULL;
495             
496             a->clientPublicValue->algorithm.parameters =
497                 malloc(sizeof(*a->clientPublicValue->algorithm.parameters));
498             if (a->clientPublicValue->algorithm.parameters == NULL) {
499                 free_DomainParameters(&dp);
500                 return ret;
501             }
502             
503             ASN1_MALLOC_ENCODE(DomainParameters,
504                                a->clientPublicValue->algorithm.parameters->data,
505                                a->clientPublicValue->algorithm.parameters->length,
506                                &dp, &size, ret);
507             free_DomainParameters(&dp);
508             if (ret)
509                 return ret;
510             if (size != a->clientPublicValue->algorithm.parameters->length)
511                 krb5_abortx(context, "Internal ASN1 encoder error");
512             
513             ret = BN_to_integer(context, dh->pub_key, &dh_pub_key);
514             if (ret)
515                 return ret;
516             
517             ASN1_MALLOC_ENCODE(DHPublicKey, dhbuf.data, dhbuf.length,
518                                &dh_pub_key, &size, ret);
519             der_free_heim_integer(&dh_pub_key);
520             if (ret)
521                 return ret;
522             if (size != dhbuf.length)
523                 krb5_abortx(context, "asn1 internal error");
524         } else if (ctx->keyex == USE_ECDH) {
525 #ifdef HAVE_OPENSSL
526             ECParameters ecp;
527             unsigned char *p;
528             int len;
529
530             /* copy in public key, XXX find the best curve that the server support or use the clients curve if possible */
531
532             ecp.element = choice_ECParameters_namedCurve;
533             ret = der_copy_oid(&asn1_oid_id_ec_group_secp256r1,
534                                &ecp.u.namedCurve);
535             if (ret)
536                 return ret;
537
538             ALLOC(a->clientPublicValue->algorithm.parameters, 1);
539             if (a->clientPublicValue->algorithm.parameters == NULL) {
540                 free_ECParameters(&ecp);
541                 return ENOMEM;
542             }
543             ASN1_MALLOC_ENCODE(ECParameters, p, len, &ecp, &size, ret);
544             free_ECParameters(&ecp);
545             if (ret)
546                 return ret;
547             if (size != len)
548                 krb5_abortx(context, "asn1 internal error");
549             
550             a->clientPublicValue->algorithm.parameters->data = p;
551             a->clientPublicValue->algorithm.parameters->length = size;
552
553             /* copy in public key */
554
555             ret = der_copy_oid(&asn1_oid_id_ecPublicKey,
556                                &a->clientPublicValue->algorithm.algorithm);
557             if (ret)
558                 return ret;
559
560             ctx->u.eckey = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
561             if (ctx->u.eckey == NULL)
562                 return ENOMEM;
563
564             ret = EC_KEY_generate_key(ctx->u.eckey);
565             if (ret != 1)
566                 return EINVAL;
567
568             /* encode onto dhkey */
569
570             len = i2o_ECPublicKey(ctx->u.eckey, NULL);
571             if (len <= 0)
572                 abort();
573
574             dhbuf.data = malloc(len);
575             if (dhbuf.data == NULL)
576                 abort();
577             dhbuf.length = len;
578             p = dhbuf.data;
579
580             len = i2o_ECPublicKey(ctx->u.eckey, &p);
581             if (len <= 0)
582                 abort();
583
584             /* XXX verify that this is right with RFC3279 */
585 #else
586             return EINVAL;
587 #endif
588         } else
589             krb5_abortx(context, "internal error");
590         a->clientPublicValue->subjectPublicKey.length = dhbuf.length * 8;
591         a->clientPublicValue->subjectPublicKey.data = dhbuf.data;
592     }
593     
594     {
595         a->supportedCMSTypes = calloc(1, sizeof(*a->supportedCMSTypes));
596         if (a->supportedCMSTypes == NULL)
597             return ENOMEM;
598
599         ret = hx509_crypto_available(ctx->id->hx509ctx, HX509_SELECT_ALL, NULL,
600                                      &a->supportedCMSTypes->val,
601                                      &a->supportedCMSTypes->len);
602         if (ret)
603             return ret;
604     }
605
606     return ret;
607 }
608
609 krb5_error_code KRB5_LIB_FUNCTION
610 _krb5_pk_mk_ContentInfo(krb5_context context,
611                         const krb5_data *buf,
612                         const heim_oid *oid,
613                         struct ContentInfo *content_info)
614 {
615     krb5_error_code ret;
616
617     ret = der_copy_oid(oid, &content_info->contentType);
618     if (ret)
619         return ret;
620     ALLOC(content_info->content, 1);
621     if (content_info->content == NULL)
622         return ENOMEM;
623     content_info->content->data = malloc(buf->length);
624     if (content_info->content->data == NULL)
625         return ENOMEM;
626     memcpy(content_info->content->data, buf->data, buf->length);
627     content_info->content->length = buf->length;
628     return 0;
629 }
630
631 static krb5_error_code
632 pk_mk_padata(krb5_context context,
633              krb5_pk_init_ctx ctx,
634              const KDC_REQ_BODY *req_body,
635              unsigned nonce,
636              METHOD_DATA *md)
637 {
638     struct ContentInfo content_info;
639     krb5_error_code ret;
640     const heim_oid *oid;
641     size_t size;
642     krb5_data buf, sd_buf;
643     int pa_type;
644
645     krb5_data_zero(&buf);
646     krb5_data_zero(&sd_buf);
647     memset(&content_info, 0, sizeof(content_info));
648
649     if (ctx->type == PKINIT_WIN2K) {
650         AuthPack_Win2k ap;
651         krb5_timestamp sec;
652         int32_t usec;
653
654         memset(&ap, 0, sizeof(ap));
655
656         /* fill in PKAuthenticator */
657         ret = copy_PrincipalName(req_body->sname, &ap.pkAuthenticator.kdcName);
658         if (ret) {
659             free_AuthPack_Win2k(&ap);
660             krb5_clear_error_message(context);
661             goto out;
662         }
663         ret = copy_Realm(&req_body->realm, &ap.pkAuthenticator.kdcRealm);
664         if (ret) {
665             free_AuthPack_Win2k(&ap);
666             krb5_clear_error_message(context);
667             goto out;
668         }
669
670         krb5_us_timeofday(context, &sec, &usec);
671         ap.pkAuthenticator.ctime = sec;
672         ap.pkAuthenticator.cusec = usec;
673         ap.pkAuthenticator.nonce = nonce;
674
675         ASN1_MALLOC_ENCODE(AuthPack_Win2k, buf.data, buf.length,
676                            &ap, &size, ret);
677         free_AuthPack_Win2k(&ap);
678         if (ret) {
679             krb5_set_error_message(context, ret,
680                                    N_("Failed encoding AuthPackWin: %d", ""),
681                                    (int)ret);
682             goto out;
683         }
684         if (buf.length != size)
685             krb5_abortx(context, "internal ASN1 encoder error");
686
687         oid = &asn1_oid_id_pkcs7_data;
688     } else if (ctx->type == PKINIT_27) {
689         AuthPack ap;
690         
691         memset(&ap, 0, sizeof(ap));
692
693         ret = build_auth_pack(context, nonce, ctx, req_body, &ap);
694         if (ret) {
695             free_AuthPack(&ap);
696             goto out;
697         }
698
699         ASN1_MALLOC_ENCODE(AuthPack, buf.data, buf.length, &ap, &size, ret);
700         free_AuthPack(&ap);
701         if (ret) {
702             krb5_set_error_message(context, ret,
703                                    N_("Failed encoding AuthPack: %d", ""),
704                                    (int)ret);
705             goto out;
706         }
707         if (buf.length != size)
708             krb5_abortx(context, "internal ASN1 encoder error");
709
710         oid = &asn1_oid_id_pkauthdata;
711     } else
712         krb5_abortx(context, "internal pkinit error");
713
714     ret = create_signature(context, oid, &buf, ctx->id,
715                            ctx->peer, &sd_buf);
716     krb5_data_free(&buf);
717     if (ret)
718         goto out;
719
720     ret = hx509_cms_wrap_ContentInfo(&asn1_oid_id_pkcs7_signedData, &sd_buf, &buf);
721     krb5_data_free(&sd_buf);
722     if (ret) {
723         krb5_set_error_message(context, ret,
724                                N_("ContentInfo wrapping of signedData failed",""));
725         goto out;
726     }
727
728     if (ctx->type == PKINIT_WIN2K) {
729         PA_PK_AS_REQ_Win2k winreq;
730
731         pa_type = KRB5_PADATA_PK_AS_REQ_WIN;
732
733         memset(&winreq, 0, sizeof(winreq));
734
735         winreq.signed_auth_pack = buf;
736
737         ASN1_MALLOC_ENCODE(PA_PK_AS_REQ_Win2k, buf.data, buf.length,
738                            &winreq, &size, ret);
739         free_PA_PK_AS_REQ_Win2k(&winreq);
740
741     } else if (ctx->type == PKINIT_27) {
742         PA_PK_AS_REQ req;
743
744         pa_type = KRB5_PADATA_PK_AS_REQ;
745
746         memset(&req, 0, sizeof(req));
747         req.signedAuthPack = buf;       
748
749         if (ctx->trustedCertifiers) {
750
751             req.trustedCertifiers = calloc(1, sizeof(*req.trustedCertifiers));
752             if (req.trustedCertifiers == NULL) {
753                 ret = ENOMEM;
754                 krb5_set_error_message(context, ret,
755                                        N_("malloc: out of memory", ""));
756                 free_PA_PK_AS_REQ(&req);
757                 goto out;
758             }
759             ret = build_edi(context, ctx->id->hx509ctx,
760                             ctx->id->anchors, req.trustedCertifiers);
761             if (ret) {
762                 krb5_set_error_message(context, ret,
763                                        N_("pk-init: failed to build "
764                                           "trustedCertifiers", ""));
765                 free_PA_PK_AS_REQ(&req);
766                 goto out;
767             }
768         }
769         req.kdcPkId = NULL;
770
771         ASN1_MALLOC_ENCODE(PA_PK_AS_REQ, buf.data, buf.length,
772                            &req, &size, ret);
773
774         free_PA_PK_AS_REQ(&req);
775
776     } else
777         krb5_abortx(context, "internal pkinit error");
778     if (ret) {
779         krb5_set_error_message(context, ret, "PA-PK-AS-REQ %d", (int)ret);
780         goto out;
781     }
782     if (buf.length != size)
783         krb5_abortx(context, "Internal ASN1 encoder error");
784
785     ret = krb5_padata_add(context, md, pa_type, buf.data, buf.length);
786     if (ret)
787         free(buf.data);
788
789     if (ret == 0)
790         krb5_padata_add(context, md, KRB5_PADATA_PK_AS_09_BINDING, NULL, 0);
791
792  out:
793     free_ContentInfo(&content_info);
794
795     return ret;
796 }
797
798
799 krb5_error_code KRB5_LIB_FUNCTION
800 _krb5_pk_mk_padata(krb5_context context,
801                    void *c,
802                    const KDC_REQ_BODY *req_body,
803                    unsigned nonce,
804                    METHOD_DATA *md)
805 {
806     krb5_pk_init_ctx ctx = c;
807     int win2k_compat;
808
809     win2k_compat = krb5_config_get_bool_default(context, NULL,
810                                                 FALSE,
811                                                 "realms",
812                                                 req_body->realm,
813                                                 "pkinit_win2k",
814                                                 NULL);
815
816     if (win2k_compat) {
817         ctx->require_binding =
818             krb5_config_get_bool_default(context, NULL,
819                                          FALSE,
820                                          "realms",
821                                          req_body->realm,
822                                          "pkinit_win2k_require_binding",
823                                          NULL);
824         ctx->type = PKINIT_WIN2K;
825     } else
826         ctx->type = PKINIT_27;
827
828     ctx->require_eku =
829         krb5_config_get_bool_default(context, NULL,
830                                      TRUE,
831                                      "realms",
832                                      req_body->realm,
833                                      "pkinit_require_eku",
834                                      NULL);
835     ctx->require_krbtgt_otherName =
836         krb5_config_get_bool_default(context, NULL,
837                                      TRUE,
838                                      "realms",
839                                      req_body->realm,
840                                      "pkinit_require_krbtgt_otherName",
841                                      NULL);
842
843     ctx->require_hostname_match =
844         krb5_config_get_bool_default(context, NULL,
845                                      FALSE,
846                                      "realms",
847                                      req_body->realm,
848                                      "pkinit_require_hostname_match",
849                                      NULL);
850
851     ctx->trustedCertifiers =
852         krb5_config_get_bool_default(context, NULL,
853                                      TRUE,
854                                      "realms",
855                                      req_body->realm,
856                                      "pkinit_trustedCertifiers",
857                                      NULL);
858
859     return pk_mk_padata(context, ctx, req_body, nonce, md);
860 }
861
862 static krb5_error_code
863 pk_verify_sign(krb5_context context,
864                const void *data,
865                size_t length,
866                struct krb5_pk_identity *id,
867                heim_oid *contentType,
868                krb5_data *content,
869                struct krb5_pk_cert **signer)
870 {
871     hx509_certs signer_certs;
872     int ret;
873
874     *signer = NULL;
875
876     ret = hx509_cms_verify_signed(id->hx509ctx,
877                                   id->verify_ctx,
878                                   HX509_CMS_VS_ALLOW_DATA_OID_MISMATCH|HX509_CMS_VS_NO_KU_CHECK,
879                                   data,
880                                   length,
881                                   NULL,
882                                   id->certpool,
883                                   contentType,
884                                   content,
885                                   &signer_certs);
886     if (ret) {
887         pk_copy_error(context, id->hx509ctx, ret,
888                       "CMS verify signed failed");
889         return ret;
890     }
891
892     *signer = calloc(1, sizeof(**signer));
893     if (*signer == NULL) {
894         krb5_clear_error_message(context);
895         ret = ENOMEM;
896         goto out;
897     }
898         
899     ret = hx509_get_one_cert(id->hx509ctx, signer_certs, &(*signer)->cert);
900     if (ret) {
901         pk_copy_error(context, id->hx509ctx, ret,
902                       "Failed to get on of the signer certs");
903         goto out;
904     }
905
906  out:
907     hx509_certs_free(&signer_certs);
908     if (ret) {
909         if (*signer) {
910             hx509_cert_free((*signer)->cert);
911             free(*signer);
912             *signer = NULL;
913         }
914     }
915
916     return ret;
917 }
918
919 static krb5_error_code
920 get_reply_key_win(krb5_context context,
921                   const krb5_data *content,
922                   unsigned nonce,
923                   krb5_keyblock **key)
924 {
925     ReplyKeyPack_Win2k key_pack;
926     krb5_error_code ret;
927     size_t size;
928
929     ret = decode_ReplyKeyPack_Win2k(content->data,
930                                     content->length,
931                                     &key_pack,
932                                     &size);
933     if (ret) {
934         krb5_set_error_message(context, ret,
935                                N_("PKINIT decoding reply key failed", ""));
936         free_ReplyKeyPack_Win2k(&key_pack);
937         return ret;
938     }
939
940     if (key_pack.nonce != nonce) {
941         krb5_set_error_message(context, ret,
942                                N_("PKINIT enckey nonce is wrong", ""));
943         free_ReplyKeyPack_Win2k(&key_pack);
944         return KRB5KRB_AP_ERR_MODIFIED;
945     }
946
947     *key = malloc (sizeof (**key));
948     if (*key == NULL) {
949         free_ReplyKeyPack_Win2k(&key_pack);
950         krb5_set_error_message(context, ENOMEM,
951                                N_("malloc: out of memory", ""));
952         return ENOMEM;
953     }
954
955     ret = copy_EncryptionKey(&key_pack.replyKey, *key);
956     free_ReplyKeyPack_Win2k(&key_pack);
957     if (ret) {
958         krb5_set_error_message(context, ret,
959                                N_("PKINIT failed copying reply key", ""));
960         free(*key);
961         *key = NULL;
962     }
963
964     return ret;
965 }
966
967 static krb5_error_code
968 get_reply_key(krb5_context context,
969               const krb5_data *content,
970               const krb5_data *req_buffer,
971               krb5_keyblock **key)
972 {
973     ReplyKeyPack key_pack;
974     krb5_error_code ret;
975     size_t size;
976
977     ret = decode_ReplyKeyPack(content->data,
978                               content->length,
979                               &key_pack,
980                               &size);
981     if (ret) {
982         krb5_set_error_message(context, ret,
983                                N_("PKINIT decoding reply key failed", ""));
984         free_ReplyKeyPack(&key_pack);
985         return ret;
986     }
987
988     {
989         krb5_crypto crypto;
990
991         /*
992          * XXX Verify kp.replyKey is a allowed enctype in the
993          * configuration file
994          */
995
996         ret = krb5_crypto_init(context, &key_pack.replyKey, 0, &crypto);
997         if (ret) {
998             free_ReplyKeyPack(&key_pack);
999             return ret;
1000         }
1001
1002         ret = krb5_verify_checksum(context, crypto, 6,
1003                                    req_buffer->data, req_buffer->length,
1004                                    &key_pack.asChecksum);
1005         krb5_crypto_destroy(context, crypto);
1006         if (ret) {
1007             free_ReplyKeyPack(&key_pack);
1008             return ret;
1009         }
1010     }
1011
1012     *key = malloc (sizeof (**key));
1013     if (*key == NULL) {
1014         free_ReplyKeyPack(&key_pack);
1015         krb5_set_error_message(context, ENOMEM,
1016                                N_("malloc: out of memory", ""));
1017         return ENOMEM;
1018     }
1019
1020     ret = copy_EncryptionKey(&key_pack.replyKey, *key);
1021     free_ReplyKeyPack(&key_pack);
1022     if (ret) {
1023         krb5_set_error_message(context, ret,
1024                                N_("PKINIT failed copying reply key", ""));
1025         free(*key);
1026         *key = NULL;
1027     }
1028
1029     return ret;
1030 }
1031
1032
1033 static krb5_error_code
1034 pk_verify_host(krb5_context context,
1035                const char *realm,
1036                const krb5_krbhst_info *hi,
1037                struct krb5_pk_init_ctx_data *ctx,
1038                struct krb5_pk_cert *host)
1039 {
1040     krb5_error_code ret = 0;
1041
1042     if (ctx->require_eku) {
1043         ret = hx509_cert_check_eku(ctx->id->hx509ctx, host->cert,
1044                                    &asn1_oid_id_pkkdcekuoid, 0);
1045         if (ret) {
1046             krb5_set_error_message(context, ret,
1047                                    N_("No PK-INIT KDC EKU in kdc certificate", ""));
1048             return ret;
1049         }
1050     }
1051     if (ctx->require_krbtgt_otherName) {
1052         hx509_octet_string_list list;
1053         int i;
1054
1055         ret = hx509_cert_find_subjectAltName_otherName(ctx->id->hx509ctx,
1056                                                        host->cert,
1057                                                        &asn1_oid_id_pkinit_san,
1058                                                        &list);
1059         if (ret) {
1060             krb5_set_error_message(context, ret,
1061                                    N_("Failed to find the PK-INIT "
1062                                       "subjectAltName in the KDC "
1063                                       "certificate", ""));
1064
1065             return ret;
1066         }
1067
1068         for (i = 0; i < list.len; i++) {
1069             KRB5PrincipalName r;
1070
1071             ret = decode_KRB5PrincipalName(list.val[i].data,
1072                                            list.val[i].length,
1073                                            &r,
1074                                            NULL);
1075             if (ret) {
1076                 krb5_set_error_message(context, ret,
1077                                        N_("Failed to decode the PK-INIT "
1078                                           "subjectAltName in the "
1079                                           "KDC certificate", ""));
1080
1081                 break;
1082             }
1083
1084             if (r.principalName.name_string.len != 2 ||
1085                 strcmp(r.principalName.name_string.val[0], KRB5_TGS_NAME) != 0 ||
1086                 strcmp(r.principalName.name_string.val[1], realm) != 0 ||
1087                 strcmp(r.realm, realm) != 0)
1088                 {
1089                     ret = KRB5_KDC_ERR_INVALID_CERTIFICATE;
1090                     krb5_set_error_message(context, ret,
1091                                            N_("KDC have wrong realm name in "
1092                                               "the certificate", ""));
1093                 }
1094
1095             free_KRB5PrincipalName(&r);
1096             if (ret)
1097                 break;
1098         }
1099         hx509_free_octet_string_list(&list);
1100     }
1101     if (ret)
1102         return ret;
1103
1104     if (hi) {
1105         ret = hx509_verify_hostname(ctx->id->hx509ctx, host->cert,
1106                                     ctx->require_hostname_match,
1107                                     HX509_HN_HOSTNAME,
1108                                     hi->hostname,
1109                                     hi->ai->ai_addr, hi->ai->ai_addrlen);
1110
1111         if (ret)
1112             krb5_set_error_message(context, ret,
1113                                    N_("Address mismatch in "
1114                                       "the KDC certificate", ""));
1115     }
1116     return ret;
1117 }
1118
1119 static krb5_error_code
1120 pk_rd_pa_reply_enckey(krb5_context context,
1121                       int type,
1122                       const heim_octet_string *indata,
1123                       const heim_oid *dataType,
1124                       const char *realm,
1125                       krb5_pk_init_ctx ctx,
1126                       krb5_enctype etype,
1127                       const krb5_krbhst_info *hi,
1128                       unsigned nonce,
1129                       const krb5_data *req_buffer,
1130                       PA_DATA *pa,
1131                       krb5_keyblock **key)
1132 {
1133     krb5_error_code ret;
1134     struct krb5_pk_cert *host = NULL;
1135     krb5_data content;
1136     heim_oid contentType = { 0, NULL };
1137     int flags = HX509_CMS_UE_DONT_REQUIRE_KU_ENCIPHERMENT;
1138
1139     if (der_heim_oid_cmp(&asn1_oid_id_pkcs7_envelopedData, dataType)) {
1140         krb5_set_error_message(context, EINVAL,
1141                                N_("PKINIT: Invalid content type", ""));
1142         return EINVAL;
1143     }
1144
1145     if (ctx->type == PKINIT_WIN2K)
1146         flags |= HX509_CMS_UE_ALLOW_WEAK;
1147
1148     ret = hx509_cms_unenvelope(ctx->id->hx509ctx,
1149                                ctx->id->certs,
1150                                flags,
1151                                indata->data,
1152                                indata->length,
1153                                NULL,
1154                                0,
1155                                &contentType,
1156                                &content);
1157     if (ret) {
1158         pk_copy_error(context, ctx->id->hx509ctx, ret,
1159                       "Failed to unenvelope CMS data in PK-INIT reply");
1160         return ret;
1161     }
1162     der_free_oid(&contentType);
1163
1164 #if 0 /* windows LH with interesting CMS packets, leaks memory */
1165     {
1166         size_t ph = 1 + der_length_len (length);
1167         unsigned char *ptr = malloc(length + ph);
1168         size_t l;
1169
1170         memcpy(ptr + ph, p, length);
1171
1172         ret = der_put_length_and_tag (ptr + ph - 1, ph, length,
1173                                       ASN1_C_UNIV, CONS, UT_Sequence, &l);
1174         if (ret)
1175             return ret;
1176         ptr += ph - l;
1177         length += l;
1178         p = ptr;
1179     }
1180 #endif
1181
1182     /* win2k uses ContentInfo */
1183     if (type == PKINIT_WIN2K) {
1184         heim_oid type;
1185         heim_octet_string out;
1186
1187         ret = hx509_cms_unwrap_ContentInfo(&content, &type, &out, NULL);
1188         if (ret)
1189             goto out;
1190         if (der_heim_oid_cmp(&type, &asn1_oid_id_pkcs7_signedData)) {
1191             ret = EINVAL; /* XXX */
1192             krb5_set_error_message(context, ret,
1193                                    N_("PKINIT: Invalid content type", ""));
1194             der_free_oid(&type);
1195             der_free_octet_string(&out);
1196             goto out;
1197         }
1198         der_free_oid(&type);
1199         krb5_data_free(&content);
1200         ret = krb5_data_copy(&content, out.data, out.length);
1201         der_free_octet_string(&out);
1202         if (ret) {
1203             krb5_set_error_message(context, ret,
1204                                    N_("malloc: out of memory", ""));
1205             goto out;
1206         }
1207     }
1208
1209     ret = pk_verify_sign(context,
1210                          content.data,
1211                          content.length,
1212                          ctx->id,
1213                          &contentType,
1214                          &content,
1215                          &host);
1216     if (ret)
1217         goto out;
1218
1219     /* make sure that it is the kdc's certificate */
1220     ret = pk_verify_host(context, realm, hi, ctx, host);
1221     if (ret) {
1222         goto out;
1223     }
1224
1225 #if 0
1226     if (type == PKINIT_WIN2K) {
1227         if (der_heim_oid_cmp(&contentType, &asn1_oid_id_pkcs7_data) != 0) {
1228             ret = KRB5KRB_AP_ERR_MSG_TYPE;
1229             krb5_set_error_message(context, ret, "PKINIT: reply key, wrong oid");
1230             goto out;
1231         }
1232     } else {
1233         if (der_heim_oid_cmp(&contentType, &asn1_oid_id_pkrkeydata) != 0) {
1234             ret = KRB5KRB_AP_ERR_MSG_TYPE;
1235             krb5_set_error_message(context, ret, "PKINIT: reply key, wrong oid");
1236             goto out;
1237         }
1238     }
1239 #endif
1240
1241     switch(type) {
1242     case PKINIT_WIN2K:
1243         ret = get_reply_key(context, &content, req_buffer, key);
1244         if (ret != 0 && ctx->require_binding == 0)
1245             ret = get_reply_key_win(context, &content, nonce, key);
1246         break;
1247     case PKINIT_27:
1248         ret = get_reply_key(context, &content, req_buffer, key);
1249         break;
1250     }
1251     if (ret)
1252         goto out;
1253
1254     /* XXX compare given etype with key->etype */
1255
1256  out:
1257     if (host)
1258         _krb5_pk_cert_free(host);
1259     der_free_oid(&contentType);
1260     krb5_data_free(&content);
1261
1262     return ret;
1263 }
1264
1265 static krb5_error_code
1266 pk_rd_pa_reply_dh(krb5_context context,
1267                   const heim_octet_string *indata,
1268                   const heim_oid *dataType,
1269                   const char *realm,
1270                   krb5_pk_init_ctx ctx,
1271                   krb5_enctype etype,
1272                   const krb5_krbhst_info *hi,
1273                   const DHNonce *c_n,
1274                   const DHNonce *k_n,
1275                   unsigned nonce,
1276                   PA_DATA *pa,
1277                   krb5_keyblock **key)
1278 {
1279     const unsigned char *p;
1280     unsigned char *dh_gen_key = NULL;
1281     struct krb5_pk_cert *host = NULL;
1282     BIGNUM *kdc_dh_pubkey = NULL;
1283     KDCDHKeyInfo kdc_dh_info;
1284     heim_oid contentType = { 0, NULL };
1285     krb5_data content;
1286     krb5_error_code ret;
1287     int dh_gen_keylen = 0;
1288     size_t size;
1289
1290     krb5_data_zero(&content);
1291     memset(&kdc_dh_info, 0, sizeof(kdc_dh_info));
1292
1293     if (der_heim_oid_cmp(&asn1_oid_id_pkcs7_signedData, dataType)) {
1294         krb5_set_error_message(context, EINVAL,
1295                                N_("PKINIT: Invalid content type", ""));
1296         return EINVAL;
1297     }
1298
1299     ret = pk_verify_sign(context,
1300                          indata->data,
1301                          indata->length,
1302                          ctx->id,
1303                          &contentType,
1304                          &content,
1305                          &host);
1306     if (ret)
1307         goto out;
1308
1309     /* make sure that it is the kdc's certificate */
1310     ret = pk_verify_host(context, realm, hi, ctx, host);
1311     if (ret)
1312         goto out;
1313
1314     if (der_heim_oid_cmp(&contentType, &asn1_oid_id_pkdhkeydata)) {
1315         ret = KRB5KRB_AP_ERR_MSG_TYPE;
1316         krb5_set_error_message(context, ret,
1317                                N_("pkinit - dh reply contains wrong oid", ""));
1318         goto out;
1319     }
1320
1321     ret = decode_KDCDHKeyInfo(content.data,
1322                               content.length,
1323                               &kdc_dh_info,
1324                               &size);
1325
1326     if (ret) {
1327         krb5_set_error_message(context, ret,
1328                                N_("pkinit - failed to decode "
1329                                   "KDC DH Key Info", ""));
1330         goto out;
1331     }
1332
1333     if (kdc_dh_info.nonce != nonce) {
1334         ret = KRB5KRB_AP_ERR_MODIFIED;
1335         krb5_set_error_message(context, ret,
1336                                N_("PKINIT: DH nonce is wrong", ""));
1337         goto out;
1338     }
1339
1340     if (kdc_dh_info.dhKeyExpiration) {
1341         if (k_n == NULL) {
1342             ret = KRB5KRB_ERR_GENERIC;
1343             krb5_set_error_message(context, ret,
1344                                    N_("pkinit; got key expiration "
1345                                       "without server nonce", ""));
1346             goto out;
1347         }
1348         if (c_n == NULL) {
1349             ret = KRB5KRB_ERR_GENERIC;
1350             krb5_set_error_message(context, ret,
1351                                    N_("pkinit; got DH reuse but no "
1352                                       "client nonce", ""));
1353             goto out;
1354         }
1355     } else {
1356         if (k_n) {
1357             ret = KRB5KRB_ERR_GENERIC;
1358             krb5_set_error_message(context, ret,
1359                                    N_("pkinit: got server nonce "
1360                                       "without key expiration", ""));
1361             goto out;
1362         }
1363         c_n = NULL;
1364     }
1365
1366
1367     p = kdc_dh_info.subjectPublicKey.data;
1368     size = (kdc_dh_info.subjectPublicKey.length + 7) / 8;
1369
1370     if (ctx->keyex == USE_DH) {
1371         DHPublicKey k;
1372         ret = decode_DHPublicKey(p, size, &k, NULL);
1373         if (ret) {
1374             krb5_set_error_message(context, ret,
1375                                    N_("pkinit: can't decode "
1376                                       "without key expiration", ""));
1377             goto out;
1378         }
1379
1380         kdc_dh_pubkey = integer_to_BN(context, "DHPublicKey", &k);
1381         free_DHPublicKey(&k);
1382         if (kdc_dh_pubkey == NULL) {
1383             ret = ENOMEM;
1384             goto out;
1385         }
1386
1387
1388         dh_gen_keylen = DH_size(ctx->u.dh);
1389         size = BN_num_bytes(ctx->u.dh->p);
1390         if (size < dh_gen_keylen)
1391             size = dh_gen_keylen;
1392
1393         dh_gen_key = malloc(size);
1394         if (dh_gen_key == NULL) {
1395             ret = ENOMEM;
1396             krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
1397             goto out;
1398         }
1399         memset(dh_gen_key, 0, size - dh_gen_keylen);
1400         
1401         dh_gen_keylen = DH_compute_key(dh_gen_key + (size - dh_gen_keylen),
1402                                        kdc_dh_pubkey, ctx->u.dh);
1403         if (dh_gen_keylen == -1) {
1404             ret = KRB5KRB_ERR_GENERIC;
1405             dh_gen_keylen = 0;
1406             krb5_set_error_message(context, ret,
1407                                    N_("PKINIT: Can't compute Diffie-Hellman key", ""));
1408             goto out;
1409         }
1410     } else {
1411 #ifdef HAVE_OPENSSL
1412         const EC_GROUP *group;
1413         EC_KEY *public = NULL;
1414
1415         group = EC_KEY_get0_group(ctx->u.eckey);
1416
1417         public = EC_KEY_new();
1418         if (public == NULL) {
1419             ret = ENOMEM;
1420             goto out;
1421         }
1422         if (EC_KEY_set_group(public, group) != 1) {
1423             EC_KEY_free(public);
1424             ret = ENOMEM;
1425             goto out;
1426         }
1427
1428         if (o2i_ECPublicKey(&public, &p, size) == NULL) {
1429             EC_KEY_free(public);
1430             ret = KRB5KRB_ERR_GENERIC;
1431             krb5_set_error_message(context, ret,
1432                                    N_("PKINIT: Can't parse ECDH public key", ""));
1433             goto out;
1434         }
1435
1436         size = (EC_GROUP_get_degree(group) + 7) / 8;
1437         dh_gen_key = malloc(size);
1438         if (dh_gen_key == NULL) {
1439             EC_KEY_free(public);
1440             ret = ENOMEM;
1441             krb5_set_error_message(context, ret,
1442                                    N_("malloc: out of memory", ""));
1443             goto out;
1444         }
1445         dh_gen_keylen = ECDH_compute_key(dh_gen_key, size,
1446                                          EC_KEY_get0_public_key(public), ctx->u.eckey, NULL);
1447         EC_KEY_free(public);
1448         if (dh_gen_keylen == -1) {
1449             ret = KRB5KRB_ERR_GENERIC;
1450             dh_gen_keylen = 0;
1451             krb5_set_error_message(context, ret,
1452                                    N_("PKINIT: Can't compute ECDH public key", ""));
1453             goto out;
1454         }
1455 #else
1456         ret = EINVAL;
1457 #endif
1458     }
1459         
1460     if (dh_gen_keylen <= 0) {
1461         ret = EINVAL;
1462         krb5_set_error_message(context, ret,
1463                                N_("PKINIT: resulting DH key <= 0", ""));
1464         dh_gen_keylen = 0;
1465         goto out;
1466     }
1467
1468     *key = malloc (sizeof (**key));
1469     if (*key == NULL) {
1470         ret = ENOMEM;
1471         krb5_set_error_message(context, ret,
1472                                N_("malloc: out of memory", ""));
1473         goto out;
1474     }
1475
1476     ret = _krb5_pk_octetstring2key(context,
1477                                    etype,
1478                                    dh_gen_key, dh_gen_keylen,
1479                                    c_n, k_n,
1480                                    *key);
1481     if (ret) {
1482         krb5_set_error_message(context, ret,
1483                                N_("PKINIT: can't create key from DH key", ""));
1484         free(*key);
1485         *key = NULL;
1486         goto out;
1487     }
1488
1489  out:
1490     if (kdc_dh_pubkey)
1491         BN_free(kdc_dh_pubkey);
1492     if (dh_gen_key) {
1493         memset(dh_gen_key, 0, dh_gen_keylen);
1494         free(dh_gen_key);
1495     }
1496     if (host)
1497         _krb5_pk_cert_free(host);
1498     if (content.data)
1499         krb5_data_free(&content);
1500     der_free_oid(&contentType);
1501     free_KDCDHKeyInfo(&kdc_dh_info);
1502
1503     return ret;
1504 }
1505
1506 krb5_error_code KRB5_LIB_FUNCTION
1507 _krb5_pk_rd_pa_reply(krb5_context context,
1508                      const char *realm,
1509                      void *c,
1510                      krb5_enctype etype,
1511                      const krb5_krbhst_info *hi,
1512                      unsigned nonce,
1513                      const krb5_data *req_buffer,
1514                      PA_DATA *pa,
1515                      krb5_keyblock **key)
1516 {
1517     krb5_pk_init_ctx ctx = c;
1518     krb5_error_code ret;
1519     size_t size;
1520
1521     /* Check for IETF PK-INIT first */
1522     if (ctx->type == PKINIT_27) {
1523         PA_PK_AS_REP rep;
1524         heim_octet_string os, data;
1525         heim_oid oid;
1526         
1527         if (pa->padata_type != KRB5_PADATA_PK_AS_REP) {
1528             krb5_set_error_message(context, EINVAL,
1529                                    N_("PKINIT: wrong padata recv", ""));
1530             return EINVAL;
1531         }
1532
1533         ret = decode_PA_PK_AS_REP(pa->padata_value.data,
1534                                   pa->padata_value.length,
1535                                   &rep,
1536                                   &size);
1537         if (ret) {
1538             krb5_set_error_message(context, ret,
1539                                    N_("Failed to decode pkinit AS rep", ""));
1540             return ret;
1541         }
1542
1543         switch (rep.element) {
1544         case choice_PA_PK_AS_REP_dhInfo:
1545             os = rep.u.dhInfo.dhSignedData;
1546             break;
1547         case choice_PA_PK_AS_REP_encKeyPack:
1548             os = rep.u.encKeyPack;
1549             break;
1550         default: {
1551             PA_PK_AS_REP_BTMM btmm;
1552             free_PA_PK_AS_REP(&rep);
1553             memset(&rep, 0, sizeof(rep));
1554             
1555             ret = decode_PA_PK_AS_REP_BTMM(pa->padata_value.data,
1556                                            pa->padata_value.length,
1557                                            &btmm,
1558                                            &size);
1559             if (ret) {
1560                 krb5_set_error_message(context, EINVAL,
1561                                        N_("PKINIT: -27 reply "
1562                                           "invalid content type", ""));
1563                 return EINVAL;
1564             }
1565
1566             if (btmm.dhSignedData || btmm.encKeyPack == NULL) {
1567                 free_PA_PK_AS_REP_BTMM(&btmm);
1568                 ret = EINVAL;
1569                 krb5_set_error_message(context, ret,
1570                                        N_("DH mode not supported for BTMM mode", ""));
1571                 return ret;
1572             }
1573
1574             /*
1575              * Transform to IETF style PK-INIT reply so that free works below
1576              */
1577
1578             rep.element = choice_PA_PK_AS_REP_encKeyPack;
1579             rep.u.encKeyPack.data = btmm.encKeyPack->data;
1580             rep.u.encKeyPack.length = btmm.encKeyPack->length;
1581             btmm.encKeyPack->data = NULL;
1582             btmm.encKeyPack->length = 0;
1583             free_PA_PK_AS_REP_BTMM(&btmm);
1584             os = rep.u.encKeyPack;
1585         }
1586         }
1587
1588         ret = hx509_cms_unwrap_ContentInfo(&os, &oid, &data, NULL);
1589         if (ret) {
1590             free_PA_PK_AS_REP(&rep);
1591             krb5_set_error_message(context, ret,
1592                                    N_("PKINIT: failed to unwrap CI", ""));
1593             return ret;
1594         }
1595
1596         switch (rep.element) {
1597         case choice_PA_PK_AS_REP_dhInfo:
1598             ret = pk_rd_pa_reply_dh(context, &data, &oid, realm, ctx, etype, hi,
1599                                     ctx->clientDHNonce,
1600                                     rep.u.dhInfo.serverDHNonce,
1601                                     nonce, pa, key);
1602             break;
1603         case choice_PA_PK_AS_REP_encKeyPack:
1604             ret = pk_rd_pa_reply_enckey(context, PKINIT_27, &data, &oid, realm,
1605                                         ctx, etype, hi, nonce, req_buffer, pa, key);
1606             break;
1607         default:
1608             krb5_abortx(context, "pk-init as-rep case not possible to happen");
1609         }
1610         der_free_octet_string(&data);
1611         der_free_oid(&oid);
1612         free_PA_PK_AS_REP(&rep);
1613
1614     } else if (ctx->type == PKINIT_WIN2K) {
1615         PA_PK_AS_REP_Win2k w2krep;
1616
1617         /* Check for Windows encoding of the AS-REP pa data */
1618
1619 #if 0 /* should this be ? */
1620         if (pa->padata_type != KRB5_PADATA_PK_AS_REP) {
1621             krb5_set_error_message(context, EINVAL,
1622                                    "PKINIT: wrong padata recv");
1623             return EINVAL;
1624         }
1625 #endif
1626
1627         memset(&w2krep, 0, sizeof(w2krep));
1628         
1629         ret = decode_PA_PK_AS_REP_Win2k(pa->padata_value.data,
1630                                         pa->padata_value.length,
1631                                         &w2krep,
1632                                         &size);
1633         if (ret) {
1634             krb5_set_error_message(context, ret,
1635                                    N_("PKINIT: Failed decoding windows "
1636                                       "pkinit reply %d", ""), (int)ret);
1637             return ret;
1638         }
1639
1640         krb5_clear_error_message(context);
1641         
1642         switch (w2krep.element) {
1643         case choice_PA_PK_AS_REP_Win2k_encKeyPack: {
1644             heim_octet_string data;
1645             heim_oid oid;
1646         
1647             ret = hx509_cms_unwrap_ContentInfo(&w2krep.u.encKeyPack,
1648                                                &oid, &data, NULL);
1649             free_PA_PK_AS_REP_Win2k(&w2krep);
1650             if (ret) {
1651                 krb5_set_error_message(context, ret,
1652                                        N_("PKINIT: failed to unwrap CI", ""));
1653                 return ret;
1654             }
1655
1656             ret = pk_rd_pa_reply_enckey(context, PKINIT_WIN2K, &data, &oid, realm,
1657                                         ctx, etype, hi, nonce, req_buffer, pa, key);
1658             der_free_octet_string(&data);
1659             der_free_oid(&oid);
1660
1661             break;
1662         }
1663         default:
1664             free_PA_PK_AS_REP_Win2k(&w2krep);
1665             ret = EINVAL;
1666             krb5_set_error_message(context, ret,
1667                                    N_("PKINIT: win2k reply invalid "
1668                                       "content type", ""));
1669             break;
1670         }
1671
1672     } else {
1673         ret = EINVAL;
1674         krb5_set_error_message(context, ret,
1675                                N_("PKINIT: unknown reply type", ""));
1676     }
1677
1678     return ret;
1679 }
1680
1681 struct prompter {
1682     krb5_context context;
1683     krb5_prompter_fct prompter;
1684     void *prompter_data;
1685 };
1686
1687 static int
1688 hx_pass_prompter(void *data, const hx509_prompt *prompter)
1689 {
1690     krb5_error_code ret;
1691     krb5_prompt prompt;
1692     krb5_data password_data;
1693     struct prompter *p = data;
1694
1695     password_data.data   = prompter->reply.data;
1696     password_data.length = prompter->reply.length;
1697
1698     prompt.prompt = prompter->prompt;
1699     prompt.hidden = hx509_prompt_hidden(prompter->type);
1700     prompt.reply  = &password_data;
1701
1702     switch (prompter->type) {
1703     case HX509_PROMPT_TYPE_INFO:
1704         prompt.type   = KRB5_PROMPT_TYPE_INFO;
1705         break;
1706     case HX509_PROMPT_TYPE_PASSWORD:
1707     case HX509_PROMPT_TYPE_QUESTION:
1708     default:
1709         prompt.type   = KRB5_PROMPT_TYPE_PASSWORD;
1710         break;
1711     }   
1712
1713     ret = (*p->prompter)(p->context, p->prompter_data, NULL, NULL, 1, &prompt);
1714     if (ret) {
1715         memset (prompter->reply.data, 0, prompter->reply.length);
1716         return 1;
1717     }
1718     return 0;
1719 }
1720
1721 krb5_error_code KRB5_LIB_FUNCTION
1722 _krb5_pk_load_id(krb5_context context,
1723                  struct krb5_pk_identity **ret_id,
1724                  int flags,
1725                  const char *user_id,
1726                  const char *anchor_id,
1727                  char * const *chain_list,
1728                  char * const *revoke_list,
1729                  krb5_prompter_fct prompter,
1730                  void *prompter_data,
1731                  char *password)
1732 {
1733     struct krb5_pk_identity *id = NULL;
1734     hx509_lock lock = NULL;
1735     struct prompter p;
1736     int ret;
1737
1738     *ret_id = NULL;
1739
1740     if (anchor_id == NULL) {
1741         krb5_set_error_message(context, HEIM_PKINIT_NO_VALID_CA,
1742                                N_("PKINIT: No anchor given", ""));
1743         return HEIM_PKINIT_NO_VALID_CA;
1744     }
1745
1746     if (user_id == NULL && (flags & 4) == 0) {
1747         krb5_set_error_message(context, HEIM_PKINIT_NO_PRIVATE_KEY,
1748                                N_("PKINIT: No user certificate given", ""));
1749         return HEIM_PKINIT_NO_PRIVATE_KEY;
1750     }
1751
1752     /* load cert */
1753
1754     id = calloc(1, sizeof(*id));
1755     if (id == NULL) {
1756         krb5_set_error_message(context, ENOMEM,
1757                                N_("malloc: out of memory", ""));
1758         return ENOMEM;
1759     }   
1760
1761     ret = hx509_context_init(&id->hx509ctx);
1762     if (ret)
1763         goto out;
1764
1765     ret = hx509_lock_init(id->hx509ctx, &lock);
1766     if (ret) {
1767         pk_copy_error(context, id->hx509ctx, ret, "Failed init lock");
1768         goto out;
1769     }
1770
1771     if (password && password[0])
1772         hx509_lock_add_password(lock, password);
1773
1774     if (prompter) {
1775         p.context = context;
1776         p.prompter = prompter;
1777         p.prompter_data = prompter_data;
1778
1779         ret = hx509_lock_set_prompter(lock, hx_pass_prompter, &p);
1780         if (ret)
1781             goto out;
1782     }
1783
1784     if (user_id) {
1785         ret = hx509_certs_init(id->hx509ctx, user_id, 0, lock, &id->certs);
1786         if (ret) {
1787             pk_copy_error(context, id->hx509ctx, ret,
1788                           "Failed to init cert certs");
1789             goto out;
1790         }
1791     } else {
1792         id->certs = NULL;
1793     }
1794
1795     ret = hx509_certs_init(id->hx509ctx, anchor_id, 0, NULL, &id->anchors);
1796     if (ret) {
1797         pk_copy_error(context, id->hx509ctx, ret,
1798                       "Failed to init anchors");
1799         goto out;
1800     }
1801
1802     ret = hx509_certs_init(id->hx509ctx, "MEMORY:pkinit-cert-chain",
1803                            0, NULL, &id->certpool);
1804     if (ret) {
1805         pk_copy_error(context, id->hx509ctx, ret,
1806                       "Failed to init chain");
1807         goto out;
1808     }
1809
1810     while (chain_list && *chain_list) {
1811         ret = hx509_certs_append(id->hx509ctx, id->certpool,
1812                                  NULL, *chain_list);
1813         if (ret) {
1814             pk_copy_error(context, id->hx509ctx, ret,
1815                           "Failed to laod chain %s",
1816                           *chain_list);
1817             goto out;
1818         }
1819         chain_list++;
1820     }
1821
1822     if (revoke_list) {
1823         ret = hx509_revoke_init(id->hx509ctx, &id->revokectx);
1824         if (ret) {
1825             pk_copy_error(context, id->hx509ctx, ret,
1826                           "Failed init revoke list");
1827             goto out;
1828         }
1829
1830         while (*revoke_list) {
1831             ret = hx509_revoke_add_crl(id->hx509ctx,
1832                                        id->revokectx,
1833                                        *revoke_list);
1834             if (ret) {
1835                 pk_copy_error(context, id->hx509ctx, ret,
1836                               "Failed load revoke list");
1837                 goto out;
1838             }
1839             revoke_list++;
1840         }
1841     } else
1842         hx509_context_set_missing_revoke(id->hx509ctx, 1);
1843
1844     ret = hx509_verify_init_ctx(id->hx509ctx, &id->verify_ctx);
1845     if (ret) {
1846         pk_copy_error(context, id->hx509ctx, ret,
1847                       "Failed init verify context");
1848         goto out;
1849     }
1850
1851     hx509_verify_attach_anchors(id->verify_ctx, id->anchors);
1852     hx509_verify_attach_revoke(id->verify_ctx, id->revokectx);
1853
1854  out:
1855     if (ret) {
1856         hx509_verify_destroy_ctx(id->verify_ctx);
1857         hx509_certs_free(&id->certs);
1858         hx509_certs_free(&id->anchors);
1859         hx509_certs_free(&id->certpool);
1860         hx509_revoke_free(&id->revokectx);
1861         hx509_context_free(&id->hx509ctx);
1862         free(id);
1863     } else
1864         *ret_id = id;
1865
1866     if (lock)
1867         hx509_lock_free(lock);
1868
1869     return ret;
1870 }
1871
1872 /*
1873  *
1874  */
1875
1876 static void
1877 pk_copy_error(krb5_context context,
1878               hx509_context hx509ctx,
1879               int hxret,
1880               const char *fmt,
1881               ...)
1882 {
1883     va_list va;
1884     char *s, *f;
1885
1886     va_start(va, fmt);
1887     vasprintf(&f, fmt, va);
1888     va_end(va);
1889     if (f == NULL) {
1890         krb5_clear_error_message(context);
1891         return;
1892     }
1893
1894     s = hx509_get_error_string(hx509ctx, hxret);
1895     if (s == NULL) {
1896         krb5_clear_error_message(context);
1897         free(f);
1898         return;
1899     }
1900     krb5_set_error_message(context, hxret, "%s: %s", f, s);
1901     free(s);
1902     free(f);
1903 }
1904
1905 static int
1906 parse_integer(krb5_context context, char **p, const char *file, int lineno,
1907               const char *name, heim_integer *integer)
1908 {
1909     int ret;
1910     char *p1;
1911     p1 = strsep(p, " \t");
1912     if (p1 == NULL) {
1913         krb5_set_error_message(context, EINVAL,
1914                                N_("moduli file %s missing %s on line %d", ""),
1915                                file, name, lineno);
1916         return EINVAL;
1917     }
1918     ret = der_parse_hex_heim_integer(p1, integer);
1919     if (ret) {
1920         krb5_set_error_message(context, ret,
1921                                N_("moduli file %s failed parsing %s "
1922                                   "on line %d", ""),
1923                                file, name, lineno);
1924         return ret;
1925     }
1926
1927     return 0;
1928 }
1929
1930 krb5_error_code
1931 _krb5_parse_moduli_line(krb5_context context,
1932                         const char *file,
1933                         int lineno,
1934                         char *p,
1935                         struct krb5_dh_moduli **m)
1936 {
1937     struct krb5_dh_moduli *m1;
1938     char *p1;
1939     int ret;
1940
1941     *m = NULL;
1942
1943     m1 = calloc(1, sizeof(*m1));
1944     if (m1 == NULL) {
1945         krb5_set_error_message(context, ENOMEM,
1946                                N_("malloc: out of memory", ""));
1947         return ENOMEM;
1948     }
1949
1950     while (isspace((unsigned char)*p))
1951         p++;
1952     if (*p  == '#') {
1953         free(m1);
1954         return 0;
1955     }
1956     ret = EINVAL;
1957
1958     p1 = strsep(&p, " \t");
1959     if (p1 == NULL) {
1960         krb5_set_error_message(context, ret,
1961                                N_("moduli file %s missing name on line %d", ""),
1962                                file, lineno);
1963         goto out;
1964     }
1965     m1->name = strdup(p1);
1966     if (m1->name == NULL) {
1967         ret = ENOMEM;
1968         krb5_set_error_message(context, ret, N_("malloc: out of memeory", ""));
1969         goto out;
1970     }
1971
1972     p1 = strsep(&p, " \t");
1973     if (p1 == NULL) {
1974         krb5_set_error_message(context, ret,
1975                                N_("moduli file %s missing bits on line %d", ""),
1976                                file, lineno);
1977         goto out;
1978     }
1979
1980     m1->bits = atoi(p1);
1981     if (m1->bits == 0) {
1982         krb5_set_error_message(context, ret,
1983                                N_("moduli file %s have un-parsable "
1984                                   "bits on line %d", ""), file, lineno);
1985         goto out;
1986     }
1987         
1988     ret = parse_integer(context, &p, file, lineno, "p", &m1->p);
1989     if (ret)
1990         goto out;
1991     ret = parse_integer(context, &p, file, lineno, "g", &m1->g);
1992     if (ret)
1993         goto out;
1994     ret = parse_integer(context, &p, file, lineno, "q", &m1->q);
1995     if (ret)
1996         goto out;
1997
1998     *m = m1;
1999
2000     return 0;
2001  out:
2002     free(m1->name);
2003     der_free_heim_integer(&m1->p);
2004     der_free_heim_integer(&m1->g);
2005     der_free_heim_integer(&m1->q);
2006     free(m1);
2007     return ret;
2008 }
2009
2010 void
2011 _krb5_free_moduli(struct krb5_dh_moduli **moduli)
2012 {
2013     int i;
2014     for (i = 0; moduli[i] != NULL; i++) {
2015         free(moduli[i]->name);
2016         der_free_heim_integer(&moduli[i]->p);
2017         der_free_heim_integer(&moduli[i]->g);
2018         der_free_heim_integer(&moduli[i]->q);
2019         free(moduli[i]);
2020     }
2021     free(moduli);
2022 }
2023
2024 static const char *default_moduli_RFC2412_MODP_group2 =
2025     /* name */
2026     "RFC2412-MODP-group2 "
2027     /* bits */
2028     "1024 "
2029     /* p */
2030     "FFFFFFFF" "FFFFFFFF" "C90FDAA2" "2168C234" "C4C6628B" "80DC1CD1"
2031     "29024E08" "8A67CC74" "020BBEA6" "3B139B22" "514A0879" "8E3404DD"
2032     "EF9519B3" "CD3A431B" "302B0A6D" "F25F1437" "4FE1356D" "6D51C245"
2033     "E485B576" "625E7EC6" "F44C42E9" "A637ED6B" "0BFF5CB6" "F406B7ED"
2034     "EE386BFB" "5A899FA5" "AE9F2411" "7C4B1FE6" "49286651" "ECE65381"
2035     "FFFFFFFF" "FFFFFFFF "
2036     /* g */
2037     "02 "
2038     /* q */
2039     "7FFFFFFF" "FFFFFFFF" "E487ED51" "10B4611A" "62633145" "C06E0E68"
2040     "94812704" "4533E63A" "0105DF53" "1D89CD91" "28A5043C" "C71A026E"
2041     "F7CA8CD9" "E69D218D" "98158536" "F92F8A1B" "A7F09AB6" "B6A8E122"
2042     "F242DABB" "312F3F63" "7A262174" "D31BF6B5" "85FFAE5B" "7A035BF6"
2043     "F71C35FD" "AD44CFD2" "D74F9208" "BE258FF3" "24943328" "F67329C0"
2044     "FFFFFFFF" "FFFFFFFF";
2045
2046 static const char *default_moduli_rfc3526_MODP_group14 =
2047     /* name */
2048     "rfc3526-MODP-group14 "
2049     /* bits */
2050     "1760 "
2051     /* p */
2052     "FFFFFFFF" "FFFFFFFF" "C90FDAA2" "2168C234" "C4C6628B" "80DC1CD1"
2053     "29024E08" "8A67CC74" "020BBEA6" "3B139B22" "514A0879" "8E3404DD"
2054     "EF9519B3" "CD3A431B" "302B0A6D" "F25F1437" "4FE1356D" "6D51C245"
2055     "E485B576" "625E7EC6" "F44C42E9" "A637ED6B" "0BFF5CB6" "F406B7ED"
2056     "EE386BFB" "5A899FA5" "AE9F2411" "7C4B1FE6" "49286651" "ECE45B3D"
2057     "C2007CB8" "A163BF05" "98DA4836" "1C55D39A" "69163FA8" "FD24CF5F"
2058     "83655D23" "DCA3AD96" "1C62F356" "208552BB" "9ED52907" "7096966D"
2059     "670C354E" "4ABC9804" "F1746C08" "CA18217C" "32905E46" "2E36CE3B"
2060     "E39E772C" "180E8603" "9B2783A2" "EC07A28F" "B5C55DF0" "6F4C52C9"
2061     "DE2BCBF6" "95581718" "3995497C" "EA956AE5" "15D22618" "98FA0510"
2062     "15728E5A" "8AACAA68" "FFFFFFFF" "FFFFFFFF "
2063     /* g */
2064     "02 "
2065     /* q */
2066     "7FFFFFFF" "FFFFFFFF" "E487ED51" "10B4611A" "62633145" "C06E0E68"
2067     "94812704" "4533E63A" "0105DF53" "1D89CD91" "28A5043C" "C71A026E"
2068     "F7CA8CD9" "E69D218D" "98158536" "F92F8A1B" "A7F09AB6" "B6A8E122"
2069     "F242DABB" "312F3F63" "7A262174" "D31BF6B5" "85FFAE5B" "7A035BF6"
2070     "F71C35FD" "AD44CFD2" "D74F9208" "BE258FF3" "24943328" "F6722D9E"
2071     "E1003E5C" "50B1DF82" "CC6D241B" "0E2AE9CD" "348B1FD4" "7E9267AF"
2072     "C1B2AE91" "EE51D6CB" "0E3179AB" "1042A95D" "CF6A9483" "B84B4B36"
2073     "B3861AA7" "255E4C02" "78BA3604" "650C10BE" "19482F23" "171B671D"
2074     "F1CF3B96" "0C074301" "CD93C1D1" "7603D147" "DAE2AEF8" "37A62964"
2075     "EF15E5FB" "4AAC0B8C" "1CCAA4BE" "754AB572" "8AE9130C" "4C7D0288"
2076     "0AB9472D" "45565534" "7FFFFFFF" "FFFFFFFF";
2077
2078 krb5_error_code
2079 _krb5_parse_moduli(krb5_context context, const char *file,
2080                    struct krb5_dh_moduli ***moduli)
2081 {
2082     /* name bits P G Q */
2083     krb5_error_code ret;
2084     struct krb5_dh_moduli **m = NULL, **m2;
2085     char buf[4096];
2086     FILE *f;
2087     int lineno = 0, n = 0;
2088
2089     *moduli = NULL;
2090
2091     m = calloc(1, sizeof(m[0]) * 3);
2092     if (m == NULL) {
2093         krb5_set_error_message(context, ENOMEM,
2094                                N_("malloc: out of memory", ""));
2095         return ENOMEM;
2096     }
2097
2098     strlcpy(buf, default_moduli_rfc3526_MODP_group14, sizeof(buf));
2099     ret = _krb5_parse_moduli_line(context, "builtin", 1, buf,  &m[0]);
2100     if (ret) {
2101         _krb5_free_moduli(m);
2102         return ret;
2103     }
2104     n++;
2105
2106     strlcpy(buf, default_moduli_RFC2412_MODP_group2, sizeof(buf));
2107     ret = _krb5_parse_moduli_line(context, "builtin", 1, buf,  &m[1]);
2108     if (ret) {
2109         _krb5_free_moduli(m);
2110         return ret;
2111     }
2112     n++;
2113
2114
2115     if (file == NULL)
2116         file = MODULI_FILE;
2117
2118     f = fopen(file, "r");
2119     if (f == NULL) {
2120         *moduli = m;
2121         return 0;
2122     }
2123     rk_cloexec_file(f);
2124
2125     while(fgets(buf, sizeof(buf), f) != NULL) {
2126         struct krb5_dh_moduli *element;
2127
2128         buf[strcspn(buf, "\n")] = '\0';
2129         lineno++;
2130
2131         m2 = realloc(m, (n + 2) * sizeof(m[0]));
2132         if (m2 == NULL) {
2133             _krb5_free_moduli(m);
2134             krb5_set_error_message(context, ENOMEM,
2135                                    N_("malloc: out of memory", ""));
2136             return ENOMEM;
2137         }
2138         m = m2;
2139         
2140         m[n] = NULL;
2141
2142         ret = _krb5_parse_moduli_line(context, file, lineno, buf,  &element);
2143         if (ret) {
2144             _krb5_free_moduli(m);
2145             return ret;
2146         }
2147         if (element == NULL)
2148             continue;
2149
2150         m[n] = element;
2151         m[n + 1] = NULL;
2152         n++;
2153     }
2154     *moduli = m;
2155     return 0;
2156 }
2157
2158 krb5_error_code
2159 _krb5_dh_group_ok(krb5_context context, unsigned long bits,
2160                   heim_integer *p, heim_integer *g, heim_integer *q,
2161                   struct krb5_dh_moduli **moduli,
2162                   char **name)
2163 {
2164     int i;
2165
2166     if (name)
2167         *name = NULL;
2168
2169     for (i = 0; moduli[i] != NULL; i++) {
2170         if (der_heim_integer_cmp(&moduli[i]->g, g) == 0 &&
2171             der_heim_integer_cmp(&moduli[i]->p, p) == 0 &&
2172             (q == NULL || der_heim_integer_cmp(&moduli[i]->q, q) == 0))
2173             {
2174                 if (bits && bits > moduli[i]->bits) {
2175                     krb5_set_error_message(context,
2176                                            KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED,
2177                                            N_("PKINIT: DH group parameter %s "
2178                                               "no accepted, not enough bits "
2179                                               "generated", ""),
2180                                            moduli[i]->name);
2181                     return KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED;
2182                 }
2183                 if (name)
2184                     *name = strdup(moduli[i]->name);
2185                 return 0;
2186             }
2187     }
2188     krb5_set_error_message(context,
2189                            KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED,
2190                            N_("PKINIT: DH group parameter no ok", ""));
2191     return KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED;
2192 }
2193 #endif /* PKINIT */
2194
2195 void KRB5_LIB_FUNCTION
2196 _krb5_get_init_creds_opt_free_pkinit(krb5_get_init_creds_opt *opt)
2197 {
2198 #ifdef PKINIT
2199     krb5_pk_init_ctx ctx;
2200
2201     if (opt->opt_private == NULL || opt->opt_private->pk_init_ctx == NULL)
2202         return;
2203     ctx = opt->opt_private->pk_init_ctx;
2204     switch (ctx->keyex) {
2205     case USE_DH:
2206         DH_free(ctx->u.dh);
2207         break;
2208     case USE_RSA:
2209         break;
2210     case USE_ECDH: 
2211 #ifdef HAVE_OPENSSL
2212         EC_KEY_free(ctx->u.eckey);
2213 #endif
2214         break;
2215     }
2216     if (ctx->id) {
2217         hx509_verify_destroy_ctx(ctx->id->verify_ctx);
2218         hx509_certs_free(&ctx->id->certs);
2219         hx509_cert_free(ctx->id->cert);
2220         hx509_certs_free(&ctx->id->anchors);
2221         hx509_certs_free(&ctx->id->certpool);
2222         hx509_context_free(&ctx->id->hx509ctx);
2223
2224         if (ctx->clientDHNonce) {
2225             krb5_free_data(NULL, ctx->clientDHNonce);
2226             ctx->clientDHNonce = NULL;
2227         }
2228         if (ctx->m)
2229             _krb5_free_moduli(ctx->m);
2230         free(ctx->id);
2231         ctx->id = NULL;
2232     }
2233     free(opt->opt_private->pk_init_ctx);
2234     opt->opt_private->pk_init_ctx = NULL;
2235 #endif
2236 }
2237
2238 krb5_error_code KRB5_LIB_FUNCTION
2239 krb5_get_init_creds_opt_set_pkinit(krb5_context context,
2240                                    krb5_get_init_creds_opt *opt,
2241                                    krb5_principal principal,
2242                                    const char *user_id,
2243                                    const char *x509_anchors,
2244                                    char * const * pool,
2245                                    char * const * pki_revoke,
2246                                    int flags,
2247                                    krb5_prompter_fct prompter,
2248                                    void *prompter_data,
2249                                    char *password)
2250 {
2251 #ifdef PKINIT
2252     krb5_error_code ret;
2253     char *anchors = NULL;
2254
2255     if (opt->opt_private == NULL) {
2256         krb5_set_error_message(context, EINVAL,
2257                                N_("PKINIT: on non extendable opt", ""));
2258         return EINVAL;
2259     }
2260
2261     opt->opt_private->pk_init_ctx =
2262         calloc(1, sizeof(*opt->opt_private->pk_init_ctx));
2263     if (opt->opt_private->pk_init_ctx == NULL) {
2264         krb5_set_error_message(context, ENOMEM,
2265                                N_("malloc: out of memory", ""));
2266         return ENOMEM;
2267     }
2268     opt->opt_private->pk_init_ctx->require_binding = 0;
2269     opt->opt_private->pk_init_ctx->require_eku = 1;
2270     opt->opt_private->pk_init_ctx->require_krbtgt_otherName = 1;
2271     opt->opt_private->pk_init_ctx->peer = NULL;
2272
2273     /* XXX implement krb5_appdefault_strings  */
2274     if (pool == NULL)
2275         pool = krb5_config_get_strings(context, NULL,
2276                                        "appdefaults",
2277                                        "pkinit_pool",
2278                                        NULL);
2279
2280     if (pki_revoke == NULL)
2281         pki_revoke = krb5_config_get_strings(context, NULL,
2282                                              "appdefaults",
2283                                              "pkinit_revoke",
2284                                              NULL);
2285
2286     if (x509_anchors == NULL) {
2287         krb5_appdefault_string(context, "kinit",
2288                                krb5_principal_get_realm(context, principal),
2289                                "pkinit_anchors", NULL, &anchors);
2290         x509_anchors = anchors;
2291     }
2292
2293     ret = _krb5_pk_load_id(context,
2294                            &opt->opt_private->pk_init_ctx->id,
2295                            flags,
2296                            user_id,
2297                            x509_anchors,
2298                            pool,
2299                            pki_revoke,
2300                            prompter,
2301                            prompter_data,
2302                            password);
2303     if (ret) {
2304         free(opt->opt_private->pk_init_ctx);
2305         opt->opt_private->pk_init_ctx = NULL;
2306         return ret;
2307     }
2308
2309     if (opt->opt_private->pk_init_ctx->id->certs) {
2310         hx509_query *q = NULL;
2311         hx509_cert cert = NULL;
2312         hx509_context hx509ctx = opt->opt_private->pk_init_ctx->id->hx509ctx;
2313
2314         ret = hx509_query_alloc(hx509ctx, &q);
2315         if (ret) {
2316             pk_copy_error(context, hx509ctx, ret,
2317                           "Allocate query to find signing certificate");
2318             return ret;
2319         }
2320         
2321         hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY);
2322         hx509_query_match_option(q, HX509_QUERY_OPTION_KU_DIGITALSIGNATURE);
2323         
2324         ret = find_cert(context, opt->opt_private->pk_init_ctx->id, q, &cert);
2325         hx509_query_free(hx509ctx, q);
2326         if (ret)
2327             return ret;
2328
2329         opt->opt_private->pk_init_ctx->id->cert = cert;
2330     } else
2331         opt->opt_private->pk_init_ctx->id->cert = NULL;
2332
2333     if ((flags & 2) == 0) {
2334         hx509_context hx509ctx = opt->opt_private->pk_init_ctx->id->hx509ctx;
2335         hx509_cert cert = opt->opt_private->pk_init_ctx->id->cert;
2336
2337         opt->opt_private->pk_init_ctx->keyex = USE_DH;
2338
2339         /*
2340          * If its a ECDSA certs, lets select ECDSA as the keyex algorithm.
2341          */
2342         if (cert) {
2343             AlgorithmIdentifier alg;
2344
2345             ret = hx509_cert_get_SPKI_AlgorithmIdentifier(hx509ctx, cert, &alg);
2346             if (ret == 0) {
2347                 if (der_heim_oid_cmp(&alg.algorithm, &asn1_oid_id_ecPublicKey) == 0)
2348                     opt->opt_private->pk_init_ctx->keyex = USE_ECDH;
2349                 free_AlgorithmIdentifier(&alg);
2350             }
2351         }
2352
2353     } else {
2354         opt->opt_private->pk_init_ctx->keyex = USE_RSA;
2355
2356         if (opt->opt_private->pk_init_ctx->id->certs == NULL) {
2357             krb5_set_error_message(context, EINVAL,
2358                                    N_("No anonymous pkinit support in RSA mode", ""));
2359             return EINVAL;
2360         }           
2361     }
2362
2363     return 0;
2364 #else
2365     krb5_set_error_message(context, EINVAL,
2366                            N_("no support for PKINIT compiled in", ""));
2367     return EINVAL;
2368 #endif
2369 }
2370
2371 #ifdef PKINIT
2372
2373 static int
2374 get_ms_san(hx509_context context, hx509_cert cert, char **upn)
2375 {
2376     hx509_octet_string_list list;
2377     int ret;
2378
2379     *upn = NULL;
2380
2381     ret = hx509_cert_find_subjectAltName_otherName(context,
2382                                                    cert,
2383                                                    &asn1_oid_id_pkinit_ms_san,
2384                                                    &list);
2385     if (ret)
2386         return 0;
2387
2388     if (list.len > 0 && list.val[0].length > 0)
2389         ret = decode_MS_UPN_SAN(list.val[0].data, list.val[0].length,
2390                                 upn, NULL);
2391     else
2392         ret = 1;
2393     hx509_free_octet_string_list(&list);           
2394
2395     return ret;
2396 }
2397
2398 static int
2399 find_ms_san(hx509_context context, hx509_cert cert, void *ctx)
2400 {
2401     char *upn;
2402     int ret;
2403
2404     ret = get_ms_san(context, cert, &upn);
2405     if (ret == 0)
2406         free(upn);
2407     return ret;
2408 }
2409
2410
2411
2412 #endif
2413
2414 /*
2415  * Private since it need to be redesigned using krb5_get_init_creds()
2416  */
2417
2418 krb5_error_code  KRB5_LIB_FUNCTION
2419 _krb5_pk_enterprise_cert(krb5_context context,
2420                          const char *user_id,
2421                          krb5_const_realm realm,
2422                          krb5_principal *principal)
2423 {
2424 #ifdef PKINIT
2425     krb5_error_code ret;
2426     hx509_context hx509ctx;
2427     hx509_certs certs, result;
2428     hx509_cert cert;
2429     hx509_query *q;
2430     char *name;
2431
2432     *principal = NULL;
2433     
2434     if (user_id == NULL)
2435         return ENOENT;
2436
2437     ret = hx509_context_init(&hx509ctx);
2438     if (ret)
2439         return ret;
2440
2441     ret = hx509_certs_init(hx509ctx, user_id, 0, NULL, &certs);
2442     if (ret) {
2443         pk_copy_error(context, hx509ctx, ret,
2444                       "Failed to init cert certs");
2445         return ret;
2446     }
2447
2448     ret = hx509_query_alloc(hx509ctx, &q);
2449     if (ret) {
2450         hx509_certs_free(&certs);
2451         return ret;
2452     }
2453
2454     hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY);
2455     hx509_query_match_option(q, HX509_QUERY_OPTION_KU_DIGITALSIGNATURE);
2456     hx509_query_match_eku(q, &asn1_oid_id_pkinit_ms_eku);
2457     hx509_query_match_cmp_func(q, find_ms_san, NULL);
2458
2459     ret = hx509_certs_filter(hx509ctx, certs, q, &result);
2460     hx509_query_free(hx509ctx, q);
2461     hx509_certs_free(&certs);
2462     if (ret)
2463         return ret;
2464     
2465     ret = hx509_get_one_cert(hx509ctx, result, &cert);
2466     hx509_certs_free(&result);
2467     if (ret)
2468         return ret;
2469
2470     ret = get_ms_san(hx509ctx, cert, &name);
2471     if (ret)
2472         return ret;
2473
2474     ret = krb5_make_principal(context, principal, realm, name, NULL);
2475     free(name);
2476     hx509_context_free(&hx509ctx);
2477     if (ret)
2478         return ret;
2479
2480     krb5_principal_set_type(context, *principal, KRB5_NT_ENTERPRISE_PRINCIPAL);
2481     
2482     return ret;
2483 #else
2484     krb5_set_error_message(context, EINVAL,
2485                            N_("no support for PKINIT compiled in", ""));
2486     return EINVAL;
2487 #endif
2488 }