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