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.
17 #include <gnutls/gnutls.h>
18 #include <gnutls/x509.h>
19 #ifdef HAVE_GNUTLS_OPENSSL_H
20 #include <gnutls/openssl.h>
23 #include <lib-lib/mem.h>
24 #include <lib-lib/str.h>
25 #include <lib-lib/macros.h>
26 #include <lib-lib/file.h>
29 #include "mutt_socket.h"
30 #include "mutt_curses.h"
31 #include "mutt_menu.h"
36 typedef struct _tlssockdata {
38 gnutls_certificate_credentials xcred;
41 /* local prototypes */
42 static int tls_socket_read (CONNECTION * conn, char *buf, size_t len);
43 static int tls_socket_write (CONNECTION * conn, const char *buf, size_t len);
44 static int tls_socket_open (CONNECTION * conn);
45 static int tls_socket_close (CONNECTION * conn);
46 static int tls_starttls_close (CONNECTION * conn);
48 static int tls_init (void);
49 static int tls_negotiate (CONNECTION * conn);
50 static int tls_check_certificate (CONNECTION * conn);
53 static int tls_init (void)
55 static unsigned char init_complete = 0;
61 err = gnutls_global_init ();
63 mutt_error (_("gnutls_global_init: %s"), gnutls_strerror (err));
72 int mutt_ssl_socket_setup (CONNECTION * conn)
77 conn->conn_open = tls_socket_open;
78 conn->conn_read = tls_socket_read;
79 conn->conn_write = tls_socket_write;
80 conn->conn_close = tls_socket_close;
85 static int tls_socket_read (CONNECTION * conn, char *buf, size_t len)
87 tlssockdata *data = conn->sockdata;
91 mutt_error (_("Error: no TLS socket open"));
96 ret = gnutls_record_recv (data->state, buf, len);
97 if (gnutls_error_is_fatal (ret) == 1) {
98 mutt_error (_("tls_socket_read (%s)"), gnutls_strerror (ret));
105 static int tls_socket_write (CONNECTION * conn, const char *buf, size_t len)
107 tlssockdata *data = conn->sockdata;
111 mutt_error (_("Error: no TLS socket open"));
116 ret = gnutls_record_send (data->state, buf, len);
117 if (gnutls_error_is_fatal (ret) == 1) {
118 mutt_error (_("tls_socket_write (%s)"), gnutls_strerror (ret));
125 static int tls_socket_open (CONNECTION * conn)
127 if (raw_socket_open (conn) < 0)
130 if (tls_negotiate (conn) < 0) {
131 tls_socket_close (conn);
138 int mutt_ssl_starttls (CONNECTION * conn)
143 if (tls_negotiate (conn) < 0)
146 conn->conn_read = tls_socket_read;
147 conn->conn_write = tls_socket_write;
148 conn->conn_close = tls_starttls_close;
153 static int protocol_priority[] = { GNUTLS_TLS1, GNUTLS_SSL3, 0 };
155 /* tls_negotiate: After TLS state has been initialised, attempt to negotiate
156 * TLS over the wire, including certificate checks. */
157 static int tls_negotiate (CONNECTION * conn)
162 data = p_new(tlssockdata, 1);
163 conn->sockdata = data;
164 err = gnutls_certificate_allocate_credentials (&data->xcred);
166 p_delete(&conn->sockdata);
167 mutt_error (_("gnutls_certificate_allocate_credentials: %s"),
168 gnutls_strerror (err));
173 gnutls_certificate_set_x509_trust_file (data->xcred, SslCertFile,
174 GNUTLS_X509_FMT_PEM);
175 /* ignore errors, maybe file doesn't exist yet */
178 gnutls_certificate_set_x509_trust_file (data->xcred, SslCACertFile,
179 GNUTLS_X509_FMT_PEM);
183 gnutls_set_x509_client_key (data->xcred, "", "");
184 gnutls_set_x509_cert_callback (data->xcred, cert_callback);
187 gnutls_init (&data->state, GNUTLS_CLIENT);
190 gnutls_transport_set_ptr (data->state, (gnutls_transport_ptr) conn->fd);
192 /* disable TLS/SSL protocols as needed */
193 if (!option (OPTTLSV1) && !option (OPTSSLV3)) {
194 mutt_error (_("All available protocols for TLS/SSL connection disabled"));
197 else if (!option (OPTTLSV1)) {
198 protocol_priority[0] = GNUTLS_SSL3;
199 protocol_priority[1] = 0;
201 else if (!option (OPTSSLV3)) {
202 protocol_priority[0] = GNUTLS_TLS1;
203 protocol_priority[1] = 0;
207 use the list set above
210 /* We use default priorities (see gnutls documentation),
211 except for protocol version */
212 gnutls_set_default_priority (data->state);
213 gnutls_protocol_set_priority (data->state, protocol_priority);
215 if (SslDHPrimeBits > 0) {
216 gnutls_dh_set_prime_bits (data->state, SslDHPrimeBits);
220 gnutls_set_cred (data->state, GNUTLS_ANON, NULL);
223 gnutls_credentials_set (data->state, GNUTLS_CRD_CERTIFICATE, data->xcred);
225 err = gnutls_handshake (data->state);
227 while (err == GNUTLS_E_AGAIN || err == GNUTLS_E_INTERRUPTED) {
228 err = gnutls_handshake (data->state);
231 if (err == GNUTLS_E_FATAL_ALERT_RECEIVED) {
232 mutt_error (_("gnutls_handshake: %s(%s)"), gnutls_strerror (err),
233 gnutls_alert_get_name (gnutls_alert_get (data->state)));
236 mutt_error (_("gnutls_handshake: %s"), gnutls_strerror (err));
242 if (!tls_check_certificate (conn))
245 /* set Security Strength Factor (SSF) for SASL */
246 /* NB: gnutls_cipher_get_key_size() returns key length in bytes */
248 gnutls_cipher_get_key_size (gnutls_cipher_get (data->state)) * 8;
250 mutt_message (_("SSL/TLS connection using %s (%s/%s/%s)"),
251 gnutls_protocol_get_name (gnutls_protocol_get_version
253 gnutls_kx_get_name (gnutls_kx_get (data->state)),
254 gnutls_cipher_get_name (gnutls_cipher_get (data->state)),
255 gnutls_mac_get_name (gnutls_mac_get (data->state)));
261 gnutls_certificate_free_credentials (data->xcred);
262 gnutls_deinit (data->state);
263 p_delete(&conn->sockdata);
267 static int tls_socket_close (CONNECTION * conn)
269 tlssockdata *data = conn->sockdata;
272 gnutls_bye (data->state, GNUTLS_SHUT_RDWR);
274 gnutls_certificate_free_credentials (data->xcred);
275 gnutls_deinit (data->state);
276 p_delete(&conn->sockdata);
279 return raw_socket_close (conn);
282 static int tls_starttls_close (CONNECTION * conn)
286 rc = tls_socket_close (conn);
287 conn->conn_read = raw_socket_read;
288 conn->conn_write = raw_socket_write;
289 conn->conn_close = raw_socket_close;
294 #define CERT_SEP "-----BEGIN"
296 /* this bit is based on read_ca_file() in gnutls */
297 static int tls_compare_certificates (const gnutls_datum * peercert)
303 gnutls_datum b64_data;
304 unsigned char *b64_data_data;
305 struct stat filestat;
307 if (stat (SslCertFile, &filestat) == -1)
310 b64_data.size = filestat.st_size + 1;
311 b64_data_data = p_new(unsigned char, b64_data.size);
312 b64_data_data[b64_data.size - 1] = '\0';
313 b64_data.data = b64_data_data;
315 fd1 = fopen (SslCertFile, "r");
320 b64_data.size = fread (b64_data.data, 1, b64_data.size, fd1);
324 ret = gnutls_pem_base64_decode_alloc (NULL, &b64_data, &cert);
326 p_delete(&b64_data_data);
330 ptr = (unsigned char *) strstr ((char*) b64_data.data, CERT_SEP) + 1;
331 ptr = (unsigned char *) strstr ((char*) ptr, CERT_SEP);
333 b64_data.size = b64_data.size - (ptr - b64_data.data);
336 if (cert.size == peercert->size) {
337 if (memcmp (cert.data, peercert->data, cert.size) == 0) {
339 gnutls_free (cert.data);
340 p_delete(&b64_data_data);
345 gnutls_free (cert.data);
346 } while (ptr != NULL);
349 p_delete(&b64_data_data);
353 static void tls_fingerprint (gnutls_digest_algorithm algo,
354 char *s, int l, const gnutls_datum * data)
356 unsigned char md[36];
362 if (gnutls_fingerprint (algo, data, (char *) md, &n) < 0) {
363 snprintf (s, l, _("[unable to calculate]"));
366 for (j = 0; j < (int) n; j++) {
369 snprintf (ch, 8, "%02X%s", md[j], (j % 2 ? " " : ""));
372 s[2 * n + n / 2 - 1] = '\0'; /* don't want trailing space */
376 static char *tls_make_date (time_t t, char *s, size_t len)
378 struct tm *l = gmtime (&t);
381 snprintf (s, len, "%s, %d %s %d %02d:%02d:%02d UTC",
382 Weekdays[l->tm_wday], l->tm_mday, Months[l->tm_mon],
383 l->tm_year + 1900, l->tm_hour, l->tm_min, l->tm_sec);
385 strfcpy (s, _("[invalid date]"), len);
390 static int tls_check_stored_hostname (const gnutls_datum * cert,
391 const char *hostname)
395 char *linestr = NULL;
399 regmatch_t pmatch[3];
401 /* try checking against names stored in stored certs file */
402 if ((fp = fopen (SslCertFile, "r"))) {
405 "^#H ([a-zA-Z0-9_\\.-]+) ([0-9A-F]{4}( [0-9A-F]{4}){7})[ \t]*$",
406 REG_ICASE | REG_EXTENDED) != 0) {
412 tls_fingerprint (GNUTLS_DIG_MD5, buf, sizeof (buf), cert);
414 mutt_read_line (linestr, &linestrsize, fp, &linenum)) != NULL) {
415 if (linestr[0] == '#' && linestr[1] == 'H') {
416 if (regexec (&preg, linestr, 3, pmatch, 0) == 0) {
417 linestr[pmatch[1].rm_eo] = '\0';
418 linestr[pmatch[2].rm_eo] = '\0';
419 if (m_strcmp(linestr + pmatch[1].rm_so, hostname) == 0 &&
420 m_strcmp(linestr + pmatch[2].rm_so, buf) == 0) {
434 /* not found a matching name */
438 static int tls_check_certificate (CONNECTION * conn)
440 tlssockdata *data = conn->sockdata;
441 gnutls_session state = data->state;
442 char helpstr[SHORT_STRING];
443 char buf[SHORT_STRING];
444 char fpbuf[SHORT_STRING];
446 char dn_common_name[SHORT_STRING];
447 char dn_email[SHORT_STRING];
448 char dn_organization[SHORT_STRING];
449 char dn_organizational_unit[SHORT_STRING];
450 char dn_locality[SHORT_STRING];
451 char dn_province[SHORT_STRING];
452 char dn_country[SHORT_STRING];
454 int done, row, i, ret;
457 const gnutls_datum *cert_list;
458 unsigned int cert_list_size = 0;
459 gnutls_certificate_status certstat;
461 gnutls_x509_crt cert;
462 gnutls_datum pemdata;
463 int certerr_expired = 0;
464 int certerr_notyetvalid = 0;
465 int certerr_hostname = 0;
466 int certerr_nottrusted = 0;
467 int certerr_revoked = 0;
468 int certerr_signernotca = 0;
470 if (gnutls_auth_get_type (state) != GNUTLS_CRD_CERTIFICATE) {
471 mutt_error (_("Unable to get certificate from peer"));
476 certstat = gnutls_certificate_verify_peers (state);
478 if (certstat == GNUTLS_E_NO_CERTIFICATE_FOUND) {
479 mutt_error (_("Unable to get certificate from peer"));
484 mutt_error (_("Certificate verification error (%s)"),
485 gnutls_strerror (certstat));
490 /* We only support X.509 certificates (not OpenPGP) at the moment */
491 if (gnutls_certificate_type_get (state) != GNUTLS_CRT_X509) {
492 mutt_error (_("Certificate is not X.509"));
497 if (gnutls_x509_crt_init (&cert) < 0) {
498 mutt_error (_("Error initialising gnutls certificate data"));
503 cert_list = gnutls_certificate_get_peers (state, &cert_list_size);
505 mutt_error (_("Unable to get certificate from peer"));
510 /* FIXME: Currently only check first certificate in chain. */
511 if (gnutls_x509_crt_import (cert, &cert_list[0], GNUTLS_X509_FMT_DER) < 0) {
512 mutt_error (_("Error processing certificate data"));
517 if (gnutls_x509_crt_get_expiration_time (cert) < time (NULL)) {
521 if (gnutls_x509_crt_get_activation_time (cert) > time (NULL)) {
522 certerr_notyetvalid = 1;
525 if (!gnutls_x509_crt_check_hostname (cert, conn->account.host) &&
526 !tls_check_stored_hostname (&cert_list[0], conn->account.host)) {
527 certerr_hostname = 1;
530 /* see whether certificate is in our cache (certificates file) */
531 if (tls_compare_certificates (&cert_list[0])) {
532 if (certstat & GNUTLS_CERT_INVALID) {
533 /* doesn't matter - have decided is valid because server
534 certificate is in our trusted cache */
535 certstat ^= GNUTLS_CERT_INVALID;
538 if (certstat & GNUTLS_CERT_SIGNER_NOT_FOUND) {
539 /* doesn't matter that we haven't found the signer, since
540 certificate is in our trusted cache */
541 certstat ^= GNUTLS_CERT_SIGNER_NOT_FOUND;
544 if (certstat & GNUTLS_CERT_SIGNER_NOT_CA) {
545 /* Hmm. Not really sure how to handle this, but let's say
546 that we don't care if the CA certificate hasn't got the
547 correct X.509 basic constraints if server certificate is
549 certstat ^= GNUTLS_CERT_SIGNER_NOT_CA;
554 if (certstat & GNUTLS_CERT_REVOKED) {
556 certstat ^= GNUTLS_CERT_REVOKED;
559 if (certstat & GNUTLS_CERT_INVALID) {
560 certerr_nottrusted = 1;
561 certstat ^= GNUTLS_CERT_INVALID;
564 if (certstat & GNUTLS_CERT_SIGNER_NOT_FOUND) {
565 /* NB: already cleared if cert in cache */
566 certerr_nottrusted = 1;
567 certstat ^= GNUTLS_CERT_SIGNER_NOT_FOUND;
570 if (certstat & GNUTLS_CERT_SIGNER_NOT_CA) {
571 /* NB: already cleared if cert in cache */
572 certerr_signernotca = 1;
573 certstat ^= GNUTLS_CERT_SIGNER_NOT_CA;
576 /* OK if signed by (or is) a trusted certificate */
577 /* we've been zeroing the interesting bits in certstat -
578 don't return OK if there are any unhandled bits we don't
580 if (!(certerr_expired || certerr_notyetvalid ||
581 certerr_hostname || certerr_nottrusted) && certstat == 0) {
582 gnutls_x509_crt_deinit (cert);
587 /* interactive check from user */
588 menu = mutt_new_menu ();
590 menu->dialog = p_new(char*, menu->max);
591 for (i = 0; i < menu->max; i++)
592 menu->dialog[i] = p_new(char, SHORT_STRING);
595 strfcpy (menu->dialog[row], _("This certificate belongs to:"),
599 buflen = sizeof (dn_common_name);
600 if (gnutls_x509_crt_get_dn_by_oid (cert, GNUTLS_OID_X520_COMMON_NAME, 0, 0,
601 dn_common_name, &buflen) != 0)
602 dn_common_name[0] = '\0';
603 buflen = sizeof (dn_email);
604 if (gnutls_x509_crt_get_dn_by_oid (cert, GNUTLS_OID_PKCS9_EMAIL, 0, 0,
605 dn_email, &buflen) != 0)
607 buflen = sizeof (dn_organization);
608 if (gnutls_x509_crt_get_dn_by_oid
609 (cert, GNUTLS_OID_X520_ORGANIZATION_NAME, 0, 0, dn_organization,
611 dn_organization[0] = '\0';
612 buflen = sizeof (dn_organizational_unit);
613 if (gnutls_x509_crt_get_dn_by_oid
614 (cert, GNUTLS_OID_X520_ORGANIZATIONAL_UNIT_NAME, 0, 0,
615 dn_organizational_unit, &buflen) != 0)
616 dn_organizational_unit[0] = '\0';
617 buflen = sizeof (dn_locality);
618 if (gnutls_x509_crt_get_dn_by_oid
619 (cert, GNUTLS_OID_X520_LOCALITY_NAME, 0, 0, dn_locality, &buflen) != 0)
620 dn_locality[0] = '\0';
621 buflen = sizeof (dn_province);
622 if (gnutls_x509_crt_get_dn_by_oid
623 (cert, GNUTLS_OID_X520_STATE_OR_PROVINCE_NAME, 0, 0, dn_province,
625 dn_province[0] = '\0';
626 buflen = sizeof (dn_country);
627 if (gnutls_x509_crt_get_dn_by_oid (cert, GNUTLS_OID_X520_COUNTRY_NAME, 0, 0,
628 dn_country, &buflen) != 0)
629 dn_country[0] = '\0';
631 snprintf (menu->dialog[row++], SHORT_STRING, " %s %s", dn_common_name,
633 snprintf (menu->dialog[row++], SHORT_STRING, " %s", dn_organization);
634 snprintf (menu->dialog[row++], SHORT_STRING, " %s",
635 dn_organizational_unit);
636 snprintf (menu->dialog[row++], SHORT_STRING, " %s %s %s", dn_locality,
637 dn_province, dn_country);
640 strfcpy (menu->dialog[row], _("This certificate was issued by:"),
644 buflen = sizeof (dn_common_name);
645 if (gnutls_x509_crt_get_issuer_dn_by_oid
646 (cert, GNUTLS_OID_X520_COMMON_NAME, 0, 0, dn_common_name, &buflen) != 0)
647 dn_common_name[0] = '\0';
648 buflen = sizeof (dn_email);
649 if (gnutls_x509_crt_get_issuer_dn_by_oid
650 (cert, GNUTLS_OID_PKCS9_EMAIL, 0, 0, dn_email, &buflen) != 0)
652 buflen = sizeof (dn_organization);
653 if (gnutls_x509_crt_get_issuer_dn_by_oid
654 (cert, GNUTLS_OID_X520_ORGANIZATION_NAME, 0, 0, dn_organization,
656 dn_organization[0] = '\0';
657 buflen = sizeof (dn_organizational_unit);
658 if (gnutls_x509_crt_get_issuer_dn_by_oid
659 (cert, GNUTLS_OID_X520_ORGANIZATIONAL_UNIT_NAME, 0, 0,
660 dn_organizational_unit, &buflen) != 0)
661 dn_organizational_unit[0] = '\0';
662 buflen = sizeof (dn_locality);
663 if (gnutls_x509_crt_get_issuer_dn_by_oid
664 (cert, GNUTLS_OID_X520_LOCALITY_NAME, 0, 0, dn_locality, &buflen) != 0)
665 dn_locality[0] = '\0';
666 buflen = sizeof (dn_province);
667 if (gnutls_x509_crt_get_issuer_dn_by_oid
668 (cert, GNUTLS_OID_X520_STATE_OR_PROVINCE_NAME, 0, 0, dn_province,
670 dn_province[0] = '\0';
671 buflen = sizeof (dn_country);
672 if (gnutls_x509_crt_get_issuer_dn_by_oid
673 (cert, GNUTLS_OID_X520_COUNTRY_NAME, 0, 0, dn_country, &buflen) != 0)
674 dn_country[0] = '\0';
676 snprintf (menu->dialog[row++], SHORT_STRING, " %s %s", dn_common_name,
678 snprintf (menu->dialog[row++], SHORT_STRING, " %s", dn_organization);
679 snprintf (menu->dialog[row++], SHORT_STRING, " %s",
680 dn_organizational_unit);
681 snprintf (menu->dialog[row++], SHORT_STRING, " %s %s %s", dn_locality,
682 dn_province, dn_country);
685 snprintf (menu->dialog[row++], SHORT_STRING,
686 _("This certificate is valid"));
688 t = gnutls_x509_crt_get_activation_time (cert);
689 snprintf (menu->dialog[row++], SHORT_STRING, _(" from %s"),
690 tls_make_date (t, datestr, 30));
692 t = gnutls_x509_crt_get_expiration_time (cert);
693 snprintf (menu->dialog[row++], SHORT_STRING, _(" to %s"),
694 tls_make_date (t, datestr, 30));
697 tls_fingerprint (GNUTLS_DIG_SHA, fpbuf, sizeof (fpbuf), &cert_list[0]);
698 snprintf (menu->dialog[row++], SHORT_STRING, _("SHA1 Fingerprint: %s"),
701 tls_fingerprint (GNUTLS_DIG_MD5, fpbuf, sizeof (fpbuf), &cert_list[0]);
702 snprintf (menu->dialog[row++], SHORT_STRING, _("MD5 Fingerprint: %s"),
705 if (certerr_notyetvalid) {
707 strfcpy (menu->dialog[row],
708 _("WARNING: Server certificate is not yet valid"), SHORT_STRING);
710 if (certerr_expired) {
712 strfcpy (menu->dialog[row], _("WARNING: Server certificate has expired"),
715 if (certerr_revoked) {
717 strfcpy (menu->dialog[row],
718 _("WARNING: Server certificate has been revoked"), SHORT_STRING);
720 if (certerr_hostname) {
722 strfcpy (menu->dialog[row],
723 _("WARNING: Server hostname does not match certificate"),
726 if (certerr_signernotca) {
728 strfcpy (menu->dialog[row],
729 _("WARNING: Signer of server certificate is not a CA"),
733 menu->title = _("TLS/SSL Certificate check");
734 /* certificates with bad dates, or that are revoked, must be
735 accepted manually each and every time */
736 if (SslCertFile && !certerr_expired && !certerr_notyetvalid
737 && !certerr_revoked) {
738 menu->prompt = _("(r)eject, accept (o)nce, (a)ccept always");
739 menu->keys = _("roa");
742 menu->prompt = _("(r)eject, accept (o)nce");
743 menu->keys = _("ro");
747 mutt_make_help (buf, sizeof (buf), _("Exit "), MENU_GENERIC, OP_EXIT);
748 strncat (helpstr, buf, sizeof (helpstr));
749 mutt_make_help (buf, sizeof (buf), _("Help"), MENU_GENERIC, OP_HELP);
750 strncat (helpstr, buf, sizeof (helpstr));
751 menu->help = helpstr;
754 set_option (OPTUNBUFFEREDINPUT);
756 switch (mutt_menuLoop (menu)) {
758 case OP_MAX + 1: /* reject */
762 case OP_MAX + 3: /* accept always */
764 if ((fp = fopen (SslCertFile, "a"))) {
765 /* save hostname if necessary */
766 if (certerr_hostname) {
767 fprintf (fp, "#H %s %s\n", conn->account.host, fpbuf);
770 if (certerr_nottrusted) {
772 ret = gnutls_pem_base64_encode_alloc ("CERTIFICATE", &cert_list[0],
775 if (fwrite (pemdata.data, pemdata.size, 1, fp) == 1) {
778 gnutls_free (pemdata.data);
784 mutt_error (_("Warning: Couldn't save certificate"));
788 mutt_message (_("Certificate saved"));
792 case OP_MAX + 2: /* accept once */
797 unset_option (OPTUNBUFFEREDINPUT);
798 mutt_menuDestroy (&menu);
799 gnutls_x509_crt_deinit (cert);
803 #endif /* USE_GNUTLS */