]> git.llucax.com Git - software/mutt-debian.git/blob - mutt_ssl.c
IMAP: only close socket when not already disconnected (Closes: 493719)
[software/mutt-debian.git] / mutt_ssl.c
1 /*
2  * Copyright (C) 1999-2001 Tommi Komulainen <Tommi.Komulainen@iki.fi>
3  * 
4  *     This program is free software; you can redistribute it and/or modify
5  *     it under the terms of the GNU General Public License as published by
6  *     the Free Software Foundation; either version 2 of the License, or
7  *     (at your option) any later version.
8  * 
9  *     This program is distributed in the hope that it will be useful,
10  *     but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *     GNU General Public License for more details.
13  * 
14  *     You should have received a copy of the GNU General Public License
15  *     along with this program; if not, write to the Free Software
16  *     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
17  */
18
19 #if HAVE_CONFIG_H
20 # include "config.h"
21 #endif
22
23 #include <openssl/ssl.h>
24 #include <openssl/x509.h>
25 #include <openssl/x509v3.h>
26 #include <openssl/err.h>
27 #include <openssl/rand.h>
28
29 #undef _
30
31 #include <string.h>
32
33 #include "mutt.h"
34 #include "mutt_socket.h"
35 #include "mutt_menu.h"
36 #include "mutt_curses.h"
37 #include "mutt_ssl.h"
38 #include "mutt_idna.h"
39
40 #if OPENSSL_VERSION_NUMBER >= 0x00904000L
41 #define READ_X509_KEY(fp, key)  PEM_read_X509(fp, key, NULL, NULL)
42 #else
43 #define READ_X509_KEY(fp, key)  PEM_read_X509(fp, key, NULL)
44 #endif
45
46 /* Just in case OpenSSL doesn't define DEVRANDOM */
47 #ifndef DEVRANDOM
48 #define DEVRANDOM "/dev/urandom"
49 #endif
50
51 /* This is ugly, but as RAND_status came in on OpenSSL version 0.9.5
52  * and the code has to support older versions too, this is seemed to
53  * be cleaner way compared to having even uglier #ifdefs all around.
54  */
55 #ifdef HAVE_RAND_STATUS
56 #define HAVE_ENTROPY()  (RAND_status() == 1)
57 #else
58 static int entropy_byte_count = 0;
59 /* OpenSSL fills the entropy pool from /dev/urandom if it exists */
60 #define HAVE_ENTROPY()  (!access(DEVRANDOM, R_OK) || entropy_byte_count >= 16)
61 #endif
62
63 /* keep a handle on accepted certificates in case we want to
64  * open up another connection to the same server in this session */
65 static STACK_OF(X509) *SslSessionCerts = NULL;
66
67 typedef struct _sslsockdata
68 {
69   SSL_CTX *ctx;
70   SSL *ssl;
71   X509 *cert;
72 }
73 sslsockdata;
74
75 /* local prototypes */
76 static int ssl_init (void);
77 static int add_entropy (const char *file);
78 static int ssl_socket_read (CONNECTION* conn, char* buf, size_t len);
79 static int ssl_socket_write (CONNECTION* conn, const char* buf, size_t len);
80 static int ssl_socket_open (CONNECTION * conn);
81 static int ssl_socket_close (CONNECTION * conn);
82 static int tls_close (CONNECTION* conn);
83 static int ssl_cache_trusted_cert (X509 *cert);
84 static int ssl_check_certificate (CONNECTION *conn, sslsockdata * data);
85 static int interactive_check_cert (X509 *cert, int idx, int len);
86 static void ssl_get_client_cert(sslsockdata *ssldata, CONNECTION *conn);
87 static int ssl_passwd_cb(char *buf, int size, int rwflag, void *userdata);
88 static int ssl_negotiate (CONNECTION *conn, sslsockdata*);
89
90 /* mutt_ssl_starttls: Negotiate TLS over an already opened connection.
91  *   TODO: Merge this code better with ssl_socket_open. */
92 int mutt_ssl_starttls (CONNECTION* conn)
93 {
94   sslsockdata* ssldata;
95   int maxbits;
96
97   if (ssl_init())
98     goto bail;
99
100   ssldata = (sslsockdata*) safe_calloc (1, sizeof (sslsockdata));
101   /* the ssl_use_xxx protocol options don't apply. We must use TLS in TLS. */
102   if (! (ssldata->ctx = SSL_CTX_new (TLSv1_client_method ())))
103   {
104     dprint (1, (debugfile, "mutt_ssl_starttls: Error allocating SSL_CTX\n"));
105     goto bail_ssldata;
106   }
107
108   ssl_get_client_cert(ssldata, conn);
109
110   if (! (ssldata->ssl = SSL_new (ssldata->ctx)))
111   {
112     dprint (1, (debugfile, "mutt_ssl_starttls: Error allocating SSL\n"));
113     goto bail_ctx;
114   }
115
116   if (SSL_set_fd (ssldata->ssl, conn->fd) != 1)
117   {
118     dprint (1, (debugfile, "mutt_ssl_starttls: Error setting fd\n"));
119     goto bail_ssl;
120   }
121
122   if (ssl_negotiate (conn, ssldata))
123     goto bail_ssl;
124
125   /* hmm. watch out if we're starting TLS over any method other than raw. */
126   conn->sockdata = ssldata;
127   conn->conn_read = ssl_socket_read;
128   conn->conn_write = ssl_socket_write;
129   conn->conn_close = tls_close;
130
131   conn->ssf = SSL_CIPHER_get_bits (SSL_get_current_cipher (ssldata->ssl),
132     &maxbits);
133
134   return 0;
135
136  bail_ssl:
137   FREE (&ssldata->ssl);
138  bail_ctx:
139   FREE (&ssldata->ctx);
140  bail_ssldata:
141   FREE (&ssldata);
142  bail:
143   return -1;
144 }
145
146 /* 
147  * OpenSSL library needs to be fed with sufficient entropy. On systems
148  * with /dev/urandom, this is done transparently by the library itself,
149  * on other systems we need to fill the entropy pool ourselves.
150  *
151  * Even though only OpenSSL 0.9.5 and later will complain about the
152  * lack of entropy, we try to our best and fill the pool with older
153  * versions also. (That's the reason for the ugly #ifdefs and macros,
154  * otherwise I could have simply #ifdef'd the whole ssl_init funcion)
155  */
156 static int ssl_init (void)
157 {
158   char path[_POSIX_PATH_MAX];
159   static unsigned char init_complete = 0;
160
161   if (init_complete)
162     return 0;
163
164   if (! HAVE_ENTROPY())
165   {
166     /* load entropy from files */
167     add_entropy (SslEntropyFile);
168     add_entropy (RAND_file_name (path, sizeof (path)));
169   
170     /* load entropy from egd sockets */
171 #ifdef HAVE_RAND_EGD
172     add_entropy (getenv ("EGDSOCKET"));
173     snprintf (path, sizeof(path), "%s/.entropy", NONULL(Homedir));
174     add_entropy (path);
175     add_entropy ("/tmp/entropy");
176 #endif
177
178     /* shuffle $RANDFILE (or ~/.rnd if unset) */
179     RAND_write_file (RAND_file_name (path, sizeof (path)));
180     mutt_clear_error ();
181     if (! HAVE_ENTROPY())
182     {
183       mutt_error (_("Failed to find enough entropy on your system"));
184       mutt_sleep (2);
185       return -1;
186     }
187   }
188
189   /* I don't think you can do this just before reading the error. The call
190    * itself might clobber the last SSL error. */
191   SSL_load_error_strings();
192   SSL_library_init();
193   init_complete = 1;
194   return 0;
195 }
196
197 static int add_entropy (const char *file)
198 {
199   struct stat st;
200   int n = -1;
201
202   if (!file) return 0;
203
204   if (stat (file, &st) == -1)
205     return errno == ENOENT ? 0 : -1;
206
207   mutt_message (_("Filling entropy pool: %s...\n"),
208                 file);
209   
210   /* check that the file permissions are secure */
211   if (st.st_uid != getuid () || 
212       ((st.st_mode & (S_IWGRP | S_IRGRP)) != 0) ||
213       ((st.st_mode & (S_IWOTH | S_IROTH)) != 0))
214   {
215     mutt_error (_("%s has insecure permissions!"), file);
216     mutt_sleep (2);
217     return -1;
218   }
219
220 #ifdef HAVE_RAND_EGD
221   n = RAND_egd (file);
222 #endif
223   if (n <= 0)
224     n = RAND_load_file (file, -1);
225
226 #ifndef HAVE_RAND_STATUS
227   if (n > 0) entropy_byte_count += n;
228 #endif
229   return n;
230 }
231
232 static int ssl_socket_open_err (CONNECTION *conn)
233 {
234   mutt_error (_("SSL disabled due the lack of entropy"));
235   mutt_sleep (2);
236   return -1;
237 }
238
239
240 int mutt_ssl_socket_setup (CONNECTION * conn)
241 {
242   if (ssl_init() < 0)
243   {
244     conn->conn_open = ssl_socket_open_err;
245     return -1;
246   }
247
248   conn->conn_open       = ssl_socket_open;
249   conn->conn_read       = ssl_socket_read;
250   conn->conn_write      = ssl_socket_write;
251   conn->conn_close      = ssl_socket_close;
252   conn->conn_poll       = raw_socket_poll;
253
254   return 0;
255 }
256
257 static int ssl_socket_read (CONNECTION* conn, char* buf, size_t len)
258 {
259   sslsockdata *data = conn->sockdata;
260   return SSL_read (data->ssl, buf, len);
261 }
262
263 static int ssl_socket_write (CONNECTION* conn, const char* buf, size_t len)
264 {
265   sslsockdata *data = conn->sockdata;
266   return SSL_write (data->ssl, buf, len);
267 }
268
269 static int ssl_socket_open (CONNECTION * conn)
270 {
271   sslsockdata *data;
272   int maxbits;
273
274   if (raw_socket_open (conn) < 0)
275     return -1;
276
277   data = (sslsockdata *) safe_calloc (1, sizeof (sslsockdata));
278   conn->sockdata = data;
279
280   data->ctx = SSL_CTX_new (SSLv23_client_method ());
281
282   /* disable SSL protocols as needed */
283   if (!option(OPTTLSV1)) 
284   {
285     SSL_CTX_set_options(data->ctx, SSL_OP_NO_TLSv1);
286   }
287   if (!option(OPTSSLV2)) 
288   {
289     SSL_CTX_set_options(data->ctx, SSL_OP_NO_SSLv2);
290   }
291   if (!option(OPTSSLV3)) 
292   {
293     SSL_CTX_set_options(data->ctx, SSL_OP_NO_SSLv3);
294   }
295
296   ssl_get_client_cert(data, conn);
297
298   data->ssl = SSL_new (data->ctx);
299   SSL_set_fd (data->ssl, conn->fd);
300
301   if (ssl_negotiate(conn, data))
302   {
303     mutt_socket_close (conn);
304     return -1;
305   }
306   
307   conn->ssf = SSL_CIPHER_get_bits (SSL_get_current_cipher (data->ssl),
308     &maxbits);
309
310   return 0;
311 }
312
313 /* ssl_negotiate: After SSL state has been initialised, attempt to negotiate
314  *   SSL over the wire, including certificate checks. */
315 static int ssl_negotiate (CONNECTION *conn, sslsockdata* ssldata)
316 {
317   int err;
318   const char* errmsg;
319
320 #if OPENSSL_VERSION_NUMBER >= 0x00906000L
321   /* This only exists in 0.9.6 and above. Without it we may get interrupted
322    *   reads or writes. Bummer. */
323   SSL_set_mode (ssldata->ssl, SSL_MODE_AUTO_RETRY);
324 #endif
325
326   if ((err = SSL_connect (ssldata->ssl)) != 1)
327   {
328     switch (SSL_get_error (ssldata->ssl, err))
329     {
330     case SSL_ERROR_SYSCALL:
331       errmsg = _("I/O error");
332       break;
333     case SSL_ERROR_SSL:
334       errmsg = ERR_error_string (ERR_get_error (), NULL);
335       break;
336     default:
337       errmsg = _("unknown error");
338     }
339     
340     mutt_error (_("SSL failed: %s"), errmsg);
341     mutt_sleep (1);
342
343     return -1;
344   }
345
346   ssldata->cert = SSL_get_peer_certificate (ssldata->ssl);
347   if (!ssldata->cert)
348   {
349     mutt_error (_("Unable to get certificate from peer"));
350     mutt_sleep (1);
351     return -1;
352   }
353
354   if (!ssl_check_certificate (conn, ssldata))
355     return -1;
356
357   mutt_message (_("SSL connection using %s (%s)"), 
358     SSL_get_cipher_version (ssldata->ssl), SSL_get_cipher_name (ssldata->ssl));
359   mutt_sleep (0);
360
361   return 0;
362 }
363
364 static int ssl_socket_close (CONNECTION * conn)
365 {
366   sslsockdata *data = conn->sockdata;
367   if (data)
368   {
369     SSL_shutdown (data->ssl);
370
371     /* hold onto this for the life of mutt, in case we want to reconnect.
372      * The purist in me wants a mutt_exit hook. */
373 #if 0
374     X509_free (data->cert);
375 #endif
376     SSL_free (data->ssl);
377     SSL_CTX_free (data->ctx);
378     FREE (&conn->sockdata);
379   }
380
381   return raw_socket_close (conn);
382 }
383
384 static int tls_close (CONNECTION* conn)
385 {
386   int rc;
387
388   rc = ssl_socket_close (conn);
389   conn->conn_read = raw_socket_read;
390   conn->conn_write = raw_socket_write;
391   conn->conn_close = raw_socket_close;
392
393   return rc;
394 }
395
396 static char *x509_get_part (char *line, const char *ndx)
397 {
398   static char ret[SHORT_STRING];
399   char *c, *c2;
400
401   strfcpy (ret, _("Unknown"), sizeof (ret));
402
403   c = strstr (line, ndx);
404   if (c)
405   {
406     c += strlen (ndx);
407     c2 = strchr (c, '/');
408     if (c2)
409       *c2 = '\0';
410     strfcpy (ret, c, sizeof (ret));
411     if (c2)
412       *c2 = '/';
413   }
414
415   return ret;
416 }
417
418 static void x509_fingerprint (char *s, int l, X509 * cert)
419 {
420   unsigned char md[EVP_MAX_MD_SIZE];
421   unsigned int n;
422   int j;
423
424   if (!X509_digest (cert, EVP_md5 (), md, &n))
425   {
426     snprintf (s, l, _("[unable to calculate]"));
427   }
428   else
429   {
430     for (j = 0; j < (int) n; j++)
431     {
432       char ch[8];
433       snprintf (ch, 8, "%02X%s", md[j], (j % 2 ? " " : ""));
434       safe_strcat (s, l, ch);
435     }
436   }
437 }
438
439 static char *asn1time_to_string (ASN1_UTCTIME *tm)
440 {
441   static char buf[64];
442   BIO *bio;
443
444   strfcpy (buf, _("[invalid date]"), sizeof (buf));
445   
446   bio = BIO_new (BIO_s_mem());
447   if (bio)
448   {
449     if (ASN1_TIME_print (bio, tm))
450       (void) BIO_read (bio, buf, sizeof (buf));
451     BIO_free (bio);
452   }
453
454   return buf;
455 }
456
457 static int check_certificate_by_signer (X509 *peercert)
458 {
459   X509_STORE_CTX xsc;
460   X509_STORE *ctx;
461   int pass = 0, i;
462
463   ctx = X509_STORE_new ();
464   if (ctx == NULL) return 0;
465
466   if (option (OPTSSLSYSTEMCERTS))
467   {
468     if (X509_STORE_set_default_paths (ctx))
469       pass++;
470     else
471       dprint (2, (debugfile, "X509_STORE_set_default_paths failed\n"));
472   }
473
474   if (X509_STORE_load_locations (ctx, SslCertFile, NULL))
475     pass++;
476   else
477     dprint (2, (debugfile, "X509_STORE_load_locations_failed\n"));
478
479   for (i = 0; i < sk_X509_num (SslSessionCerts); i++)
480     pass += (X509_STORE_add_cert (ctx, sk_X509_value (SslSessionCerts, i)) != 0);
481
482   if (pass == 0)
483   {
484     /* nothing to do */
485     X509_STORE_free (ctx);
486     return 0;
487   }
488
489   X509_STORE_CTX_init (&xsc, ctx, peercert, SslSessionCerts);
490
491   pass = (X509_verify_cert (&xsc) > 0);
492 #ifdef DEBUG
493   if (! pass)
494   {
495     char buf[SHORT_STRING];
496     int err;
497
498     err = X509_STORE_CTX_get_error (&xsc);
499     snprintf (buf, sizeof (buf), "%s (%d)", 
500         X509_verify_cert_error_string(err), err);
501     dprint (2, (debugfile, "X509_verify_cert: %s\n", buf));
502     dprint (2, (debugfile, " [%s]\n", peercert->name));
503   }
504 #endif
505   X509_STORE_CTX_cleanup (&xsc);
506   X509_STORE_free (ctx);
507
508   return pass;
509 }
510
511 static int compare_certificates (X509 *cert, X509 *peercert,
512   unsigned char *peermd, unsigned int peermdlen)
513 {
514   unsigned char md[EVP_MAX_MD_SIZE];
515   unsigned int mdlen;
516   
517   /* Avoid CPU-intensive digest calculation if the certificates are
518     * not even remotely equal.
519     */
520   if (X509_subject_name_cmp (cert, peercert) != 0 ||
521       X509_issuer_name_cmp (cert, peercert) != 0)
522     return -1;
523   
524   if (!X509_digest (cert, EVP_sha1(), md, &mdlen) || peermdlen != mdlen)
525     return -1;
526   
527   if (memcmp(peermd, md, mdlen) != 0)
528     return -1;
529
530   return 0;
531 }
532
533 static int check_certificate_cache (X509 *peercert)
534 {
535   unsigned char peermd[EVP_MAX_MD_SIZE];
536   unsigned int peermdlen;
537   X509 *cert;
538   int i;
539
540   if (!X509_digest (peercert, EVP_sha1(), peermd, &peermdlen)
541       || !SslSessionCerts)
542   {
543     return 0;
544   }
545   
546   for (i = sk_X509_num (SslSessionCerts); i-- > 0;)
547   {
548     cert = sk_X509_value (SslSessionCerts, i);
549     if (!compare_certificates (cert, peercert, peermd, peermdlen))
550     {
551       return 1;
552     }
553   }
554   
555   return 0;
556 }
557
558 static int check_certificate_by_digest (X509 *peercert)
559 {
560   unsigned char peermd[EVP_MAX_MD_SIZE];
561   unsigned int peermdlen;
562   X509 *cert = NULL;
563   int pass = 0;
564   FILE *fp;
565
566   /* expiration check */
567   if (X509_cmp_current_time (X509_get_notBefore (peercert)) >= 0)
568   {
569     dprint (2, (debugfile, "Server certificate is not yet valid\n"));
570     mutt_error (_("Server certificate is not yet valid"));
571     mutt_sleep (2);
572     return 0;
573   }
574   if (X509_cmp_current_time (X509_get_notAfter (peercert)) <= 0)
575   {
576     dprint (2, (debugfile, "Server certificate has expired"));
577     mutt_error (_("Server certificate has expired"));
578     mutt_sleep (2);
579     return 0;
580   }
581
582   if ((fp = fopen (SslCertFile, "rt")) == NULL)
583     return 0;
584
585   if (!X509_digest (peercert, EVP_sha1(), peermd, &peermdlen))
586   {
587     fclose (fp);
588     return 0;
589   }
590
591   while ((cert = READ_X509_KEY (fp, &cert)) != NULL)
592   {
593     pass = compare_certificates (cert, peercert, peermd, peermdlen) ? 0 : 1;
594     
595     if (pass)
596       break;
597   }
598   X509_free (cert);
599   fclose (fp);
600
601   return pass;
602 }
603
604 /* port to mutt from msmtp's tls.c */
605 static int hostname_match (const char *hostname, const char *certname)
606 {
607   const char *cmp1, *cmp2;
608
609   if (strncmp(certname, "*.", 2) == 0)
610   {
611     cmp1 = certname + 2;
612     cmp2 = strchr(hostname, '.');
613     if (!cmp2)
614     {
615       return 0;
616     }
617     else
618     {
619       cmp2++;
620     }
621   }
622   else
623   {
624     cmp1 = certname;
625     cmp2 = hostname;
626   }
627
628   if (*cmp1 == '\0' || *cmp2 == '\0')
629   {
630     return 0;
631   }
632
633   if (strcasecmp(cmp1, cmp2) != 0)
634   {
635     return 0;
636   }
637
638   return 1;
639 }
640
641 /* port to mutt from msmtp's tls.c */
642 static int check_host (X509 *x509cert, const char *hostname, char *err, size_t errlen)
643 {
644   int i, rc = 0;
645   /* hostname in ASCII format: */
646   char *hostname_ascii = NULL;
647   /* needed to get the common name: */
648   X509_NAME *x509_subject;
649   char *buf = NULL;
650   int bufsize;
651   /* needed to get the DNS subjectAltNames: */
652   STACK *subj_alt_names;
653   int subj_alt_names_count;
654   GENERAL_NAME *subj_alt_name;
655   /* did we find a name matching hostname? */
656   int match_found;
657
658   /* Check if 'hostname' matches the one of the subjectAltName extensions of
659    * type DNS or the Common Name (CN). */
660
661 #ifdef HAVE_LIBIDN
662   if (idna_to_ascii_lz(hostname, &hostname_ascii, 0) != IDNA_SUCCESS)
663   {
664     hostname_ascii = safe_strdup(hostname);
665   }
666 #else
667   hostname_ascii = safe_strdup(hostname);
668 #endif
669
670   /* Try the DNS subjectAltNames. */
671   match_found = 0;
672   if ((subj_alt_names = X509_get_ext_d2i(x509cert, NID_subject_alt_name,
673                                          NULL, NULL)))
674   {
675     subj_alt_names_count = sk_GENERAL_NAME_num(subj_alt_names);
676     for (i = 0; i < subj_alt_names_count; i++)
677     {
678       subj_alt_name = sk_GENERAL_NAME_value(subj_alt_names, i);
679       if (subj_alt_name->type == GEN_DNS)
680       {
681         if ((match_found = hostname_match(hostname_ascii,
682                                           (char *)(subj_alt_name->d.ia5->data))))
683         {
684           break;
685         }
686       }
687     }
688   }
689
690   if (!match_found)
691   {
692     /* Try the common name */
693     if (!(x509_subject = X509_get_subject_name(x509cert)))
694     {
695       if (err && errlen)
696         strfcpy (err, _("cannot get certificate subject"), errlen);
697       goto out;
698     }
699
700     bufsize = X509_NAME_get_text_by_NID(x509_subject, NID_commonName,
701                                         NULL, 0);
702     bufsize++;
703     buf = safe_malloc((size_t)bufsize);
704     if (X509_NAME_get_text_by_NID(x509_subject, NID_commonName,
705                                   buf, bufsize) == -1)
706     {
707       if (err && errlen)
708         strfcpy (err, _("cannot get certificate common name"), errlen);
709       goto out;
710     }
711     match_found = hostname_match(hostname_ascii, buf);
712   }
713
714   if (!match_found)
715   {
716     if (err && errlen)
717       snprintf (err, errlen, _("certificate owner does not match hostname %s"),
718                 hostname);
719     goto out;
720   }
721
722   rc = 1;
723
724 out:
725   FREE(&buf);
726   FREE(&hostname_ascii);
727
728   return rc;
729 }
730
731 static int ssl_cache_trusted_cert (X509 *c)
732 {
733   dprint (1, (debugfile, "trusted: %s\n", c->name));
734   if (!SslSessionCerts)
735     SslSessionCerts = sk_new_null();
736   return (sk_X509_push (SslSessionCerts, X509_dup(c)));
737 }
738
739 /* check whether cert is preauthorized */
740 static int ssl_check_preauth (X509 *cert, CONNECTION *conn)
741 {
742   char buf[SHORT_STRING];
743
744   /* check session cache first */
745   if (check_certificate_cache (cert))
746   {
747     dprint (2, (debugfile, "ssl_check_preauth: using cached certificate\n"));
748     return 1;
749   }
750
751   buf[0] = 0;
752   if (!check_host (cert, conn->account.host, buf, sizeof (buf)))
753   {
754     mutt_error (_("Certificate host check failed: %s"), buf);
755     mutt_sleep (2);
756     return -1;
757   }
758   dprint (2, (debugfile, "ssl_check_preauth: hostname check passed\n"));
759
760   if (check_certificate_by_signer (cert))
761   {
762     dprint (2, (debugfile, "ssl_check_preauth: signer check passed\n"));
763     return 1;
764   }
765
766   /* automatic check from user's database */
767   if (SslCertFile && check_certificate_by_digest (cert))
768   {
769     dprint (2, (debugfile, "ssl_check_preauth: digest check passed\n"));
770     return 1;
771   }
772
773   return 0;
774 }
775
776 static int ssl_check_certificate (CONNECTION *conn, sslsockdata *data)
777 {
778   int i, preauthrc, chain_len;
779   STACK_OF(X509) *chain;
780   X509 *cert;
781
782   if ((preauthrc = ssl_check_preauth (data->cert, conn)) > 0)
783     return preauthrc;
784
785   chain = SSL_get_peer_cert_chain (data->ssl);
786   chain_len = sk_X509_num (chain);
787   if (!chain || (chain_len < 1))
788     return interactive_check_cert (data->cert, 0, 0);
789
790   /* check the chain from root to peer */
791   for (i = chain_len-1; i >= 0; i--)
792   {
793     cert = sk_X509_value (chain, i);
794     if (check_certificate_cache (cert))
795       dprint (2, (debugfile, "ssl chain: already cached: %s\n", cert->name));
796     else if (i /* 0 is the peer */ || !preauthrc)
797     {
798       if (check_certificate_by_signer (cert))
799       {
800         dprint (2, (debugfile, "ssl chain: checked by signer: %s\n", cert->name));
801         ssl_cache_trusted_cert (cert);
802         return 1;
803       }
804       else if (SslCertFile && check_certificate_by_digest (cert))
805       {
806         dprint (2, (debugfile, "ssl chain: trusted with file: %s\n", cert->name));
807         ssl_cache_trusted_cert (cert);
808         return 1;
809       }
810       else /* allow users to shoot their foot */
811       {
812         dprint (2, (debugfile, "ssl chain: check failed: %s\n", cert->name));
813         if (interactive_check_cert (cert, i, chain_len))
814           return 1;
815       }
816     }
817     else /* highly suspicious because (i==0 && preauthrc < 0) */
818       if (interactive_check_cert (cert, i, chain_len))
819         return 1;
820   }
821
822   return 0;
823 }
824
825 static int interactive_check_cert (X509 *cert, int idx, int len)
826 {
827   char *part[] =
828     {"/CN=", "/Email=", "/O=", "/OU=", "/L=", "/ST=", "/C="};
829   char helpstr[LONG_STRING];
830   char buf[STRING];
831   char title[STRING];
832   MUTTMENU *menu = mutt_new_menu (-1);
833   int done, row, i;
834   FILE *fp;
835   char *name = NULL, *c;
836
837   dprint (2, (debugfile, "interactive_check_cert: %s\n", cert->name));
838
839   menu->max = 19;
840   menu->dialog = (char **) safe_calloc (1, menu->max * sizeof (char *));
841   for (i = 0; i < menu->max; i++)
842     menu->dialog[i] = (char *) safe_calloc (1, SHORT_STRING * sizeof (char));
843
844   row = 0;
845   strfcpy (menu->dialog[row], _("This certificate belongs to:"), SHORT_STRING);
846   row++;
847   name = X509_NAME_oneline (X509_get_subject_name (cert),
848                             buf, sizeof (buf));
849   dprint (2, (debugfile, "oneline: %s\n", name));
850   
851   for (i = 0; i < 5; i++)
852   {
853     c = x509_get_part (name, part[i]);
854     snprintf (menu->dialog[row++], SHORT_STRING, "   %s", c);
855   }
856
857   row++;
858   strfcpy (menu->dialog[row], _("This certificate was issued by:"), SHORT_STRING);
859   row++;
860   name = X509_NAME_oneline (X509_get_issuer_name (cert),
861                             buf, sizeof (buf));
862   for (i = 0; i < 5; i++)
863   {
864     c = x509_get_part (name, part[i]);
865     snprintf (menu->dialog[row++], SHORT_STRING, "   %s", c);
866   }
867
868   row++;
869   snprintf (menu->dialog[row++], SHORT_STRING, _("This certificate is valid"));
870   snprintf (menu->dialog[row++], SHORT_STRING, _("   from %s"), 
871       asn1time_to_string (X509_get_notBefore (cert)));
872   snprintf (menu->dialog[row++], SHORT_STRING, _("     to %s"), 
873       asn1time_to_string (X509_get_notAfter (cert)));
874
875   row++;
876   buf[0] = '\0';
877   x509_fingerprint (buf, sizeof (buf), cert);
878   snprintf (menu->dialog[row++], SHORT_STRING, _("Fingerprint: %s"), buf);
879
880   snprintf (title, sizeof (title),
881             _("SSL Certificate check (certificate %d of %d in chain)"),
882             len - idx, len);
883   menu->title = title;
884   if (SslCertFile && X509_cmp_current_time (X509_get_notAfter (cert)) >= 0
885       && X509_cmp_current_time (X509_get_notBefore (cert)) < 0)
886   {
887     menu->prompt = _("(r)eject, accept (o)nce, (a)ccept always");
888     menu->keys = _("roa");
889   }
890   else
891   {
892     menu->prompt = _("(r)eject, accept (o)nce");
893     menu->keys = _("ro");
894   }
895   
896   helpstr[0] = '\0';
897   mutt_make_help (buf, sizeof (buf), _("Exit  "), MENU_GENERIC, OP_EXIT);
898   safe_strcat (helpstr, sizeof (helpstr), buf);
899   mutt_make_help (buf, sizeof (buf), _("Help"), MENU_GENERIC, OP_HELP);
900   safe_strcat (helpstr, sizeof (helpstr), buf);
901   menu->help = helpstr;
902
903   done = 0;
904   set_option(OPTUNBUFFEREDINPUT);
905   while (!done)
906   {
907     switch (mutt_menuLoop (menu))
908     {
909       case -1:                  /* abort */
910       case OP_MAX + 1:          /* reject */
911       case OP_EXIT:
912         done = 1;
913         break;
914       case OP_MAX + 3:          /* accept always */
915         done = 0;
916         if ((fp = fopen (SslCertFile, "a")))
917         {
918           if (PEM_write_X509 (fp, cert))
919             done = 1;
920           fclose (fp);
921         }
922         if (!done)
923         {
924           mutt_error (_("Warning: Couldn't save certificate"));
925           mutt_sleep (2);
926         }
927         else
928         {
929           mutt_message (_("Certificate saved"));
930           mutt_sleep (0);
931         }
932         /* fall through */
933       case OP_MAX + 2:          /* accept once */
934         done = 2;
935         ssl_cache_trusted_cert (cert);
936         break;
937     }
938   }
939   unset_option(OPTUNBUFFEREDINPUT);
940   mutt_menuDestroy (&menu);
941   dprint (2, (debugfile, "ssl interactive_check_cert: done=%d\n", done));
942   return (done == 2);
943 }
944
945 static void ssl_get_client_cert(sslsockdata *ssldata, CONNECTION *conn)
946 {
947   if (SslClientCert)
948   {
949     dprint (2, (debugfile, "Using client certificate %s\n", SslClientCert));
950     SSL_CTX_set_default_passwd_cb_userdata(ssldata->ctx, &conn->account);
951     SSL_CTX_set_default_passwd_cb(ssldata->ctx, ssl_passwd_cb);
952     SSL_CTX_use_certificate_file(ssldata->ctx, SslClientCert, SSL_FILETYPE_PEM);
953     SSL_CTX_use_PrivateKey_file(ssldata->ctx, SslClientCert, SSL_FILETYPE_PEM);
954     
955     /* if we are using a client cert, SASL may expect an external auth name */
956     mutt_account_getuser (&conn->account);
957   }
958 }
959
960 static int ssl_passwd_cb(char *buf, int size, int rwflag, void *userdata)
961 {
962   ACCOUNT *account = (ACCOUNT*)userdata;
963
964   if (mutt_account_getuser (account))
965     return 0;
966
967   dprint (2, (debugfile, "ssl_passwd_cb: getting password for %s@%s:%u\n",
968               account->user, account->host, account->port));
969   
970   if (mutt_account_getpass (account))
971     return 0;
972
973   return snprintf(buf, size, "%s", account->pass);
974 }