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