rationnalize includes a lot:
[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 (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 #if 0
337     X509_free (data->cert);
338 #endif
339     SSL_free (data->ssl);
340     SSL_CTX_free (data->ctx);
341     p_delete(&conn->sockdata);
342   }
343
344   return raw_socket_close (conn);
345 }
346
347 static int compare_certificates (X509 *cert, X509 *peercert, 
348                                  unsigned char *peermd,
349                                  unsigned int peermdlen) {
350   unsigned char md[EVP_MAX_MD_SIZE];
351   unsigned int mdlen;
352
353   /* Avoid CPU-intensive digest calculation if the certificates are
354   * not even remotely equal.
355   */
356   if (X509_subject_name_cmp (cert, peercert) != 0 || 
357       X509_issuer_name_cmp (cert, peercert) != 0)
358     return -1;
359
360   if (!X509_digest (cert, EVP_sha1(), md, &mdlen) || peermdlen != mdlen)
361     return -1;
362
363   if (memcmp(peermd, md, mdlen) != 0)
364     return -1;
365
366   return 0;
367 }
368
369 static int check_certificate_cache (X509 *peercert) {
370   unsigned char peermd[EVP_MAX_MD_SIZE];
371   unsigned int peermdlen;
372   X509 *cert;
373   string_list_t *scert;
374
375   if (!X509_digest (peercert, EVP_sha1(), peermd, &peermdlen)) 
376     return 0;
377
378   for (scert = SslSessionCerts; scert; scert = scert->next) {
379     cert = *(X509**)scert->data;
380     if (!compare_certificates (cert, peercert, peermd, peermdlen)) {
381       return 1;
382     }
383   }
384  return 0;
385 }
386
387 static int tls_close (CONNECTION * conn)
388 {
389   int rc;
390
391   rc = ssl_socket_close (conn);
392   conn->conn_read = raw_socket_read;
393   conn->conn_write = raw_socket_write;
394   conn->conn_close = raw_socket_close;
395
396   return rc;
397 }
398
399 static char *x509_get_part (char *line, const char *ndx)
400 {
401   static char ret[SHORT_STRING];
402   char *c, *c2;
403
404   m_strcpy(ret, sizeof(ret), _("Unknown"));
405
406   c = strstr (line, ndx);
407   if (c) {
408     c += m_strlen(ndx);
409     c2 = strchr (c, '/');
410     if (c2)
411       *c2 = '\0';
412     m_strcpy(ret, sizeof(ret), c);
413     if (c2)
414       *c2 = '/';
415   }
416
417   return ret;
418 }
419
420 static void x509_fingerprint (char *s, int l, X509 * cert)
421 {
422   unsigned char md[EVP_MAX_MD_SIZE];
423   unsigned int n;
424   int j;
425
426   if (!X509_digest (cert, EVP_md5 (), md, &n)) {
427     m_strcpy(s, l, _("[unable to calculate]"));
428   }
429   else {
430     for (j = 0; j < (int) n; j++) {
431       char ch[8];
432
433       snprintf(ch, 8, "%02X%s", md[j], (j % 2 ? " " : ""));
434       m_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   m_strcpy(buf, sizeof(buf), _("[invalid date]"));
445
446   bio = BIO_new (BIO_s_mem ());
447   if (bio) {
448     if (ASN1_TIME_print (bio, tm))
449       (void) BIO_read (bio, buf, sizeof (buf));
450     BIO_free (bio);
451   }
452
453   return buf;
454 }
455
456 static int check_certificate_by_signer (X509 * peercert)
457 {
458   X509_STORE_CTX xsc;
459   X509_STORE *ctx;
460   int pass = 0;
461
462   ctx = X509_STORE_new ();
463   if (ctx == NULL)
464     return 0;
465
466   if (option (OPTSSLSYSTEMCERTS)) {
467     if (X509_STORE_set_default_paths (ctx))
468       pass++;
469   }
470
471   if (X509_STORE_load_locations (ctx, SslCertFile, NULL))
472     pass++;
473
474   if (pass == 0) {
475     /* nothing to do */
476     X509_STORE_free (ctx);
477     return 0;
478   }
479
480   X509_STORE_CTX_init (&xsc, ctx, peercert, NULL);
481
482   pass = (X509_verify_cert (&xsc) > 0);
483   X509_STORE_CTX_cleanup (&xsc);
484   X509_STORE_free (ctx);
485
486   return pass;
487 }
488
489 static int check_certificate_by_digest (X509 * peercert)
490 {
491   unsigned char peermd[EVP_MAX_MD_SIZE];
492   unsigned int peermdlen;
493   X509 *cert = NULL;
494   int pass = 0;
495   FILE *fp;
496
497   /* expiration check */
498   if (X509_cmp_current_time (X509_get_notBefore (peercert)) >= 0) {
499     mutt_error (_("Server certificate is not yet valid"));
500     mutt_sleep (2);
501     return 0;
502   }
503   if (X509_cmp_current_time (X509_get_notAfter (peercert)) <= 0) {
504     mutt_error (_("Server certificate has expired"));
505     mutt_sleep (2);
506     return 0;
507   }
508
509   if ((fp = fopen (SslCertFile, "rt")) == NULL)
510     return 0;
511
512   if (!X509_digest (peercert, EVP_sha1 (), peermd, &peermdlen)) {
513     fclose (fp);
514     return 0;
515   }
516
517   while ((cert = READ_X509_KEY (fp, &cert)) != NULL) {
518     pass = compare_certificates (cert, peercert, peermd, peermdlen) ? 0 : 1;
519     if (pass)
520       break;
521   }
522   X509_free (cert);
523   fclose (fp);
524
525   return pass;
526 }
527
528 static int ssl_check_certificate (sslsockdata * data)
529 {
530   char *part[] = { "/CN=", "/Email=", "/O=", "/OU=", "/L=", "/ST=", "/C=" };
531   char helpstr[SHORT_STRING];
532   char buf[SHORT_STRING];
533   MUTTMENU *menu;
534   int done, row, i;
535   FILE *fp;
536   char *name = NULL, *c;
537
538   /* check session cache first */
539   if (check_certificate_cache (data->cert)) {
540     return 1;
541   }
542
543   if (check_certificate_by_signer (data->cert)) {
544     return 1;
545   }
546
547   /* automatic check from user's database */
548   if (SslCertFile && check_certificate_by_digest (data->cert)) {
549     return 1;
550   }
551
552   /* interactive check from user */
553   menu = mutt_new_menu ();
554   menu->max = 19;
555   menu->dialog = p_new(char *, menu->max);
556   for (i = 0; i < menu->max; i++)
557     menu->dialog[i] = p_new(char, SHORT_STRING);
558
559   row = 0;
560   m_strcpy(menu->dialog[row], SHORT_STRING,
561            _("This certificate belongs to:"));
562   row++;
563   name = X509_NAME_oneline (X509_get_subject_name (data->cert),
564                             buf, sizeof (buf));
565   for (i = 0; i < 5; i++) {
566     c = x509_get_part (name, part[i]);
567     snprintf (menu->dialog[row++], SHORT_STRING, "   %s", c);
568   }
569
570   row++;
571   m_strcpy(menu->dialog[row], SHORT_STRING,
572            _("This certificate was issued by:"));
573   row++;
574   name = X509_NAME_oneline (X509_get_issuer_name (data->cert),
575                             buf, sizeof (buf));
576   for (i = 0; i < 5; i++) {
577     c = x509_get_part (name, part[i]);
578     snprintf (menu->dialog[row++], SHORT_STRING, "   %s", c);
579   }
580
581   row++;
582   snprintf (menu->dialog[row++], SHORT_STRING, "%s",
583             _("This certificate is valid"));
584   snprintf (menu->dialog[row++], SHORT_STRING, _("   from %s"),
585             asn1time_to_string (X509_get_notBefore (data->cert)));
586   snprintf (menu->dialog[row++], SHORT_STRING, _("     to %s"),
587             asn1time_to_string (X509_get_notAfter (data->cert)));
588
589   row++;
590   buf[0] = '\0';
591   x509_fingerprint (buf, sizeof (buf), data->cert);
592   snprintf (menu->dialog[row++], SHORT_STRING, _("Fingerprint: %s"), buf);
593
594   menu->title = _("SSL Certificate check");
595
596   if (SslCertFile && X509_cmp_current_time (X509_get_notAfter (data->cert)) >= 0
597       && X509_cmp_current_time (X509_get_notBefore (data->cert)) < 0) {
598     menu->prompt = _("(r)eject, accept (o)nce, (a)ccept always");
599     menu->keys = _("roa");
600   }
601   else {
602     menu->prompt = _("(r)eject, accept (o)nce");
603     menu->keys = _("ro");
604   }
605
606   helpstr[0] = '\0';
607   mutt_make_help (buf, sizeof (buf), _("Exit  "), MENU_GENERIC, OP_EXIT);
608   m_strcat(helpstr, sizeof(helpstr), buf);
609   mutt_make_help (buf, sizeof (buf), _("Help"), MENU_GENERIC, OP_HELP);
610   m_strcat(helpstr, sizeof(helpstr), buf);
611   menu->help = helpstr;
612
613   done = 0;
614   set_option (OPTUNBUFFEREDINPUT);
615   while (!done) {
616     switch (mutt_menuLoop (menu)) {
617     case -1:                   /* abort */
618     case OP_MAX + 1:           /* reject */
619     case OP_EXIT:
620       done = 1;
621       break;
622     case OP_MAX + 3:           /* accept always */
623       done = 0;
624       if ((fp = fopen (SslCertFile, "a"))) {
625         if (PEM_write_X509 (fp, data->cert))
626           done = 1;
627         fclose (fp);
628       }
629       if (!done) {
630         mutt_error (_("Warning: Couldn't save certificate"));
631         mutt_sleep (2);
632       }
633       else {
634         mutt_message (_("Certificate saved"));
635         mutt_sleep (0);
636       }
637       /* fall through */
638     case OP_MAX + 2:           /* accept once */
639       done = 2;
640       /* keep a handle on accepted certificates in case we want to
641        * open up another connection to the same server in this session */
642       SslSessionCerts = mutt_add_list_n (SslSessionCerts, &data->cert,
643                                          sizeof (X509 **));
644       break;
645     }
646   }
647   unset_option (OPTUNBUFFEREDINPUT);
648   mutt_menuDestroy (&menu);
649   return (done == 2);
650 }
651
652 static void ssl_get_client_cert (sslsockdata * ssldata, CONNECTION * conn)
653 {
654   if (SslClientCert) {
655     SSL_CTX_set_default_passwd_cb_userdata (ssldata->ctx, &conn->account);
656     SSL_CTX_set_default_passwd_cb (ssldata->ctx, ssl_passwd_cb);
657     SSL_CTX_use_certificate_file (ssldata->ctx, SslClientCert,
658                                   SSL_FILETYPE_PEM);
659     SSL_CTX_use_PrivateKey_file (ssldata->ctx, SslClientCert,
660                                  SSL_FILETYPE_PEM);
661   }
662 }
663
664 static int ssl_passwd_cb (char *buf, int size, int rwflag, void *userdata)
665 {
666   ACCOUNT *account = (ACCOUNT *) userdata;
667
668   if (mutt_account_getuser (account))
669     return 0;
670
671   if (mutt_account_getpass (account))
672     return 0;
673
674   return snprintf (buf, size, "%s", account->pass);
675 }
676
677 #endif /* USE_SSL */