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