]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - crypto/heimdal/kdc/pkinit.c
Upgrade to 1.8.1.
[FreeBSD/FreeBSD.git] / crypto / heimdal / kdc / pkinit.c
1 /*
2  * Copyright (c) 2003 - 2008 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 "kdc_locl.h"
37
38 #ifdef PKINIT
39
40 #include <heim_asn1.h>
41 #include <rfc2459_asn1.h>
42 #include <cms_asn1.h>
43 #include <pkinit_asn1.h>
44
45 #include <hx509.h>
46 #include "crypto-headers.h"
47
48 struct pk_client_params {
49     enum krb5_pk_type type;
50     enum { USE_RSA, USE_DH, USE_ECDH } keyex;
51     union {
52         struct {
53             BIGNUM *public_key;
54             DH *key;
55         } dh;
56 #ifdef HAVE_OPENSSL
57         struct {
58             EC_KEY *public_key;
59             EC_KEY *key;
60         } ecdh;
61 #endif
62     } u;
63     hx509_cert cert;
64     unsigned nonce;
65     EncryptionKey reply_key;
66     char *dh_group_name;
67     hx509_peer_info peer;
68     hx509_certs client_anchors;
69     hx509_verify_ctx verify_ctx;
70 };
71
72 struct pk_principal_mapping {
73     unsigned int len;
74     struct pk_allowed_princ {
75         krb5_principal principal;
76         char *subject;
77     } *val;
78 };
79
80 static struct krb5_pk_identity *kdc_identity;
81 static struct pk_principal_mapping principal_mappings;
82 static struct krb5_dh_moduli **moduli;
83
84 static struct {
85     krb5_data data;
86     time_t expire;
87     time_t next_update;
88 } ocsp;
89
90 /*
91  *
92  */
93
94 static krb5_error_code
95 pk_check_pkauthenticator_win2k(krb5_context context,
96                                PKAuthenticator_Win2k *a,
97                                const KDC_REQ *req)
98 {
99     krb5_timestamp now;
100
101     krb5_timeofday (context, &now);
102
103     /* XXX cusec */
104     if (a->ctime == 0 || abs(a->ctime - now) > context->max_skew) {
105         krb5_clear_error_message(context);
106         return KRB5KRB_AP_ERR_SKEW;
107     }
108     return 0;
109 }
110
111 static krb5_error_code
112 pk_check_pkauthenticator(krb5_context context,
113                          PKAuthenticator *a,
114                          const KDC_REQ *req)
115 {
116     u_char *buf = NULL;
117     size_t buf_size;
118     krb5_error_code ret;
119     size_t len = 0;
120     krb5_timestamp now;
121     Checksum checksum;
122
123     krb5_timeofday (context, &now);
124
125     /* XXX cusec */
126     if (a->ctime == 0 || abs(a->ctime - now) > context->max_skew) {
127         krb5_clear_error_message(context);
128         return KRB5KRB_AP_ERR_SKEW;
129     }
130
131     ASN1_MALLOC_ENCODE(KDC_REQ_BODY, buf, buf_size, &req->req_body, &len, ret);
132     if (ret) {
133         krb5_clear_error_message(context);
134         return ret;
135     }
136     if (buf_size != len)
137         krb5_abortx(context, "Internal error in ASN.1 encoder");
138
139     ret = krb5_create_checksum(context,
140                                NULL,
141                                0,
142                                CKSUMTYPE_SHA1,
143                                buf,
144                                len,
145                                &checksum);
146     free(buf);
147     if (ret) {
148         krb5_clear_error_message(context);
149         return ret;
150     }
151
152     if (a->paChecksum == NULL) {
153         krb5_clear_error_message(context);
154         ret = KRB5_KDC_ERR_PA_CHECKSUM_MUST_BE_INCLUDED;
155         goto out;
156     }
157
158     if (der_heim_octet_string_cmp(a->paChecksum, &checksum.checksum) != 0) {
159         krb5_clear_error_message(context);
160         ret = KRB5KRB_ERR_GENERIC;
161     }
162
163 out:
164     free_Checksum(&checksum);
165
166     return ret;
167 }
168
169 void
170 _kdc_pk_free_client_param(krb5_context context, pk_client_params *cp)
171 {
172     if (cp == NULL)
173         return;
174     if (cp->cert)
175         hx509_cert_free(cp->cert);
176     if (cp->verify_ctx)
177         hx509_verify_destroy_ctx(cp->verify_ctx);
178     if (cp->keyex == USE_DH) {
179         if (cp->u.dh.key)
180             DH_free(cp->u.dh.key);
181         if (cp->u.dh.public_key)
182             BN_free(cp->u.dh.public_key);
183     }
184 #ifdef HAVE_OPENSSL
185     if (cp->keyex == USE_ECDH) {
186         if (cp->u.ecdh.key)
187             EC_KEY_free(cp->u.ecdh.key);
188         if (cp->u.ecdh.public_key)
189             EC_KEY_free(cp->u.ecdh.public_key);
190     }
191 #endif
192     krb5_free_keyblock_contents(context, &cp->reply_key);
193     if (cp->dh_group_name)
194         free(cp->dh_group_name);
195     if (cp->peer)
196         hx509_peer_info_free(cp->peer);
197     if (cp->client_anchors)
198         hx509_certs_free(&cp->client_anchors);
199     memset(cp, 0, sizeof(*cp));
200     free(cp);
201 }
202
203 static krb5_error_code
204 generate_dh_keyblock(krb5_context context,
205                      pk_client_params *client_params,
206                      krb5_enctype enctype)
207 {
208     unsigned char *dh_gen_key = NULL;
209     krb5_keyblock key;
210     krb5_error_code ret;
211     size_t dh_gen_keylen, size;
212
213     memset(&key, 0, sizeof(key));
214
215     if (client_params->keyex == USE_DH) {
216
217         if (client_params->u.dh.public_key == NULL) {
218             ret = KRB5KRB_ERR_GENERIC;
219             krb5_set_error_message(context, ret, "public_key");
220             goto out;
221         }
222
223         if (!DH_generate_key(client_params->u.dh.key)) {
224             ret = KRB5KRB_ERR_GENERIC;
225             krb5_set_error_message(context, ret,
226                                    "Can't generate Diffie-Hellman keys");
227             goto out;
228         }
229
230         size = DH_size(client_params->u.dh.key);
231
232         dh_gen_key = malloc(size);
233         if (dh_gen_key == NULL) {
234             ret = ENOMEM;
235             krb5_set_error_message(context, ret, "malloc: out of memory");
236             goto out;
237         }
238
239         dh_gen_keylen = DH_compute_key(dh_gen_key,client_params->u.dh.public_key, client_params->u.dh.key);
240         if (dh_gen_keylen == (size_t)-1) {
241             ret = KRB5KRB_ERR_GENERIC;
242             krb5_set_error_message(context, ret,
243                                    "Can't compute Diffie-Hellman key");
244             goto out;
245         }
246         if (dh_gen_keylen < size) {
247             size -= dh_gen_keylen;
248             memmove(dh_gen_key + size, dh_gen_key, dh_gen_keylen);
249             memset(dh_gen_key, 0, size);
250         }
251
252         ret = 0;
253 #ifdef HAVE_OPENSSL
254     } else if (client_params->keyex == USE_ECDH) {
255
256         if (client_params->u.ecdh.public_key == NULL) {
257             ret = KRB5KRB_ERR_GENERIC;
258             krb5_set_error_message(context, ret, "public_key");
259             goto out;
260         }
261
262         client_params->u.ecdh.key = EC_KEY_new();
263         if (client_params->u.ecdh.key == NULL) {
264             ret = ENOMEM;
265             goto out;
266         }
267         EC_KEY_set_group(client_params->u.ecdh.key,
268                          EC_KEY_get0_group(client_params->u.ecdh.public_key));
269
270         if (EC_KEY_generate_key(client_params->u.ecdh.key) != 1) {
271             ret = ENOMEM;
272             goto out;
273         }
274
275         size = (EC_GROUP_get_degree(EC_KEY_get0_group(client_params->u.ecdh.key)) + 7) / 8;
276         dh_gen_key = malloc(size);
277         if (dh_gen_key == NULL) {
278             ret = ENOMEM;
279             krb5_set_error_message(context, ret,
280                                    N_("malloc: out of memory", ""));
281             goto out;
282         }
283
284         dh_gen_keylen = ECDH_compute_key(dh_gen_key, size,
285                                          EC_KEY_get0_public_key(client_params->u.ecdh.public_key),
286                                          client_params->u.ecdh.key, NULL);
287
288 #endif /* HAVE_OPENSSL */
289     } else {
290         ret = KRB5KRB_ERR_GENERIC;
291         krb5_set_error_message(context, ret,
292                                "Diffie-Hellman not selected keys");
293         goto out;
294     }
295
296     ret = _krb5_pk_octetstring2key(context,
297                                    enctype,
298                                    dh_gen_key, dh_gen_keylen,
299                                    NULL, NULL,
300                                    &client_params->reply_key);
301
302  out:
303     if (dh_gen_key)
304         free(dh_gen_key);
305     if (key.keyvalue.data)
306         krb5_free_keyblock_contents(context, &key);
307
308     return ret;
309 }
310
311 static BIGNUM *
312 integer_to_BN(krb5_context context, const char *field, heim_integer *f)
313 {
314     BIGNUM *bn;
315
316     bn = BN_bin2bn((const unsigned char *)f->data, f->length, NULL);
317     if (bn == NULL) {
318         krb5_set_error_message(context, KRB5_BADMSGTYPE,
319                                "PKINIT: parsing BN failed %s", field);
320         return NULL;
321     }
322     BN_set_negative(bn, f->negative);
323     return bn;
324 }
325
326 static krb5_error_code
327 get_dh_param(krb5_context context,
328              krb5_kdc_configuration *config,
329              SubjectPublicKeyInfo *dh_key_info,
330              pk_client_params *client_params)
331 {
332     DomainParameters dhparam;
333     DH *dh = NULL;
334     BIGNUM *p, *q, *g;
335     krb5_error_code ret;
336
337     memset(&dhparam, 0, sizeof(dhparam));
338
339     if ((dh_key_info->subjectPublicKey.length % 8) != 0) {
340         ret = KRB5_BADMSGTYPE;
341         krb5_set_error_message(context, ret,
342                                "PKINIT: subjectPublicKey not aligned "
343                                "to 8 bit boundary");
344         goto out;
345     }
346
347     if (dh_key_info->algorithm.parameters == NULL) {
348         krb5_set_error_message(context, KRB5_BADMSGTYPE,
349                                "PKINIT missing algorithm parameter "
350                               "in clientPublicValue");
351         return KRB5_BADMSGTYPE;
352     }
353
354     ret = decode_DomainParameters(dh_key_info->algorithm.parameters->data,
355                                   dh_key_info->algorithm.parameters->length,
356                                   &dhparam,
357                                   NULL);
358     if (ret) {
359         krb5_set_error_message(context, ret, "Can't decode algorithm "
360                                "parameters in clientPublicValue");
361         goto out;
362     }
363
364     ret = _krb5_dh_group_ok(context, config->pkinit_dh_min_bits,
365                             &dhparam.p, &dhparam.g, &dhparam.q, moduli,
366                             &client_params->dh_group_name);
367     if (ret) {
368         /* XXX send back proposal of better group */
369         goto out;
370     }
371
372     dh = DH_new();
373     if (dh == NULL) {
374         ret = ENOMEM;
375         krb5_set_error_message(context, ret, "Cannot create DH structure");
376         goto out;
377     }
378     ret = KRB5_BADMSGTYPE;
379     p = integer_to_BN(context, "DH prime", &dhparam.p);
380     g = integer_to_BN(context, "DH base", &dhparam.g);
381     q = integer_to_BN(context, "DH p-1 factor", &dhparam.q);
382     if (p == NULL || g == NULL || q == NULL) {
383         BN_free(p);
384         BN_free(g);
385         BN_free(q);
386         goto out;
387     }
388     if (DH_set0_pqg(dh, p, g, q) != 1) {
389         BN_free(p);
390         BN_free(g);
391         BN_free(q);
392         goto out;
393     }
394
395     {
396         heim_integer glue;
397         size_t size;
398
399         ret = decode_DHPublicKey(dh_key_info->subjectPublicKey.data,
400                                  dh_key_info->subjectPublicKey.length / 8,
401                                  &glue,
402                                  &size);
403         if (ret) {
404             krb5_clear_error_message(context);
405             return ret;
406         }
407
408         client_params->u.dh.public_key = integer_to_BN(context,
409                                                        "subjectPublicKey",
410                                                        &glue);
411         der_free_heim_integer(&glue);
412         if (client_params->u.dh.public_key == NULL) {
413             ret = KRB5_BADMSGTYPE;
414             goto out;
415         }
416     }
417
418     client_params->u.dh.key = dh;
419     dh = NULL;
420     ret = 0;
421
422  out:
423     if (dh)
424         DH_free(dh);
425     free_DomainParameters(&dhparam);
426     return ret;
427 }
428
429 #ifdef HAVE_OPENSSL
430
431 static krb5_error_code
432 get_ecdh_param(krb5_context context,
433                krb5_kdc_configuration *config,
434                SubjectPublicKeyInfo *dh_key_info,
435                pk_client_params *client_params)
436 {
437     ECParameters ecp;
438     EC_KEY *public = NULL;
439     krb5_error_code ret;
440     const unsigned char *p;
441     size_t len;
442     int nid;
443
444     if (dh_key_info->algorithm.parameters == NULL) {
445         krb5_set_error_message(context, KRB5_BADMSGTYPE,
446                                "PKINIT missing algorithm parameter "
447                                "in clientPublicValue");
448         return KRB5_BADMSGTYPE;
449     }
450
451     memset(&ecp, 0, sizeof(ecp));
452
453     ret = decode_ECParameters(dh_key_info->algorithm.parameters->data,
454                               dh_key_info->algorithm.parameters->length, &ecp, &len);
455     if (ret)
456         goto out;
457
458     if (ecp.element != choice_ECParameters_namedCurve) {
459         ret = KRB5_BADMSGTYPE;
460         goto out;
461     }
462
463     if (der_heim_oid_cmp(&ecp.u.namedCurve, &asn1_oid_id_ec_group_secp256r1) == 0)
464         nid = NID_X9_62_prime256v1;
465     else {
466         ret = KRB5_BADMSGTYPE;
467         goto out;
468     }
469
470     /* XXX verify group is ok */
471
472     public = EC_KEY_new_by_curve_name(nid);
473
474     p = dh_key_info->subjectPublicKey.data;
475     len = dh_key_info->subjectPublicKey.length / 8;
476     if (o2i_ECPublicKey(&public, &p, len) == NULL) {
477         ret = KRB5_BADMSGTYPE;
478         krb5_set_error_message(context, ret,
479                                "PKINIT failed to decode ECDH key");
480         goto out;
481     }
482     client_params->u.ecdh.public_key = public;
483     public = NULL;
484
485  out:
486     if (public)
487         EC_KEY_free(public);
488     free_ECParameters(&ecp);
489     return ret;
490 }
491
492 #endif /* HAVE_OPENSSL */
493
494 krb5_error_code
495 _kdc_pk_rd_padata(krb5_context context,
496                   krb5_kdc_configuration *config,
497                   const KDC_REQ *req,
498                   const PA_DATA *pa,
499                   hdb_entry_ex *client,
500                   pk_client_params **ret_params)
501 {
502     pk_client_params *cp;
503     krb5_error_code ret;
504     heim_oid eContentType = { 0, NULL }, contentInfoOid = { 0, NULL };
505     krb5_data eContent = { 0, NULL };
506     krb5_data signed_content = { 0, NULL };
507     const char *type = "unknown type";
508     hx509_certs trust_anchors;
509     int have_data = 0;
510     const HDB_Ext_PKINIT_cert *pc;
511
512     *ret_params = NULL;
513
514     if (!config->enable_pkinit) {
515         kdc_log(context, config, 0, "PK-INIT request but PK-INIT not enabled");
516         krb5_clear_error_message(context);
517         return 0;
518     }
519
520     cp = calloc(1, sizeof(*cp));
521     if (cp == NULL) {
522         krb5_clear_error_message(context);
523         ret = ENOMEM;
524         goto out;
525     }
526
527     ret = hx509_certs_init(context->hx509ctx,
528                            "MEMORY:trust-anchors",
529                            0, NULL, &trust_anchors);
530     if (ret) {
531         krb5_set_error_message(context, ret, "failed to create trust anchors");
532         goto out;
533     }
534
535     ret = hx509_certs_merge(context->hx509ctx, trust_anchors,
536                             kdc_identity->anchors);
537     if (ret) {
538         hx509_certs_free(&trust_anchors);
539         krb5_set_error_message(context, ret, "failed to create verify context");
540         goto out;
541     }
542
543     /* Add any registered certificates for this client as trust anchors */
544     ret = hdb_entry_get_pkinit_cert(&client->entry, &pc);
545     if (ret == 0 && pc != NULL) {
546         hx509_cert cert;
547         unsigned int i;
548
549         for (i = 0; i < pc->len; i++) {
550             ret = hx509_cert_init_data(context->hx509ctx,
551                                        pc->val[i].cert.data,
552                                        pc->val[i].cert.length,
553                                        &cert);
554             if (ret)
555                 continue;
556             hx509_certs_add(context->hx509ctx, trust_anchors, cert);
557             hx509_cert_free(cert);
558         }
559     }
560
561     ret = hx509_verify_init_ctx(context->hx509ctx, &cp->verify_ctx);
562     if (ret) {
563         hx509_certs_free(&trust_anchors);
564         krb5_set_error_message(context, ret, "failed to create verify context");
565         goto out;
566     }
567
568     hx509_verify_set_time(cp->verify_ctx, kdc_time);
569     hx509_verify_attach_anchors(cp->verify_ctx, trust_anchors);
570     hx509_certs_free(&trust_anchors);
571
572     if (config->pkinit_allow_proxy_certs)
573         hx509_verify_set_proxy_certificate(cp->verify_ctx, 1);
574
575     if (pa->padata_type == KRB5_PADATA_PK_AS_REQ_WIN) {
576         PA_PK_AS_REQ_Win2k r;
577
578         type = "PK-INIT-Win2k";
579
580         if (req->req_body.kdc_options.request_anonymous) {
581             ret = KRB5_KDC_ERR_PUBLIC_KEY_ENCRYPTION_NOT_SUPPORTED;
582             krb5_set_error_message(context, ret,
583                                    "Anon not supported in RSA mode");
584             goto out;
585         }
586
587         ret = decode_PA_PK_AS_REQ_Win2k(pa->padata_value.data,
588                                         pa->padata_value.length,
589                                         &r,
590                                         NULL);
591         if (ret) {
592             krb5_set_error_message(context, ret, "Can't decode "
593                                    "PK-AS-REQ-Win2k: %d", ret);
594             goto out;
595         }
596
597         ret = hx509_cms_unwrap_ContentInfo(&r.signed_auth_pack,
598                                            &contentInfoOid,
599                                            &signed_content,
600                                            &have_data);
601         free_PA_PK_AS_REQ_Win2k(&r);
602         if (ret) {
603             krb5_set_error_message(context, ret,
604                                    "Can't unwrap ContentInfo(win): %d", ret);
605             goto out;
606         }
607
608     } else if (pa->padata_type == KRB5_PADATA_PK_AS_REQ) {
609         PA_PK_AS_REQ r;
610
611         type = "PK-INIT-IETF";
612
613         ret = decode_PA_PK_AS_REQ(pa->padata_value.data,
614                                   pa->padata_value.length,
615                                   &r,
616                                   NULL);
617         if (ret) {
618             krb5_set_error_message(context, ret,
619                                    "Can't decode PK-AS-REQ: %d", ret);
620             goto out;
621         }
622
623         /* XXX look at r.kdcPkId */
624         if (r.trustedCertifiers) {
625             ExternalPrincipalIdentifiers *edi = r.trustedCertifiers;
626             unsigned int i, maxedi;
627
628             ret = hx509_certs_init(context->hx509ctx,
629                                    "MEMORY:client-anchors",
630                                    0, NULL,
631                                    &cp->client_anchors);
632             if (ret) {
633                 krb5_set_error_message(context, ret,
634                                        "Can't allocate client anchors: %d",
635                                        ret);
636                 goto out;
637
638             }
639             /*
640              * If the client sent more then 10 EDI, don't bother
641              * looking more then 10 of performance reasons.
642              */
643             maxedi = edi->len;
644             if (maxedi > 10)
645                 maxedi = 10;
646             for (i = 0; i < maxedi; i++) {
647                 IssuerAndSerialNumber iasn;
648                 hx509_query *q;
649                 hx509_cert cert;
650                 size_t size;
651
652                 if (edi->val[i].issuerAndSerialNumber == NULL)
653                     continue;
654
655                 ret = hx509_query_alloc(context->hx509ctx, &q);
656                 if (ret) {
657                     krb5_set_error_message(context, ret,
658                                           "Failed to allocate hx509_query");
659                     goto out;
660                 }
661
662                 ret = decode_IssuerAndSerialNumber(edi->val[i].issuerAndSerialNumber->data,
663                                                    edi->val[i].issuerAndSerialNumber->length,
664                                                    &iasn,
665                                                    &size);
666                 if (ret) {
667                     hx509_query_free(context->hx509ctx, q);
668                     continue;
669                 }
670                 ret = hx509_query_match_issuer_serial(q, &iasn.issuer, &iasn.serialNumber);
671                 free_IssuerAndSerialNumber(&iasn);
672                 if (ret) {
673                     hx509_query_free(context->hx509ctx, q);
674                     continue;
675                 }
676
677                 ret = hx509_certs_find(context->hx509ctx,
678                                        kdc_identity->certs,
679                                        q,
680                                        &cert);
681                 hx509_query_free(context->hx509ctx, q);
682                 if (ret)
683                     continue;
684                 hx509_certs_add(context->hx509ctx,
685                                 cp->client_anchors, cert);
686                 hx509_cert_free(cert);
687             }
688         }
689
690         ret = hx509_cms_unwrap_ContentInfo(&r.signedAuthPack,
691                                            &contentInfoOid,
692                                            &signed_content,
693                                            &have_data);
694         free_PA_PK_AS_REQ(&r);
695         if (ret) {
696             krb5_set_error_message(context, ret,
697                                    "Can't unwrap ContentInfo: %d", ret);
698             goto out;
699         }
700
701     } else {
702         krb5_clear_error_message(context);
703         ret = KRB5KDC_ERR_PADATA_TYPE_NOSUPP;
704         goto out;
705     }
706
707     ret = der_heim_oid_cmp(&contentInfoOid, &asn1_oid_id_pkcs7_signedData);
708     if (ret != 0) {
709         ret = KRB5KRB_ERR_GENERIC;
710         krb5_set_error_message(context, ret,
711                                "PK-AS-REQ-Win2k invalid content type oid");
712         goto out;
713     }
714
715     if (!have_data) {
716         ret = KRB5KRB_ERR_GENERIC;
717         krb5_set_error_message(context, ret,
718                               "PK-AS-REQ-Win2k no signed auth pack");
719         goto out;
720     }
721
722     {
723         hx509_certs signer_certs;
724         int flags = HX509_CMS_VS_ALLOW_DATA_OID_MISMATCH; /* BTMM */
725
726         if (req->req_body.kdc_options.request_anonymous)
727             flags |= HX509_CMS_VS_ALLOW_ZERO_SIGNER;
728
729         ret = hx509_cms_verify_signed(context->hx509ctx,
730                                       cp->verify_ctx,
731                                       flags,
732                                       signed_content.data,
733                                       signed_content.length,
734                                       NULL,
735                                       kdc_identity->certpool,
736                                       &eContentType,
737                                       &eContent,
738                                       &signer_certs);
739         if (ret) {
740             char *s = hx509_get_error_string(context->hx509ctx, ret);
741             krb5_warnx(context, "PKINIT: failed to verify signature: %s: %d",
742                        s, ret);
743             free(s);
744             goto out;
745         }
746
747         if (signer_certs) {
748             ret = hx509_get_one_cert(context->hx509ctx, signer_certs,
749                                      &cp->cert);
750             hx509_certs_free(&signer_certs);
751         }
752         if (ret)
753             goto out;
754     }
755
756     /* Signature is correct, now verify the signed message */
757     if (der_heim_oid_cmp(&eContentType, &asn1_oid_id_pkcs7_data) != 0 &&
758         der_heim_oid_cmp(&eContentType, &asn1_oid_id_pkauthdata) != 0)
759     {
760         ret = KRB5_BADMSGTYPE;
761         krb5_set_error_message(context, ret, "got wrong oid for pkauthdata");
762         goto out;
763     }
764
765     if (pa->padata_type == KRB5_PADATA_PK_AS_REQ_WIN) {
766         AuthPack_Win2k ap;
767
768         ret = decode_AuthPack_Win2k(eContent.data,
769                                     eContent.length,
770                                     &ap,
771                                     NULL);
772         if (ret) {
773             krb5_set_error_message(context, ret,
774                                    "Can't decode AuthPack: %d", ret);
775             goto out;
776         }
777
778         ret = pk_check_pkauthenticator_win2k(context,
779                                              &ap.pkAuthenticator,
780                                              req);
781         if (ret) {
782             free_AuthPack_Win2k(&ap);
783             goto out;
784         }
785
786         cp->type = PKINIT_WIN2K;
787         cp->nonce = ap.pkAuthenticator.nonce;
788
789         if (ap.clientPublicValue) {
790             ret = KRB5KRB_ERR_GENERIC;
791             krb5_set_error_message(context, ret,
792                                    "DH not supported for windows");
793             goto out;
794         }
795         free_AuthPack_Win2k(&ap);
796
797     } else if (pa->padata_type == KRB5_PADATA_PK_AS_REQ) {
798         AuthPack ap;
799
800         ret = decode_AuthPack(eContent.data,
801                               eContent.length,
802                               &ap,
803                               NULL);
804         if (ret) {
805             krb5_set_error_message(context, ret,
806                                    "Can't decode AuthPack: %d", ret);
807             free_AuthPack(&ap);
808             goto out;
809         }
810
811         if (req->req_body.kdc_options.request_anonymous &&
812             ap.clientPublicValue == NULL) {
813             free_AuthPack(&ap);
814             ret = KRB5_KDC_ERR_PUBLIC_KEY_ENCRYPTION_NOT_SUPPORTED;
815             krb5_set_error_message(context, ret,
816                                    "Anon not supported in RSA mode");
817             goto out;
818         }
819
820         ret = pk_check_pkauthenticator(context,
821                                        &ap.pkAuthenticator,
822                                        req);
823         if (ret) {
824             free_AuthPack(&ap);
825             goto out;
826         }
827
828         cp->type = PKINIT_27;
829         cp->nonce = ap.pkAuthenticator.nonce;
830
831         if (ap.clientPublicValue) {
832             if (der_heim_oid_cmp(&ap.clientPublicValue->algorithm.algorithm, &asn1_oid_id_dhpublicnumber) == 0) {
833                 cp->keyex = USE_DH;
834                 ret = get_dh_param(context, config,
835                                    ap.clientPublicValue, cp);
836 #ifdef HAVE_OPENSSL
837             } else if (der_heim_oid_cmp(&ap.clientPublicValue->algorithm.algorithm, &asn1_oid_id_ecPublicKey) == 0) {
838                 cp->keyex = USE_ECDH;
839                 ret = get_ecdh_param(context, config,
840                                      ap.clientPublicValue, cp);
841 #endif /* HAVE_OPENSSL */
842             } else {
843                 ret = KRB5_BADMSGTYPE;
844                 krb5_set_error_message(context, ret, "PKINIT unknown DH mechanism");
845             }
846             if (ret) {
847                 free_AuthPack(&ap);
848                 goto out;
849             }
850         } else
851             cp->keyex = USE_RSA;
852
853         ret = hx509_peer_info_alloc(context->hx509ctx,
854                                         &cp->peer);
855         if (ret) {
856             free_AuthPack(&ap);
857             goto out;
858         }
859
860         if (ap.supportedCMSTypes) {
861             ret = hx509_peer_info_set_cms_algs(context->hx509ctx,
862                                                cp->peer,
863                                                ap.supportedCMSTypes->val,
864                                                ap.supportedCMSTypes->len);
865             if (ret) {
866                 free_AuthPack(&ap);
867                 goto out;
868             }
869         } else {
870             /* assume old client */
871             hx509_peer_info_add_cms_alg(context->hx509ctx, cp->peer,
872                                         hx509_crypto_des_rsdi_ede3_cbc());
873             hx509_peer_info_add_cms_alg(context->hx509ctx, cp->peer,
874                                         hx509_signature_rsa_with_sha1());
875             hx509_peer_info_add_cms_alg(context->hx509ctx, cp->peer,
876                                         hx509_signature_sha1());
877         }
878         free_AuthPack(&ap);
879     } else
880         krb5_abortx(context, "internal pkinit error");
881
882     kdc_log(context, config, 0, "PK-INIT request of type %s", type);
883
884 out:
885     if (ret)
886         krb5_warn(context, ret, "PKINIT");
887
888     if (signed_content.data)
889         free(signed_content.data);
890     krb5_data_free(&eContent);
891     der_free_oid(&eContentType);
892     der_free_oid(&contentInfoOid);
893     if (ret) {
894         _kdc_pk_free_client_param(context, cp);
895     } else
896         *ret_params = cp;
897     return ret;
898 }
899
900 /*
901  *
902  */
903
904 static krb5_error_code
905 BN_to_integer(krb5_context context, const BIGNUM *bn, heim_integer *integer)
906 {
907     integer->length = BN_num_bytes(bn);
908     integer->data = malloc(integer->length);
909     if (integer->data == NULL) {
910         krb5_clear_error_message(context);
911         return ENOMEM;
912     }
913     BN_bn2bin(bn, integer->data);
914     integer->negative = BN_is_negative(bn);
915     return 0;
916 }
917
918 static krb5_error_code
919 pk_mk_pa_reply_enckey(krb5_context context,
920                       krb5_kdc_configuration *config,
921                       pk_client_params *cp,
922                       const KDC_REQ *req,
923                       const krb5_data *req_buffer,
924                       krb5_keyblock *reply_key,
925                       ContentInfo *content_info,
926                       hx509_cert *kdc_cert)
927 {
928     const heim_oid *envelopedAlg = NULL, *sdAlg = NULL, *evAlg = NULL;
929     krb5_error_code ret;
930     krb5_data buf, signed_data;
931     size_t size = 0;
932     int do_win2k = 0;
933
934     krb5_data_zero(&buf);
935     krb5_data_zero(&signed_data);
936
937     *kdc_cert = NULL;
938
939     /*
940      * If the message client is a win2k-type but it send pa data
941      * 09-binding it expects a IETF (checksum) reply so there can be
942      * no replay attacks.
943      */
944
945     switch (cp->type) {
946     case PKINIT_WIN2K: {
947         int i = 0;
948         if (_kdc_find_padata(req, &i, KRB5_PADATA_PK_AS_09_BINDING) == NULL
949             && config->pkinit_require_binding == 0)
950         {
951             do_win2k = 1;
952         }
953         sdAlg = &asn1_oid_id_pkcs7_data;
954         evAlg = &asn1_oid_id_pkcs7_data;
955         envelopedAlg = &asn1_oid_id_rsadsi_des_ede3_cbc;
956         break;
957     }
958     case PKINIT_27:
959         sdAlg = &asn1_oid_id_pkrkeydata;
960         evAlg = &asn1_oid_id_pkcs7_signedData;
961         break;
962     default:
963         krb5_abortx(context, "internal pkinit error");
964     }
965
966     if (do_win2k) {
967         ReplyKeyPack_Win2k kp;
968         memset(&kp, 0, sizeof(kp));
969
970         ret = copy_EncryptionKey(reply_key, &kp.replyKey);
971         if (ret) {
972             krb5_clear_error_message(context);
973             goto out;
974         }
975         kp.nonce = cp->nonce;
976
977         ASN1_MALLOC_ENCODE(ReplyKeyPack_Win2k,
978                            buf.data, buf.length,
979                            &kp, &size,ret);
980         free_ReplyKeyPack_Win2k(&kp);
981     } else {
982         krb5_crypto ascrypto;
983         ReplyKeyPack kp;
984         memset(&kp, 0, sizeof(kp));
985
986         ret = copy_EncryptionKey(reply_key, &kp.replyKey);
987         if (ret) {
988             krb5_clear_error_message(context);
989             goto out;
990         }
991
992         ret = krb5_crypto_init(context, reply_key, 0, &ascrypto);
993         if (ret) {
994             krb5_clear_error_message(context);
995             goto out;
996         }
997
998         ret = krb5_create_checksum(context, ascrypto, 6, 0,
999                                    req_buffer->data, req_buffer->length,
1000                                    &kp.asChecksum);
1001         if (ret) {
1002             krb5_clear_error_message(context);
1003             goto out;
1004         }
1005
1006         ret = krb5_crypto_destroy(context, ascrypto);
1007         if (ret) {
1008             krb5_clear_error_message(context);
1009             goto out;
1010         }
1011         ASN1_MALLOC_ENCODE(ReplyKeyPack, buf.data, buf.length, &kp, &size,ret);
1012         free_ReplyKeyPack(&kp);
1013     }
1014     if (ret) {
1015         krb5_set_error_message(context, ret, "ASN.1 encoding of ReplyKeyPack "
1016                                "failed (%d)", ret);
1017         goto out;
1018     }
1019     if (buf.length != size)
1020         krb5_abortx(context, "Internal ASN.1 encoder error");
1021
1022     {
1023         hx509_query *q;
1024         hx509_cert cert;
1025
1026         ret = hx509_query_alloc(context->hx509ctx, &q);
1027         if (ret)
1028             goto out;
1029
1030         hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY);
1031         if (config->pkinit_kdc_friendly_name)
1032             hx509_query_match_friendly_name(q, config->pkinit_kdc_friendly_name);
1033
1034         ret = hx509_certs_find(context->hx509ctx,
1035                                kdc_identity->certs,
1036                                q,
1037                                &cert);
1038         hx509_query_free(context->hx509ctx, q);
1039         if (ret)
1040             goto out;
1041
1042         ret = hx509_cms_create_signed_1(context->hx509ctx,
1043                                         0,
1044                                         sdAlg,
1045                                         buf.data,
1046                                         buf.length,
1047                                         NULL,
1048                                         cert,
1049                                         cp->peer,
1050                                         cp->client_anchors,
1051                                         kdc_identity->certpool,
1052                                         &signed_data);
1053         *kdc_cert = cert;
1054     }
1055
1056     krb5_data_free(&buf);
1057     if (ret)
1058         goto out;
1059
1060     if (cp->type == PKINIT_WIN2K) {
1061         ret = hx509_cms_wrap_ContentInfo(&asn1_oid_id_pkcs7_signedData,
1062                                          &signed_data,
1063                                          &buf);
1064         if (ret)
1065             goto out;
1066         krb5_data_free(&signed_data);
1067         signed_data = buf;
1068     }
1069
1070     ret = hx509_cms_envelope_1(context->hx509ctx,
1071                                HX509_CMS_EV_NO_KU_CHECK,
1072                                cp->cert,
1073                                signed_data.data, signed_data.length,
1074                                envelopedAlg,
1075                                evAlg, &buf);
1076     if (ret)
1077         goto out;
1078
1079     ret = _krb5_pk_mk_ContentInfo(context,
1080                                   &buf,
1081                                   &asn1_oid_id_pkcs7_envelopedData,
1082                                   content_info);
1083 out:
1084     if (ret && *kdc_cert) {
1085         hx509_cert_free(*kdc_cert);
1086         *kdc_cert = NULL;
1087     }
1088
1089     krb5_data_free(&buf);
1090     krb5_data_free(&signed_data);
1091     return ret;
1092 }
1093
1094 /*
1095  *
1096  */
1097
1098 static krb5_error_code
1099 pk_mk_pa_reply_dh(krb5_context context,
1100                   krb5_kdc_configuration *config,
1101                   pk_client_params *cp,
1102                   ContentInfo *content_info,
1103                   hx509_cert *kdc_cert)
1104 {
1105     KDCDHKeyInfo dh_info;
1106     krb5_data signed_data, buf;
1107     ContentInfo contentinfo;
1108     krb5_error_code ret;
1109     hx509_cert cert;
1110     hx509_query *q;
1111     size_t size = 0;
1112
1113     memset(&contentinfo, 0, sizeof(contentinfo));
1114     memset(&dh_info, 0, sizeof(dh_info));
1115     krb5_data_zero(&signed_data);
1116     krb5_data_zero(&buf);
1117
1118     *kdc_cert = NULL;
1119
1120     if (cp->keyex == USE_DH) {
1121         DH *kdc_dh = cp->u.dh.key;
1122         const BIGNUM *pub_key;
1123         heim_integer i;
1124
1125         DH_get0_key(kdc_dh, &pub_key, NULL);
1126         ret = BN_to_integer(context, pub_key, &i);
1127         if (ret)
1128             return ret;
1129
1130         ASN1_MALLOC_ENCODE(DHPublicKey, buf.data, buf.length, &i, &size, ret);
1131         der_free_heim_integer(&i);
1132         if (ret) {
1133             krb5_set_error_message(context, ret, "ASN.1 encoding of "
1134                                    "DHPublicKey failed (%d)", ret);
1135             return ret;
1136         }
1137         if (buf.length != size)
1138             krb5_abortx(context, "Internal ASN.1 encoder error");
1139
1140         dh_info.subjectPublicKey.length = buf.length * 8;
1141         dh_info.subjectPublicKey.data = buf.data;
1142         krb5_data_zero(&buf);
1143 #ifdef HAVE_OPENSSL
1144     } else if (cp->keyex == USE_ECDH) {
1145         unsigned char *p;
1146         int len;
1147
1148         len = i2o_ECPublicKey(cp->u.ecdh.key, NULL);
1149         if (len <= 0)
1150             abort();
1151
1152         p = malloc(len);
1153         if (p == NULL)
1154             abort();
1155
1156         dh_info.subjectPublicKey.length = len * 8;
1157         dh_info.subjectPublicKey.data = p;
1158
1159         len = i2o_ECPublicKey(cp->u.ecdh.key, &p);
1160         if (len <= 0)
1161             abort();
1162 #endif
1163     } else
1164         krb5_abortx(context, "no keyex selected ?");
1165
1166
1167     dh_info.nonce = cp->nonce;
1168
1169     ASN1_MALLOC_ENCODE(KDCDHKeyInfo, buf.data, buf.length, &dh_info, &size,
1170                        ret);
1171     if (ret) {
1172         krb5_set_error_message(context, ret, "ASN.1 encoding of "
1173                                "KdcDHKeyInfo failed (%d)", ret);
1174         goto out;
1175     }
1176     if (buf.length != size)
1177         krb5_abortx(context, "Internal ASN.1 encoder error");
1178
1179     /*
1180      * Create the SignedData structure and sign the KdcDHKeyInfo
1181      * filled in above
1182      */
1183
1184     ret = hx509_query_alloc(context->hx509ctx, &q);
1185     if (ret)
1186         goto out;
1187
1188     hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY);
1189     if (config->pkinit_kdc_friendly_name)
1190         hx509_query_match_friendly_name(q, config->pkinit_kdc_friendly_name);
1191
1192     ret = hx509_certs_find(context->hx509ctx,
1193                            kdc_identity->certs,
1194                            q,
1195                            &cert);
1196     hx509_query_free(context->hx509ctx, q);
1197     if (ret)
1198         goto out;
1199
1200     ret = hx509_cms_create_signed_1(context->hx509ctx,
1201                                     0,
1202                                     &asn1_oid_id_pkdhkeydata,
1203                                     buf.data,
1204                                     buf.length,
1205                                     NULL,
1206                                     cert,
1207                                     cp->peer,
1208                                     cp->client_anchors,
1209                                     kdc_identity->certpool,
1210                                     &signed_data);
1211     if (ret) {
1212         kdc_log(context, config, 0, "Failed signing the DH* reply: %d", ret);
1213         goto out;
1214     }
1215     *kdc_cert = cert;
1216
1217     ret = _krb5_pk_mk_ContentInfo(context,
1218                                   &signed_data,
1219                                   &asn1_oid_id_pkcs7_signedData,
1220                                   content_info);
1221     if (ret)
1222         goto out;
1223
1224  out:
1225     if (ret && *kdc_cert) {
1226         hx509_cert_free(*kdc_cert);
1227         *kdc_cert = NULL;
1228     }
1229
1230     krb5_data_free(&buf);
1231     krb5_data_free(&signed_data);
1232     free_KDCDHKeyInfo(&dh_info);
1233
1234     return ret;
1235 }
1236
1237 /*
1238  *
1239  */
1240
1241 krb5_error_code
1242 _kdc_pk_mk_pa_reply(krb5_context context,
1243                     krb5_kdc_configuration *config,
1244                     pk_client_params *cp,
1245                     const hdb_entry_ex *client,
1246                     krb5_enctype sessionetype,
1247                     const KDC_REQ *req,
1248                     const krb5_data *req_buffer,
1249                     krb5_keyblock **reply_key,
1250                     krb5_keyblock *sessionkey,
1251                     METHOD_DATA *md)
1252 {
1253     krb5_error_code ret;
1254     void *buf = NULL;
1255     size_t len = 0, size = 0;
1256     krb5_enctype enctype;
1257     int pa_type;
1258     hx509_cert kdc_cert = NULL;
1259     size_t i;
1260
1261     if (!config->enable_pkinit) {
1262         krb5_clear_error_message(context);
1263         return 0;
1264     }
1265
1266     if (req->req_body.etype.len > 0) {
1267         for (i = 0; i < req->req_body.etype.len; i++)
1268             if (krb5_enctype_valid(context, req->req_body.etype.val[i]) == 0)
1269                 break;
1270         if (req->req_body.etype.len <= i) {
1271             ret = KRB5KRB_ERR_GENERIC;
1272             krb5_set_error_message(context, ret,
1273                                    "No valid enctype available from client");
1274             goto out;
1275         }
1276         enctype = req->req_body.etype.val[i];
1277     } else
1278         enctype = ETYPE_DES3_CBC_SHA1;
1279
1280     if (cp->type == PKINIT_27) {
1281         PA_PK_AS_REP rep;
1282         const char *type, *other = "";
1283
1284         memset(&rep, 0, sizeof(rep));
1285
1286         pa_type = KRB5_PADATA_PK_AS_REP;
1287
1288         if (cp->keyex == USE_RSA) {
1289             ContentInfo info;
1290
1291             type = "enckey";
1292
1293             rep.element = choice_PA_PK_AS_REP_encKeyPack;
1294
1295             ret = krb5_generate_random_keyblock(context, enctype,
1296                                                 &cp->reply_key);
1297             if (ret) {
1298                 free_PA_PK_AS_REP(&rep);
1299                 goto out;
1300             }
1301             ret = pk_mk_pa_reply_enckey(context,
1302                                         config,
1303                                         cp,
1304                                         req,
1305                                         req_buffer,
1306                                         &cp->reply_key,
1307                                         &info,
1308                                         &kdc_cert);
1309             if (ret) {
1310                 free_PA_PK_AS_REP(&rep);
1311                 goto out;
1312             }
1313             ASN1_MALLOC_ENCODE(ContentInfo, rep.u.encKeyPack.data,
1314                                rep.u.encKeyPack.length, &info, &size,
1315                                ret);
1316             free_ContentInfo(&info);
1317             if (ret) {
1318                 krb5_set_error_message(context, ret, "encoding of Key ContentInfo "
1319                                        "failed %d", ret);
1320                 free_PA_PK_AS_REP(&rep);
1321                 goto out;
1322             }
1323             if (rep.u.encKeyPack.length != size)
1324                 krb5_abortx(context, "Internal ASN.1 encoder error");
1325
1326             ret = krb5_generate_random_keyblock(context, sessionetype,
1327                                                 sessionkey);
1328             if (ret) {
1329                 free_PA_PK_AS_REP(&rep);
1330                 goto out;
1331             }
1332
1333         } else {
1334             ContentInfo info;
1335
1336             switch (cp->keyex) {
1337             case USE_DH: type = "dh"; break;
1338 #ifdef HAVE_OPENSSL
1339             case USE_ECDH: type = "ecdh"; break;
1340 #endif
1341             default: krb5_abortx(context, "unknown keyex"); break;
1342             }
1343
1344             if (cp->dh_group_name)
1345                 other = cp->dh_group_name;
1346
1347             rep.element = choice_PA_PK_AS_REP_dhInfo;
1348
1349             ret = generate_dh_keyblock(context, cp, enctype);
1350             if (ret)
1351                 return ret;
1352
1353             ret = pk_mk_pa_reply_dh(context, config,
1354                                     cp,
1355                                     &info,
1356                                     &kdc_cert);
1357             if (ret) {
1358                 free_PA_PK_AS_REP(&rep);
1359                 krb5_set_error_message(context, ret,
1360                                        "create pa-reply-dh "
1361                                        "failed %d", ret);
1362                 goto out;
1363             }
1364
1365             ASN1_MALLOC_ENCODE(ContentInfo, rep.u.dhInfo.dhSignedData.data,
1366                                rep.u.dhInfo.dhSignedData.length, &info, &size,
1367                                ret);
1368             free_ContentInfo(&info);
1369             if (ret) {
1370                 krb5_set_error_message(context, ret,
1371                                        "encoding of Key ContentInfo "
1372                                        "failed %d", ret);
1373                 free_PA_PK_AS_REP(&rep);
1374                 goto out;
1375             }
1376             if (rep.u.encKeyPack.length != size)
1377                 krb5_abortx(context, "Internal ASN.1 encoder error");
1378
1379             /* XXX KRB-FX-CF2 */
1380             ret = krb5_generate_random_keyblock(context, sessionetype,
1381                                                 sessionkey);
1382             if (ret) {
1383                 free_PA_PK_AS_REP(&rep);
1384                 goto out;
1385             }
1386
1387             /* XXX Add PA-PKINIT-KX */
1388
1389         }
1390
1391 #define use_btmm_with_enckey 0
1392         if (use_btmm_with_enckey && rep.element == choice_PA_PK_AS_REP_encKeyPack) {
1393             PA_PK_AS_REP_BTMM btmm;
1394             heim_any any;
1395
1396             any.data = rep.u.encKeyPack.data;
1397             any.length = rep.u.encKeyPack.length;
1398
1399             btmm.dhSignedData = NULL;
1400             btmm.encKeyPack = &any;
1401
1402             ASN1_MALLOC_ENCODE(PA_PK_AS_REP_BTMM, buf, len, &btmm, &size, ret);
1403         } else {
1404             ASN1_MALLOC_ENCODE(PA_PK_AS_REP, buf, len, &rep, &size, ret);
1405         }
1406
1407         free_PA_PK_AS_REP(&rep);
1408         if (ret) {
1409             krb5_set_error_message(context, ret,
1410                                    "encode PA-PK-AS-REP failed %d", ret);
1411             goto out;
1412         }
1413         if (len != size)
1414             krb5_abortx(context, "Internal ASN.1 encoder error");
1415
1416         kdc_log(context, config, 0, "PK-INIT using %s %s", type, other);
1417
1418     } else if (cp->type == PKINIT_WIN2K) {
1419         PA_PK_AS_REP_Win2k rep;
1420         ContentInfo info;
1421
1422         if (cp->keyex != USE_RSA) {
1423             ret = KRB5KRB_ERR_GENERIC;
1424             krb5_set_error_message(context, ret,
1425                                    "Windows PK-INIT doesn't support DH");
1426             goto out;
1427         }
1428
1429         memset(&rep, 0, sizeof(rep));
1430
1431         pa_type = KRB5_PADATA_PK_AS_REP_19;
1432         rep.element = choice_PA_PK_AS_REP_Win2k_encKeyPack;
1433
1434         ret = krb5_generate_random_keyblock(context, enctype,
1435                                             &cp->reply_key);
1436         if (ret) {
1437             free_PA_PK_AS_REP_Win2k(&rep);
1438             goto out;
1439         }
1440         ret = pk_mk_pa_reply_enckey(context,
1441                                     config,
1442                                     cp,
1443                                     req,
1444                                     req_buffer,
1445                                     &cp->reply_key,
1446                                     &info,
1447                                     &kdc_cert);
1448         if (ret) {
1449             free_PA_PK_AS_REP_Win2k(&rep);
1450             goto out;
1451         }
1452         ASN1_MALLOC_ENCODE(ContentInfo, rep.u.encKeyPack.data,
1453                            rep.u.encKeyPack.length, &info, &size,
1454                            ret);
1455         free_ContentInfo(&info);
1456         if (ret) {
1457             krb5_set_error_message(context, ret, "encoding of Key ContentInfo "
1458                                   "failed %d", ret);
1459             free_PA_PK_AS_REP_Win2k(&rep);
1460             goto out;
1461         }
1462         if (rep.u.encKeyPack.length != size)
1463             krb5_abortx(context, "Internal ASN.1 encoder error");
1464
1465         ASN1_MALLOC_ENCODE(PA_PK_AS_REP_Win2k, buf, len, &rep, &size, ret);
1466         free_PA_PK_AS_REP_Win2k(&rep);
1467         if (ret) {
1468             krb5_set_error_message(context, ret,
1469                                   "encode PA-PK-AS-REP-Win2k failed %d", ret);
1470             goto out;
1471         }
1472         if (len != size)
1473             krb5_abortx(context, "Internal ASN.1 encoder error");
1474
1475         ret = krb5_generate_random_keyblock(context, sessionetype,
1476                                             sessionkey);
1477         if (ret) {
1478             free(buf);
1479             goto out;
1480         }
1481
1482     } else
1483         krb5_abortx(context, "PK-INIT internal error");
1484
1485
1486     ret = krb5_padata_add(context, md, pa_type, buf, len);
1487     if (ret) {
1488         krb5_set_error_message(context, ret,
1489                                "Failed adding PA-PK-AS-REP %d", ret);
1490         free(buf);
1491         goto out;
1492     }
1493
1494     if (config->pkinit_kdc_ocsp_file) {
1495
1496         if (ocsp.expire == 0 && ocsp.next_update > kdc_time) {
1497             struct stat sb;
1498             int fd;
1499
1500             krb5_data_free(&ocsp.data);
1501
1502             ocsp.expire = 0;
1503             ocsp.next_update = kdc_time + 60 * 5;
1504
1505             fd = open(config->pkinit_kdc_ocsp_file, O_RDONLY);
1506             if (fd < 0) {
1507                 kdc_log(context, config, 0,
1508                         "PK-INIT failed to open ocsp data file %d", errno);
1509                 goto out_ocsp;
1510             }
1511             ret = fstat(fd, &sb);
1512             if (ret) {
1513                 ret = errno;
1514                 close(fd);
1515                 kdc_log(context, config, 0,
1516                         "PK-INIT failed to stat ocsp data %d", ret);
1517                 goto out_ocsp;
1518             }
1519
1520             ret = krb5_data_alloc(&ocsp.data, sb.st_size);
1521             if (ret) {
1522                 close(fd);
1523                 kdc_log(context, config, 0,
1524                         "PK-INIT failed to stat ocsp data %d", ret);
1525                 goto out_ocsp;
1526             }
1527             ocsp.data.length = sb.st_size;
1528             ret = read(fd, ocsp.data.data, sb.st_size);
1529             close(fd);
1530             if (ret != sb.st_size) {
1531                 kdc_log(context, config, 0,
1532                         "PK-INIT failed to read ocsp data %d", errno);
1533                 goto out_ocsp;
1534             }
1535
1536             ret = hx509_ocsp_verify(context->hx509ctx,
1537                                     kdc_time,
1538                                     kdc_cert,
1539                                     0,
1540                                     ocsp.data.data, ocsp.data.length,
1541                                     &ocsp.expire);
1542             if (ret) {
1543                 kdc_log(context, config, 0,
1544                         "PK-INIT failed to verify ocsp data %d", ret);
1545                 krb5_data_free(&ocsp.data);
1546                 ocsp.expire = 0;
1547             } else if (ocsp.expire > 180) {
1548                 ocsp.expire -= 180; /* refetch the ocsp before it expire */
1549                 ocsp.next_update = ocsp.expire;
1550             } else {
1551                 ocsp.next_update = kdc_time;
1552             }
1553         out_ocsp:
1554             ret = 0;
1555         }
1556
1557         if (ocsp.expire != 0 && ocsp.expire > kdc_time) {
1558
1559             ret = krb5_padata_add(context, md,
1560                                   KRB5_PADATA_PA_PK_OCSP_RESPONSE,
1561                                   ocsp.data.data, ocsp.data.length);
1562             if (ret) {
1563                 krb5_set_error_message(context, ret,
1564                                        "Failed adding OCSP response %d", ret);
1565                 goto out;
1566             }
1567         }
1568     }
1569
1570 out:
1571     if (kdc_cert)
1572         hx509_cert_free(kdc_cert);
1573
1574     if (ret == 0)
1575         *reply_key = &cp->reply_key;
1576     return ret;
1577 }
1578
1579 static int
1580 match_rfc_san(krb5_context context,
1581               krb5_kdc_configuration *config,
1582               hx509_context hx509ctx,
1583               hx509_cert client_cert,
1584               krb5_const_principal match)
1585 {
1586     hx509_octet_string_list list;
1587     int ret, found = 0;
1588     size_t i;
1589
1590     memset(&list, 0 , sizeof(list));
1591
1592     ret = hx509_cert_find_subjectAltName_otherName(hx509ctx,
1593                                                    client_cert,
1594                                                    &asn1_oid_id_pkinit_san,
1595                                                    &list);
1596     if (ret)
1597         goto out;
1598
1599     for (i = 0; !found && i < list.len; i++) {
1600         krb5_principal_data principal;
1601         KRB5PrincipalName kn;
1602         size_t size;
1603
1604         ret = decode_KRB5PrincipalName(list.val[i].data,
1605                                        list.val[i].length,
1606                                        &kn, &size);
1607         if (ret) {
1608             const char *msg = krb5_get_error_message(context, ret);
1609             kdc_log(context, config, 0,
1610                     "Decoding kerberos name in certificate failed: %s", msg);
1611             krb5_free_error_message(context, msg);
1612             break;
1613         }
1614         if (size != list.val[i].length) {
1615             kdc_log(context, config, 0,
1616                     "Decoding kerberos name have extra bits on the end");
1617             return KRB5_KDC_ERR_CLIENT_NAME_MISMATCH;
1618         }
1619
1620         principal.name = kn.principalName;
1621         principal.realm = kn.realm;
1622
1623         if (krb5_principal_compare(context, &principal, match) == TRUE)
1624             found = 1;
1625         free_KRB5PrincipalName(&kn);
1626     }
1627
1628 out:
1629     hx509_free_octet_string_list(&list);
1630     if (ret)
1631         return ret;
1632
1633     if (!found)
1634         return KRB5_KDC_ERR_CLIENT_NAME_MISMATCH;
1635
1636     return 0;
1637 }
1638
1639 static int
1640 match_ms_upn_san(krb5_context context,
1641                  krb5_kdc_configuration *config,
1642                  hx509_context hx509ctx,
1643                  hx509_cert client_cert,
1644                  HDB *clientdb,
1645                  hdb_entry_ex *client)
1646 {
1647     hx509_octet_string_list list;
1648     krb5_principal principal = NULL;
1649     int ret;
1650     MS_UPN_SAN upn;
1651     size_t size;
1652
1653     memset(&list, 0 , sizeof(list));
1654
1655     ret = hx509_cert_find_subjectAltName_otherName(hx509ctx,
1656                                                    client_cert,
1657                                                    &asn1_oid_id_pkinit_ms_san,
1658                                                    &list);
1659     if (ret)
1660         goto out;
1661
1662     if (list.len != 1) {
1663         kdc_log(context, config, 0,
1664                 "More then one PK-INIT MS UPN SAN");
1665         goto out;
1666     }
1667
1668     ret = decode_MS_UPN_SAN(list.val[0].data, list.val[0].length, &upn, &size);
1669     if (ret) {
1670         kdc_log(context, config, 0, "Decode of MS-UPN-SAN failed");
1671         goto out;
1672     }
1673     if (size != list.val[0].length) {
1674         free_MS_UPN_SAN(&upn);
1675         kdc_log(context, config, 0, "Trailing data in ");
1676         ret = KRB5_KDC_ERR_CLIENT_NAME_MISMATCH;
1677         goto out;
1678     }
1679
1680     kdc_log(context, config, 0, "found MS UPN SAN: %s", upn);
1681
1682     ret = krb5_parse_name(context, upn, &principal);
1683     free_MS_UPN_SAN(&upn);
1684     if (ret) {
1685         kdc_log(context, config, 0, "Failed to parse principal in MS UPN SAN");
1686         goto out;
1687     }
1688
1689     if (clientdb->hdb_check_pkinit_ms_upn_match) {
1690         ret = clientdb->hdb_check_pkinit_ms_upn_match(context, clientdb, client, principal);
1691     } else {
1692
1693         /*
1694          * This is very wrong, but will do for a fallback
1695          */
1696         strupr(principal->realm);
1697
1698         if (krb5_principal_compare(context, principal, client->entry.principal) == FALSE)
1699             ret = KRB5_KDC_ERR_CLIENT_NAME_MISMATCH;
1700     }
1701
1702 out:
1703     if (principal)
1704         krb5_free_principal(context, principal);
1705     hx509_free_octet_string_list(&list);
1706
1707     return ret;
1708 }
1709
1710 krb5_error_code
1711 _kdc_pk_check_client(krb5_context context,
1712                      krb5_kdc_configuration *config,
1713                      HDB *clientdb,
1714                      hdb_entry_ex *client,
1715                      pk_client_params *cp,
1716                      char **subject_name)
1717 {
1718     const HDB_Ext_PKINIT_acl *acl;
1719     const HDB_Ext_PKINIT_cert *pc;
1720     krb5_error_code ret;
1721     hx509_name name;
1722     size_t i;
1723
1724     if (cp->cert == NULL) {
1725
1726         *subject_name = strdup("anonymous client client");
1727         if (*subject_name == NULL)
1728             return ENOMEM;
1729         return 0;
1730     }
1731
1732     ret = hx509_cert_get_base_subject(context->hx509ctx,
1733                                       cp->cert,
1734                                       &name);
1735     if (ret)
1736         return ret;
1737
1738     ret = hx509_name_to_string(name, subject_name);
1739     hx509_name_free(&name);
1740     if (ret)
1741         return ret;
1742
1743     kdc_log(context, config, 0,
1744             "Trying to authorize PK-INIT subject DN %s",
1745             *subject_name);
1746
1747     ret = hdb_entry_get_pkinit_cert(&client->entry, &pc);
1748     if (ret == 0 && pc) {
1749         hx509_cert cert;
1750         size_t j;
1751
1752         for (j = 0; j < pc->len; j++) {
1753             ret = hx509_cert_init_data(context->hx509ctx,
1754                                        pc->val[j].cert.data,
1755                                        pc->val[j].cert.length,
1756                                        &cert);
1757             if (ret)
1758                 continue;
1759             ret = hx509_cert_cmp(cert, cp->cert);
1760             hx509_cert_free(cert);
1761             if (ret == 0) {
1762                 kdc_log(context, config, 5,
1763                         "Found matching PK-INIT cert in hdb");
1764                 return 0;
1765             }
1766         }
1767     }
1768
1769
1770     if (config->pkinit_princ_in_cert) {
1771         ret = match_rfc_san(context, config,
1772                             context->hx509ctx,
1773                             cp->cert,
1774                             client->entry.principal);
1775         if (ret == 0) {
1776             kdc_log(context, config, 5,
1777                     "Found matching PK-INIT SAN in certificate");
1778             return 0;
1779         }
1780         ret = match_ms_upn_san(context, config,
1781                                context->hx509ctx,
1782                                cp->cert,
1783                                clientdb,
1784                                client);
1785         if (ret == 0) {
1786             kdc_log(context, config, 5,
1787                     "Found matching MS UPN SAN in certificate");
1788             return 0;
1789         }
1790     }
1791
1792     ret = hdb_entry_get_pkinit_acl(&client->entry, &acl);
1793     if (ret == 0 && acl != NULL) {
1794         /*
1795          * Cheat here and compare the generated name with the string
1796          * and not the reverse.
1797          */
1798         for (i = 0; i < acl->len; i++) {
1799             if (strcmp(*subject_name, acl->val[0].subject) != 0)
1800                 continue;
1801
1802             /* Don't support isser and anchor checking right now */
1803             if (acl->val[0].issuer)
1804                 continue;
1805             if (acl->val[0].anchor)
1806                 continue;
1807
1808             kdc_log(context, config, 5,
1809                     "Found matching PK-INIT database ACL");
1810             return 0;
1811         }
1812     }
1813
1814     for (i = 0; i < principal_mappings.len; i++) {
1815         krb5_boolean b;
1816
1817         b = krb5_principal_compare(context,
1818                                    client->entry.principal,
1819                                    principal_mappings.val[i].principal);
1820         if (b == FALSE)
1821             continue;
1822         if (strcmp(principal_mappings.val[i].subject, *subject_name) != 0)
1823             continue;
1824         kdc_log(context, config, 5,
1825                 "Found matching PK-INIT FILE ACL");
1826         return 0;
1827     }
1828
1829     ret = KRB5_KDC_ERR_CLIENT_NAME_MISMATCH;
1830     krb5_set_error_message(context, ret,
1831                           "PKINIT no matching principals for %s",
1832                           *subject_name);
1833
1834     kdc_log(context, config, 5,
1835             "PKINIT no matching principals for %s",
1836             *subject_name);
1837
1838     free(*subject_name);
1839     *subject_name = NULL;
1840
1841     return ret;
1842 }
1843
1844 static krb5_error_code
1845 add_principal_mapping(krb5_context context,
1846                       const char *principal_name,
1847                       const char * subject)
1848 {
1849    struct pk_allowed_princ *tmp;
1850    krb5_principal principal;
1851    krb5_error_code ret;
1852
1853    tmp = realloc(principal_mappings.val,
1854                  (principal_mappings.len + 1) * sizeof(*tmp));
1855    if (tmp == NULL)
1856        return ENOMEM;
1857    principal_mappings.val = tmp;
1858
1859    ret = krb5_parse_name(context, principal_name, &principal);
1860    if (ret)
1861        return ret;
1862
1863    principal_mappings.val[principal_mappings.len].principal = principal;
1864
1865    principal_mappings.val[principal_mappings.len].subject = strdup(subject);
1866    if (principal_mappings.val[principal_mappings.len].subject == NULL) {
1867        krb5_free_principal(context, principal);
1868        return ENOMEM;
1869    }
1870    principal_mappings.len++;
1871
1872    return 0;
1873 }
1874
1875 krb5_error_code
1876 _kdc_add_inital_verified_cas(krb5_context context,
1877                              krb5_kdc_configuration *config,
1878                              pk_client_params *cp,
1879                              EncTicketPart *tkt)
1880 {
1881     AD_INITIAL_VERIFIED_CAS cas;
1882     krb5_error_code ret;
1883     krb5_data data;
1884     size_t size = 0;
1885
1886     memset(&cas, 0, sizeof(cas));
1887
1888     /* XXX add CAs to cas here */
1889
1890     ASN1_MALLOC_ENCODE(AD_INITIAL_VERIFIED_CAS, data.data, data.length,
1891                        &cas, &size, ret);
1892     if (ret)
1893         return ret;
1894     if (data.length != size)
1895         krb5_abortx(context, "internal asn.1 encoder error");
1896
1897     ret = _kdc_tkt_add_if_relevant_ad(context, tkt,
1898                                       KRB5_AUTHDATA_INITIAL_VERIFIED_CAS,
1899                                       &data);
1900     krb5_data_free(&data);
1901     return ret;
1902 }
1903
1904 /*
1905  *
1906  */
1907
1908 static void
1909 load_mappings(krb5_context context, const char *fn)
1910 {
1911     krb5_error_code ret;
1912     char buf[1024];
1913     unsigned long lineno = 0;
1914     FILE *f;
1915
1916     f = fopen(fn, "r");
1917     if (f == NULL)
1918         return;
1919
1920     while (fgets(buf, sizeof(buf), f) != NULL) {
1921         char *subject_name, *p;
1922
1923         buf[strcspn(buf, "\n")] = '\0';
1924         lineno++;
1925
1926         p = buf + strspn(buf, " \t");
1927
1928         if (*p == '#' || *p == '\0')
1929             continue;
1930
1931         subject_name = strchr(p, ':');
1932         if (subject_name == NULL) {
1933             krb5_warnx(context, "pkinit mapping file line %lu "
1934                        "missing \":\" :%s",
1935                        lineno, buf);
1936             continue;
1937         }
1938         *subject_name++ = '\0';
1939
1940         ret = add_principal_mapping(context, p, subject_name);
1941         if (ret) {
1942             krb5_warn(context, ret, "failed to add line %lu \":\" :%s\n",
1943                       lineno, buf);
1944             continue;
1945         }
1946     }
1947
1948     fclose(f);
1949 }
1950
1951 /*
1952  *
1953  */
1954
1955 krb5_error_code
1956 krb5_kdc_pk_initialize(krb5_context context,
1957                        krb5_kdc_configuration *config,
1958                        const char *user_id,
1959                        const char *anchors,
1960                        char **pool,
1961                        char **revoke_list)
1962 {
1963     const char *file;
1964     char *fn = NULL;
1965     krb5_error_code ret;
1966
1967     file = krb5_config_get_string(context, NULL,
1968                                   "libdefaults", "moduli", NULL);
1969
1970     ret = _krb5_parse_moduli(context, file, &moduli);
1971     if (ret)
1972         krb5_err(context, 1, ret, "PKINIT: failed to load modidi file");
1973
1974     principal_mappings.len = 0;
1975     principal_mappings.val = NULL;
1976
1977     ret = _krb5_pk_load_id(context,
1978                            &kdc_identity,
1979                            user_id,
1980                            anchors,
1981                            pool,
1982                            revoke_list,
1983                            NULL,
1984                            NULL,
1985                            NULL);
1986     if (ret) {
1987         krb5_warn(context, ret, "PKINIT: ");
1988         config->enable_pkinit = 0;
1989         return ret;
1990     }
1991
1992     {
1993         hx509_query *q;
1994         hx509_cert cert;
1995
1996         ret = hx509_query_alloc(context->hx509ctx, &q);
1997         if (ret) {
1998             krb5_warnx(context, "PKINIT: out of memory");
1999             return ENOMEM;
2000         }
2001
2002         hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY);
2003         if (config->pkinit_kdc_friendly_name)
2004             hx509_query_match_friendly_name(q, config->pkinit_kdc_friendly_name);
2005
2006         ret = hx509_certs_find(context->hx509ctx,
2007                                kdc_identity->certs,
2008                                q,
2009                                &cert);
2010         hx509_query_free(context->hx509ctx, q);
2011         if (ret == 0) {
2012             if (hx509_cert_check_eku(context->hx509ctx, cert,
2013                                      &asn1_oid_id_pkkdcekuoid, 0)) {
2014                 hx509_name name;
2015                 char *str;
2016                 ret = hx509_cert_get_subject(cert, &name);
2017                 if (ret == 0) {
2018                     hx509_name_to_string(name, &str);
2019                     krb5_warnx(context, "WARNING Found KDC certificate (%s)"
2020                                "is missing the PK-INIT KDC EKU, this is bad for "
2021                                "interoperability.", str);
2022                     hx509_name_free(&name);
2023                     free(str);
2024                 }
2025             }
2026             hx509_cert_free(cert);
2027         } else
2028             krb5_warnx(context, "PKINIT: failed to find a signing "
2029                        "certifiate with a public key");
2030     }
2031
2032     if (krb5_config_get_bool_default(context,
2033                                      NULL,
2034                                      FALSE,
2035                                      "kdc",
2036                                      "pkinit_allow_proxy_certificate",
2037                                      NULL))
2038         config->pkinit_allow_proxy_certs = 1;
2039
2040     file = krb5_config_get_string(context,
2041                                   NULL,
2042                                   "kdc",
2043                                   "pkinit_mappings_file",
2044                                   NULL);
2045     if (file == NULL) {
2046         asprintf(&fn, "%s/pki-mapping", hdb_db_dir(context));
2047         file = fn;
2048     }
2049
2050     load_mappings(context, file);
2051     if (fn)
2052         free(fn);
2053
2054     return 0;
2055 }
2056
2057 #endif /* PKINIT */