s4:heimdal: import lorikeet-heimdal-200906080040 (commit 904d0124b46eed7a8ad6e5b73e89...
[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  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  *
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * 3. Neither the name of the Institute nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33
34 #include "kdc_locl.h"
35
36 RCSID("$Id$");
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         dh_gen_keylen = DH_size(client_params->u.dh.key);
231         size = BN_num_bytes(client_params->u.dh.key->p);
232         if (size < dh_gen_keylen)
233             size = dh_gen_keylen;
234
235         dh_gen_key = malloc(size);
236         if (dh_gen_key == NULL) {
237             ret = ENOMEM;
238             krb5_set_error_message(context, ret, "malloc: out of memory");
239             goto out;
240         }
241         memset(dh_gen_key, 0, size - dh_gen_keylen);
242
243         dh_gen_keylen = DH_compute_key(dh_gen_key + (size - dh_gen_keylen),
244                                        client_params->u.dh.public_key,
245                                        client_params->u.dh.key);
246         if (dh_gen_keylen == -1) {
247             ret = KRB5KRB_ERR_GENERIC;
248             krb5_set_error_message(context, ret,
249                                    "Can't compute Diffie-Hellman key");
250             goto out;
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         ret = 0;
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(kdc_identity->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(kdc_identity->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(kdc_identity->hx509ctx,
544                                        pc->val[i].cert.data,
545                                        pc->val[i].cert.length,
546                                        &cert);
547             if (ret)
548                 continue;
549             hx509_certs_add(kdc_identity->hx509ctx, trust_anchors, cert);
550             hx509_cert_free(cert);
551         }
552     }
553
554     ret = hx509_verify_init_ctx(kdc_identity->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(kdc_identity->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(kdc_identity->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(kdc_identity->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(kdc_identity->hx509ctx, q);
667                     continue;
668                 }
669
670                 ret = hx509_certs_find(kdc_identity->hx509ctx,
671                                        kdc_identity->certs,
672                                        q,
673                                        &cert);
674                 hx509_query_free(kdc_identity->hx509ctx, q);
675                 if (ret)
676                     continue;
677                 hx509_certs_add(kdc_identity->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(kdc_identity->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(kdc_identity->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(kdc_identity->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(kdc_identity->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(kdc_identity->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(kdc_identity->hx509ctx, cp->peer,
865                                         hx509_crypto_des_rsdi_ede3_cbc());
866             hx509_peer_info_add_cms_alg(kdc_identity->hx509ctx, cp->peer,
867                                         hx509_signature_rsa_with_sha1());
868             hx509_peer_info_add_cms_alg(kdc_identity->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(kdc_identity->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(kdc_identity->hx509ctx,
1028                                kdc_identity->certs,
1029                                q,
1030                                &cert);
1031         hx509_query_free(kdc_identity->hx509ctx, q);
1032         if (ret)
1033             goto out;
1034         
1035         ret = hx509_cms_create_signed_1(kdc_identity->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(kdc_identity->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(kdc_identity->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(kdc_identity->hx509ctx,
1184                            kdc_identity->certs,
1185                            q,
1186                            &cert);
1187     hx509_query_free(kdc_identity->hx509ctx, q);
1188     if (ret)
1189         goto out;
1190     
1191     ret = hx509_cms_create_signed_1(kdc_identity->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         ASN1_MALLOC_ENCODE(PA_PK_AS_REP, buf, len, &rep, &size, ret);
1383         free_PA_PK_AS_REP(&rep);
1384         if (ret) {
1385             krb5_set_error_message(context, ret,
1386                                    "encode PA-PK-AS-REP failed %d", ret);
1387             goto out;
1388         }
1389         if (len != size)
1390             krb5_abortx(context, "Internal ASN.1 encoder error");
1391
1392         kdc_log(context, config, 0, "PK-INIT using %s %s", type, other);
1393
1394     } else if (cp->type == PKINIT_WIN2K) {
1395         PA_PK_AS_REP_Win2k rep;
1396         ContentInfo info;
1397
1398         if (cp->keyex != USE_RSA) {
1399             ret = KRB5KRB_ERR_GENERIC;
1400             krb5_set_error_message(context, ret,
1401                                    "Windows PK-INIT doesn't support DH");
1402             goto out;
1403         }
1404
1405         memset(&rep, 0, sizeof(rep));
1406
1407         pa_type = KRB5_PADATA_PK_AS_REP_19;
1408         rep.element = choice_PA_PK_AS_REP_encKeyPack;
1409
1410         ret = krb5_generate_random_keyblock(context, enctype,
1411                                             &cp->reply_key);
1412         if (ret) {
1413             free_PA_PK_AS_REP_Win2k(&rep);
1414             goto out;
1415         }
1416         ret = pk_mk_pa_reply_enckey(context,
1417                                     config,
1418                                     cp,
1419                                     req,
1420                                     req_buffer,
1421                                     &cp->reply_key,
1422                                     &info,
1423                                     &kdc_cert);
1424         if (ret) {
1425             free_PA_PK_AS_REP_Win2k(&rep);
1426             goto out;
1427         }
1428         ASN1_MALLOC_ENCODE(ContentInfo, rep.u.encKeyPack.data,
1429                            rep.u.encKeyPack.length, &info, &size,
1430                            ret);
1431         free_ContentInfo(&info);
1432         if (ret) {
1433             krb5_set_error_message(context, ret, "encoding of Key ContentInfo "
1434                                   "failed %d", ret);
1435             free_PA_PK_AS_REP_Win2k(&rep);
1436             goto out;
1437         }
1438         if (rep.u.encKeyPack.length != size)
1439             krb5_abortx(context, "Internal ASN.1 encoder error");
1440
1441         ASN1_MALLOC_ENCODE(PA_PK_AS_REP_Win2k, buf, len, &rep, &size, ret);
1442         free_PA_PK_AS_REP_Win2k(&rep);
1443         if (ret) {
1444             krb5_set_error_message(context, ret,
1445                                   "encode PA-PK-AS-REP-Win2k failed %d", ret);
1446             goto out;
1447         }
1448         if (len != size)
1449             krb5_abortx(context, "Internal ASN.1 encoder error");
1450
1451         ret = krb5_generate_random_keyblock(context, sessionetype, 
1452                                             sessionkey);
1453         if (ret)
1454             goto out;
1455
1456     } else
1457         krb5_abortx(context, "PK-INIT internal error");
1458
1459
1460     ret = krb5_padata_add(context, md, pa_type, buf, len);
1461     if (ret) {
1462         krb5_set_error_message(context, ret,
1463                                "Failed adding PA-PK-AS-REP %d", ret);
1464         free(buf);
1465         goto out;
1466     }
1467
1468     if (config->pkinit_kdc_ocsp_file) {
1469
1470         if (ocsp.expire == 0 && ocsp.next_update > kdc_time) {
1471             struct stat sb;
1472             int fd;
1473
1474             krb5_data_free(&ocsp.data);
1475
1476             ocsp.expire = 0;
1477             ocsp.next_update = kdc_time + 60 * 5;
1478
1479             fd = open(config->pkinit_kdc_ocsp_file, O_RDONLY);
1480             if (fd < 0) {
1481                 kdc_log(context, config, 0,
1482                         "PK-INIT failed to open ocsp data file %d", errno);
1483                 goto out_ocsp;
1484             }
1485             ret = fstat(fd, &sb);
1486             if (ret) {
1487                 ret = errno;
1488                 close(fd);
1489                 kdc_log(context, config, 0,
1490                         "PK-INIT failed to stat ocsp data %d", ret);
1491                 goto out_ocsp;
1492             }
1493         
1494             ret = krb5_data_alloc(&ocsp.data, sb.st_size);
1495             if (ret) {
1496                 close(fd);
1497                 kdc_log(context, config, 0,
1498                         "PK-INIT failed to stat ocsp data %d", ret);
1499                 goto out_ocsp;
1500             }
1501             ocsp.data.length = sb.st_size;
1502             ret = read(fd, ocsp.data.data, sb.st_size);
1503             close(fd);
1504             if (ret != sb.st_size) {
1505                 kdc_log(context, config, 0,
1506                         "PK-INIT failed to read ocsp data %d", errno);
1507                 goto out_ocsp;
1508             }
1509
1510             ret = hx509_ocsp_verify(kdc_identity->hx509ctx,
1511                                     kdc_time,
1512                                     kdc_cert,
1513                                     0,
1514                                     ocsp.data.data, ocsp.data.length,
1515                                     &ocsp.expire);
1516             if (ret) {
1517                 kdc_log(context, config, 0,
1518                         "PK-INIT failed to verify ocsp data %d", ret);
1519                 krb5_data_free(&ocsp.data);
1520                 ocsp.expire = 0;
1521             } else if (ocsp.expire > 180) {
1522                 ocsp.expire -= 180; /* refetch the ocsp before it expire */
1523                 ocsp.next_update = ocsp.expire;
1524             } else {
1525                 ocsp.next_update = kdc_time;
1526             }
1527         out_ocsp:
1528             ret = 0;
1529         }
1530
1531         if (ocsp.expire != 0 && ocsp.expire > kdc_time) {
1532
1533             ret = krb5_padata_add(context, md,
1534                                   KRB5_PADATA_PA_PK_OCSP_RESPONSE,
1535                                   ocsp.data.data, ocsp.data.length);
1536             if (ret) {
1537                 krb5_set_error_message(context, ret,
1538                                        "Failed adding OCSP response %d", ret);
1539                 goto out;
1540             }
1541         }
1542     }
1543
1544 out:
1545     if (kdc_cert)
1546         hx509_cert_free(kdc_cert);
1547
1548     if (ret == 0)
1549         *reply_key = &cp->reply_key;
1550     return ret;
1551 }
1552
1553 static int
1554 match_rfc_san(krb5_context context,
1555               krb5_kdc_configuration *config,
1556               hx509_context hx509ctx,
1557               hx509_cert client_cert,
1558               krb5_const_principal match)
1559 {
1560     hx509_octet_string_list list;
1561     int ret, i, found = 0;
1562
1563     memset(&list, 0 , sizeof(list));
1564
1565     ret = hx509_cert_find_subjectAltName_otherName(hx509ctx,
1566                                                    client_cert,
1567                                                    &asn1_oid_id_pkinit_san,
1568                                                    &list);
1569     if (ret)
1570         goto out;
1571
1572     for (i = 0; !found && i < list.len; i++) {
1573         krb5_principal_data principal;
1574         KRB5PrincipalName kn;
1575         size_t size;
1576
1577         ret = decode_KRB5PrincipalName(list.val[i].data,
1578                                        list.val[i].length,
1579                                        &kn, &size);
1580         if (ret) {
1581             kdc_log(context, config, 0,
1582                     "Decoding kerberos name in certificate failed: %s",
1583                     krb5_get_err_text(context, ret));
1584             break;
1585         }
1586         if (size != list.val[i].length) {
1587             kdc_log(context, config, 0,
1588                     "Decoding kerberos name have extra bits on the end");
1589             return KRB5_KDC_ERR_CLIENT_NAME_MISMATCH;
1590         }
1591
1592         principal.name = kn.principalName;
1593         principal.realm = kn.realm;
1594
1595         if (krb5_principal_compare(context, &principal, match) == TRUE)
1596             found = 1;
1597         free_KRB5PrincipalName(&kn);
1598     }
1599
1600 out:
1601     hx509_free_octet_string_list(&list);
1602     if (ret)
1603         return ret;
1604
1605     if (!found)
1606         return KRB5_KDC_ERR_CLIENT_NAME_MISMATCH;
1607
1608     return 0;
1609 }
1610
1611 static int
1612 match_ms_upn_san(krb5_context context,
1613                  krb5_kdc_configuration *config,
1614                  hx509_context hx509ctx,
1615                  hx509_cert client_cert,
1616                  krb5_const_principal match)
1617 {
1618     hx509_octet_string_list list;
1619     krb5_principal principal = NULL;
1620     int ret, found = 0;
1621     MS_UPN_SAN upn;
1622     size_t size;
1623
1624     memset(&list, 0 , sizeof(list));
1625
1626     ret = hx509_cert_find_subjectAltName_otherName(hx509ctx,
1627                                                    client_cert,
1628                                                    &asn1_oid_id_pkinit_ms_san,
1629                                                    &list);
1630     if (ret)
1631         goto out;
1632
1633     if (list.len != 1) {
1634         kdc_log(context, config, 0,
1635                 "More then one PK-INIT MS UPN SAN");
1636         goto out;
1637     }
1638
1639     ret = decode_MS_UPN_SAN(list.val[0].data, list.val[0].length, &upn, &size);
1640     if (ret) {
1641         kdc_log(context, config, 0, "Decode of MS-UPN-SAN failed");
1642         goto out;
1643     }
1644
1645     kdc_log(context, config, 0, "found MS UPN SAN: %s", upn);
1646
1647     ret = krb5_parse_name(context, upn, &principal);
1648     free_MS_UPN_SAN(&upn);
1649     if (ret) {
1650         kdc_log(context, config, 0, "Failed to parse principal in MS UPN SAN");
1651         goto out;
1652     }
1653
1654     /*
1655      * This is very wrong, but will do for now, should really and a
1656      * plugin to the windc layer to very this ACL.
1657     */
1658     strupr(principal->realm);
1659
1660     if (krb5_principal_compare(context, principal, match) == TRUE)
1661         found = 1;
1662
1663 out:
1664     if (principal)
1665         krb5_free_principal(context, principal);
1666     hx509_free_octet_string_list(&list);
1667     if (ret)
1668         return ret;
1669
1670     if (!found)
1671         return KRB5_KDC_ERR_CLIENT_NAME_MISMATCH;
1672
1673     return 0;
1674 }
1675
1676 krb5_error_code
1677 _kdc_pk_check_client(krb5_context context,
1678                      krb5_kdc_configuration *config,
1679                      const hdb_entry_ex *client,
1680                      pk_client_params *cp,
1681                      char **subject_name)
1682 {
1683     const HDB_Ext_PKINIT_acl *acl;
1684     const HDB_Ext_PKINIT_cert *pc;
1685     krb5_error_code ret;
1686     hx509_name name;
1687     int i;
1688
1689     if (cp->cert == NULL) {
1690
1691         *subject_name = strdup("anonymous client client");
1692         if (*subject_name == NULL)
1693             return ENOMEM;
1694         return 0;
1695     }
1696
1697     ret = hx509_cert_get_base_subject(kdc_identity->hx509ctx,
1698                                       cp->cert,
1699                                       &name);
1700     if (ret)
1701         return ret;
1702
1703     ret = hx509_name_to_string(name, subject_name);
1704     hx509_name_free(&name);
1705     if (ret)
1706         return ret;
1707
1708     kdc_log(context, config, 0,
1709             "Trying to authorize PK-INIT subject DN %s",
1710             *subject_name);
1711
1712     ret = hdb_entry_get_pkinit_cert(&client->entry, &pc);
1713     if (ret == 0 && pc) {
1714         hx509_cert cert;
1715         unsigned int i;
1716         
1717         for (i = 0; i < pc->len; i++) {
1718             ret = hx509_cert_init_data(kdc_identity->hx509ctx,
1719                                        pc->val[i].cert.data,
1720                                        pc->val[i].cert.length,
1721                                        &cert);
1722             if (ret)
1723                 continue;
1724             ret = hx509_cert_cmp(cert, cp->cert);
1725             hx509_cert_free(cert);
1726             if (ret == 0) {
1727                 kdc_log(context, config, 5,
1728                         "Found matching PK-INIT cert in hdb");
1729                 return 0;
1730             }
1731         }
1732     }
1733
1734
1735     if (config->pkinit_princ_in_cert) {
1736         ret = match_rfc_san(context, config,
1737                             kdc_identity->hx509ctx,
1738                             cp->cert,
1739                             client->entry.principal);
1740         if (ret == 0) {
1741             kdc_log(context, config, 5,
1742                     "Found matching PK-INIT SAN in certificate");
1743             return 0;
1744         }
1745         ret = match_ms_upn_san(context, config,
1746                                kdc_identity->hx509ctx,
1747                                cp->cert,
1748                                client->entry.principal);
1749         if (ret == 0) {
1750             kdc_log(context, config, 5,
1751                     "Found matching MS UPN SAN in certificate");
1752             return 0;
1753         }
1754     }
1755
1756     ret = hdb_entry_get_pkinit_acl(&client->entry, &acl);
1757     if (ret == 0 && acl != NULL) {
1758         /*
1759          * Cheat here and compare the generated name with the string
1760          * and not the reverse.
1761          */
1762         for (i = 0; i < acl->len; i++) {
1763             if (strcmp(*subject_name, acl->val[0].subject) != 0)
1764                 continue;
1765
1766             /* Don't support isser and anchor checking right now */
1767             if (acl->val[0].issuer)
1768                 continue;
1769             if (acl->val[0].anchor)
1770                 continue;
1771
1772             kdc_log(context, config, 5,
1773                     "Found matching PK-INIT database ACL");
1774             return 0;
1775         }
1776     }
1777
1778     for (i = 0; i < principal_mappings.len; i++) {
1779         krb5_boolean b;
1780
1781         b = krb5_principal_compare(context,
1782                                    client->entry.principal,
1783                                    principal_mappings.val[i].principal);
1784         if (b == FALSE)
1785             continue;
1786         if (strcmp(principal_mappings.val[i].subject, *subject_name) != 0)
1787             continue;
1788         kdc_log(context, config, 5,
1789                 "Found matching PK-INIT FILE ACL");
1790         return 0;
1791     }
1792
1793     ret = KRB5_KDC_ERR_CLIENT_NAME_MISMATCH;
1794     krb5_set_error_message(context, ret,
1795                           "PKINIT no matching principals for %s",
1796                           *subject_name);
1797
1798     kdc_log(context, config, 5,
1799             "PKINIT no matching principals for %s",
1800             *subject_name);
1801
1802     free(*subject_name);
1803     *subject_name = NULL;
1804
1805     return ret;
1806 }
1807
1808 static krb5_error_code
1809 add_principal_mapping(krb5_context context,
1810                       const char *principal_name,
1811                       const char * subject)
1812 {
1813    struct pk_allowed_princ *tmp;
1814    krb5_principal principal;
1815    krb5_error_code ret;
1816
1817    tmp = realloc(principal_mappings.val,
1818                  (principal_mappings.len + 1) * sizeof(*tmp));
1819    if (tmp == NULL)
1820        return ENOMEM;
1821    principal_mappings.val = tmp;
1822
1823    ret = krb5_parse_name(context, principal_name, &principal);
1824    if (ret)
1825        return ret;
1826
1827    principal_mappings.val[principal_mappings.len].principal = principal;
1828
1829    principal_mappings.val[principal_mappings.len].subject = strdup(subject);
1830    if (principal_mappings.val[principal_mappings.len].subject == NULL) {
1831        krb5_free_principal(context, principal);
1832        return ENOMEM;
1833    }
1834    principal_mappings.len++;
1835
1836    return 0;
1837 }
1838
1839 krb5_error_code
1840 _kdc_add_inital_verified_cas(krb5_context context,
1841                              krb5_kdc_configuration *config,
1842                              pk_client_params *cp,
1843                              EncTicketPart *tkt)
1844 {
1845     AD_INITIAL_VERIFIED_CAS cas;
1846     krb5_error_code ret;
1847     krb5_data data;
1848     size_t size;
1849
1850     memset(&cas, 0, sizeof(cas));
1851
1852     /* XXX add CAs to cas here */
1853
1854     ASN1_MALLOC_ENCODE(AD_INITIAL_VERIFIED_CAS, data.data, data.length,
1855                        &cas, &size, ret);
1856     if (ret)
1857         return ret;
1858     if (data.length != size)
1859         krb5_abortx(context, "internal asn.1 encoder error");
1860
1861     ret = _kdc_tkt_add_if_relevant_ad(context, tkt,
1862                                       KRB5_AUTHDATA_INITIAL_VERIFIED_CAS,
1863                                       &data);
1864     krb5_data_free(&data);
1865     return ret;
1866 }
1867
1868 /*
1869  *
1870  */
1871
1872 static void
1873 load_mappings(krb5_context context, const char *fn)
1874 {
1875     krb5_error_code ret;
1876     char buf[1024];
1877     unsigned long lineno = 0;
1878     FILE *f;
1879
1880     f = fopen(fn, "r");
1881     if (f == NULL)
1882         return;
1883
1884     while (fgets(buf, sizeof(buf), f) != NULL) {
1885         char *subject_name, *p;
1886
1887         buf[strcspn(buf, "\n")] = '\0';
1888         lineno++;
1889
1890         p = buf + strspn(buf, " \t");
1891
1892         if (*p == '#' || *p == '\0')
1893             continue;
1894
1895         subject_name = strchr(p, ':');
1896         if (subject_name == NULL) {
1897             krb5_warnx(context, "pkinit mapping file line %lu "
1898                        "missing \":\" :%s",
1899                        lineno, buf);
1900             continue;
1901         }
1902         *subject_name++ = '\0';
1903
1904         ret = add_principal_mapping(context, p, subject_name);
1905         if (ret) {
1906             krb5_warn(context, ret, "failed to add line %lu \":\" :%s\n",
1907                       lineno, buf);
1908             continue;
1909         }
1910     }
1911
1912     fclose(f);
1913 }
1914                 
1915 /*
1916  *
1917  */
1918
1919 krb5_error_code
1920 _kdc_pk_initialize(krb5_context context,
1921                    krb5_kdc_configuration *config,
1922                    const char *user_id,
1923                    const char *anchors,
1924                    char **pool,
1925                    char **revoke_list)
1926 {
1927     const char *file;
1928     char *fn = NULL;
1929     krb5_error_code ret;
1930
1931     file = krb5_config_get_string(context, NULL,
1932                                   "libdefaults", "moduli", NULL);
1933
1934     ret = _krb5_parse_moduli(context, file, &moduli);
1935     if (ret)
1936         krb5_err(context, 1, ret, "PKINIT: failed to load modidi file");
1937
1938     principal_mappings.len = 0;
1939     principal_mappings.val = NULL;
1940
1941     ret = _krb5_pk_load_id(context,
1942                            &kdc_identity,
1943                            0,
1944                            user_id,
1945                            anchors,
1946                            pool,
1947                            revoke_list,
1948                            NULL,
1949                            NULL,
1950                            NULL);
1951     if (ret) {
1952         krb5_warn(context, ret, "PKINIT: ");
1953         config->enable_pkinit = 0;
1954         return ret;
1955     }
1956
1957     {
1958         hx509_query *q;
1959         hx509_cert cert;
1960         
1961         ret = hx509_query_alloc(kdc_identity->hx509ctx, &q);
1962         if (ret) {
1963             krb5_warnx(context, "PKINIT: out of memory");
1964             return ENOMEM;
1965         }
1966         
1967         hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY);
1968         if (config->pkinit_kdc_friendly_name)
1969             hx509_query_match_friendly_name(q, config->pkinit_kdc_friendly_name);
1970         
1971         ret = hx509_certs_find(kdc_identity->hx509ctx,
1972                                kdc_identity->certs,
1973                                q,
1974                                &cert);
1975         hx509_query_free(kdc_identity->hx509ctx, q);
1976         if (ret == 0) {
1977             if (hx509_cert_check_eku(kdc_identity->hx509ctx, cert,
1978                                      &asn1_oid_id_pkkdcekuoid, 0)) {
1979                 hx509_name name;
1980                 char *str;
1981                 ret = hx509_cert_get_subject(cert, &name);
1982                 hx509_name_to_string(name, &str);
1983                 krb5_warnx(context, "WARNING Found KDC certificate (%s)"
1984                            "is missing the PK-INIT KDC EKU, this is bad for "
1985                            "interoperability.", str);
1986                 hx509_name_free(&name);
1987                 free(str);
1988             }
1989             hx509_cert_free(cert);
1990         } else
1991             krb5_warnx(context, "PKINIT: failed to find a signing "
1992                        "certifiate with a public key");
1993     }
1994
1995     if (krb5_config_get_bool_default(context,
1996                                      NULL,
1997                                      FALSE,
1998                                      "kdc",
1999                                      "pkinit_allow_proxy_certificate",
2000                                      NULL))
2001         config->pkinit_allow_proxy_certs = 1;
2002
2003     file = krb5_config_get_string(context,
2004                                   NULL,
2005                                   "kdc",
2006                                   "pkinit_mappings_file",
2007                                   NULL);
2008     if (file == NULL) {
2009         asprintf(&fn, "%s/pki-mapping", hdb_db_dir(context));
2010         file = fn;
2011     }
2012
2013     load_mappings(context, file);
2014     if (fn)
2015         free(fn);
2016
2017     return 0;
2018 }
2019
2020 #endif /* PKINIT */