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