r8302: import mini HEIMDAL into the tree
[samba.git] / source4 / heimdal / lib / krb5 / pkinit.c
1 /*
2  * Copyright (c) 2003 - 2005 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 "krb5_locl.h"
35
36 RCSID("$Id: pkinit.c,v 1.55 2005/05/19 18:49:05 lha Exp $");
37
38 #ifdef PKINIT
39
40 #include <openssl/evp.h>
41 #include <openssl/x509.h>
42 #include <openssl/pem.h>
43 #include <openssl/err.h>
44 #include <openssl/dh.h>
45 #include <openssl/bn.h>
46 #include <openssl/engine.h>
47 #include <openssl/ui.h>
48
49 #ifdef HAVE_DIRENT_H
50 #include <dirent.h>
51 #endif
52
53 #include "heim_asn1.h"
54 #include "rfc2459_asn1.h"
55 #include "cms_asn1.h"
56 #include "pkinit_asn1.h"
57
58 enum {
59     COMPAT_WIN2K = 1,
60     COMPAT_19 = 2,
61     COMPAT_25 = 3
62 };
63
64
65
66 #define OPENSSL_ASN1_MALLOC_ENCODE(T, B, BL, S, R)                      \
67 {                                                                       \
68   unsigned char *p;                                                     \
69   (BL) = i2d_##T((S), NULL);                                            \
70   if ((BL) <= 0) {                                                      \
71      (R) = EINVAL;                                                      \
72   } else {                                                              \
73     (B) = malloc((BL));                                                 \
74     if ((B) == NULL) {                                                  \
75        (R) = ENOMEM;                                                    \
76     } else {                                                            \
77         p = (B);                                                        \
78         (R) = 0;                                                        \
79         (BL) = i2d_##T((S), &p);                                        \
80         if ((BL) <= 0) {                                                \
81            free((B));                                                   \
82            (R) = ASN1_OVERRUN;                                          \
83         }                                                               \
84     }                                                                   \
85   }                                                                     \
86 }
87
88 /* ENGING_load_private_key requires a UI_METHOD and data  
89  * if to be usable from PAM 
90  */
91
92 struct krb5_ui_data {
93     krb5_context context;
94     krb5_prompter_fct prompter;
95     void * prompter_data;
96 };
97
98 struct krb5_pk_identity {
99     EVP_PKEY *private_key;
100     STACK_OF(X509) *cert;
101     STACK_OF(X509) *trusted_certs;
102     STACK_OF(X509_CRL) *crls;
103     ENGINE *engine;
104 };
105
106 struct krb5_pk_cert {
107     X509 *cert;
108 };
109
110 struct krb5_pk_init_ctx_data {
111     struct krb5_pk_identity *id;
112     DH *dh;
113 };
114
115
116 void KRB5_LIB_FUNCTION
117 _krb5_pk_cert_free(struct krb5_pk_cert *cert)
118 {
119     if (cert->cert)
120         X509_free(cert->cert);
121     free(cert);
122 }
123
124 static krb5_error_code
125 BN_to_integer(krb5_context context, BIGNUM *bn, heim_integer *integer)
126 {
127     integer->length = BN_num_bytes(bn);
128     integer->data = malloc(integer->length);
129     if (integer->data == NULL) {
130         krb5_clear_error_string(context);
131         return ENOMEM;
132     }
133     BN_bn2bin(bn, integer->data);
134     integer->negative = bn->neg;
135     return 0;
136 }
137
138 /*
139  * UI ex_data has the callback_data as passed to Engine. This is far
140  * from being complete, we will only process one prompt
141  */
142
143 static int
144 krb5_ui_method_read_string(UI *ui, UI_STRING *uis)
145 {
146     char *buffer;
147     size_t length;
148     krb5_error_code ret;
149     krb5_prompt prompt;
150     krb5_data password_data;
151     struct krb5_ui_data *ui_data;
152   
153     ui_data = (struct krb5_ui_data *)UI_get_app_data(ui);
154
155     switch (UI_get_string_type(uis)) {
156     case UIT_INFO:
157     case UIT_ERROR:
158         /* looks like the RedHat pam_prompter might handle 
159          * INFO and ERROR, Will see what happens */
160     case UIT_VERIFY:
161     case UIT_PROMPT:
162         length = UI_get_result_maxsize(uis);
163         buffer = malloc(length);
164         if (buffer == NULL) {
165             krb5_set_error_string(ui_data->context, "malloc: out of memory");
166             return 0;
167         }
168         password_data.data = buffer;
169         password_data.length = length;
170
171         prompt.prompt = UI_get0_output_string(uis);
172         prompt.hidden = !(UI_get_input_flags(uis) & UI_INPUT_FLAG_ECHO);
173         prompt.reply  = &password_data;
174         prompt.type   = KRB5_PROMPT_TYPE_PASSWORD;
175   
176         ret = (*ui_data->prompter)(ui_data->context, 
177                                    ui_data->prompter_data, 
178                                    NULL, NULL, 1, &prompt);
179         if (ret == 0) {
180             buffer[length - 1] = '\0';
181             UI_set_result(ui, uis, password_data.data);
182
183             /*
184              * RedHat pam_krb5 pam_prompter does a strdup but others
185              * may copy into buffer. XXX should we just leak the
186              * memory instead ?
187              */
188
189             if (buffer != password_data.data)
190                 free(password_data.data);
191             memset (buffer, 0, length);
192             free(buffer);
193             return 1;
194         }
195         memset (buffer, 0, length);
196         free(buffer);
197         break;
198     case UIT_NONE:
199     case UIT_BOOLEAN:
200         /* XXX for now do not handle */
201         break;
202
203     }
204     return 0;
205 }
206
207
208 static krb5_error_code
209 set_digest_alg(DigestAlgorithmIdentifier *id,
210                const heim_oid *oid,
211                void *param, size_t length)
212 {
213     krb5_error_code ret;
214     if (param) {
215         id->parameters = malloc(sizeof(*id->parameters));
216         if (id->parameters == NULL)
217             return ENOMEM;
218         id->parameters->data = malloc(length);
219         if (id->parameters->data == NULL) {
220             free(id->parameters);
221             id->parameters = NULL;
222             return ENOMEM;
223         }
224         memcpy(id->parameters->data, param, length);
225         id->parameters->length = length;
226     } else
227         id->parameters = NULL;
228     ret = copy_oid(oid, &id->algorithm);
229     if (ret) {
230         if (id->parameters) {
231             free(id->parameters->data);
232             free(id->parameters);
233             id->parameters = NULL;
234         }
235         return ret;
236     }
237     return 0;
238 }
239
240 krb5_error_code KRB5_LIB_FUNCTION
241 _krb5_pk_create_sign(krb5_context context,
242                      const heim_oid *eContentType,
243                      krb5_data *eContent,
244                      struct krb5_pk_identity *id,
245                      krb5_data *sd_data)
246 {
247     SignerInfo *signer_info;
248     X509 *user_cert;
249     heim_integer *serial;
250     krb5_error_code ret;
251     krb5_data buf;
252     SignedData sd;
253     EVP_MD_CTX md;
254     int len, i;
255     size_t size;
256     
257     X509_NAME *issuer_name;
258
259     memset(&sd, 0, sizeof(sd));
260
261     if (id == NULL)
262         return HEIM_PKINIT_NO_CERTIFICATE;
263     if (id->cert == NULL)
264         return HEIM_PKINIT_NO_CERTIFICATE;
265     if (id->private_key == NULL)
266         return HEIM_PKINIT_NO_PRIVATE_KEY;
267
268     if (sk_X509_num(id->cert) == 0)
269         return HEIM_PKINIT_NO_CERTIFICATE;
270
271     sd.version = 3;
272
273     sd.digestAlgorithms.len = 0;
274     sd.digestAlgorithms.val = NULL;
275     copy_oid(eContentType, &sd.encapContentInfo.eContentType);
276     ALLOC(sd.encapContentInfo.eContent, 1);
277     if (sd.encapContentInfo.eContent == NULL) {
278         krb5_clear_error_string(context);
279         ret = ENOMEM;
280         goto out;
281     }
282
283     ret = krb5_data_copy(&buf, eContent->data, eContent->length);
284     if (ret) {
285         krb5_clear_error_string(context);
286         ret = ENOMEM;
287         goto out;
288     }
289
290     sd.encapContentInfo.eContent->data = buf.data;
291     sd.encapContentInfo.eContent->length = buf.length;
292
293     ALLOC_SEQ(&sd.signerInfos, 1);
294     if (sd.signerInfos.val == NULL) {
295         krb5_set_error_string(context, "malloc: out of memory");
296         ret = ENOMEM;
297         goto out;
298     }
299
300     signer_info = &sd.signerInfos.val[0];
301
302     user_cert = sk_X509_value(id->cert, 0);
303     if (user_cert == NULL) {
304         krb5_set_error_string(context, "pkinit: no user certificate");
305         ret = HEIM_PKINIT_NO_CERTIFICATE;
306         goto out;
307     }
308
309     signer_info->version = 1;
310
311     issuer_name = X509_get_issuer_name(user_cert);
312
313     OPENSSL_ASN1_MALLOC_ENCODE(X509_NAME, 
314                                buf.data,
315                                buf.length,
316                                issuer_name,
317                                ret);
318     if (ret) {
319         krb5_set_error_string(context, "pkinit: failed encoding name");
320         goto out;
321     }
322     ret = decode_Name(buf.data, buf.length,
323                       &signer_info->sid.u.issuerAndSerialNumber.issuer,
324                       NULL);
325     free(buf.data);
326     if (ret) {
327         krb5_set_error_string(context, "pkinit: failed to parse Name");
328         goto out;
329     }
330     signer_info->sid.element = choice_CMSIdentifier_issuerAndSerialNumber;
331
332     serial = &signer_info->sid.u.issuerAndSerialNumber.serialNumber;
333     {
334         ASN1_INTEGER *isn = X509_get_serialNumber(user_cert);
335         BIGNUM *bn = ASN1_INTEGER_to_BN(isn, NULL);
336         if (bn == NULL) {
337             ret = ENOMEM;
338             krb5_set_error_string(context, "pkinit: failed allocating "
339                                   "serial number");
340             goto out;
341         }
342         ret = BN_to_integer(context, bn, serial);
343         BN_free(bn);
344         if (ret) {
345             krb5_set_error_string(context, "pkinit: failed encoding "
346                                   "serial number");
347             goto out;
348         }
349     }
350
351     ret = set_digest_alg(&signer_info->digestAlgorithm,
352                          oid_id_secsig_sha_1(), "\x05\x00", 2);
353     if (ret) {
354         krb5_set_error_string(context, "malloc: out of memory");
355         goto out;
356     }
357
358     signer_info->signedAttrs = NULL;
359     signer_info->unsignedAttrs = NULL;
360
361     copy_oid(oid_id_pkcs1_rsaEncryption(),
362              &signer_info->signatureAlgorithm.algorithm);
363     signer_info->signatureAlgorithm.parameters = NULL;
364
365     buf.data = malloc(EVP_PKEY_size(id->private_key));
366     if (buf.data == NULL) {
367         krb5_set_error_string(context, "malloc: out of memory");
368         ret = ENOMEM;
369         goto out;
370     }
371
372     EVP_SignInit(&md, EVP_sha1());
373     EVP_SignUpdate(&md,
374                    sd.encapContentInfo.eContent->data,
375                    sd.encapContentInfo.eContent->length);
376     ret = EVP_SignFinal(&md, buf.data, &len, id->private_key);
377     if (ret != 1) {
378         free(buf.data);
379         krb5_set_error_string(context, "PKINIT: failed to sign with "
380                               "private key: %s",
381                               ERR_error_string(ERR_get_error(), NULL));
382         ret = EINVAL;
383         goto out;
384     }
385
386     signer_info->signature.data = buf.data;
387     signer_info->signature.length = len;
388
389     ALLOC_SEQ(&sd.digestAlgorithms, 1);
390     if (sd.digestAlgorithms.val == NULL) {
391         krb5_clear_error_string(context);
392         ret = ENOMEM;
393         goto out;
394     }
395
396     ret = set_digest_alg(&sd.digestAlgorithms.val[0],
397                          oid_id_secsig_sha_1(), "\x05\x00", 2);
398     if (ret) {
399         krb5_set_error_string(context, "malloc: out of memory");
400         goto out;
401     }
402
403     ALLOC(sd.certificates, 1);
404     if (sd.certificates == NULL) {
405         krb5_clear_error_string(context);
406         ret = ENOMEM;
407         goto out;
408     }
409
410     sd.certificates->data = NULL;
411     sd.certificates->length = 0;
412
413     for (i = 0; i < sk_X509_num(id->cert); i++) {
414         void *data;
415
416         OPENSSL_ASN1_MALLOC_ENCODE(X509, 
417                                    buf.data,
418                                    buf.length,
419                                    sk_X509_value(id->cert, i),
420                                    ret);
421         if (ret) {
422             krb5_clear_error_string(context);
423             goto out;
424         }
425         data = realloc(sd.certificates->data, 
426                        sd.certificates->length + buf.length);
427         if (data == NULL) {
428             free(buf.data);
429             krb5_clear_error_string(context);
430             ret = ENOMEM;
431             goto out;
432         }
433         memcpy(((char *)data) + sd.certificates->length,
434                buf.data, buf.length);
435         sd.certificates->length += buf.length;
436         sd.certificates->data = data;
437         free(buf.data);
438     }
439
440     ASN1_MALLOC_ENCODE(SignedData, sd_data->data, sd_data->length, 
441                        &sd, &size, ret);
442     if (ret) {
443         krb5_set_error_string(context, "SignedData failed %d", ret);
444         goto out;
445     }
446     if (sd_data->length != size)
447         krb5_abortx(context, "internal ASN1 encoder error");
448
449  out:
450     free_SignedData(&sd);
451
452     return ret;
453 }
454
455 static krb5_error_code
456 build_auth_pack_win2k(krb5_context context,
457                       unsigned nonce,
458                       const KDC_REQ_BODY *body,
459                       AuthPack_Win2k *a)
460 {
461     krb5_error_code ret;
462     krb5_timestamp sec;
463     int32_t usec;
464
465     /* fill in PKAuthenticator */
466     ret = copy_PrincipalName(body->sname, &a->pkAuthenticator.kdcName);
467     if (ret)
468         return ret;
469     ret = copy_Realm(&body->realm, &a->pkAuthenticator.kdcRealm);
470     if (ret)
471         return ret;
472
473     krb5_us_timeofday(context, &sec, &usec);
474     a->pkAuthenticator.ctime = sec;
475     a->pkAuthenticator.cusec = usec;
476     a->pkAuthenticator.nonce = nonce;
477
478     return 0;
479 }
480
481 static krb5_error_code
482 build_auth_pack_19(krb5_context context,
483                    unsigned nonce,
484                    const KDC_REQ_BODY *body,
485                    AuthPack_19 *a)
486 {
487     size_t buf_size, len;
488     krb5_cksumtype cksum;
489     krb5_error_code ret;
490     void *buf;
491     krb5_timestamp sec;
492     int32_t usec;
493
494     krb5_clear_error_string(context);
495
496     /* XXX some PACKETCABLE needs implemetations need md5 */
497     cksum = CKSUMTYPE_RSA_MD5;
498
499     krb5_us_timeofday(context, &sec, &usec);
500     a->pkAuthenticator.ctime = sec;
501     a->pkAuthenticator.nonce = nonce;
502
503     ASN1_MALLOC_ENCODE(KDC_REQ_BODY, buf, buf_size, body, &len, ret);
504     if (ret)
505         return ret;
506     if (buf_size != len)
507         krb5_abortx(context, "internal error in ASN.1 encoder");
508
509     ret = krb5_create_checksum(context,
510                                NULL,
511                                0,
512                                cksum,
513                                buf,
514                                len,
515                                &a->pkAuthenticator.paChecksum);
516     free(buf);
517
518     return ret;
519 }
520
521 static krb5_error_code
522 build_auth_pack(krb5_context context,
523                 unsigned nonce,
524                 DH *dh,
525                 const KDC_REQ_BODY *body,
526                 AuthPack *a)
527 {
528     size_t buf_size, len;
529     krb5_error_code ret;
530     void *buf;
531     krb5_timestamp sec;
532     int32_t usec;
533     Checksum checksum;
534
535     krb5_clear_error_string(context);
536
537     memset(&checksum, 0, sizeof(checksum));
538
539     krb5_us_timeofday(context, &sec, &usec);
540     a->pkAuthenticator.ctime = sec;
541     a->pkAuthenticator.nonce = nonce;
542
543     ASN1_MALLOC_ENCODE(KDC_REQ_BODY, buf, buf_size, body, &len, ret);
544     if (ret)
545         return ret;
546     if (buf_size != len)
547         krb5_abortx(context, "internal error in ASN.1 encoder");
548
549     ret = krb5_create_checksum(context,
550                                NULL,
551                                0,
552                                CKSUMTYPE_SHA1,
553                                buf,
554                                len,
555                                &checksum);
556     free(buf);
557     if (ret == 0) {
558         ret = krb5_data_copy(&a->pkAuthenticator.paChecksum,
559                              checksum.checksum.data, checksum.checksum.length);
560         free_Checksum(&checksum);
561     }
562
563     if (ret == 0 && dh) {
564         DomainParameters dp;
565         heim_integer dh_pub_key;
566         krb5_data buf;
567         size_t size;
568
569         ALLOC(a->clientPublicValue, 1);
570         if (a->clientPublicValue == NULL)
571             return ENOMEM;
572         ret = copy_oid(oid_id_dhpublicnumber(),
573                        &a->clientPublicValue->algorithm.algorithm);
574         if (ret)
575             return ret;
576         
577         memset(&dp, 0, sizeof(dp));
578
579         ret = BN_to_integer(context, dh->p, &dp.p);
580         if (ret) {
581             free_DomainParameters(&dp);
582             return ret;
583         }
584         ret = BN_to_integer(context, dh->g, &dp.g);
585         if (ret) {
586             free_DomainParameters(&dp);
587             return ret;
588         }
589         ret = BN_to_integer(context, dh->q, &dp.q);
590         if (ret) {
591             free_DomainParameters(&dp);
592             return ret;
593         }
594         dp.j = NULL;
595         dp.validationParms = NULL;
596
597         a->clientPublicValue->algorithm.parameters = 
598             malloc(sizeof(*a->clientPublicValue->algorithm.parameters));
599         if (a->clientPublicValue->algorithm.parameters == NULL) {
600             free_DomainParameters(&dp);
601             return ret;
602         }
603
604         ASN1_MALLOC_ENCODE(DomainParameters,
605                            a->clientPublicValue->algorithm.parameters->data,
606                            a->clientPublicValue->algorithm.parameters->length,
607                            &dp, &size, ret);
608         free_DomainParameters(&dp);
609         if (ret)
610             return ret;
611         if (size != a->clientPublicValue->algorithm.parameters->length)
612             krb5_abortx(context, "Internal ASN1 encoder error");
613
614         ret = BN_to_integer(context, dh->pub_key, &dh_pub_key);
615         if (ret)
616             return ret;
617
618         buf.length = length_heim_integer(&dh_pub_key);
619         buf.data = malloc(buf.length);
620         if (buf.data == NULL) {
621             free_heim_integer(&dh_pub_key);
622             krb5_set_error_string(context, "malloc: out of memory");
623             return ret;
624         }
625         ret = der_put_heim_integer((char *)buf.data + buf.length - 1,
626                                    buf.length, &dh_pub_key, &size);
627         free_heim_integer(&dh_pub_key);
628         if (ret) {
629             free(buf.data);
630             return ret;
631         }
632         if (size != buf.length)
633             krb5_abortx(context, "asn1 internal error");
634
635         a->clientPublicValue->subjectPublicKey.length = buf.length * 8;
636         a->clientPublicValue->subjectPublicKey.data = buf.data;
637     }
638
639     return ret;
640 }
641
642 krb5_error_code KRB5_LIB_FUNCTION
643 _krb5_pk_mk_ContentInfo(krb5_context context,
644                         const krb5_data *buf, 
645                         const heim_oid *oid,
646                         struct ContentInfo *content_info)
647 {
648     krb5_error_code ret;
649
650     ret = copy_oid(oid, &content_info->contentType);
651     if (ret)
652         return ret;
653     ALLOC(content_info->content, 1);
654     if (content_info->content == NULL)
655         return ENOMEM;
656     content_info->content->data = malloc(buf->length);
657     if (content_info->content->data == NULL)
658         return ENOMEM;
659     memcpy(content_info->content->data, buf->data, buf->length);
660     content_info->content->length = buf->length;
661     return 0;
662 }
663
664 static krb5_error_code
665 pk_mk_padata(krb5_context context,
666              int compat,
667              krb5_pk_init_ctx ctx,
668              const KDC_REQ_BODY *req_body,
669              unsigned nonce,
670              METHOD_DATA *md)
671 {
672     struct ContentInfo content_info;
673     krb5_error_code ret;
674     const heim_oid *oid;
675     PA_PK_AS_REQ req;
676     size_t size;
677     krb5_data buf, sd_buf;
678     int pa_type;
679
680     krb5_data_zero(&buf);
681     krb5_data_zero(&sd_buf);
682     memset(&req, 0, sizeof(req));
683     memset(&content_info, 0, sizeof(content_info));
684
685     if (compat == COMPAT_WIN2K) {
686         AuthPack_Win2k ap;
687
688         memset(&ap, 0, sizeof(ap));
689
690         ret = build_auth_pack_win2k(context, nonce, req_body, &ap);
691         if (ret) {
692             free_AuthPack_Win2k(&ap);
693             goto out;
694         }
695
696         ASN1_MALLOC_ENCODE(AuthPack_Win2k, buf.data, buf.length,
697                            &ap, &size, ret);
698         free_AuthPack_Win2k(&ap);
699         if (ret) {
700             krb5_set_error_string(context, "AuthPack_Win2k: %d", ret);
701             goto out;
702         }
703         if (buf.length != size)
704             krb5_abortx(context, "internal ASN1 encoder error");
705
706         oid = oid_id_pkcs7_data();
707     } else if (compat == COMPAT_19) {
708         AuthPack_19 ap;
709         
710         memset(&ap, 0, sizeof(ap));
711
712         ret = build_auth_pack_19(context, nonce, req_body, &ap);
713         if (ret) {
714             free_AuthPack_19(&ap);
715             goto out;
716         }
717
718         ASN1_MALLOC_ENCODE(AuthPack_19, buf.data, buf.length, &ap, &size, ret);
719         free_AuthPack_19(&ap);
720         if (ret) {
721             krb5_set_error_string(context, "AuthPack_19: %d", ret);
722             goto out;
723         }
724         if (buf.length != size)
725             krb5_abortx(context, "internal ASN1 encoder error");
726
727         oid = oid_id_pkauthdata();
728     } else if (compat == COMPAT_25) {
729         AuthPack ap;
730         
731         memset(&ap, 0, sizeof(ap));
732
733         ret = build_auth_pack(context, nonce, ctx->dh, req_body, &ap);
734         if (ret) {
735             free_AuthPack(&ap);
736             goto out;
737         }
738
739         ASN1_MALLOC_ENCODE(AuthPack, buf.data, buf.length, &ap, &size, ret);
740         free_AuthPack(&ap);
741         if (ret) {
742             krb5_set_error_string(context, "AuthPack: %d", ret);
743             goto out;
744         }
745         if (buf.length != size)
746             krb5_abortx(context, "internal ASN1 encoder error");
747
748         oid = oid_id_pkauthdata();
749     } else
750         krb5_abortx(context, "internal pkinit error");
751
752     ret = _krb5_pk_create_sign(context,
753                                oid,
754                                &buf,
755                                ctx->id, 
756                                &sd_buf);
757     krb5_data_free(&buf);
758     if (ret)
759         goto out;
760
761     ret = _krb5_pk_mk_ContentInfo(context, &sd_buf, oid_id_pkcs7_signedData(), 
762                                   &content_info);
763     krb5_data_free(&sd_buf);
764     if (ret)
765         goto out;
766
767     /* XXX tell the kdc what CAs the client is willing to accept */
768     req.trustedCertifiers = NULL;
769     req.kdcPkId = NULL;
770
771     if (compat == COMPAT_WIN2K) {
772         PA_PK_AS_REQ_Win2k winreq;
773
774         pa_type = KRB5_PADATA_PK_AS_REQ_WIN;
775
776         memset(&winreq, 0, sizeof(winreq));
777
778         ASN1_MALLOC_ENCODE(ContentInfo,
779                            winreq.signed_auth_pack.data,
780                            winreq.signed_auth_pack.length,
781                            &content_info,
782                            &size,
783                            ret);
784         if (ret)
785             goto out;
786         if (winreq.signed_auth_pack.length != size)
787             krb5_abortx(context, "Internal ASN1 encoder error");
788
789         ASN1_MALLOC_ENCODE(PA_PK_AS_REQ_Win2k, buf.data, buf.length,
790                            &winreq, &size, ret);
791         free_PA_PK_AS_REQ_Win2k(&winreq);
792
793     } else if (compat == COMPAT_19) {
794         PA_PK_AS_REQ_19 req_19;
795
796         pa_type = KRB5_PADATA_PK_AS_REQ_19;
797
798         memset(&req_19, 0, sizeof(req_19));
799
800         ret = copy_ContentInfo(&content_info, &req_19.signedAuthPack);
801         if (ret) {
802             krb5_clear_error_string(context);
803             goto out;
804         }
805         req_19.kdcCert = NULL;
806         req_19.trustedCertifiers = NULL;
807         req_19.encryptionCert = NULL;
808
809         ASN1_MALLOC_ENCODE(PA_PK_AS_REQ_19, buf.data, buf.length,
810                            &req_19, &size, ret);
811
812         free_PA_PK_AS_REQ_19(&req_19);
813
814     } else if (compat == COMPAT_25) {
815
816         pa_type = KRB5_PADATA_PK_AS_REQ;
817
818         ASN1_MALLOC_ENCODE(ContentInfo,
819                            req.signedAuthPack.data,
820                            req.signedAuthPack.length,
821                            &content_info,
822                            &size,
823                            ret);
824         if (ret)
825             goto out;
826         if (req.signedAuthPack.length != size)
827             krb5_abortx(context, "Internal ASN1 encoder error");
828
829         ASN1_MALLOC_ENCODE(PA_PK_AS_REQ, buf.data, buf.length,
830                            &req, &size, ret);
831
832     } else
833         krb5_abortx(context, "internal pkinit error");
834     if (ret) {
835         krb5_set_error_string(context, "PA-PK-AS-REQ %d", ret);
836         goto out;
837     }
838     if (buf.length != size)
839         krb5_abortx(context, "Internal ASN1 encoder error");
840
841     ret = krb5_padata_add(context, md, pa_type, buf.data, buf.length);
842     if (ret)
843         free(buf.data);
844  out:
845     free_ContentInfo(&content_info);
846
847     return ret;
848 }
849
850
851 krb5_error_code KRB5_LIB_FUNCTION 
852 _krb5_pk_mk_padata(krb5_context context,
853                    void *c,
854                    const KDC_REQ_BODY *req_body,
855                    unsigned nonce,
856                    METHOD_DATA *md)
857 {
858     krb5_pk_init_ctx ctx = c;
859     krb5_error_code ret;
860     size_t size;
861     krb5_data buf;
862     const char *provisioning_server;
863     int win2k_compat;
864
865     win2k_compat = krb5_config_get_bool_default(context, NULL,
866                                                 FALSE,
867                                                 "realms",
868                                                 req_body->realm,
869                                                 "win2k_pkinit",
870                                                 NULL);
871     if (context->pkinit_flags & KRB5_PKINIT_WIN2K)
872         win2k_compat = 1;
873
874     if (win2k_compat) {
875         ret = pk_mk_padata(context, COMPAT_WIN2K, ctx, req_body, nonce, md);
876         if (ret)
877             goto out;
878     } else {
879         ret = pk_mk_padata(context, COMPAT_19, ctx, req_body, nonce, md);
880         if (ret)
881             goto out;
882
883         ret = pk_mk_padata(context, COMPAT_25, ctx, req_body, nonce, md);
884         if (ret)
885             goto out;
886     }
887
888     provisioning_server =
889         krb5_config_get_string(context, NULL,
890                                "realms",
891                                req_body->realm,
892                                "packet-cable-provisioning-server",
893                                NULL);
894
895     if (provisioning_server) {
896         /* PacketCable requires the PROV-SRV-LOCATION authenticator */
897         const PROV_SRV_LOCATION prov_server = (char *)provisioning_server;
898
899         ASN1_MALLOC_ENCODE(PROV_SRV_LOCATION, buf.data, buf.length,
900                            &prov_server, &size, ret);
901         if (ret)
902             goto out;
903         if (buf.length != size)
904             krb5_abortx(context, "Internal ASN1 encoder error");
905
906         /* PacketCable uses -1 (application specific) as the auth data type */
907         ret = krb5_padata_add(context, md, -1, buf.data, buf.length);
908         if (ret)
909             free(buf.data);
910     }
911  out:
912     return ret;
913 }
914
915 static krb5_boolean
916 pk_peer_compare(krb5_context context,
917                 const SignerIdentifier *peer1, 
918                 X509 *peer2)
919 {
920     switch (peer1->element) {
921     case choice_CMSIdentifier_issuerAndSerialNumber: {
922         ASN1_INTEGER *i;
923         const heim_integer *serial;
924         X509_NAME *name;
925         unsigned char *p;
926         size_t len;
927
928         i = X509_get_serialNumber(peer2);
929         serial = &peer1->u.issuerAndSerialNumber.serialNumber;
930
931         if (i->length != serial->length ||
932             memcmp(i->data, serial->data, i->length) != 0)
933             return FALSE;
934
935         p = peer1->u.issuerAndSerialNumber.issuer._save.data;
936         len = peer1->u.issuerAndSerialNumber.issuer._save.length;
937         name = d2i_X509_NAME(NULL, &p, len);
938         if (name == NULL)
939             return FALSE;
940         
941         if (X509_NAME_cmp(name, X509_get_issuer_name(peer2)) != 0) {
942             X509_NAME_free(name);
943             return FALSE;
944         }
945         X509_NAME_free(name);
946         break;
947     }
948     case choice_CMSIdentifier_subjectKeyIdentifier:
949         return FALSE;
950     default:
951         return FALSE;
952     }
953     return TRUE;
954 }
955
956 static krb5_error_code
957 pk_decrypt_key(krb5_context context,
958                heim_octet_string *encrypted_key,
959                EVP_PKEY *priv_key,
960                krb5_keyblock *key)
961 {
962     int ret;
963     unsigned char *buf;
964
965     buf = malloc(EVP_PKEY_size(priv_key));
966     if (buf == NULL) {
967         krb5_set_error_string(context, "malloc: out of memory");
968         return ENOMEM;
969     }
970     ret = EVP_PKEY_decrypt(buf,
971                            encrypted_key->data,
972                            encrypted_key->length,
973                            priv_key);
974     if (ret <= 0) {
975         free(buf);
976         krb5_set_error_string(context, "Can't decrypt key: %s",
977                               ERR_error_string(ERR_get_error(), NULL));
978         return ENOMEM;
979     }
980
981     key->keytype = 0;
982     key->keyvalue.length = ret;
983     key->keyvalue.data = malloc(ret);
984     if (key->keyvalue.data == NULL) {
985         free(buf);
986         krb5_set_error_string(context, "malloc: out of memory");
987         return ENOMEM;
988     }
989     memcpy(key->keyvalue.data, buf, ret);
990     free(buf);
991     return 0;
992 }
993
994
995 static krb5_error_code 
996 pk_verify_chain_standard(krb5_context context,
997                          struct krb5_pk_identity *id,
998                          const SignerIdentifier *client,
999                          STACK_OF(X509) *chain,
1000                          X509 **client_cert)
1001 {
1002     X509_STORE *cert_store = NULL;
1003     X509_STORE_CTX *store_ctx = NULL;
1004     X509 *cert = NULL;
1005     int i;
1006     int ret;
1007
1008     ret = KRB5_KDC_ERROR_CLIENT_NAME_MISMATCH;
1009     for (i = 0; i < sk_X509_num(chain); i++) {
1010         cert = sk_X509_value(chain, i);
1011         if (pk_peer_compare(context, client, cert) == TRUE) {
1012             ret = 0;
1013             break;
1014         }
1015     }
1016     if (ret) {
1017         krb5_set_error_string(context, "PKINIT: verify chain failed "
1018                               "to find client in chain");
1019         return ret;
1020     }
1021
1022     cert_store = X509_STORE_new();
1023     if (cert_store == NULL) {
1024         ret = ENOMEM;
1025         krb5_set_error_string(context, "PKINIT: can't create X509 store: %s",
1026                               ERR_error_string(ERR_get_error(), NULL));
1027     }
1028
1029     store_ctx = X509_STORE_CTX_new();
1030     if (store_ctx == NULL) {
1031         ret = ENOMEM;
1032         krb5_set_error_string(context,
1033                               "PKINIT: can't create X509 store ctx: %s",
1034                               ERR_error_string(ERR_get_error(), NULL));
1035         goto end;
1036     }
1037    
1038     X509_STORE_CTX_init(store_ctx, cert_store, cert, chain);
1039     X509_STORE_CTX_trusted_stack(store_ctx, id->trusted_certs);
1040     X509_verify_cert(store_ctx);
1041     /* the last checked certificate is in store_ctx->current_cert */
1042     krb5_clear_error_string(context);
1043     switch(store_ctx->error) {
1044     case X509_V_OK:
1045         ret = 0;
1046         break;
1047     case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY:
1048         ret = KRB5_KDC_ERROR_CANT_VERIFY_CERTIFICATE;
1049         krb5_set_error_string(context, "PKINIT: failed to verify "
1050                               "certificate: %s ",
1051                               X509_verify_cert_error_string(store_ctx->error));
1052         break;
1053     case X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY:
1054     case X509_V_ERR_CERT_SIGNATURE_FAILURE:
1055     case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD:
1056     case X509_V_ERR_CERT_NOT_YET_VALID:
1057     case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD:
1058     case X509_V_ERR_CERT_HAS_EXPIRED:
1059         ret = KRB5_KDC_ERROR_INVALID_CERTIFICATE;
1060         krb5_set_error_string(context, "PKINIT: invalid certificate: %s ",
1061                               X509_verify_cert_error_string(store_ctx->error));
1062         break;
1063     case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
1064     case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN:
1065     case X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE:
1066     case X509_V_ERR_CERT_CHAIN_TOO_LONG:
1067     case X509_V_ERR_PATH_LENGTH_EXCEEDED:
1068     case X509_V_ERR_INVALID_CA:
1069         ret = KRB5_KDC_ERROR_INVALID_CERTIFICATE;
1070         krb5_set_error_string(context, "PKINIT: unknown CA or can't "
1071                               "verify certificate: %s",
1072                               X509_verify_cert_error_string(store_ctx->error));
1073         break;
1074     default:
1075         ret = KRB5_KDC_ERROR_INVALID_CERTIFICATE; /* XXX */
1076         krb5_set_error_string(context, "PKINIT: failed to verify "
1077                               "certificate: %s (%ld) ",
1078                               X509_verify_cert_error_string(store_ctx->error),
1079                               (long)store_ctx->error);
1080         break;
1081     }
1082     if (ret)
1083         goto end;
1084
1085     /* 
1086      * Since X509_verify_cert() doesn't do CRL checking at all, we have to
1087      * perform own verification against CRLs
1088      */
1089 #if 0
1090     ret = pk_verify_crl(context, store_ctx, id->crls);
1091     if (ret)
1092         goto end;
1093 #endif
1094
1095     if (client_cert && cert)
1096         *client_cert = X509_dup(cert);
1097
1098  end:
1099     if (cert_store)
1100         X509_STORE_free(cert_store);
1101     if (store_ctx)
1102         X509_STORE_CTX_free(store_ctx);
1103     return ret;
1104 }
1105
1106 static int
1107 cert_to_X509(krb5_context context, CertificateSetReal *set,
1108              STACK_OF(X509_CRL) **certs)
1109 {
1110     krb5_error_code ret;
1111     int i;
1112
1113     *certs = sk_X509_new_null();
1114
1115     ret = 0;
1116     for (i = 0; i < set->len; i++) {
1117         unsigned char *p;
1118         X509 *cert;
1119
1120         p = set->val[i].data;
1121         cert = d2i_X509(NULL, &p, set->val[i].length);
1122         if (cert == NULL) {
1123             ret = ASN1_BAD_FORMAT;
1124             break;
1125         }
1126         sk_X509_insert(*certs, cert, i);
1127     }
1128     if (ret) {
1129         krb5_set_error_string(context,
1130                               "PKINIT: Failed to decode certificate chain");
1131         sk_X509_free(*certs);
1132         *certs = NULL;
1133     }
1134     return ret;
1135 }
1136
1137 static krb5_error_code
1138 any_to_CertificateSet(krb5_context context, heim_any *cert, 
1139                       CertificateSetReal *set)
1140 {
1141     size_t size, len, length;
1142     heim_any *val;
1143     int ret;
1144     char *p;
1145     
1146     set->len = 0;
1147     set->val = NULL;
1148
1149     len = 0;
1150     p = cert->data;
1151     length = cert->length;
1152     while (len < cert->length) {
1153         val = realloc(set->val, (set->len + 1) * sizeof(set->val[0]));
1154         if (val == NULL) {
1155             ret = ENOMEM;
1156             goto out;
1157         }
1158         set->val = val;
1159         ret = decode_heim_any(p, length, &set->val[set->len], &size);
1160         if (ret)
1161             goto out;
1162         set->len++;
1163
1164         p += size;
1165         len += size;
1166         length -= size;
1167     }
1168     return 0;
1169  out:
1170     krb5_clear_error_string(context);
1171     free_CertificateSetReal(set);
1172     set->val = NULL;
1173     return ret;
1174 }
1175
1176 krb5_error_code KRB5_LIB_FUNCTION
1177 _krb5_pk_verify_sign(krb5_context context,
1178                      const char *data,
1179                      size_t length,
1180                      struct krb5_pk_identity *id,
1181                      heim_oid *contentType,
1182                      krb5_data *content,
1183                      struct krb5_pk_cert **signer)
1184 {
1185     STACK_OF(X509) *certificates;
1186     SignerInfo *signer_info;
1187     const EVP_MD *evp_type;
1188     EVP_PKEY *public_key;
1189     krb5_error_code ret;
1190     CertificateSetReal set;
1191     EVP_MD_CTX md;
1192     X509 *cert;
1193     SignedData sd;
1194     size_t size;
1195     
1196     *signer = NULL;
1197     krb5_data_zero(content);
1198     contentType->length = 0;
1199     contentType->components = NULL;
1200
1201     memset(&sd, 0, sizeof(sd));
1202
1203     ret = decode_SignedData(data, length, &sd, &size);
1204     if (ret) {
1205         krb5_set_error_string(context, 
1206                               "PKINIT: decoding failed SignedData: %d",
1207                               ret);
1208         goto out;
1209     }
1210
1211     if (sd.encapContentInfo.eContent == NULL) {
1212         krb5_set_error_string(context, 
1213                               "PKINIT: signature missing encapContent");
1214         ret = KRB5KRB_AP_ERR_MSG_TYPE;
1215         goto out;
1216     }
1217
1218     /* XXX Check CMS version */
1219
1220     if (sd.signerInfos.len < 1) {
1221         krb5_set_error_string(context,
1222                               "PKINIT: signature information missing from "
1223                               "pkinit response");
1224         ret = KRB5_KDC_ERR_INVALID_SIG;
1225         goto out;
1226     }
1227
1228     signer_info = &sd.signerInfos.val[0];
1229   
1230     ret = any_to_CertificateSet(context, sd.certificates, &set);
1231     if (ret) {
1232         krb5_set_error_string(context,
1233                               "PKINIT: failed to decode CertificateSet");
1234         goto out;
1235     }
1236
1237     ret = cert_to_X509(context, &set, &certificates);
1238     free_CertificateSetReal(&set);
1239     if (ret) {
1240         krb5_set_error_string(context,
1241                               "PKINIT: failed to decode Certificates");
1242         goto out;
1243     }
1244
1245     ret = pk_verify_chain_standard(context, id,
1246                                    &signer_info->sid,
1247                                    certificates,
1248                                    &cert);
1249     sk_X509_free(certificates);
1250     if (ret)
1251         goto out;
1252   
1253     if (signer_info->signature.length == 0) {
1254         free_SignedData(&sd);
1255         X509_free(cert);
1256         krb5_set_error_string(context, "PKINIT: signature missing from"
1257                               "pkinit response");
1258         return KRB5_KDC_ERR_INVALID_SIG; 
1259     }
1260
1261     public_key = X509_get_pubkey(cert);
1262
1263     /* verify signature */
1264     if (heim_oid_cmp(&signer_info->digestAlgorithm.algorithm,
1265                 oid_id_pkcs1_sha1WithRSAEncryption()) == 0)
1266         evp_type = EVP_sha1();
1267     else if (heim_oid_cmp(&signer_info->digestAlgorithm.algorithm,
1268                           oid_id_pkcs1_md5WithRSAEncryption()) == 0) 
1269         evp_type = EVP_md5();
1270     else if (heim_oid_cmp(&signer_info->digestAlgorithm.algorithm, 
1271                           oid_id_secsig_sha_1()) == 0)
1272         evp_type = EVP_sha1();
1273     else {
1274         X509_free(cert);
1275         krb5_set_error_string(context, "PKINIT: The requested digest "
1276                               "algorithm is not supported");
1277         ret = KRB5_KDC_ERR_INVALID_SIG;
1278         goto out;
1279     }
1280
1281     EVP_VerifyInit(&md, evp_type);
1282     EVP_VerifyUpdate(&md,
1283                      sd.encapContentInfo.eContent->data,
1284                      sd.encapContentInfo.eContent->length);
1285     ret = EVP_VerifyFinal(&md,
1286                           signer_info->signature.data,
1287                           signer_info->signature.length,
1288                           public_key);
1289     if (ret != 1) {
1290         X509_free(cert);
1291         krb5_set_error_string(context, "PKINIT: signature didn't verify: %s",
1292                               ERR_error_string(ERR_get_error(), NULL));
1293         ret = KRB5_KDC_ERR_INVALID_SIG;
1294         goto out;
1295     }
1296
1297     ret = copy_oid(&sd.encapContentInfo.eContentType, contentType);
1298     if (ret) {
1299         krb5_clear_error_string(context);
1300         goto out;
1301     }
1302
1303     content->data = malloc(sd.encapContentInfo.eContent->length);
1304     if (content->data == NULL) {
1305         krb5_clear_error_string(context);
1306         ret = ENOMEM;
1307         goto out;
1308     }
1309     content->length = sd.encapContentInfo.eContent->length;
1310     memcpy(content->data,sd.encapContentInfo.eContent->data,content->length);
1311
1312     *signer = malloc(sizeof(**signer));
1313     if (*signer == NULL) {
1314         krb5_clear_error_string(context);
1315         ret = ENOMEM;
1316         goto out;
1317     }
1318     (*signer)->cert = cert;
1319
1320  out:
1321     free_SignedData(&sd);
1322     if (ret) {
1323         free_oid(contentType);
1324         krb5_data_free(content);
1325     }
1326     return ret;
1327 }
1328
1329 static krb5_error_code
1330 get_reply_key(krb5_context context,
1331               const krb5_data *content,
1332               unsigned nonce,
1333               krb5_keyblock **key)
1334 {
1335     ReplyKeyPack_19 key_pack;
1336     krb5_error_code ret;
1337     size_t size;
1338
1339     ret = decode_ReplyKeyPack_19(content->data,
1340                                  content->length,
1341                                  &key_pack,
1342                                  &size);
1343     if (ret) {
1344         krb5_set_error_string(context, "PKINIT decoding reply key failed");
1345         free_ReplyKeyPack_19(&key_pack);
1346         return ret;
1347     }
1348      
1349     if (key_pack.nonce != nonce) {
1350         krb5_set_error_string(context, "PKINIT enckey nonce is wrong");
1351         free_ReplyKeyPack_19(&key_pack);
1352         return KRB5KRB_AP_ERR_MODIFIED;
1353     }
1354
1355     *key = malloc (sizeof (**key));
1356     if (*key == NULL) {
1357         krb5_set_error_string(context, "PKINIT failed allocating reply key");
1358         free_ReplyKeyPack_19(&key_pack);
1359         krb5_set_error_string(context, "malloc: out of memory");
1360         return ENOMEM;
1361     }
1362
1363     ret = copy_EncryptionKey(&key_pack.replyKey, *key);
1364     free_ReplyKeyPack_19(&key_pack);
1365     if (ret) {
1366         krb5_set_error_string(context, "PKINIT failed copying reply key");
1367         free(*key);
1368     }
1369
1370     return ret;
1371 }
1372
1373 static krb5_error_code
1374 pk_verify_host(krb5_context context, struct krb5_pk_cert *host)
1375 {
1376     /* XXX */
1377     return 0;
1378 }
1379
1380 static krb5_error_code
1381 pk_rd_pa_reply_enckey(krb5_context context,
1382                       int win2k_compat,
1383                       ContentInfo *rep,
1384                       krb5_pk_init_ctx ctx,
1385                       krb5_enctype etype,
1386                       unsigned nonce,
1387                       PA_DATA *pa,
1388                       krb5_keyblock **key) 
1389 {
1390     krb5_error_code ret;
1391     EnvelopedData ed;
1392     krb5_keyblock tmp_key;
1393     krb5_crypto crypto;
1394     krb5_data plain;
1395     KeyTransRecipientInfo *ri;
1396     int length;
1397     size_t size;
1398     X509 *user_cert;
1399     char *p;
1400     krb5_boolean bret;
1401     krb5_data content;
1402     heim_oid contentType = { 0, NULL };
1403     struct krb5_pk_cert *host = NULL;
1404     heim_octet_string encryptedContent;
1405     heim_octet_string *any;
1406     krb5_data ivec;
1407     krb5_data params;
1408
1409
1410     memset(&tmp_key, 0, sizeof(tmp_key));
1411     memset(&ed, 0, sizeof(ed));
1412     krb5_data_zero(&plain);
1413     krb5_data_zero(&content);
1414     krb5_data_zero(&encryptedContent);
1415     krb5_data_zero(&ivec);
1416
1417     user_cert = sk_X509_value(ctx->id->cert, 0);
1418
1419     if (heim_oid_cmp(oid_id_pkcs7_envelopedData(), &rep->contentType)) {
1420         krb5_set_error_string(context, "PKINIT: Invalid content type");
1421         return EINVAL;
1422     }
1423
1424     if (rep->content == NULL) {
1425         krb5_set_error_string(context, "PKINIT: No content in reply");
1426         return EINVAL;
1427     }
1428
1429     ret = decode_EnvelopedData(rep->content->data,
1430                                rep->content->length,
1431                                &ed,
1432                                &size);
1433     if (ret) {
1434         free_EnvelopedData(&ed);
1435         return ret;
1436     }
1437
1438     if (ed.recipientInfos.len != 1) {
1439         free_EnvelopedData(&ed);
1440         krb5_set_error_string(context, "pkinit: Number of recipient infos "
1441                               "not one (%d)",
1442                               ed.recipientInfos.len);
1443         return EINVAL; /* XXX */
1444     }
1445
1446     ri = &ed.recipientInfos.val[0];
1447
1448     /* XXX make SignerIdentifier and RecipientIdentifier the same */
1449     bret = pk_peer_compare(context, (SignerIdentifier *)&ri->rid, user_cert);
1450     if (bret == FALSE) {
1451         ret = KRB5KRB_AP_ERR_BADMATCH; /* XXX */
1452         goto out;
1453     }
1454
1455     if (heim_oid_cmp(oid_id_pkcs1_rsaEncryption(),
1456                      &ri->keyEncryptionAlgorithm.algorithm)) {
1457         krb5_set_error_string(context, "PKINIT: invalid content type");
1458         return EINVAL;
1459     }
1460     
1461     ret = pk_decrypt_key(context, &ri->encryptedKey,
1462                          ctx->id->private_key, &tmp_key);
1463     if (ret)
1464         goto out;
1465
1466   
1467     /* verify content type */
1468     if (win2k_compat) {
1469         if (heim_oid_cmp(&ed.encryptedContentInfo.contentType, oid_id_pkcs7_data())) {
1470             ret = KRB5KRB_AP_ERR_MSG_TYPE;
1471             goto out;
1472         }
1473     } else {
1474         if (heim_oid_cmp(&ed.encryptedContentInfo.contentType, oid_id_pkcs7_signedData())) {
1475             ret = KRB5KRB_AP_ERR_MSG_TYPE;
1476             goto out;
1477         }
1478     }
1479
1480     if (ed.encryptedContentInfo.encryptedContent == NULL) {
1481         krb5_set_error_string(context, "PKINIT: OPTIONAL encryptedContent "
1482                               "field not filled in in KDC reply");
1483         ret = KRB5_BADMSGTYPE;
1484         goto out;
1485     }
1486
1487     any = ed.encryptedContentInfo.encryptedContent;
1488     ret = der_get_octet_string(any->data, any->length,
1489                                &encryptedContent, NULL);
1490     if (ret) {
1491         krb5_set_error_string(context,
1492                               "PKINIT: encryptedContent content invalid");
1493         goto out;
1494     }
1495
1496     if (ed.encryptedContentInfo.contentEncryptionAlgorithm.parameters == NULL){
1497         krb5_set_error_string(context,
1498                               "PKINIT: encryptedContent parameter missing");
1499         ret = KRB5_BADMSGTYPE;
1500         goto out;
1501     }
1502
1503     params.data = ed.encryptedContentInfo.contentEncryptionAlgorithm.parameters->data;
1504     params.length = ed.encryptedContentInfo.contentEncryptionAlgorithm.parameters->length;
1505
1506     ret = _krb5_oid_to_enctype(context,
1507                                &ed.encryptedContentInfo.contentEncryptionAlgorithm.algorithm,
1508                                &tmp_key.keytype);
1509     if (ret)
1510         goto out;
1511
1512     ret = krb5_crypto_init(context, &tmp_key, 0, &crypto);
1513     if (ret)
1514         goto out;
1515
1516     ret = krb5_crypto_get_params(context, crypto, &params, &ivec);
1517     if (ret)
1518         goto out;
1519
1520     ret = krb5_decrypt_ivec(context, crypto,
1521                             0,
1522                             encryptedContent.data,
1523                             encryptedContent.length,
1524                             &plain,
1525                             ivec.data);
1526
1527     p = plain.data;
1528     length = plain.length;
1529
1530     /* win2k uses ContentInfo */
1531     if (win2k_compat) {
1532         ContentInfo ci;
1533         size_t size;
1534
1535         ret = decode_ContentInfo(p, length, &ci, &size);
1536         if (ret) {
1537             krb5_set_error_string(context,
1538                                   "PKINIT: failed decoding ContentInfo: %d",
1539                                   ret);
1540             goto out;
1541         }
1542
1543         if (heim_oid_cmp(&ci.contentType, oid_id_pkcs7_signedData())) {
1544             ret = EINVAL; /* XXX */
1545             krb5_set_error_string(context, "PKINIT: Invalid content type");
1546             goto out;
1547         }
1548         p = ci.content->data;
1549         length = ci.content->length;
1550     } 
1551
1552     ret = _krb5_pk_verify_sign(context, 
1553                                p,
1554                                length,
1555                                ctx->id,
1556                                &contentType,
1557                                &content,
1558                                &host);
1559     if (ret)
1560         goto out;
1561
1562     /* make sure that it is the kdc's certificate */
1563     ret = pk_verify_host(context, host);
1564     if (ret) {
1565         krb5_set_error_string(context, "PKINIT: failed verify host: %d", ret);
1566         goto out;
1567     }
1568
1569     if (win2k_compat) {
1570         if (heim_oid_cmp(&contentType, oid_id_pkcs7_data()) != 0) {
1571             krb5_set_error_string(context, "PKINIT: reply key, wrong oid");
1572             ret = KRB5KRB_AP_ERR_MSG_TYPE;
1573             goto out;
1574         }
1575     } else {
1576         if (heim_oid_cmp(&contentType, oid_id_pkrkeydata()) != 0) {
1577             krb5_set_error_string(context, "PKINIT: reply key, wrong oid");
1578             ret = KRB5KRB_AP_ERR_MSG_TYPE;
1579             goto out;
1580         }
1581     }
1582
1583     ret = get_reply_key(context, &content, nonce, key);
1584     if (ret)
1585         goto out;
1586
1587     /* XXX compare given etype with key->etype */
1588
1589  out:
1590     if (host)
1591         _krb5_pk_cert_free(host);
1592     free_oid(&contentType);
1593     free_octet_string(&encryptedContent);
1594     krb5_data_free(&content);
1595     krb5_free_keyblock_contents(context, &tmp_key);
1596     krb5_data_free(&plain);
1597     krb5_data_free(&ivec);
1598
1599     return ret;
1600 }
1601
1602 static krb5_error_code
1603 pk_rd_pa_reply_dh(krb5_context context,
1604                   ContentInfo *rep,
1605                   krb5_pk_init_ctx ctx,
1606                   krb5_enctype etype,
1607                   unsigned nonce,
1608                   PA_DATA *pa,
1609                   krb5_keyblock **key)
1610 {
1611     unsigned char *p, *dh_gen_key = NULL;
1612     ASN1_INTEGER *dh_pub_key = NULL;
1613     struct krb5_pk_cert *host = NULL;
1614     BIGNUM *kdc_dh_pubkey = NULL;
1615     KDCDHKeyInfo kdc_dh_info;
1616     heim_oid contentType = { 0, NULL };
1617     krb5_data content;
1618     krb5_error_code ret;
1619     int dh_gen_keylen;
1620     size_t size;
1621
1622     krb5_data_zero(&content);
1623     memset(&kdc_dh_info, 0, sizeof(kdc_dh_info));
1624
1625     if (heim_oid_cmp(oid_id_pkcs7_signedData(), &rep->contentType)) {
1626         krb5_set_error_string(context, "PKINIT: Invalid content type");
1627         return EINVAL;
1628     }
1629
1630     if (rep->content == NULL) {
1631         krb5_set_error_string(context, "PKINIT: No content in reply");
1632         return EINVAL;
1633     }
1634
1635     ret = _krb5_pk_verify_sign(context, 
1636                                rep->content->data,
1637                                rep->content->length,
1638                                ctx->id,
1639                                &contentType,
1640                                &content,
1641                                &host);
1642     if (ret)
1643         goto out;
1644
1645     /* make sure that it is the kdc's certificate */
1646     ret = pk_verify_host(context, host);
1647     if (ret)
1648         goto out;
1649
1650     if (heim_oid_cmp(&contentType, oid_id_pkdhkeydata())) {
1651         ret = KRB5KRB_AP_ERR_MSG_TYPE; /* XXX */
1652         goto out;
1653     }
1654
1655     ret = decode_KDCDHKeyInfo(content.data,
1656                               content.length,
1657                               &kdc_dh_info,
1658                               &size);
1659
1660     if (ret)
1661         goto out;
1662
1663     if (kdc_dh_info.nonce != nonce) {
1664         krb5_set_error_string(context, "PKINIT: DH nonce is wrong");
1665         ret = KRB5KRB_AP_ERR_MODIFIED;
1666         goto out;
1667     }
1668
1669     p = kdc_dh_info.subjectPublicKey.data;
1670     size = (kdc_dh_info.subjectPublicKey.length + 7) / 8;
1671     dh_pub_key = d2i_ASN1_INTEGER(NULL, &p, size);
1672     if (dh_pub_key == NULL) {
1673         krb5_set_error_string(context,
1674                               "PKINIT: Can't parse KDC's DH public key");
1675         ret = KRB5KRB_ERR_GENERIC;
1676         goto out;
1677     }
1678
1679     kdc_dh_pubkey = ASN1_INTEGER_to_BN(dh_pub_key, NULL);
1680     if (kdc_dh_pubkey == NULL) {
1681         krb5_set_error_string(context,
1682                               "PKINIT: Can't convert KDC's DH public key");
1683         ret = KRB5KRB_ERR_GENERIC;
1684         goto out;
1685     }
1686
1687     dh_gen_key = malloc(DH_size(ctx->dh));
1688     if (dh_gen_key == NULL) {
1689         krb5_set_error_string(context, "malloc: out of memory");
1690         ret = ENOMEM;
1691         goto out;
1692     }
1693
1694     dh_gen_keylen = DH_compute_key(dh_gen_key, kdc_dh_pubkey, ctx->dh);
1695     if (dh_gen_keylen == -1) {
1696         krb5_set_error_string(context, 
1697                               "PKINIT: Can't compute Diffie-Hellman key (%s)",
1698                               ERR_error_string(ERR_get_error(), NULL));
1699         ret = KRB5KRB_ERR_GENERIC;
1700         goto out;
1701     }
1702
1703     *key = malloc (sizeof (**key));
1704     if (*key == NULL) {
1705         krb5_set_error_string(context, "malloc: out of memory");
1706         ret = ENOMEM;
1707         goto out;
1708     }
1709
1710     ret = krb5_random_to_key(context, etype, dh_gen_key, dh_gen_keylen, *key);
1711     if (ret) {
1712         krb5_set_error_string(context,
1713                               "PKINIT: can't create key from DH key");
1714         free(*key);
1715         *key = NULL;
1716         goto out;
1717     }
1718
1719  out:
1720     if (kdc_dh_pubkey)
1721         BN_free(kdc_dh_pubkey);
1722     if (dh_gen_key) {
1723         memset(dh_gen_key, 0, DH_size(ctx->dh));
1724         free(dh_gen_key);
1725     }
1726     if (dh_pub_key)
1727         ASN1_INTEGER_free(dh_pub_key);
1728     if (host)
1729         _krb5_pk_cert_free(host);
1730     if (content.data)
1731         krb5_data_free(&content);
1732     free_KDCDHKeyInfo(&kdc_dh_info);
1733
1734     return ret;
1735 }
1736
1737 krb5_error_code KRB5_LIB_FUNCTION
1738 _krb5_pk_rd_pa_reply(krb5_context context,
1739                      void *c,
1740                      krb5_enctype etype,
1741                      unsigned nonce,
1742                      PA_DATA *pa,
1743                      krb5_keyblock **key)
1744 {
1745     krb5_pk_init_ctx ctx = c;
1746     krb5_error_code ret;
1747     ContentInfo ci;
1748     size_t size;
1749
1750     /* Check for PK-INIT -25 */
1751     if (pa->padata_type == KRB5_PADATA_PK_AS_REP) {
1752         PA_PK_AS_REP rep;
1753
1754         memset(&rep, 0, sizeof(rep));
1755
1756         ret = decode_PA_PK_AS_REP(pa->padata_value.data,
1757                                   pa->padata_value.length,
1758                                   &rep,
1759                                   &size);
1760         if (ret)
1761             return ret;
1762
1763         switch (rep.element) {
1764         case choice_PA_PK_AS_REP_encKeyPack:
1765             ret = decode_ContentInfo(rep.u.encKeyPack.data,
1766                                      rep.u.encKeyPack.length,
1767                                      &ci,
1768                                      &size);
1769             free_PA_PK_AS_REP(&rep);
1770             if (ret) {
1771                 krb5_set_error_string(context,
1772                                       "PKINIT: -25 decoding failed "
1773                                       "ContentInfo: %d", ret);
1774                 break;
1775             }
1776             ret = pk_rd_pa_reply_enckey(context, 0, &ci, ctx,
1777                                         etype, nonce, pa, key);
1778             free_ContentInfo(&ci);
1779             return ret;
1780         default:
1781             free_PA_PK_AS_REP(&rep);
1782             krb5_set_error_string(context, "PKINIT: -25 reply "
1783                                   "invalid content type");
1784             break;
1785         }
1786     }
1787
1788     /* Check for PK-INIT -19 */
1789     {
1790         PA_PK_AS_REP_19 rep19;
1791
1792         memset(&rep19, 0, sizeof(rep19));
1793
1794         ret = decode_PA_PK_AS_REP_19(pa->padata_value.data,
1795                                      pa->padata_value.length,
1796                                      &rep19,
1797                                      &size);
1798         if (ret == 0) {
1799             switch(rep19.element) {
1800             case choice_PA_PK_AS_REP_19_dhSignedData:
1801                 ret = pk_rd_pa_reply_dh(context, &rep19.u.dhSignedData, ctx,
1802                                         etype, nonce, pa, key);
1803                 break;
1804             case choice_PA_PK_AS_REP_19_encKeyPack:
1805                 ret = pk_rd_pa_reply_enckey(context, 0,
1806                                             &rep19.u.encKeyPack, ctx,
1807                                             etype, nonce, pa, key);
1808                 break;
1809             default:
1810                 krb5_set_error_string(context, "PKINIT: -19 reply invalid "
1811                                       "content type");
1812                 ret = EINVAL;
1813                 break;
1814             }
1815             free_PA_PK_AS_REP_19(&rep19);
1816             if (ret == 0)
1817                 return 0;
1818         }
1819     }
1820
1821     /* Check for Windows encoding of the AS-REP pa data */ 
1822     {
1823         PA_PK_AS_REP_Win2k w2krep;
1824
1825         memset(&w2krep, 0, sizeof(w2krep));
1826         
1827         ret = decode_PA_PK_AS_REP_Win2k(pa->padata_value.data,
1828                                         pa->padata_value.length,
1829                                         &w2krep,
1830                                         &size);
1831         if (ret) {
1832             krb5_set_error_string(context, "PKINIT: Failed decoding windows"
1833                                   "pkinit reply %d", ret);
1834             return ret;
1835         }
1836         
1837         switch (w2krep.element) {
1838         case choice_PA_PK_AS_REP_Win2k_encKeyPack:
1839             ret = decode_ContentInfo(w2krep.u.encKeyPack.data,
1840                                      w2krep.u.encKeyPack.length,
1841                                      &ci,
1842                                      &size);
1843             free_PA_PK_AS_REP_Win2k(&w2krep);
1844             if (ret) {
1845                 krb5_set_error_string(context,
1846                                       "PKINIT: decoding failed "
1847                                       "ContentInfo: %d",
1848                                       ret);
1849                 return ret;
1850             }
1851             ret = pk_rd_pa_reply_enckey(context, 1, &ci, ctx,
1852                                         etype, nonce, pa, key);
1853             free_ContentInfo(&ci);
1854             break;
1855         default:
1856             free_PA_PK_AS_REP_Win2k(&w2krep);
1857             krb5_set_error_string(context, "PKINIT: win2k reply invalid "
1858                                   "content type");
1859             ret = EINVAL;
1860             break;
1861         }
1862     
1863     }
1864
1865     return ret;
1866 }
1867
1868 static int
1869 ssl_pass_cb(char *buf, int size, int rwflag, void *u)
1870 {
1871     krb5_error_code ret;
1872     krb5_prompt prompt;
1873     krb5_data password_data;
1874     krb5_prompter_fct prompter = u;
1875    
1876     password_data.data   = buf;
1877     password_data.length = size;
1878     prompt.prompt = "Enter your private key passphrase: ";
1879     prompt.hidden = 1;
1880     prompt.reply  = &password_data;
1881     prompt.type   = KRB5_PROMPT_TYPE_PASSWORD;
1882    
1883     ret = (*prompter)(NULL, NULL, NULL, NULL, 1, &prompt);
1884     if (ret) {
1885         memset (buf, 0, size);
1886         return 0;
1887     }
1888     return strlen(buf);
1889 }
1890
1891 static krb5_error_code
1892 load_openssl_cert(krb5_context context,
1893                   const char *file,
1894                   STACK_OF(X509) **c)
1895 {
1896     STACK_OF(X509) *certificate;
1897     krb5_error_code ret;
1898     FILE *f;
1899
1900     f = fopen(file, "r");
1901     if (f == NULL) {
1902         ret = errno;
1903         krb5_set_error_string(context, "PKINIT: open failed %s: %s", 
1904                               file, strerror(ret));
1905         return ret;
1906     }
1907
1908     certificate = sk_X509_new_null();
1909     while (1) {
1910         /* see http://www.openssl.org/docs/crypto/pem.html section BUGS */
1911         X509 *cert;
1912         cert = PEM_read_X509(f, NULL, NULL, NULL);
1913         if (cert == NULL) {
1914             if (ERR_GET_REASON(ERR_peek_error()) == PEM_R_NO_START_LINE) {
1915                 /* End of file reached. no error */
1916                 ERR_clear_error();
1917                 break;
1918             }
1919             krb5_set_error_string(context, "PKINIT: Can't read certificate");
1920             fclose(f);
1921             return HEIM_PKINIT_CERTIFICATE_INVALID;
1922         }
1923         sk_X509_insert(certificate, cert, sk_X509_num(certificate));
1924     }
1925     fclose(f);
1926     if (sk_X509_num(certificate) == 0) {
1927         krb5_set_error_string(context, "PKINIT: No certificate found");
1928         return HEIM_PKINIT_NO_CERTIFICATE;
1929     }
1930     *c = certificate;
1931     return 0;
1932 }
1933
1934 static krb5_error_code
1935 load_openssl_file(krb5_context context,
1936                   char *password,
1937                   krb5_prompter_fct prompter,
1938                   void *prompter_data,
1939                   const char *user_id,
1940                   struct krb5_pk_identity *id)
1941 {
1942     krb5_error_code ret;
1943     STACK_OF(X509) *certificate = NULL;
1944     char *cert_file = NULL, *key_file;
1945     EVP_PKEY *private_key = NULL;
1946     FILE *f;
1947
1948     cert_file = strdup(user_id);
1949     if (cert_file == NULL) {
1950         krb5_set_error_string(context, "malloc: out of memory");
1951         return ENOMEM;
1952     }
1953     key_file = strchr(cert_file, ',');
1954     if (key_file == NULL) {
1955         krb5_set_error_string(context, "PKINIT: key file missing");
1956         ret = HEIM_PKINIT_NO_PRIVATE_KEY;
1957         goto out;
1958     }
1959     *key_file++ = '\0';
1960
1961     ret = load_openssl_cert(context, cert_file, &certificate);
1962     if (ret)
1963         goto out;
1964
1965     /* load private key */
1966     f = fopen(key_file, "r");
1967     if (f == NULL) {
1968         ret = errno;
1969         krb5_set_error_string(context, "PKINIT: open %s: %s",
1970                               key_file, strerror(ret));
1971         goto out;
1972     }
1973     if (password == NULL || password[0] == '\0') {
1974         if (prompter == NULL)
1975             prompter = krb5_prompter_posix;
1976         private_key = PEM_read_PrivateKey(f, NULL, ssl_pass_cb, prompter);
1977     } else
1978         private_key = PEM_read_PrivateKey(f, NULL, NULL, password);
1979     fclose(f);
1980     if (private_key == NULL) {
1981         krb5_set_error_string(context, "PKINIT: Can't read private key");
1982         ret = HEIM_PKINIT_PRIVATE_KEY_INVALID;
1983         goto out;
1984     }
1985     ret = X509_check_private_key(sk_X509_value(certificate, 0), private_key);
1986     if (ret != 1) {
1987         ret = HEIM_PKINIT_PRIVATE_KEY_INVALID;
1988         krb5_set_error_string(context,
1989                               "PKINIT: The private key doesn't match "
1990                               "the public key certificate");
1991         goto out;
1992     }
1993
1994     id->private_key = private_key;
1995     id->cert = certificate;
1996
1997     return 0;
1998  out:
1999     if (cert_file)
2000         free(cert_file);
2001     if (certificate)
2002         sk_X509_pop_free(certificate, X509_free);
2003     if (private_key)
2004         EVP_PKEY_free(private_key);
2005
2006     return ret;
2007 }
2008
2009 static int
2010 add_pair(krb5_context context, char *str, char ***cmds, int *num)
2011 {
2012     char **c;
2013     char *p;
2014     int i;
2015
2016     p = strchr(str, ':');
2017     if (p) {
2018         *p = '\0';
2019         p++;
2020     }
2021
2022     /* filter out dup keys */
2023     for (i = 0; i < *num; i++)
2024         if (strcmp((*cmds)[i * 2], str) == 0)
2025             return 0;
2026
2027     c = realloc(*cmds, sizeof(*c) * ((*num + 1) * 2));
2028     if (c == NULL) {
2029         krb5_set_error_string(context, "malloc: out of memory");
2030         return ENOMEM;
2031     }
2032
2033     c[(*num * 2)] = str;
2034     c[(*num * 2) + 1] = p;
2035     *num += 1;
2036     *cmds = c;
2037     return 0;
2038 }
2039
2040 static krb5_error_code
2041 eval_pairs(krb5_context context, ENGINE *e, const char *name,
2042            const char *type, char **cmds, int num)
2043 {
2044     int i;
2045
2046     for (i = 0; i < num; i++) {
2047         char *a1 = cmds[i * 2], *a2 = cmds[(i * 2) + 1];
2048         if(!ENGINE_ctrl_cmd_string(e, a1, a2, 0)) {
2049             krb5_set_error_string(context,
2050                                   "PKINIT: Failed %scommand (%s - %s:%s): %s", 
2051                                   type, name, a1, a2 ? a2 : "(NULL)",
2052                                   ERR_error_string(ERR_get_error(), NULL));
2053             return HEIM_PKINIT_NO_PRIVATE_KEY;
2054         }
2055     }
2056     return 0;
2057 }
2058
2059 struct engine_context {
2060     char **pre_cmds;
2061     char **post_cmds;
2062     int num_pre;
2063     int num_post;
2064     char *engine_name;
2065     char *cert_file;
2066     char *key_id;
2067 };
2068
2069 static krb5_error_code
2070 parse_openssl_engine_conf(krb5_context context, 
2071                           struct engine_context *ctx,
2072                           char *line)
2073 {
2074     krb5_error_code ret;
2075     char *last, *p, *q;
2076
2077     for (p = strtok_r(line, ",", &last);
2078          p != NULL;
2079          p = strtok_r(NULL, ",", &last)) {
2080
2081         q = strchr(p, '=');
2082         if (q == NULL) {
2083             krb5_set_error_string(context, 
2084                                   "PKINIT: openssl engine configuration "
2085                                   "key %s missing = and thus value", p);
2086             return HEIM_PKINIT_NO_PRIVATE_KEY;
2087         }
2088         *q = '\0';
2089         q++;
2090         if (strcasecmp("PRE", p) == 0) {
2091             ret = add_pair(context, q, &ctx->pre_cmds, &ctx->num_pre);
2092             if (ret)
2093                 return ret;
2094         } else if (strcasecmp("POST", p) == 0) {
2095             ret = add_pair(context, q, &ctx->post_cmds, &ctx->num_post);
2096             if (ret)
2097                 return ret;
2098         } else if (strcasecmp("KEY", p) == 0) {
2099             ctx->key_id = q;
2100         } else if (strcasecmp("CERT", p) == 0) {
2101             ctx->cert_file = q;
2102         } else if (strcasecmp("ENGINE", p) == 0) {
2103             ctx->engine_name = q;
2104         } else {
2105             krb5_set_error_string(context, 
2106                                   "PKINIT: openssl engine configuration "
2107                                   "key %s is unknown", p);
2108             return HEIM_PKINIT_NO_PRIVATE_KEY;
2109         }
2110     }
2111     return 0;
2112 }
2113
2114
2115 static krb5_error_code
2116 load_openssl_engine(krb5_context context,
2117                     char *password,
2118                     krb5_prompter_fct prompter,
2119                     void *prompter_data,
2120                     const char *string,
2121                     struct krb5_pk_identity *id)
2122 {
2123     struct engine_context ctx;
2124     krb5_error_code ret;
2125     const char *f;
2126     char *file_conf = NULL, *user_conf = NULL;
2127     ENGINE *e = NULL;
2128
2129     memset(&ctx, 0, sizeof(ctx));
2130
2131     ENGINE_load_builtin_engines();
2132
2133     user_conf = strdup(string);
2134     if (user_conf == NULL) {
2135         krb5_set_error_string(context, "malloc: out of memory");
2136         return ENOMEM;
2137     }
2138
2139     ret = parse_openssl_engine_conf(context, &ctx, user_conf);
2140     if (ret)
2141         goto out;
2142
2143     f = krb5_config_get_string_default(context, NULL, NULL,
2144                                        "libdefaults", 
2145                                        "pkinit-openssl-engine", 
2146                                        NULL);
2147     if (f) {
2148         file_conf = strdup(f);
2149         if (file_conf) {
2150             ret = parse_openssl_engine_conf(context, &ctx, file_conf);
2151             if (ret)
2152                 goto out;
2153         }
2154     }
2155
2156     if (ctx.cert_file == NULL) {
2157         krb5_set_error_string(context, 
2158                               "PKINIT: openssl engine missing certificate");
2159         ret = HEIM_PKINIT_NO_CERTIFICATE;
2160         goto out;
2161     }
2162     if (ctx.key_id == NULL) {
2163         krb5_set_error_string(context,
2164                               "PKINIT: openssl engine missing key id");
2165         ret = HEIM_PKINIT_NO_PRIVATE_KEY;
2166         goto out;
2167     }
2168     if (ctx.engine_name == NULL) {
2169         krb5_set_error_string(context,
2170                               "PKINIT: openssl engine missing engine name");
2171         ret = HEIM_PKINIT_NO_PRIVATE_KEY;
2172         goto out;
2173     }
2174
2175     e = ENGINE_by_id(ctx.engine_name);
2176     if (e == NULL) {
2177         krb5_set_error_string(context, 
2178                               "PKINIT: failed getting openssl engine %s: %s", 
2179                               ctx.engine_name,
2180                               ERR_error_string(ERR_get_error(), NULL));
2181         ret = HEIM_PKINIT_NO_PRIVATE_KEY;
2182         goto out;
2183     }
2184
2185     ret = eval_pairs(context, e, ctx.engine_name, "pre", 
2186                      ctx.pre_cmds, ctx.num_pre);
2187     if (ret)
2188         goto out;
2189
2190     if(!ENGINE_init(e)) {
2191         ret = HEIM_PKINIT_NO_PRIVATE_KEY;
2192         krb5_set_error_string(context,
2193                               "PKINIT: openssl engine init %s failed: %s", 
2194                               ctx.engine_name,
2195                               ERR_error_string(ERR_get_error(), NULL));
2196         ENGINE_free(e);
2197         goto out;
2198     }
2199
2200     ret = eval_pairs(context, e, ctx.engine_name, "post", 
2201                      ctx.post_cmds, ctx.num_post);
2202     if (ret)
2203         goto out;
2204
2205     /*
2206      * If the engine supports a LOAD_CERT_CTRL function, lets try
2207      * it. OpenSC support this function. Eventially this should be
2208      * a ENGINE_load_cert function if it failes, treat it like a
2209      * non fatal error.
2210      */
2211     {
2212         struct {
2213             const char * cert_id;
2214             X509 * cert;
2215         } parms;
2216
2217         parms.cert_id = ctx.cert_file;
2218         parms.cert = NULL;
2219         ENGINE_ctrl_cmd(e, "LOAD_CERT_CTRL", 0, &parms, NULL, 1); 
2220         if (parms.cert) {
2221             id->cert = sk_X509_new_null();
2222             sk_X509_insert(id->cert, parms.cert, 0);    
2223         }  
2224     }
2225
2226     if (id->cert == NULL) {
2227         ret = load_openssl_cert(context, ctx.cert_file, &id->cert);
2228         if (ret)
2229             goto out;
2230     }
2231
2232     {
2233         UI_METHOD * krb5_ui_method = NULL;
2234         struct krb5_ui_data ui_data;
2235
2236         krb5_ui_method = UI_create_method("Krb5 ui method");
2237         if (krb5_ui_method == NULL) {
2238             krb5_set_error_string(context,
2239                                   "PKINIT: failed to setup prompter "
2240                                   "function: %s",
2241                                   ERR_error_string(ERR_get_error(), NULL));
2242             ret = HEIM_PKINIT_NO_PRIVATE_KEY;
2243             goto out;
2244         }
2245         UI_method_set_reader(krb5_ui_method, krb5_ui_method_read_string);
2246
2247         ui_data.context = context;
2248         ui_data.prompter = prompter;
2249         if (prompter == NULL)
2250             ui_data.prompter = krb5_prompter_posix;
2251         ui_data.prompter_data = prompter_data;
2252
2253         id->private_key = ENGINE_load_private_key(e,
2254                                                   ctx.key_id, 
2255                                                   krb5_ui_method,
2256                                                   (void*) &ui_data);
2257         UI_destroy_method(krb5_ui_method);
2258     }
2259         
2260     if (id->private_key == NULL) {
2261         krb5_set_error_string(context,
2262                               "PKINIT: failed to load private key: %s",
2263                               ERR_error_string(ERR_get_error(), NULL));
2264         ret = HEIM_PKINIT_NO_PRIVATE_KEY;
2265         goto out;
2266     }
2267
2268     ret = X509_check_private_key(sk_X509_value(id->cert, 0), id->private_key);
2269     if (ret != 1) {
2270         ret = HEIM_PKINIT_PRIVATE_KEY_INVALID;
2271         krb5_set_error_string(context,
2272                               "PKINIT: The private key doesn't match "
2273                               "the public key certificate");
2274         goto out;
2275     }
2276
2277     if (user_conf)
2278         free(user_conf);
2279     if (file_conf)
2280         free(file_conf);
2281
2282     id->engine = e;
2283
2284     return 0;
2285
2286  out:
2287     if (user_conf)
2288         free(user_conf);
2289     if (file_conf)
2290         free(file_conf);
2291     if (e) {
2292         ENGINE_finish(e); /* make sure all shared libs are unloaded */
2293         ENGINE_free(e);
2294     }
2295         
2296     return ret;
2297 }
2298
2299 krb5_error_code KRB5_LIB_FUNCTION
2300 _krb5_pk_load_openssl_id(krb5_context context,
2301                          struct krb5_pk_identity **ret_id,
2302                          const char *user_id,
2303                          const char *x509_anchors,
2304                          krb5_prompter_fct prompter,
2305                          void *prompter_data,
2306                          char *password)
2307 {
2308     STACK_OF(X509) *trusted_certs = NULL;
2309     struct krb5_pk_identity *id = NULL;
2310     krb5_error_code ret;
2311     struct dirent *file;
2312     char *dirname = NULL;
2313     DIR *dir;
2314     FILE *f;
2315     krb5_error_code (*load_pair)(krb5_context, 
2316                                  char *, 
2317                                  krb5_prompter_fct prompter,
2318                                  void * prompter_data,
2319                                  const char *,
2320                                  struct krb5_pk_identity *) = NULL;
2321
2322
2323     *ret_id = NULL;
2324
2325     if (x509_anchors == NULL) {
2326         krb5_set_error_string(context, "PKINIT: No root ca directory given");
2327         return HEIM_PKINIT_NO_VALID_CA;
2328     }
2329
2330     if (user_id == NULL) {
2331         krb5_set_error_string(context,
2332                               "PKINIT: No user X509 source given given");
2333         return HEIM_PKINIT_NO_PRIVATE_KEY;
2334     }
2335
2336     /* 
2337      *
2338      */
2339
2340     if (strncasecmp(user_id, "FILE:", 5) == 0) {
2341         load_pair = load_openssl_file;
2342         user_id += 5;
2343     } else if (strncasecmp(user_id, "ENGINE:", 7) == 0) {
2344         load_pair = load_openssl_engine;
2345         user_id += 7;
2346     } else {
2347         krb5_set_error_string(context, "PKINIT: user identity not FILE");
2348         return HEIM_PKINIT_NO_CERTIFICATE;
2349     }
2350     if (strncasecmp(x509_anchors, "OPENSSL-ANCHOR-DIR:", 19) != 0) {
2351         krb5_set_error_string(context, "PKINIT: anchor OPENSSL-ANCHOR-DIR");
2352         return HEIM_PKINIT_NO_VALID_CA;
2353     }
2354     x509_anchors += 19;
2355
2356     id = malloc(sizeof(*id));
2357     if (id == NULL) {
2358         krb5_set_error_string(context, "malloc: out of memory");
2359         ret = ENOMEM;
2360         goto out;
2361     }   
2362     memset(id, 0, sizeof(*id));
2363
2364     OpenSSL_add_all_algorithms();
2365     ERR_load_crypto_strings();
2366
2367     
2368     ret = (*load_pair)(context, password, prompter, prompter_data, user_id, id);
2369     if (ret)
2370         goto out;
2371
2372     /* load anchors */
2373
2374     dirname = strdup(x509_anchors);
2375     if (dirname == NULL) {
2376         krb5_set_error_string(context, "malloc: out of memory");
2377         ret = ENOMEM;
2378         goto out;
2379     }
2380
2381     {
2382         size_t len;
2383         len = strlen(dirname);
2384         if (dirname[len - 1] == '/')
2385             dirname[len - 1] = '\0';
2386     }
2387
2388     /* read ca certificates */
2389     dir = opendir(dirname);
2390     if (dir == NULL) {
2391         ret = errno;
2392         krb5_set_error_string(context, "PKINIT: open directory %s: %s",
2393                               dirname, strerror(ret));
2394         goto out;
2395     }
2396
2397     trusted_certs = sk_X509_new_null();
2398     while ((file = readdir(dir)) != NULL) {
2399         X509 *cert;
2400         char *filename;
2401
2402         /*
2403          * Assume the certificate filenames constist of hashed subject
2404          * name followed by suffix ".0"
2405          */
2406
2407         if (strlen(file->d_name) == 10 && strcmp(&file->d_name[8],".0") == 0) {
2408             asprintf(&filename, "%s/%s", dirname, file->d_name);
2409             if (filename == NULL) {
2410                 ret = ENOMEM;
2411                 krb5_set_error_string(context, "malloc: out or memory");
2412                 goto out;
2413             }
2414             f = fopen(filename, "r");
2415             if (f == NULL) {
2416                 ret = errno;
2417                 krb5_set_error_string(context, "PKINIT: open %s: %s",
2418                                       filename, strerror(ret));
2419                 free(filename);
2420                 closedir(dir);
2421                 goto out;
2422             }
2423             cert = PEM_read_X509(f, NULL, NULL, NULL);
2424             fclose(f);
2425             if (cert != NULL) {
2426                 /* order of the certs is not important */
2427                 sk_X509_push(trusted_certs, cert);
2428             }
2429             free(filename);
2430         }
2431     }
2432     closedir(dir);
2433
2434     if (sk_X509_num(trusted_certs) == 0) {
2435         krb5_set_error_string(context,
2436                               "PKINIT: No CA certificate(s) found in %s",
2437                               dirname);
2438         ret = HEIM_PKINIT_NO_VALID_CA;
2439         goto out;
2440     }
2441
2442     id->trusted_certs = trusted_certs;
2443
2444     *ret_id = id;
2445
2446     return 0;
2447
2448  out:
2449     if (dirname)
2450         free(dirname);
2451     if (trusted_certs)
2452         sk_X509_pop_free(trusted_certs, X509_free);
2453     if (id) {
2454         if (id->cert)
2455             sk_X509_pop_free(id->cert, X509_free);
2456         if (id->private_key)
2457             EVP_PKEY_free(id->private_key);
2458         free(id);
2459     }
2460
2461     return ret;
2462 }
2463
2464 #endif /* PKINIT */
2465
2466 void KRB5_LIB_FUNCTION
2467 _krb5_get_init_creds_opt_free_pkinit(krb5_get_init_creds_opt *opt)
2468 {
2469 #ifdef PKINIT
2470     krb5_pk_init_ctx ctx;
2471
2472     if (opt->private == NULL || opt->private->pk_init_ctx == NULL)
2473         return;
2474     ctx = opt->private->pk_init_ctx;
2475     if (ctx->dh)
2476         DH_free(ctx->dh);
2477         ctx->dh = NULL;
2478     if (ctx->id) {
2479         if (ctx->id->cert)
2480             sk_X509_pop_free(ctx->id->cert, X509_free);
2481         if (ctx->id->trusted_certs)
2482             sk_X509_pop_free(ctx->id->trusted_certs, X509_free);
2483         if (ctx->id->private_key)
2484             EVP_PKEY_free(ctx->id->private_key);
2485         if (ctx->id->engine) {
2486                 ENGINE_finish(ctx->id->engine); /* unload shared libs etc */
2487             ENGINE_free(ctx->id->engine);
2488                 ctx->id->engine = NULL;
2489         }
2490         free(ctx->id);
2491         ctx->id = NULL;
2492     }
2493     opt->private->pk_init_ctx = NULL;
2494 #endif
2495 }
2496     
2497 krb5_error_code KRB5_LIB_FUNCTION
2498 krb5_get_init_creds_opt_set_pkinit(krb5_context context,
2499                                    krb5_get_init_creds_opt *opt,
2500                                    krb5_principal principal,
2501                                    const char *user_id,
2502                                    const char *x509_anchors,
2503                                    int flags,
2504                                    krb5_prompter_fct prompter,
2505                                    void *prompter_data,
2506                                    char *password)
2507 {
2508 #ifdef PKINIT
2509     krb5_error_code ret;
2510
2511     if (opt->private == NULL) {
2512         krb5_set_error_string(context, "PKINIT: on non extendable opt");
2513         return EINVAL;
2514     }
2515
2516     opt->private->pk_init_ctx = malloc(sizeof(*opt->private->pk_init_ctx));
2517     if (opt->private->pk_init_ctx == NULL) {
2518         krb5_set_error_string(context, "malloc: out of memory");
2519         return ENOMEM;
2520     }
2521     opt->private->pk_init_ctx->dh = NULL;
2522     opt->private->pk_init_ctx->id = NULL;
2523     ret = _krb5_pk_load_openssl_id(context,
2524                                    &opt->private->pk_init_ctx->id,
2525                                    user_id,
2526                                    x509_anchors,
2527                                    prompter,
2528                                    prompter_data,
2529                                    password);
2530     if (ret) {
2531         free(opt->private->pk_init_ctx);
2532         opt->private->pk_init_ctx = NULL;
2533     }
2534
2535     /* XXX */
2536     if (ret == 0 && (flags & 1) && !(flags & 2)) { 
2537         DH *dh;
2538         const char *P =
2539             "FFFFFFFF" "FFFFFFFF" "C90FDAA2" "2168C234" "C4C6628B" "80DC1CD1"
2540             "29024E08" "8A67CC74" "020BBEA6" "3B139B22" "514A0879" "8E3404DD"
2541             "EF9519B3" "CD3A431B" "302B0A6D" "F25F1437" "4FE1356D" "6D51C245"
2542             "E485B576" "625E7EC6" "F44C42E9" "A637ED6B" "0BFF5CB6" "F406B7ED"
2543             "EE386BFB" "5A899FA5" "AE9F2411" "7C4B1FE6" "49286651" "ECE65381"
2544             "FFFFFFFF" "FFFFFFFF";
2545         const char *G = "2";
2546         const char *Q =
2547             "7FFFFFFF" "FFFFFFFF" "E487ED51" "10B4611A" "62633145" "C06E0E68"
2548             "94812704" "4533E63A" "0105DF53" "1D89CD91" "28A5043C" "C71A026E"
2549             "F7CA8CD9" "E69D218D" "98158536" "F92F8A1B" "A7F09AB6" "B6A8E122"
2550             "F242DABB" "312F3F63" "7A262174" "D31BF6B5" "85FFAE5B" "7A035BF6"
2551             "F71C35FD" "AD44CFD2" "D74F9208" "BE258FF3" "24943328" "F67329C0"
2552             "FFFFFFFF" "FFFFFFFF";
2553
2554         dh = DH_new();
2555         if (dh == NULL) {
2556             _krb5_get_init_creds_opt_free_pkinit(opt);
2557             return ENOMEM;
2558         }
2559         opt->private->pk_init_ctx->dh = dh;
2560         if (!BN_hex2bn(&dh->p, P)) {
2561             _krb5_get_init_creds_opt_free_pkinit(opt);
2562             return ENOMEM;
2563         }
2564         if (!BN_hex2bn(&dh->g, G)) {
2565             _krb5_get_init_creds_opt_free_pkinit(opt);
2566             return ENOMEM;
2567         }
2568         if (!BN_hex2bn(&dh->q, Q)) {
2569             _krb5_get_init_creds_opt_free_pkinit(opt);
2570             return ENOMEM;
2571         }
2572         /* XXX generate a new key for each request ? */
2573         if (DH_generate_key(dh) != 1) {
2574             _krb5_get_init_creds_opt_free_pkinit(opt);
2575             return ENOMEM;
2576         }
2577     }
2578     return ret;
2579 #else
2580     krb5_set_error_string(context, "no support for PKINIT compiled in");
2581     return EINVAL;
2582 #endif
2583 }