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