push username, homedir and tmpdir in lua too.
[apps/madmutt.git] / lib-sys / mutt_ssl.c
1 /*
2  * Copyright notice from original mutt:
3  * Copyright (C) 1999-2001 Tommi Komulainen <Tommi.Komulainen@iki.fi>
4  *
5  * This file is part of mutt-ng, see http://www.muttng.org/.
6  * It's licensed under the GNU General Public License,
7  * please see the file GPL in the top level source directory.
8  */
9
10 #include <lib-lib/lib-lib.h>
11
12 #ifdef USE_SSL
13
14 #include <openssl/ssl.h>
15 #include <openssl/x509.h>
16 #include <openssl/err.h>
17 #include <openssl/rand.h>
18
19 #include <lib-ui/curses.h>
20 #include <lib-ui/menu.h>
21
22 #include "mutt.h"
23 #include "mutt_socket.h"
24 #include "mutt_ssl.h"
25
26
27 #if OPENSSL_VERSION_NUMBER >= 0x00904000L
28 #define READ_X509_KEY(fp, key)  PEM_read_X509(fp, key, NULL, NULL)
29 #else
30 #define READ_X509_KEY(fp, key)  PEM_read_X509(fp, key, NULL)
31 #endif
32
33 /* Just in case OpenSSL doesn't define DEVRANDOM */
34 #ifndef DEVRANDOM
35 #define DEVRANDOM "/dev/urandom"
36 #endif
37
38 /* This is ugly, but as RAND_status came in on OpenSSL version 0.9.5
39  * and the code has to support older versions too, this is seemed to
40  * be cleaner way compared to having even uglier #ifdefs all around.
41  */
42 #ifdef HAVE_RAND_STATUS
43 #define HAVE_ENTROPY()  (RAND_status() == 1)
44 #else
45 static int entropy_byte_count = 0;
46
47 /* OpenSSL fills the entropy pool from /dev/urandom if it exists */
48 #define HAVE_ENTROPY()  (!access(DEVRANDOM, R_OK) || entropy_byte_count >= 16)
49 #endif
50
51 typedef struct _sslsockdata {
52   SSL_CTX *ctx;
53   SSL *ssl;
54   X509 *cert;
55 } sslsockdata;
56
57 /* local prototypes */
58 static int ssl_init (void);
59 static int add_entropy (const char *file);
60 static int ssl_socket_read (CONNECTION * conn, char *buf, size_t len);
61 static int ssl_socket_write (CONNECTION * conn, const char *buf, size_t len);
62 static int ssl_socket_open (CONNECTION * conn);
63 static int ssl_socket_close (CONNECTION * conn);
64 static int tls_close (CONNECTION * conn);
65 static int ssl_check_certificate (sslsockdata * data);
66 static void ssl_get_client_cert (sslsockdata * ssldata, CONNECTION * conn);
67 static int ssl_passwd_cb (char *buf, int size, int rwflag, void *userdata);
68 static int ssl_negotiate (sslsockdata *);
69
70 /* mutt_ssl_starttls: Negotiate TLS over an already opened connection.
71  *   TODO: Merge this code better with ssl_socket_open. */
72 int mutt_ssl_starttls (CONNECTION * conn)
73 {
74   sslsockdata *ssldata;
75   int maxbits;
76
77   if (ssl_init ())
78     goto bail;
79
80   ssldata = p_new(sslsockdata, 1);
81   /* the ssl_use_xxx protocol options don't apply. We must use TLS in TLS. */
82   if (!(ssldata->ctx = SSL_CTX_new (TLSv1_client_method ()))) {
83     goto bail_ssldata;
84   }
85
86   ssl_get_client_cert (ssldata, conn);
87
88   if (!(ssldata->ssl = SSL_new (ssldata->ctx))) {
89     goto bail_ctx;
90   }
91
92   if (SSL_set_fd (ssldata->ssl, conn->fd) != 1) {
93     goto bail_ssl;
94   }
95
96   if (ssl_negotiate (ssldata))
97     goto bail_ssl;
98
99   /* hmm. watch out if we're starting TLS over any method other than raw. */
100   conn->sockdata = ssldata;
101   conn->conn_read = ssl_socket_read;
102   conn->conn_write = ssl_socket_write;
103   conn->conn_close = tls_close;
104
105   conn->ssf = SSL_CIPHER_get_bits (SSL_get_current_cipher (ssldata->ssl),
106                                    &maxbits);
107
108   return 0;
109
110 bail_ssl:
111   p_delete(&ssldata->ssl);
112 bail_ctx:
113   p_delete(&ssldata->ctx);
114 bail_ssldata:
115   p_delete(&ssldata);
116 bail:
117   return -1;
118 }
119
120 /* 
121  * OpenSSL library needs to be fed with sufficient entropy. On systems
122  * with /dev/urandom, this is done transparently by the library itself,
123  * on other systems we need to fill the entropy pool ourselves.
124  *
125  * Even though only OpenSSL 0.9.5 and later will complain about the
126  * lack of entropy, we try to our best and fill the pool with older
127  * versions also. (That's the reason for the ugly #ifdefs and macros,
128  * otherwise I could have simply #ifdef'd the whole ssl_init funcion)
129  */
130 static int ssl_init (void)
131 {
132   char path[_POSIX_PATH_MAX];
133   static unsigned char init_complete = 0;
134
135   if (init_complete)
136     return 0;
137
138   if (!HAVE_ENTROPY ()) {
139     /* load entropy from files */
140     add_entropy (SslEntropyFile);
141     add_entropy (RAND_file_name (path, sizeof (path)));
142
143     /* load entropy from egd sockets */
144 #ifdef HAVE_RAND_EGD
145     add_entropy (getenv ("EGDSOCKET"));
146     snprintf (path, sizeof (path), "%s/.entropy", NONULL(MCore.homedir));
147     add_entropy (path);
148     add_entropy ("/tmp/entropy");
149 #endif
150
151     /* shuffle $RANDFILE (or ~/.rnd if unset) */
152     RAND_write_file (RAND_file_name (path, sizeof (path)));
153     mutt_clear_error ();
154     if (!HAVE_ENTROPY ()) {
155       mutt_error (_("Failed to find enough entropy on your system"));
156       mutt_sleep (2);
157       return -1;
158     }
159   }
160
161   /* I don't think you can do this just before reading the error. The call
162    * itself might clobber the last SSL error. */
163   SSL_load_error_strings ();
164   SSL_library_init ();
165   init_complete = 1;
166   return 0;
167 }
168
169 static int add_entropy (const char *file)
170 {
171   struct stat st;
172   int n = -1;
173
174   if (!file)
175     return 0;
176
177   if (stat (file, &st) == -1)
178     return errno == ENOENT ? 0 : -1;
179
180   mutt_message (_("Filling entropy pool: %s...\n"), file);
181
182   /* check that the file permissions are secure */
183   if (st.st_uid != getuid () ||
184       ((st.st_mode & (S_IWGRP | S_IRGRP)) != 0) ||
185       ((st.st_mode & (S_IWOTH | S_IROTH)) != 0)) {
186     mutt_error (_("%s has insecure permissions!"), file);
187     mutt_sleep (2);
188     return -1;
189   }
190
191 #ifdef HAVE_RAND_EGD
192   n = RAND_egd (file);
193 #endif
194   if (n <= 0)
195     n = RAND_load_file (file, -1);
196
197 #ifndef HAVE_RAND_STATUS
198   if (n > 0)
199     entropy_byte_count += n;
200 #endif
201   return n;
202 }
203
204 static int ssl_socket_open_err (CONNECTION * conn)
205 {
206   mutt_error (_("SSL disabled due the lack of entropy"));
207   mutt_sleep (2);
208   return -1;
209 }
210
211
212 int mutt_ssl_socket_setup (CONNECTION * conn)
213 {
214   if (ssl_init () < 0) {
215     conn->conn_open = ssl_socket_open_err;
216     return -1;
217   }
218
219   conn->conn_open = ssl_socket_open;
220   conn->conn_read = ssl_socket_read;
221   conn->conn_write = ssl_socket_write;
222   conn->conn_close = ssl_socket_close;
223
224   return 0;
225 }
226
227 static int ssl_socket_read (CONNECTION * conn, char *buf, size_t len)
228 {
229   sslsockdata *data = conn->sockdata;
230
231   return SSL_read (data->ssl, buf, len);
232 }
233
234 static int ssl_socket_write (CONNECTION * conn, const char *buf, size_t len)
235 {
236   sslsockdata *data = conn->sockdata;
237
238   return SSL_write (data->ssl, buf, len);
239 }
240
241 static int ssl_socket_open (CONNECTION * conn)
242 {
243   sslsockdata *data;
244   int maxbits;
245
246   if (raw_socket_open (conn) < 0)
247     return -1;
248
249   data = p_new(sslsockdata, 1);
250   conn->sockdata = data;
251
252   data->ctx = SSL_CTX_new (SSLv23_client_method ());
253
254   /* disable SSL protocols as needed */
255   if (!option (OPTTLSV1)) {
256     SSL_CTX_set_options (data->ctx, SSL_OP_NO_TLSv1);
257   }
258   if (!option (OPTSSLV2)) {
259     SSL_CTX_set_options (data->ctx, SSL_OP_NO_SSLv2);
260   }
261   if (!option (OPTSSLV3)) {
262     SSL_CTX_set_options (data->ctx, SSL_OP_NO_SSLv3);
263   }
264
265   ssl_get_client_cert (data, conn);
266
267   data->ssl = SSL_new (data->ctx);
268   SSL_set_fd (data->ssl, conn->fd);
269
270   if (ssl_negotiate (data)) {
271     mutt_socket_close (conn);
272     return -1;
273   }
274
275   conn->ssf = SSL_CIPHER_get_bits (SSL_get_current_cipher (data->ssl),
276                                    &maxbits);
277
278   return 0;
279 }
280
281 /* ssl_negotiate: After SSL state has been initialised, attempt to negotiate
282  *   SSL over the wire, including certificate checks. */
283 static int ssl_negotiate (sslsockdata * ssldata)
284 {
285   int err;
286   const char *errmsg;
287
288 #if OPENSSL_VERSION_NUMBER >= 0x00906000L
289   /* This only exists in 0.9.6 and above. Without it we may get interrupted
290    *   reads or writes. Bummer. */
291   SSL_set_mode (ssldata->ssl, SSL_MODE_AUTO_RETRY);
292 #endif
293
294   if ((err = SSL_connect (ssldata->ssl)) != 1) {
295     switch (SSL_get_error (ssldata->ssl, err)) {
296     case SSL_ERROR_SYSCALL:
297       errmsg = _("I/O error");
298       break;
299     case SSL_ERROR_SSL:
300       errmsg = ERR_error_string (ERR_get_error (), NULL);
301       break;
302     default:
303       errmsg = _("unknown error");
304     }
305
306     mutt_error (_("SSL failed: %s"), errmsg);
307     mutt_sleep (1);
308
309     return -1;
310   }
311
312   ssldata->cert = SSL_get_peer_certificate (ssldata->ssl);
313   if (!ssldata->cert) {
314     mutt_error (_("Unable to get certificate from peer"));
315     mutt_sleep (1);
316     return -1;
317   }
318
319   if (!ssl_check_certificate (ssldata))
320     return -1;
321
322   mutt_message (_("SSL connection using %s (%s)"),
323                 SSL_get_cipher_version (ssldata->ssl),
324                 SSL_get_cipher_name (ssldata->ssl));
325   mutt_sleep (0);
326
327   return 0;
328 }
329
330 static int ssl_socket_close (CONNECTION * conn)
331 {
332   sslsockdata *data = conn->sockdata;
333
334   if (data) {
335     SSL_shutdown (data->ssl);
336     SSL_free (data->ssl);
337     SSL_CTX_free (data->ctx);
338     p_delete(&conn->sockdata);
339   }
340
341   return raw_socket_close (conn);
342 }
343
344 static int compare_certificates (X509 *cert, X509 *peercert, 
345                                  unsigned char *peermd,
346                                  unsigned int peermdlen) {
347   unsigned char md[EVP_MAX_MD_SIZE];
348   unsigned int mdlen;
349
350   /* Avoid CPU-intensive digest calculation if the certificates are
351   * not even remotely equal.
352   */
353   if (X509_subject_name_cmp (cert, peercert) != 0 || 
354       X509_issuer_name_cmp (cert, peercert) != 0)
355     return -1;
356
357   if (!X509_digest (cert, EVP_sha1(), md, &mdlen) || peermdlen != mdlen)
358     return -1;
359
360   if (memcmp(peermd, md, mdlen) != 0)
361     return -1;
362
363   return 0;
364 }
365
366 static int check_certificate_cache (X509 *peercert) {
367   unsigned char peermd[EVP_MAX_MD_SIZE];
368   unsigned int peermdlen;
369   X509 *cert;
370   string_list_t *scert;
371
372   if (!X509_digest (peercert, EVP_sha1(), peermd, &peermdlen)) 
373     return 0;
374
375   for (scert = SslSessionCerts; scert; scert = scert->next) {
376     cert = *(X509**)scert->data;
377     if (!compare_certificates (cert, peercert, peermd, peermdlen)) {
378       return 1;
379     }
380   }
381  return 0;
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[STRING];
399   char *c, *c2;
400
401   m_strcpy(ret, sizeof(ret), _("Unknown"));
402
403   c = strstr (line, ndx);
404   if (c) {
405     c += m_strlen(ndx);
406     c2 = strchr (c, '/');
407     if (c2)
408       *c2 = '\0';
409     m_strcpy(ret, sizeof(ret), c);
410     if (c2)
411       *c2 = '/';
412   }
413
414   return ret;
415 }
416
417 static void x509_fingerprint (char *s, int l, X509 * cert)
418 {
419   unsigned char md[EVP_MAX_MD_SIZE];
420   unsigned int n;
421   int j;
422
423   if (!X509_digest (cert, EVP_md5 (), md, &n)) {
424     m_strcpy(s, l, _("[unable to calculate]"));
425   }
426   else {
427     for (j = 0; j < (int) n; j++) {
428       char ch[8];
429
430       snprintf(ch, 8, "%02X%s", md[j], (j % 2 ? " " : ""));
431       m_strcat(s, l, ch);
432     }
433   }
434 }
435
436 static char *asn1time_to_string (ASN1_UTCTIME * tm)
437 {
438   static char buf[64];
439   BIO *bio;
440
441   m_strcpy(buf, sizeof(buf), _("[invalid date]"));
442
443   bio = BIO_new (BIO_s_mem ());
444   if (bio) {
445     if (ASN1_TIME_print (bio, tm))
446       (void) BIO_read (bio, buf, sizeof (buf));
447     BIO_free (bio);
448   }
449
450   return buf;
451 }
452
453 static int check_certificate_by_signer (X509 * peercert)
454 {
455   X509_STORE_CTX xsc;
456   X509_STORE *ctx;
457   int pass = 0;
458
459   ctx = X509_STORE_new ();
460   if (ctx == NULL)
461     return 0;
462
463   if (option (OPTSSLSYSTEMCERTS)) {
464     if (X509_STORE_set_default_paths (ctx))
465       pass++;
466   }
467
468   if (X509_STORE_load_locations (ctx, SslCertFile, NULL))
469     pass++;
470
471   if (pass == 0) {
472     /* nothing to do */
473     X509_STORE_free (ctx);
474     return 0;
475   }
476
477   X509_STORE_CTX_init (&xsc, ctx, peercert, NULL);
478
479   pass = (X509_verify_cert (&xsc) > 0);
480   X509_STORE_CTX_cleanup (&xsc);
481   X509_STORE_free (ctx);
482
483   return pass;
484 }
485
486 static int check_certificate_by_digest (X509 * peercert)
487 {
488   unsigned char peermd[EVP_MAX_MD_SIZE];
489   unsigned int peermdlen;
490   X509 *cert = NULL;
491   int pass = 0;
492   FILE *fp;
493
494   /* expiration check */
495   if (X509_cmp_current_time (X509_get_notBefore (peercert)) >= 0) {
496     mutt_error (_("Server certificate is not yet valid"));
497     mutt_sleep (2);
498     return 0;
499   }
500   if (X509_cmp_current_time (X509_get_notAfter (peercert)) <= 0) {
501     mutt_error (_("Server certificate has expired"));
502     mutt_sleep (2);
503     return 0;
504   }
505
506   if ((fp = fopen (SslCertFile, "rt")) == NULL)
507     return 0;
508
509   if (!X509_digest (peercert, EVP_sha1 (), peermd, &peermdlen)) {
510     m_fclose(&fp);
511     return 0;
512   }
513
514   while ((cert = READ_X509_KEY (fp, &cert)) != NULL) {
515     pass = compare_certificates (cert, peercert, peermd, peermdlen) ? 0 : 1;
516     if (pass)
517       break;
518   }
519   X509_free (cert);
520   m_fclose(&fp);
521
522   return pass;
523 }
524
525 static int ssl_check_certificate (sslsockdata * data)
526 {
527   char *part[] = { "/CN=", "/Email=", "/O=", "/OU=", "/L=", "/ST=", "/C=" };
528   char helpstr[STRING];
529   char buf[STRING];
530   MUTTMENU *menu;
531   int done, row, i;
532   FILE *fp;
533   char *name = NULL, *c;
534
535   /* check session cache first */
536   if (check_certificate_cache (data->cert)) {
537     return 1;
538   }
539
540   if (check_certificate_by_signer (data->cert)) {
541     return 1;
542   }
543
544   /* automatic check from user's database */
545   if (SslCertFile && check_certificate_by_digest (data->cert)) {
546     return 1;
547   }
548
549   /* interactive check from user */
550   menu = mutt_new_menu ();
551   menu->max = 19;
552   menu->dialog = p_new(char *, menu->max);
553   for (i = 0; i < menu->max; i++)
554     menu->dialog[i] = p_new(char, STRING);
555
556   row = 0;
557   m_strcpy(menu->dialog[row], STRING,
558            _("This certificate belongs to:"));
559   row++;
560   name = X509_NAME_oneline (X509_get_subject_name (data->cert),
561                             buf, sizeof (buf));
562   for (i = 0; i < 5; i++) {
563     c = x509_get_part (name, part[i]);
564     snprintf (menu->dialog[row++], STRING, "   %s", c);
565   }
566
567   row++;
568   m_strcpy(menu->dialog[row], STRING,
569            _("This certificate was issued by:"));
570   row++;
571   name = X509_NAME_oneline (X509_get_issuer_name (data->cert),
572                             buf, sizeof (buf));
573   for (i = 0; i < 5; i++) {
574     c = x509_get_part (name, part[i]);
575     snprintf (menu->dialog[row++], STRING, "   %s", c);
576   }
577
578   row++;
579   snprintf (menu->dialog[row++], STRING, "%s",
580             _("This certificate is valid"));
581   snprintf (menu->dialog[row++], STRING, _("   from %s"),
582             asn1time_to_string (X509_get_notBefore (data->cert)));
583   snprintf (menu->dialog[row++], STRING, _("     to %s"),
584             asn1time_to_string (X509_get_notAfter (data->cert)));
585
586   row++;
587   buf[0] = '\0';
588   x509_fingerprint (buf, sizeof (buf), data->cert);
589   snprintf (menu->dialog[row++], STRING, _("Fingerprint: %s"), buf);
590
591   menu->title = _("SSL Certificate check");
592
593   if (SslCertFile && X509_cmp_current_time (X509_get_notAfter (data->cert)) >= 0
594       && X509_cmp_current_time (X509_get_notBefore (data->cert)) < 0) {
595     menu->prompt = _("(r)eject, accept (o)nce, (a)ccept always");
596     menu->keys = _("roa");
597   }
598   else {
599     menu->prompt = _("(r)eject, accept (o)nce");
600     menu->keys = _("ro");
601   }
602
603   helpstr[0] = '\0';
604   mutt_make_help (buf, sizeof (buf), _("Exit  "), MENU_GENERIC, OP_EXIT);
605   m_strcat(helpstr, sizeof(helpstr), buf);
606   mutt_make_help (buf, sizeof (buf), _("Help"), MENU_GENERIC, OP_HELP);
607   m_strcat(helpstr, sizeof(helpstr), buf);
608   menu->help = helpstr;
609
610   done = 0;
611   set_option (OPTUNBUFFEREDINPUT);
612   while (!done) {
613     switch (mutt_menuLoop (menu)) {
614     case -1:                   /* abort */
615     case OP_MAX + 1:           /* reject */
616     case OP_EXIT:
617       done = 1;
618       break;
619     case OP_MAX + 3:           /* accept always */
620       done = 0;
621       if ((fp = fopen (SslCertFile, "a"))) {
622         if (PEM_write_X509 (fp, data->cert))
623           done = 1;
624         m_fclose(&fp);
625       }
626       if (!done) {
627         mutt_error (_("Warning: Couldn't save certificate"));
628         mutt_sleep (2);
629       }
630       else {
631         mutt_message (_("Certificate saved"));
632         mutt_sleep (0);
633       }
634       /* fall through */
635     case OP_MAX + 2:           /* accept once */
636       done = 2;
637       /* keep a handle on accepted certificates in case we want to
638        * open up another connection to the same server in this session */
639       SslSessionCerts = mutt_add_list_n (SslSessionCerts, &data->cert,
640                                          sizeof (X509 **));
641       break;
642     }
643   }
644   unset_option (OPTUNBUFFEREDINPUT);
645   mutt_menuDestroy (&menu);
646   return (done == 2);
647 }
648
649 static void ssl_get_client_cert (sslsockdata * ssldata, CONNECTION * conn)
650 {
651   if (SslClientCert) {
652     SSL_CTX_set_default_passwd_cb_userdata (ssldata->ctx, &conn->account);
653     SSL_CTX_set_default_passwd_cb (ssldata->ctx, ssl_passwd_cb);
654     SSL_CTX_use_certificate_file (ssldata->ctx, SslClientCert,
655                                   SSL_FILETYPE_PEM);
656     SSL_CTX_use_PrivateKey_file (ssldata->ctx, SslClientCert,
657                                  SSL_FILETYPE_PEM);
658   }
659 }
660
661 static int ssl_passwd_cb (char *buf, int size, int rwflag, void *userdata)
662 {
663   ACCOUNT *account = (ACCOUNT *) userdata;
664
665   if (mutt_account_getuser (account))
666     return 0;
667
668   if (mutt_account_getpass (account))
669     return 0;
670
671   return snprintf (buf, size, "%s", account->pass);
672 }
673
674 #endif /* USE_SSL */