2 * Copyright notice from original mutt:
3 * Copyright (C) 2001 Marco d'Itri <md@linux.it>
4 * Copyright (C) 2001-2004 Andrew McDonald <andrew@mcdonald.org.uk>
6 * This file is part of mutt-ng, see http://www.muttng.org/.
7 * It's licensed under the GNU General Public License,
8 * please see the file GPL in the top level source directory.
11 #include <lib-lib/lib-lib.h>
13 #include <gnutls/gnutls.h>
14 #include <gnutls/x509.h>
15 #ifdef HAVE_GNUTLS_OPENSSL_H
16 #include <gnutls/openssl.h>
19 #include <lib-ui/curses.h>
20 #include <lib-ui/menu.h>
23 #include "mutt_socket.h"
25 typedef struct _tlssockdata {
27 gnutls_certificate_credentials xcred;
30 /* local prototypes */
31 static int tls_socket_read (CONNECTION * conn, char *buf, ssize_t len);
32 static int tls_socket_write (CONNECTION * conn, const char *buf, ssize_t len);
33 static int tls_socket_open (CONNECTION * conn);
34 static int tls_socket_close (CONNECTION * conn);
35 static int tls_starttls_close (CONNECTION * conn);
37 static int tls_init (void);
38 static int tls_negotiate (CONNECTION * conn);
39 static int tls_check_certificate (CONNECTION * conn);
42 static int tls_init (void)
44 static unsigned char init_complete = 0;
50 err = gnutls_global_init ();
52 mutt_error (_("gnutls_global_init: %s"), gnutls_strerror (err));
61 int mutt_ssl_socket_setup (CONNECTION * conn)
66 conn->conn_open = tls_socket_open;
67 conn->conn_read = tls_socket_read;
68 conn->conn_write = tls_socket_write;
69 conn->conn_close = tls_socket_close;
74 static int tls_socket_read (CONNECTION * conn, char *buf, ssize_t len)
76 tlssockdata *data = conn->sockdata;
80 mutt_error (_("Error: no TLS socket open"));
85 ret = gnutls_record_recv (data->state, buf, len);
86 if (gnutls_error_is_fatal (ret) == 1) {
87 mutt_error (_("tls_socket_read (%s)"), gnutls_strerror (ret));
94 static int tls_socket_write (CONNECTION * conn, const char *buf, ssize_t len)
96 tlssockdata *data = conn->sockdata;
100 mutt_error (_("Error: no TLS socket open"));
105 ret = gnutls_record_send (data->state, buf, len);
106 if (gnutls_error_is_fatal (ret) == 1) {
107 mutt_error (_("tls_socket_write (%s)"), gnutls_strerror (ret));
114 static int tls_socket_open (CONNECTION * conn)
116 if (raw_socket_open (conn) < 0)
119 if (tls_negotiate (conn) < 0) {
120 tls_socket_close (conn);
127 int mutt_ssl_starttls (CONNECTION * conn)
132 if (tls_negotiate (conn) < 0)
135 conn->conn_read = tls_socket_read;
136 conn->conn_write = tls_socket_write;
137 conn->conn_close = tls_starttls_close;
142 static int protocol_priority[] = { GNUTLS_TLS1, GNUTLS_SSL3, 0 };
144 /* tls_negotiate: After TLS state has been initialised, attempt to negotiate
145 * TLS over the wire, including certificate checks. */
146 static int tls_negotiate (CONNECTION * conn)
151 data = p_new(tlssockdata, 1);
152 conn->sockdata = data;
153 err = gnutls_certificate_allocate_credentials (&data->xcred);
155 p_delete(&conn->sockdata);
156 mutt_error (_("gnutls_certificate_allocate_credentials: %s"),
157 gnutls_strerror (err));
162 gnutls_certificate_set_x509_trust_file (data->xcred, SslCertFile,
163 GNUTLS_X509_FMT_PEM);
164 /* ignore errors, maybe file doesn't exist yet */
167 gnutls_certificate_set_x509_trust_file (data->xcred, SslCACertFile,
168 GNUTLS_X509_FMT_PEM);
172 gnutls_set_x509_client_key (data->xcred, "", "");
173 gnutls_set_x509_cert_callback (data->xcred, cert_callback);
176 gnutls_init (&data->state, GNUTLS_CLIENT);
179 gnutls_transport_set_ptr (data->state, (gnutls_transport_ptr)(intptr_t)conn->fd);
181 /* disable TLS/SSL protocols as needed */
182 if (!option (OPTTLSV1) && !option (OPTSSLV3)) {
183 mutt_error (_("All available protocols for TLS/SSL connection disabled"));
186 else if (!option (OPTTLSV1)) {
187 protocol_priority[0] = GNUTLS_SSL3;
188 protocol_priority[1] = 0;
190 else if (!option (OPTSSLV3)) {
191 protocol_priority[0] = GNUTLS_TLS1;
192 protocol_priority[1] = 0;
196 use the list set above
199 /* We use default priorities (see gnutls documentation),
200 except for protocol version */
201 gnutls_set_default_priority (data->state);
202 gnutls_protocol_set_priority (data->state, protocol_priority);
204 if (SslDHPrimeBits > 0) {
205 gnutls_dh_set_prime_bits (data->state, SslDHPrimeBits);
209 gnutls_set_cred (data->state, GNUTLS_ANON, NULL);
212 gnutls_credentials_set (data->state, GNUTLS_CRD_CERTIFICATE, data->xcred);
214 err = gnutls_handshake (data->state);
216 while (err == GNUTLS_E_AGAIN || err == GNUTLS_E_INTERRUPTED) {
217 err = gnutls_handshake (data->state);
220 if (err == GNUTLS_E_FATAL_ALERT_RECEIVED) {
221 mutt_error (_("gnutls_handshake: %s(%s)"), gnutls_strerror (err),
222 gnutls_alert_get_name (gnutls_alert_get (data->state)));
225 mutt_error (_("gnutls_handshake: %s"), gnutls_strerror (err));
231 if (!tls_check_certificate (conn))
234 /* set Security Strength Factor (SSF) for SASL */
235 /* NB: gnutls_cipher_get_key_size() returns key length in bytes */
237 gnutls_cipher_get_key_size (gnutls_cipher_get (data->state)) * 8;
239 mutt_message (_("SSL/TLS connection using %s (%s/%s/%s)"),
240 gnutls_protocol_get_name (gnutls_protocol_get_version
242 gnutls_kx_get_name (gnutls_kx_get (data->state)),
243 gnutls_cipher_get_name (gnutls_cipher_get (data->state)),
244 gnutls_mac_get_name (gnutls_mac_get (data->state)));
250 gnutls_certificate_free_credentials (data->xcred);
251 gnutls_deinit (data->state);
252 p_delete(&conn->sockdata);
256 static int tls_socket_close (CONNECTION * conn)
258 tlssockdata *data = conn->sockdata;
261 gnutls_bye (data->state, GNUTLS_SHUT_RDWR);
263 gnutls_certificate_free_credentials (data->xcred);
264 gnutls_deinit (data->state);
265 p_delete(&conn->sockdata);
268 return raw_socket_close (conn);
271 static int tls_starttls_close (CONNECTION * conn)
275 rc = tls_socket_close (conn);
276 conn->conn_read = raw_socket_read;
277 conn->conn_write = raw_socket_write;
278 conn->conn_close = raw_socket_close;
283 #define CERT_SEP "-----BEGIN"
285 /* this bit is based on read_ca_file() in gnutls */
286 static int tls_compare_certificates (const gnutls_datum * peercert)
292 gnutls_datum b64_data;
293 unsigned char *b64_data_data;
294 struct stat filestat;
296 if (stat (SslCertFile, &filestat) == -1)
299 b64_data.size = filestat.st_size + 1;
300 b64_data_data = p_new(unsigned char, b64_data.size);
301 b64_data_data[b64_data.size - 1] = '\0';
302 b64_data.data = b64_data_data;
304 fd1 = fopen (SslCertFile, "r");
309 b64_data.size = fread (b64_data.data, 1, b64_data.size, fd1);
313 ret = gnutls_pem_base64_decode_alloc (NULL, &b64_data, &cert);
315 p_delete(&b64_data_data);
319 ptr = (unsigned char *) strstr ((char*) b64_data.data, CERT_SEP) + 1;
320 ptr = (unsigned char *) strstr ((char*) ptr, CERT_SEP);
322 b64_data.size = b64_data.size - (ptr - b64_data.data);
325 if (cert.size == peercert->size) {
326 if (memcmp (cert.data, peercert->data, cert.size) == 0) {
328 gnutls_free (cert.data);
329 p_delete(&b64_data_data);
334 gnutls_free (cert.data);
335 } while (ptr != NULL);
338 p_delete(&b64_data_data);
342 static void tls_fingerprint (gnutls_digest_algorithm algo,
343 char *s, int l, const gnutls_datum * data)
351 if (gnutls_fingerprint(algo, data, md, (size_t *)&n) < 0) {
352 snprintf (s, l, _("[unable to calculate]"));
355 for (j = 0; j < (int) n; j++) {
358 snprintf (ch, 8, "%02X%s", md[j], (j % 2 ? " " : ""));
361 s[2 * n + n / 2 - 1] = '\0'; /* don't want trailing space */
365 static char *tls_make_date (time_t t, char *s, ssize_t len)
367 struct tm *l = gmtime (&t);
370 snprintf (s, len, "%s, %d %s %d %02d:%02d:%02d UTC",
371 Weekdays[l->tm_wday], l->tm_mday, Months[l->tm_mon],
372 l->tm_year + 1900, l->tm_hour, l->tm_min, l->tm_sec);
374 m_strcpy(s, len, _("[invalid date]"));
379 static int tls_check_stored_hostname (const gnutls_datum * cert,
380 const char *hostname)
384 char *linestr = NULL;
388 regmatch_t pmatch[3];
390 /* try checking against names stored in stored certs file */
391 if ((fp = fopen (SslCertFile, "r"))) {
394 "^#H ([a-zA-Z0-9_\\.-]+) ([0-9A-F]{4}( [0-9A-F]{4}){7})[ \t]*$",
395 REG_ICASE | REG_EXTENDED) != 0) {
401 tls_fingerprint (GNUTLS_DIG_MD5, buf, sizeof (buf), cert);
403 mutt_read_line (linestr, &linestrsize, fp, &linenum)) != NULL) {
404 if (linestr[0] == '#' && linestr[1] == 'H') {
405 if (regexec (&preg, linestr, 3, pmatch, 0) == 0) {
406 linestr[pmatch[1].rm_eo] = '\0';
407 linestr[pmatch[2].rm_eo] = '\0';
408 if (m_strcmp(linestr + pmatch[1].rm_so, hostname) == 0 &&
409 m_strcmp(linestr + pmatch[2].rm_so, buf) == 0) {
423 /* not found a matching name */
427 static int tls_check_certificate (CONNECTION * conn)
429 tlssockdata *data = conn->sockdata;
430 gnutls_session state = data->state;
431 char helpstr[STRING];
435 char dn_common_name[STRING];
436 char dn_email[STRING];
437 char dn_organization[STRING];
438 char dn_organizational_unit[STRING];
439 char dn_locality[STRING];
440 char dn_province[STRING];
441 char dn_country[STRING];
443 int done, row, i, ret;
446 const gnutls_datum *cert_list;
447 unsigned int cert_list_size = 0;
448 gnutls_certificate_status_t certstat;
450 gnutls_x509_crt cert;
451 gnutls_datum pemdata;
452 int certerr_expired = 0;
453 int certerr_notyetvalid = 0;
454 int certerr_hostname = 0;
455 int certerr_nottrusted = 0;
456 int certerr_revoked = 0;
457 int certerr_signernotca = 0;
459 if (gnutls_auth_get_type (state) != GNUTLS_CRD_CERTIFICATE) {
460 mutt_error (_("Unable to get certificate from peer"));
465 if (gnutls_certificate_verify_peers2(state, &certstat) < 0) {
466 mutt_error (_("Certificate verification error (%s)"),
467 gnutls_strerror(certstat));
472 /* We only support X.509 certificates (not OpenPGP) at the moment */
473 if (gnutls_certificate_type_get (state) != GNUTLS_CRT_X509) {
474 mutt_error (_("Certificate is not X.509"));
479 if (gnutls_x509_crt_init (&cert) < 0) {
480 mutt_error (_("Error initialising gnutls certificate data"));
485 cert_list = gnutls_certificate_get_peers (state, &cert_list_size);
487 mutt_error (_("Unable to get certificate from peer"));
492 /* FIXME: Currently only check first certificate in chain. */
493 if (gnutls_x509_crt_import (cert, &cert_list[0], GNUTLS_X509_FMT_DER) < 0) {
494 mutt_error (_("Error processing certificate data"));
499 if (gnutls_x509_crt_get_expiration_time (cert) < time (NULL)) {
503 if (gnutls_x509_crt_get_activation_time (cert) > time (NULL)) {
504 certerr_notyetvalid = 1;
507 if (!gnutls_x509_crt_check_hostname (cert, conn->account.host) &&
508 !tls_check_stored_hostname (&cert_list[0], conn->account.host)) {
509 certerr_hostname = 1;
512 /* see whether certificate is in our cache (certificates file) */
513 if (tls_compare_certificates (&cert_list[0])) {
514 if (certstat & GNUTLS_CERT_INVALID) {
515 /* doesn't matter - have decided is valid because server
516 certificate is in our trusted cache */
517 certstat ^= GNUTLS_CERT_INVALID;
520 if (certstat & GNUTLS_CERT_SIGNER_NOT_FOUND) {
521 /* doesn't matter that we haven't found the signer, since
522 certificate is in our trusted cache */
523 certstat ^= GNUTLS_CERT_SIGNER_NOT_FOUND;
526 if (certstat & GNUTLS_CERT_SIGNER_NOT_CA) {
527 /* Hmm. Not really sure how to handle this, but let's say
528 that we don't care if the CA certificate hasn't got the
529 correct X.509 basic constraints if server certificate is
531 certstat ^= GNUTLS_CERT_SIGNER_NOT_CA;
536 if (certstat & GNUTLS_CERT_REVOKED) {
538 certstat ^= GNUTLS_CERT_REVOKED;
541 if (certstat & GNUTLS_CERT_INVALID) {
542 certerr_nottrusted = 1;
543 certstat ^= GNUTLS_CERT_INVALID;
546 if (certstat & GNUTLS_CERT_SIGNER_NOT_FOUND) {
547 /* NB: already cleared if cert in cache */
548 certerr_nottrusted = 1;
549 certstat ^= GNUTLS_CERT_SIGNER_NOT_FOUND;
552 if (certstat & GNUTLS_CERT_SIGNER_NOT_CA) {
553 /* NB: already cleared if cert in cache */
554 certerr_signernotca = 1;
555 certstat ^= GNUTLS_CERT_SIGNER_NOT_CA;
558 /* OK if signed by (or is) a trusted certificate */
559 /* we've been zeroing the interesting bits in certstat -
560 don't return OK if there are any unhandled bits we don't
562 if (!(certerr_expired || certerr_notyetvalid ||
563 certerr_hostname || certerr_nottrusted) && certstat == 0) {
564 gnutls_x509_crt_deinit (cert);
569 /* interactive check from user */
570 menu = mutt_new_menu ();
572 menu->dialog = p_new(char*, menu->max);
573 for (i = 0; i < menu->max; i++)
574 menu->dialog[i] = p_new(char, STRING);
577 m_strcpy(menu->dialog[row], STRING,
578 _("This certificate belongs to:"));
581 buflen = sizeof (dn_common_name);
582 if (gnutls_x509_crt_get_dn_by_oid (cert, GNUTLS_OID_X520_COMMON_NAME, 0, 0,
583 dn_common_name, (size_t *)&buflen) != 0)
584 dn_common_name[0] = '\0';
585 buflen = sizeof (dn_email);
586 if (gnutls_x509_crt_get_dn_by_oid (cert, GNUTLS_OID_PKCS9_EMAIL, 0, 0,
587 dn_email, (size_t *)&buflen) != 0)
589 buflen = sizeof (dn_organization);
590 if (gnutls_x509_crt_get_dn_by_oid
591 (cert, GNUTLS_OID_X520_ORGANIZATION_NAME, 0, 0, dn_organization,
592 (size_t *)&buflen) != 0)
593 dn_organization[0] = '\0';
594 buflen = sizeof (dn_organizational_unit);
595 if (gnutls_x509_crt_get_dn_by_oid
596 (cert, GNUTLS_OID_X520_ORGANIZATIONAL_UNIT_NAME, 0, 0,
597 dn_organizational_unit, (size_t *)&buflen) != 0)
598 dn_organizational_unit[0] = '\0';
599 buflen = sizeof (dn_locality);
600 if (gnutls_x509_crt_get_dn_by_oid
601 (cert, GNUTLS_OID_X520_LOCALITY_NAME, 0, 0, dn_locality, (size_t *)&buflen) != 0)
602 dn_locality[0] = '\0';
603 buflen = sizeof (dn_province);
604 if (gnutls_x509_crt_get_dn_by_oid
605 (cert, GNUTLS_OID_X520_STATE_OR_PROVINCE_NAME, 0, 0, dn_province,
606 (size_t *)&buflen) != 0)
607 dn_province[0] = '\0';
608 buflen = sizeof (dn_country);
609 if (gnutls_x509_crt_get_dn_by_oid (cert, GNUTLS_OID_X520_COUNTRY_NAME, 0, 0,
610 dn_country, (size_t *)&buflen) != 0)
611 dn_country[0] = '\0';
613 snprintf (menu->dialog[row++], STRING, " %s %s", dn_common_name,
615 snprintf (menu->dialog[row++], STRING, " %s", dn_organization);
616 snprintf (menu->dialog[row++], STRING, " %s",
617 dn_organizational_unit);
618 snprintf (menu->dialog[row++], STRING, " %s %s %s", dn_locality,
619 dn_province, dn_country);
622 m_strcpy(menu->dialog[row], STRING,
623 _("This certificate was issued by:"));
626 buflen = sizeof (dn_common_name);
627 if (gnutls_x509_crt_get_issuer_dn_by_oid
628 (cert, GNUTLS_OID_X520_COMMON_NAME, 0, 0, dn_common_name, (size_t *)&buflen) != 0)
629 dn_common_name[0] = '\0';
630 buflen = sizeof (dn_email);
631 if (gnutls_x509_crt_get_issuer_dn_by_oid
632 (cert, GNUTLS_OID_PKCS9_EMAIL, 0, 0, dn_email, (size_t *)&buflen) != 0)
634 buflen = sizeof (dn_organization);
635 if (gnutls_x509_crt_get_issuer_dn_by_oid
636 (cert, GNUTLS_OID_X520_ORGANIZATION_NAME, 0, 0, dn_organization,
637 (size_t *)&buflen) != 0)
638 dn_organization[0] = '\0';
639 buflen = sizeof (dn_organizational_unit);
640 if (gnutls_x509_crt_get_issuer_dn_by_oid
641 (cert, GNUTLS_OID_X520_ORGANIZATIONAL_UNIT_NAME, 0, 0,
642 dn_organizational_unit, (size_t *)&buflen) != 0)
643 dn_organizational_unit[0] = '\0';
644 buflen = sizeof (dn_locality);
645 if (gnutls_x509_crt_get_issuer_dn_by_oid
646 (cert, GNUTLS_OID_X520_LOCALITY_NAME, 0, 0, dn_locality, (size_t *)&buflen) != 0)
647 dn_locality[0] = '\0';
648 buflen = sizeof (dn_province);
649 if (gnutls_x509_crt_get_issuer_dn_by_oid
650 (cert, GNUTLS_OID_X520_STATE_OR_PROVINCE_NAME, 0, 0, dn_province,
651 (size_t *)&buflen) != 0)
652 dn_province[0] = '\0';
653 buflen = sizeof (dn_country);
654 if (gnutls_x509_crt_get_issuer_dn_by_oid
655 (cert, GNUTLS_OID_X520_COUNTRY_NAME, 0, 0, dn_country, (size_t *)&buflen) != 0)
656 dn_country[0] = '\0';
658 snprintf (menu->dialog[row++], STRING, " %s %s", dn_common_name,
660 snprintf (menu->dialog[row++], STRING, " %s", dn_organization);
661 snprintf (menu->dialog[row++], STRING, " %s",
662 dn_organizational_unit);
663 snprintf (menu->dialog[row++], STRING, " %s %s %s", dn_locality,
664 dn_province, dn_country);
667 snprintf (menu->dialog[row++], STRING,
668 _("This certificate is valid"));
670 t = gnutls_x509_crt_get_activation_time (cert);
671 snprintf (menu->dialog[row++], STRING, _(" from %s"),
672 tls_make_date (t, datestr, 30));
674 t = gnutls_x509_crt_get_expiration_time (cert);
675 snprintf (menu->dialog[row++], STRING, _(" to %s"),
676 tls_make_date (t, datestr, 30));
679 tls_fingerprint (GNUTLS_DIG_SHA, fpbuf, sizeof (fpbuf), &cert_list[0]);
680 snprintf (menu->dialog[row++], STRING, _("SHA1 Fingerprint: %s"),
683 tls_fingerprint (GNUTLS_DIG_MD5, fpbuf, sizeof (fpbuf), &cert_list[0]);
684 snprintf (menu->dialog[row++], STRING, _("MD5 Fingerprint: %s"),
687 if (certerr_notyetvalid) {
689 m_strcpy(menu->dialog[row], STRING,
690 _("WARNING: Server certificate is not yet valid"));
692 if (certerr_expired) {
694 m_strcpy(menu->dialog[row], STRING,
695 _("WARNING: Server certificate has expired"));
697 if (certerr_revoked) {
699 m_strcpy(menu->dialog[row], STRING,
700 _("WARNING: Server certificate has been revoked"));
702 if (certerr_hostname) {
704 m_strcpy(menu->dialog[row], STRING,
705 _("WARNING: Server hostname does not match certificate"));
707 if (certerr_signernotca) {
709 m_strcpy(menu->dialog[row], STRING,
710 _("WARNING: Signer of server certificate is not a CA"));
713 menu->title = _("TLS/SSL Certificate check");
714 /* certificates with bad dates, or that are revoked, must be
715 accepted manually each and every time */
716 if (SslCertFile && !certerr_expired && !certerr_notyetvalid
717 && !certerr_revoked) {
718 menu->prompt = _("(r)eject, accept (o)nce, (a)ccept always");
719 menu->keys = _("roa");
722 menu->prompt = _("(r)eject, accept (o)nce");
723 menu->keys = _("ro");
727 mutt_make_help (buf, sizeof (buf), _("Exit "), MENU_GENERIC, OP_EXIT);
728 strncat (helpstr, buf, sizeof (helpstr));
729 mutt_make_help (buf, sizeof (buf), _("Help"), MENU_GENERIC, OP_HELP);
730 strncat (helpstr, buf, sizeof (helpstr));
731 menu->help = helpstr;
734 set_option (OPTUNBUFFEREDINPUT);
736 switch (mutt_menuLoop (menu)) {
738 case OP_MAX + 1: /* reject */
742 case OP_MAX + 3: /* accept always */
744 if ((fp = fopen (SslCertFile, "a"))) {
745 /* save hostname if necessary */
746 if (certerr_hostname) {
747 fprintf (fp, "#H %s %s\n", conn->account.host, fpbuf);
750 if (certerr_nottrusted) {
752 ret = gnutls_pem_base64_encode_alloc ("CERTIFICATE", &cert_list[0],
755 if (fwrite (pemdata.data, pemdata.size, 1, fp) == 1) {
758 gnutls_free (pemdata.data);
764 mutt_error (_("Warning: Couldn't save certificate"));
768 mutt_message (_("Certificate saved"));
772 case OP_MAX + 2: /* accept once */
777 unset_option (OPTUNBUFFEREDINPUT);
778 mutt_menuDestroy (&menu);
779 gnutls_x509_crt_deinit (cert);