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