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>
28 #include "mutt_socket.h"
29 #include "mutt_curses.h"
30 #include "mutt_menu.h"
35 typedef struct _tlssockdata {
37 gnutls_certificate_credentials xcred;
40 /* local prototypes */
41 static int tls_socket_read (CONNECTION * conn, char *buf, size_t len);
42 static int tls_socket_write (CONNECTION * conn, const char *buf, size_t len);
43 static int tls_socket_open (CONNECTION * conn);
44 static int tls_socket_close (CONNECTION * conn);
45 static int tls_starttls_close (CONNECTION * conn);
47 static int tls_init (void);
48 static int tls_negotiate (CONNECTION * conn);
49 static int tls_check_certificate (CONNECTION * conn);
52 static int tls_init (void)
54 static unsigned char init_complete = 0;
60 err = gnutls_global_init ();
62 mutt_error (_("gnutls_global_init: %s"), gnutls_strerror (err));
71 int mutt_ssl_socket_setup (CONNECTION * conn)
76 conn->conn_open = tls_socket_open;
77 conn->conn_read = tls_socket_read;
78 conn->conn_write = tls_socket_write;
79 conn->conn_close = tls_socket_close;
84 static int tls_socket_read (CONNECTION * conn, char *buf, size_t len)
86 tlssockdata *data = conn->sockdata;
90 mutt_error (_("Error: no TLS socket open"));
95 ret = gnutls_record_recv (data->state, buf, len);
96 if (gnutls_error_is_fatal (ret) == 1) {
97 mutt_error (_("tls_socket_read (%s)"), gnutls_strerror (ret));
104 static int tls_socket_write (CONNECTION * conn, const char *buf, size_t len)
106 tlssockdata *data = conn->sockdata;
110 mutt_error (_("Error: no TLS socket open"));
115 ret = gnutls_record_send (data->state, buf, len);
116 if (gnutls_error_is_fatal (ret) == 1) {
117 mutt_error (_("tls_socket_write (%s)"), gnutls_strerror (ret));
124 static int tls_socket_open (CONNECTION * conn)
126 if (raw_socket_open (conn) < 0)
129 if (tls_negotiate (conn) < 0) {
130 tls_socket_close (conn);
137 int mutt_ssl_starttls (CONNECTION * conn)
142 if (tls_negotiate (conn) < 0)
145 conn->conn_read = tls_socket_read;
146 conn->conn_write = tls_socket_write;
147 conn->conn_close = tls_starttls_close;
152 static int protocol_priority[] = { GNUTLS_TLS1, GNUTLS_SSL3, 0 };
154 /* tls_negotiate: After TLS state has been initialised, attempt to negotiate
155 * TLS over the wire, including certificate checks. */
156 static int tls_negotiate (CONNECTION * conn)
161 data = p_new(tlssockdata, 1);
162 conn->sockdata = data;
163 err = gnutls_certificate_allocate_credentials (&data->xcred);
165 p_delete(&conn->sockdata);
166 mutt_error (_("gnutls_certificate_allocate_credentials: %s"),
167 gnutls_strerror (err));
172 gnutls_certificate_set_x509_trust_file (data->xcred, SslCertFile,
173 GNUTLS_X509_FMT_PEM);
174 /* ignore errors, maybe file doesn't exist yet */
177 gnutls_certificate_set_x509_trust_file (data->xcred, SslCACertFile,
178 GNUTLS_X509_FMT_PEM);
182 gnutls_set_x509_client_key (data->xcred, "", "");
183 gnutls_set_x509_cert_callback (data->xcred, cert_callback);
186 gnutls_init (&data->state, GNUTLS_CLIENT);
189 gnutls_transport_set_ptr (data->state, (gnutls_transport_ptr) conn->fd);
191 /* disable TLS/SSL protocols as needed */
192 if (!option (OPTTLSV1) && !option (OPTSSLV3)) {
193 mutt_error (_("All available protocols for TLS/SSL connection disabled"));
196 else if (!option (OPTTLSV1)) {
197 protocol_priority[0] = GNUTLS_SSL3;
198 protocol_priority[1] = 0;
200 else if (!option (OPTSSLV3)) {
201 protocol_priority[0] = GNUTLS_TLS1;
202 protocol_priority[1] = 0;
206 use the list set above
209 /* We use default priorities (see gnutls documentation),
210 except for protocol version */
211 gnutls_set_default_priority (data->state);
212 gnutls_protocol_set_priority (data->state, protocol_priority);
214 if (SslDHPrimeBits > 0) {
215 gnutls_dh_set_prime_bits (data->state, SslDHPrimeBits);
219 gnutls_set_cred (data->state, GNUTLS_ANON, NULL);
222 gnutls_credentials_set (data->state, GNUTLS_CRD_CERTIFICATE, data->xcred);
224 err = gnutls_handshake (data->state);
226 while (err == GNUTLS_E_AGAIN || err == GNUTLS_E_INTERRUPTED) {
227 err = gnutls_handshake (data->state);
230 if (err == GNUTLS_E_FATAL_ALERT_RECEIVED) {
231 mutt_error (_("gnutls_handshake: %s(%s)"), gnutls_strerror (err),
232 gnutls_alert_get_name (gnutls_alert_get (data->state)));
235 mutt_error (_("gnutls_handshake: %s"), gnutls_strerror (err));
241 if (!tls_check_certificate (conn))
244 /* set Security Strength Factor (SSF) for SASL */
245 /* NB: gnutls_cipher_get_key_size() returns key length in bytes */
247 gnutls_cipher_get_key_size (gnutls_cipher_get (data->state)) * 8;
249 mutt_message (_("SSL/TLS connection using %s (%s/%s/%s)"),
250 gnutls_protocol_get_name (gnutls_protocol_get_version
252 gnutls_kx_get_name (gnutls_kx_get (data->state)),
253 gnutls_cipher_get_name (gnutls_cipher_get (data->state)),
254 gnutls_mac_get_name (gnutls_mac_get (data->state)));
260 gnutls_certificate_free_credentials (data->xcred);
261 gnutls_deinit (data->state);
262 p_delete(&conn->sockdata);
266 static int tls_socket_close (CONNECTION * conn)
268 tlssockdata *data = conn->sockdata;
271 gnutls_bye (data->state, GNUTLS_SHUT_RDWR);
273 gnutls_certificate_free_credentials (data->xcred);
274 gnutls_deinit (data->state);
275 p_delete(&conn->sockdata);
278 return raw_socket_close (conn);
281 static int tls_starttls_close (CONNECTION * conn)
285 rc = tls_socket_close (conn);
286 conn->conn_read = raw_socket_read;
287 conn->conn_write = raw_socket_write;
288 conn->conn_close = raw_socket_close;
293 #define CERT_SEP "-----BEGIN"
295 /* this bit is based on read_ca_file() in gnutls */
296 static int tls_compare_certificates (const gnutls_datum * peercert)
302 gnutls_datum b64_data;
303 unsigned char *b64_data_data;
304 struct stat filestat;
306 if (stat (SslCertFile, &filestat) == -1)
309 b64_data.size = filestat.st_size + 1;
310 b64_data_data = p_new(unsigned char, b64_data.size);
311 b64_data_data[b64_data.size - 1] = '\0';
312 b64_data.data = b64_data_data;
314 fd1 = fopen (SslCertFile, "r");
319 b64_data.size = fread (b64_data.data, 1, b64_data.size, fd1);
323 ret = gnutls_pem_base64_decode_alloc (NULL, &b64_data, &cert);
325 p_delete(&b64_data_data);
329 ptr = (unsigned char *) strstr ((char*) b64_data.data, CERT_SEP) + 1;
330 ptr = (unsigned char *) strstr ((char*) ptr, CERT_SEP);
332 b64_data.size = b64_data.size - (ptr - b64_data.data);
335 if (cert.size == peercert->size) {
336 if (memcmp (cert.data, peercert->data, cert.size) == 0) {
338 gnutls_free (cert.data);
339 p_delete(&b64_data_data);
344 gnutls_free (cert.data);
345 } while (ptr != NULL);
348 p_delete(&b64_data_data);
352 static void tls_fingerprint (gnutls_digest_algorithm algo,
353 char *s, int l, const gnutls_datum * data)
355 unsigned char md[36];
361 if (gnutls_fingerprint (algo, data, (char *) md, &n) < 0) {
362 snprintf (s, l, _("[unable to calculate]"));
365 for (j = 0; j < (int) n; j++) {
368 snprintf (ch, 8, "%02X%s", md[j], (j % 2 ? " " : ""));
371 s[2 * n + n / 2 - 1] = '\0'; /* don't want trailing space */
375 static char *tls_make_date (time_t t, char *s, size_t len)
377 struct tm *l = gmtime (&t);
380 snprintf (s, len, "%s, %d %s %d %02d:%02d:%02d UTC",
381 Weekdays[l->tm_wday], l->tm_mday, Months[l->tm_mon],
382 l->tm_year + 1900, l->tm_hour, l->tm_min, l->tm_sec);
384 strfcpy (s, _("[invalid date]"), len);
389 static int tls_check_stored_hostname (const gnutls_datum * cert,
390 const char *hostname)
394 char *linestr = NULL;
398 regmatch_t pmatch[3];
400 /* try checking against names stored in stored certs file */
401 if ((fp = fopen (SslCertFile, "r"))) {
404 "^#H ([a-zA-Z0-9_\\.-]+) ([0-9A-F]{4}( [0-9A-F]{4}){7})[ \t]*$",
405 REG_ICASE | REG_EXTENDED) != 0) {
411 tls_fingerprint (GNUTLS_DIG_MD5, buf, sizeof (buf), cert);
413 mutt_read_line (linestr, &linestrsize, fp, &linenum)) != NULL) {
414 if (linestr[0] == '#' && linestr[1] == 'H') {
415 if (regexec (&preg, linestr, 3, pmatch, 0) == 0) {
416 linestr[pmatch[1].rm_eo] = '\0';
417 linestr[pmatch[2].rm_eo] = '\0';
418 if (str_cmp (linestr + pmatch[1].rm_so, hostname) == 0 &&
419 str_cmp (linestr + pmatch[2].rm_so, buf) == 0) {
433 /* not found a matching name */
437 static int tls_check_certificate (CONNECTION * conn)
439 tlssockdata *data = conn->sockdata;
440 gnutls_session state = data->state;
441 char helpstr[SHORT_STRING];
442 char buf[SHORT_STRING];
443 char fpbuf[SHORT_STRING];
445 char dn_common_name[SHORT_STRING];
446 char dn_email[SHORT_STRING];
447 char dn_organization[SHORT_STRING];
448 char dn_organizational_unit[SHORT_STRING];
449 char dn_locality[SHORT_STRING];
450 char dn_province[SHORT_STRING];
451 char dn_country[SHORT_STRING];
453 int done, row, i, ret;
456 const gnutls_datum *cert_list;
457 unsigned int cert_list_size = 0;
458 gnutls_certificate_status certstat;
460 gnutls_x509_crt cert;
461 gnutls_datum pemdata;
462 int certerr_expired = 0;
463 int certerr_notyetvalid = 0;
464 int certerr_hostname = 0;
465 int certerr_nottrusted = 0;
466 int certerr_revoked = 0;
467 int certerr_signernotca = 0;
469 if (gnutls_auth_get_type (state) != GNUTLS_CRD_CERTIFICATE) {
470 mutt_error (_("Unable to get certificate from peer"));
475 certstat = gnutls_certificate_verify_peers (state);
477 if (certstat == GNUTLS_E_NO_CERTIFICATE_FOUND) {
478 mutt_error (_("Unable to get certificate from peer"));
483 mutt_error (_("Certificate verification error (%s)"),
484 gnutls_strerror (certstat));
489 /* We only support X.509 certificates (not OpenPGP) at the moment */
490 if (gnutls_certificate_type_get (state) != GNUTLS_CRT_X509) {
491 mutt_error (_("Certificate is not X.509"));
496 if (gnutls_x509_crt_init (&cert) < 0) {
497 mutt_error (_("Error initialising gnutls certificate data"));
502 cert_list = gnutls_certificate_get_peers (state, &cert_list_size);
504 mutt_error (_("Unable to get certificate from peer"));
509 /* FIXME: Currently only check first certificate in chain. */
510 if (gnutls_x509_crt_import (cert, &cert_list[0], GNUTLS_X509_FMT_DER) < 0) {
511 mutt_error (_("Error processing certificate data"));
516 if (gnutls_x509_crt_get_expiration_time (cert) < time (NULL)) {
520 if (gnutls_x509_crt_get_activation_time (cert) > time (NULL)) {
521 certerr_notyetvalid = 1;
524 if (!gnutls_x509_crt_check_hostname (cert, conn->account.host) &&
525 !tls_check_stored_hostname (&cert_list[0], conn->account.host)) {
526 certerr_hostname = 1;
529 /* see whether certificate is in our cache (certificates file) */
530 if (tls_compare_certificates (&cert_list[0])) {
531 if (certstat & GNUTLS_CERT_INVALID) {
532 /* doesn't matter - have decided is valid because server
533 certificate is in our trusted cache */
534 certstat ^= GNUTLS_CERT_INVALID;
537 if (certstat & GNUTLS_CERT_SIGNER_NOT_FOUND) {
538 /* doesn't matter that we haven't found the signer, since
539 certificate is in our trusted cache */
540 certstat ^= GNUTLS_CERT_SIGNER_NOT_FOUND;
543 if (certstat & GNUTLS_CERT_SIGNER_NOT_CA) {
544 /* Hmm. Not really sure how to handle this, but let's say
545 that we don't care if the CA certificate hasn't got the
546 correct X.509 basic constraints if server certificate is
548 certstat ^= GNUTLS_CERT_SIGNER_NOT_CA;
553 if (certstat & GNUTLS_CERT_REVOKED) {
555 certstat ^= GNUTLS_CERT_REVOKED;
558 if (certstat & GNUTLS_CERT_INVALID) {
559 certerr_nottrusted = 1;
560 certstat ^= GNUTLS_CERT_INVALID;
563 if (certstat & GNUTLS_CERT_SIGNER_NOT_FOUND) {
564 /* NB: already cleared if cert in cache */
565 certerr_nottrusted = 1;
566 certstat ^= GNUTLS_CERT_SIGNER_NOT_FOUND;
569 if (certstat & GNUTLS_CERT_SIGNER_NOT_CA) {
570 /* NB: already cleared if cert in cache */
571 certerr_signernotca = 1;
572 certstat ^= GNUTLS_CERT_SIGNER_NOT_CA;
575 /* OK if signed by (or is) a trusted certificate */
576 /* we've been zeroing the interesting bits in certstat -
577 don't return OK if there are any unhandled bits we don't
579 if (!(certerr_expired || certerr_notyetvalid ||
580 certerr_hostname || certerr_nottrusted) && certstat == 0) {
581 gnutls_x509_crt_deinit (cert);
586 /* interactive check from user */
587 menu = mutt_new_menu ();
589 menu->dialog = p_new(char*, menu->max);
590 for (i = 0; i < menu->max; i++)
591 menu->dialog[i] = p_new(char, SHORT_STRING);
594 strfcpy (menu->dialog[row], _("This certificate belongs to:"),
598 buflen = sizeof (dn_common_name);
599 if (gnutls_x509_crt_get_dn_by_oid (cert, GNUTLS_OID_X520_COMMON_NAME, 0, 0,
600 dn_common_name, &buflen) != 0)
601 dn_common_name[0] = '\0';
602 buflen = sizeof (dn_email);
603 if (gnutls_x509_crt_get_dn_by_oid (cert, GNUTLS_OID_PKCS9_EMAIL, 0, 0,
604 dn_email, &buflen) != 0)
606 buflen = sizeof (dn_organization);
607 if (gnutls_x509_crt_get_dn_by_oid
608 (cert, GNUTLS_OID_X520_ORGANIZATION_NAME, 0, 0, dn_organization,
610 dn_organization[0] = '\0';
611 buflen = sizeof (dn_organizational_unit);
612 if (gnutls_x509_crt_get_dn_by_oid
613 (cert, GNUTLS_OID_X520_ORGANIZATIONAL_UNIT_NAME, 0, 0,
614 dn_organizational_unit, &buflen) != 0)
615 dn_organizational_unit[0] = '\0';
616 buflen = sizeof (dn_locality);
617 if (gnutls_x509_crt_get_dn_by_oid
618 (cert, GNUTLS_OID_X520_LOCALITY_NAME, 0, 0, dn_locality, &buflen) != 0)
619 dn_locality[0] = '\0';
620 buflen = sizeof (dn_province);
621 if (gnutls_x509_crt_get_dn_by_oid
622 (cert, GNUTLS_OID_X520_STATE_OR_PROVINCE_NAME, 0, 0, dn_province,
624 dn_province[0] = '\0';
625 buflen = sizeof (dn_country);
626 if (gnutls_x509_crt_get_dn_by_oid (cert, GNUTLS_OID_X520_COUNTRY_NAME, 0, 0,
627 dn_country, &buflen) != 0)
628 dn_country[0] = '\0';
630 snprintf (menu->dialog[row++], SHORT_STRING, " %s %s", dn_common_name,
632 snprintf (menu->dialog[row++], SHORT_STRING, " %s", dn_organization);
633 snprintf (menu->dialog[row++], SHORT_STRING, " %s",
634 dn_organizational_unit);
635 snprintf (menu->dialog[row++], SHORT_STRING, " %s %s %s", dn_locality,
636 dn_province, dn_country);
639 strfcpy (menu->dialog[row], _("This certificate was issued by:"),
643 buflen = sizeof (dn_common_name);
644 if (gnutls_x509_crt_get_issuer_dn_by_oid
645 (cert, GNUTLS_OID_X520_COMMON_NAME, 0, 0, dn_common_name, &buflen) != 0)
646 dn_common_name[0] = '\0';
647 buflen = sizeof (dn_email);
648 if (gnutls_x509_crt_get_issuer_dn_by_oid
649 (cert, GNUTLS_OID_PKCS9_EMAIL, 0, 0, dn_email, &buflen) != 0)
651 buflen = sizeof (dn_organization);
652 if (gnutls_x509_crt_get_issuer_dn_by_oid
653 (cert, GNUTLS_OID_X520_ORGANIZATION_NAME, 0, 0, dn_organization,
655 dn_organization[0] = '\0';
656 buflen = sizeof (dn_organizational_unit);
657 if (gnutls_x509_crt_get_issuer_dn_by_oid
658 (cert, GNUTLS_OID_X520_ORGANIZATIONAL_UNIT_NAME, 0, 0,
659 dn_organizational_unit, &buflen) != 0)
660 dn_organizational_unit[0] = '\0';
661 buflen = sizeof (dn_locality);
662 if (gnutls_x509_crt_get_issuer_dn_by_oid
663 (cert, GNUTLS_OID_X520_LOCALITY_NAME, 0, 0, dn_locality, &buflen) != 0)
664 dn_locality[0] = '\0';
665 buflen = sizeof (dn_province);
666 if (gnutls_x509_crt_get_issuer_dn_by_oid
667 (cert, GNUTLS_OID_X520_STATE_OR_PROVINCE_NAME, 0, 0, dn_province,
669 dn_province[0] = '\0';
670 buflen = sizeof (dn_country);
671 if (gnutls_x509_crt_get_issuer_dn_by_oid
672 (cert, GNUTLS_OID_X520_COUNTRY_NAME, 0, 0, dn_country, &buflen) != 0)
673 dn_country[0] = '\0';
675 snprintf (menu->dialog[row++], SHORT_STRING, " %s %s", dn_common_name,
677 snprintf (menu->dialog[row++], SHORT_STRING, " %s", dn_organization);
678 snprintf (menu->dialog[row++], SHORT_STRING, " %s",
679 dn_organizational_unit);
680 snprintf (menu->dialog[row++], SHORT_STRING, " %s %s %s", dn_locality,
681 dn_province, dn_country);
684 snprintf (menu->dialog[row++], SHORT_STRING,
685 _("This certificate is valid"));
687 t = gnutls_x509_crt_get_activation_time (cert);
688 snprintf (menu->dialog[row++], SHORT_STRING, _(" from %s"),
689 tls_make_date (t, datestr, 30));
691 t = gnutls_x509_crt_get_expiration_time (cert);
692 snprintf (menu->dialog[row++], SHORT_STRING, _(" to %s"),
693 tls_make_date (t, datestr, 30));
696 tls_fingerprint (GNUTLS_DIG_SHA, fpbuf, sizeof (fpbuf), &cert_list[0]);
697 snprintf (menu->dialog[row++], SHORT_STRING, _("SHA1 Fingerprint: %s"),
700 tls_fingerprint (GNUTLS_DIG_MD5, fpbuf, sizeof (fpbuf), &cert_list[0]);
701 snprintf (menu->dialog[row++], SHORT_STRING, _("MD5 Fingerprint: %s"),
704 if (certerr_notyetvalid) {
706 strfcpy (menu->dialog[row],
707 _("WARNING: Server certificate is not yet valid"), SHORT_STRING);
709 if (certerr_expired) {
711 strfcpy (menu->dialog[row], _("WARNING: Server certificate has expired"),
714 if (certerr_revoked) {
716 strfcpy (menu->dialog[row],
717 _("WARNING: Server certificate has been revoked"), SHORT_STRING);
719 if (certerr_hostname) {
721 strfcpy (menu->dialog[row],
722 _("WARNING: Server hostname does not match certificate"),
725 if (certerr_signernotca) {
727 strfcpy (menu->dialog[row],
728 _("WARNING: Signer of server certificate is not a CA"),
732 menu->title = _("TLS/SSL Certificate check");
733 /* certificates with bad dates, or that are revoked, must be
734 accepted manually each and every time */
735 if (SslCertFile && !certerr_expired && !certerr_notyetvalid
736 && !certerr_revoked) {
737 menu->prompt = _("(r)eject, accept (o)nce, (a)ccept always");
738 menu->keys = _("roa");
741 menu->prompt = _("(r)eject, accept (o)nce");
742 menu->keys = _("ro");
746 mutt_make_help (buf, sizeof (buf), _("Exit "), MENU_GENERIC, OP_EXIT);
747 strncat (helpstr, buf, sizeof (helpstr));
748 mutt_make_help (buf, sizeof (buf), _("Help"), MENU_GENERIC, OP_HELP);
749 strncat (helpstr, buf, sizeof (helpstr));
750 menu->help = helpstr;
753 set_option (OPTUNBUFFEREDINPUT);
755 switch (mutt_menuLoop (menu)) {
757 case OP_MAX + 1: /* reject */
761 case OP_MAX + 3: /* accept always */
763 if ((fp = fopen (SslCertFile, "a"))) {
764 /* save hostname if necessary */
765 if (certerr_hostname) {
766 fprintf (fp, "#H %s %s\n", conn->account.host, fpbuf);
769 if (certerr_nottrusted) {
771 ret = gnutls_pem_base64_encode_alloc ("CERTIFICATE", &cert_list[0],
774 if (fwrite (pemdata.data, pemdata.size, 1, fp) == 1) {
777 gnutls_free (pemdata.data);
783 mutt_error (_("Warning: Couldn't save certificate"));
787 mutt_message (_("Certificate saved"));
791 case OP_MAX + 2: /* accept once */
796 unset_option (OPTUNBUFFEREDINPUT);
797 mutt_menuDestroy (&menu);
798 gnutls_x509_crt_deinit (cert);
802 #endif /* USE_GNUTLS */