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>
28 #include <lib-ui/curses.h>
31 #include "mutt_socket.h"
32 #include "mutt_menu.h"
37 typedef struct _tlssockdata {
39 gnutls_certificate_credentials xcred;
42 /* local prototypes */
43 static int tls_socket_read (CONNECTION * conn, char *buf, size_t len);
44 static int tls_socket_write (CONNECTION * conn, const char *buf, size_t len);
45 static int tls_socket_open (CONNECTION * conn);
46 static int tls_socket_close (CONNECTION * conn);
47 static int tls_starttls_close (CONNECTION * conn);
49 static int tls_init (void);
50 static int tls_negotiate (CONNECTION * conn);
51 static int tls_check_certificate (CONNECTION * conn);
54 static int tls_init (void)
56 static unsigned char init_complete = 0;
62 err = gnutls_global_init ();
64 mutt_error (_("gnutls_global_init: %s"), gnutls_strerror (err));
73 int mutt_ssl_socket_setup (CONNECTION * conn)
78 conn->conn_open = tls_socket_open;
79 conn->conn_read = tls_socket_read;
80 conn->conn_write = tls_socket_write;
81 conn->conn_close = tls_socket_close;
86 static int tls_socket_read (CONNECTION * conn, char *buf, size_t len)
88 tlssockdata *data = conn->sockdata;
92 mutt_error (_("Error: no TLS socket open"));
97 ret = gnutls_record_recv (data->state, buf, len);
98 if (gnutls_error_is_fatal (ret) == 1) {
99 mutt_error (_("tls_socket_read (%s)"), gnutls_strerror (ret));
106 static int tls_socket_write (CONNECTION * conn, const char *buf, size_t len)
108 tlssockdata *data = conn->sockdata;
112 mutt_error (_("Error: no TLS socket open"));
117 ret = gnutls_record_send (data->state, buf, len);
118 if (gnutls_error_is_fatal (ret) == 1) {
119 mutt_error (_("tls_socket_write (%s)"), gnutls_strerror (ret));
126 static int tls_socket_open (CONNECTION * conn)
128 if (raw_socket_open (conn) < 0)
131 if (tls_negotiate (conn) < 0) {
132 tls_socket_close (conn);
139 int mutt_ssl_starttls (CONNECTION * conn)
144 if (tls_negotiate (conn) < 0)
147 conn->conn_read = tls_socket_read;
148 conn->conn_write = tls_socket_write;
149 conn->conn_close = tls_starttls_close;
154 static int protocol_priority[] = { GNUTLS_TLS1, GNUTLS_SSL3, 0 };
156 /* tls_negotiate: After TLS state has been initialised, attempt to negotiate
157 * TLS over the wire, including certificate checks. */
158 static int tls_negotiate (CONNECTION * conn)
163 data = p_new(tlssockdata, 1);
164 conn->sockdata = data;
165 err = gnutls_certificate_allocate_credentials (&data->xcred);
167 p_delete(&conn->sockdata);
168 mutt_error (_("gnutls_certificate_allocate_credentials: %s"),
169 gnutls_strerror (err));
174 gnutls_certificate_set_x509_trust_file (data->xcred, SslCertFile,
175 GNUTLS_X509_FMT_PEM);
176 /* ignore errors, maybe file doesn't exist yet */
179 gnutls_certificate_set_x509_trust_file (data->xcred, SslCACertFile,
180 GNUTLS_X509_FMT_PEM);
184 gnutls_set_x509_client_key (data->xcred, "", "");
185 gnutls_set_x509_cert_callback (data->xcred, cert_callback);
188 gnutls_init (&data->state, GNUTLS_CLIENT);
191 gnutls_transport_set_ptr (data->state, (gnutls_transport_ptr) conn->fd);
193 /* disable TLS/SSL protocols as needed */
194 if (!option (OPTTLSV1) && !option (OPTSSLV3)) {
195 mutt_error (_("All available protocols for TLS/SSL connection disabled"));
198 else if (!option (OPTTLSV1)) {
199 protocol_priority[0] = GNUTLS_SSL3;
200 protocol_priority[1] = 0;
202 else if (!option (OPTSSLV3)) {
203 protocol_priority[0] = GNUTLS_TLS1;
204 protocol_priority[1] = 0;
208 use the list set above
211 /* We use default priorities (see gnutls documentation),
212 except for protocol version */
213 gnutls_set_default_priority (data->state);
214 gnutls_protocol_set_priority (data->state, protocol_priority);
216 if (SslDHPrimeBits > 0) {
217 gnutls_dh_set_prime_bits (data->state, SslDHPrimeBits);
221 gnutls_set_cred (data->state, GNUTLS_ANON, NULL);
224 gnutls_credentials_set (data->state, GNUTLS_CRD_CERTIFICATE, data->xcred);
226 err = gnutls_handshake (data->state);
228 while (err == GNUTLS_E_AGAIN || err == GNUTLS_E_INTERRUPTED) {
229 err = gnutls_handshake (data->state);
232 if (err == GNUTLS_E_FATAL_ALERT_RECEIVED) {
233 mutt_error (_("gnutls_handshake: %s(%s)"), gnutls_strerror (err),
234 gnutls_alert_get_name (gnutls_alert_get (data->state)));
237 mutt_error (_("gnutls_handshake: %s"), gnutls_strerror (err));
243 if (!tls_check_certificate (conn))
246 /* set Security Strength Factor (SSF) for SASL */
247 /* NB: gnutls_cipher_get_key_size() returns key length in bytes */
249 gnutls_cipher_get_key_size (gnutls_cipher_get (data->state)) * 8;
251 mutt_message (_("SSL/TLS connection using %s (%s/%s/%s)"),
252 gnutls_protocol_get_name (gnutls_protocol_get_version
254 gnutls_kx_get_name (gnutls_kx_get (data->state)),
255 gnutls_cipher_get_name (gnutls_cipher_get (data->state)),
256 gnutls_mac_get_name (gnutls_mac_get (data->state)));
262 gnutls_certificate_free_credentials (data->xcred);
263 gnutls_deinit (data->state);
264 p_delete(&conn->sockdata);
268 static int tls_socket_close (CONNECTION * conn)
270 tlssockdata *data = conn->sockdata;
273 gnutls_bye (data->state, GNUTLS_SHUT_RDWR);
275 gnutls_certificate_free_credentials (data->xcred);
276 gnutls_deinit (data->state);
277 p_delete(&conn->sockdata);
280 return raw_socket_close (conn);
283 static int tls_starttls_close (CONNECTION * conn)
287 rc = tls_socket_close (conn);
288 conn->conn_read = raw_socket_read;
289 conn->conn_write = raw_socket_write;
290 conn->conn_close = raw_socket_close;
295 #define CERT_SEP "-----BEGIN"
297 /* this bit is based on read_ca_file() in gnutls */
298 static int tls_compare_certificates (const gnutls_datum * peercert)
304 gnutls_datum b64_data;
305 unsigned char *b64_data_data;
306 struct stat filestat;
308 if (stat (SslCertFile, &filestat) == -1)
311 b64_data.size = filestat.st_size + 1;
312 b64_data_data = p_new(unsigned char, b64_data.size);
313 b64_data_data[b64_data.size - 1] = '\0';
314 b64_data.data = b64_data_data;
316 fd1 = fopen (SslCertFile, "r");
321 b64_data.size = fread (b64_data.data, 1, b64_data.size, fd1);
325 ret = gnutls_pem_base64_decode_alloc (NULL, &b64_data, &cert);
327 p_delete(&b64_data_data);
331 ptr = (unsigned char *) strstr ((char*) b64_data.data, CERT_SEP) + 1;
332 ptr = (unsigned char *) strstr ((char*) ptr, CERT_SEP);
334 b64_data.size = b64_data.size - (ptr - b64_data.data);
337 if (cert.size == peercert->size) {
338 if (memcmp (cert.data, peercert->data, cert.size) == 0) {
340 gnutls_free (cert.data);
341 p_delete(&b64_data_data);
346 gnutls_free (cert.data);
347 } while (ptr != NULL);
350 p_delete(&b64_data_data);
354 static void tls_fingerprint (gnutls_digest_algorithm algo,
355 char *s, int l, const gnutls_datum * data)
357 unsigned char md[36];
363 if (gnutls_fingerprint (algo, data, (char *) md, &n) < 0) {
364 snprintf (s, l, _("[unable to calculate]"));
367 for (j = 0; j < (int) n; j++) {
370 snprintf (ch, 8, "%02X%s", md[j], (j % 2 ? " " : ""));
373 s[2 * n + n / 2 - 1] = '\0'; /* don't want trailing space */
377 static char *tls_make_date (time_t t, char *s, size_t len)
379 struct tm *l = gmtime (&t);
382 snprintf (s, len, "%s, %d %s %d %02d:%02d:%02d UTC",
383 Weekdays[l->tm_wday], l->tm_mday, Months[l->tm_mon],
384 l->tm_year + 1900, l->tm_hour, l->tm_min, l->tm_sec);
386 m_strcpy(s, len, _("[invalid date]"));
391 static int tls_check_stored_hostname (const gnutls_datum * cert,
392 const char *hostname)
396 char *linestr = NULL;
400 regmatch_t pmatch[3];
402 /* try checking against names stored in stored certs file */
403 if ((fp = fopen (SslCertFile, "r"))) {
406 "^#H ([a-zA-Z0-9_\\.-]+) ([0-9A-F]{4}( [0-9A-F]{4}){7})[ \t]*$",
407 REG_ICASE | REG_EXTENDED) != 0) {
413 tls_fingerprint (GNUTLS_DIG_MD5, buf, sizeof (buf), cert);
415 mutt_read_line (linestr, &linestrsize, fp, &linenum)) != NULL) {
416 if (linestr[0] == '#' && linestr[1] == 'H') {
417 if (regexec (&preg, linestr, 3, pmatch, 0) == 0) {
418 linestr[pmatch[1].rm_eo] = '\0';
419 linestr[pmatch[2].rm_eo] = '\0';
420 if (m_strcmp(linestr + pmatch[1].rm_so, hostname) == 0 &&
421 m_strcmp(linestr + pmatch[2].rm_so, buf) == 0) {
435 /* not found a matching name */
439 static int tls_check_certificate (CONNECTION * conn)
441 tlssockdata *data = conn->sockdata;
442 gnutls_session state = data->state;
443 char helpstr[SHORT_STRING];
444 char buf[SHORT_STRING];
445 char fpbuf[SHORT_STRING];
447 char dn_common_name[SHORT_STRING];
448 char dn_email[SHORT_STRING];
449 char dn_organization[SHORT_STRING];
450 char dn_organizational_unit[SHORT_STRING];
451 char dn_locality[SHORT_STRING];
452 char dn_province[SHORT_STRING];
453 char dn_country[SHORT_STRING];
455 int done, row, i, ret;
458 const gnutls_datum *cert_list;
459 unsigned int cert_list_size = 0;
460 gnutls_certificate_status certstat;
462 gnutls_x509_crt cert;
463 gnutls_datum pemdata;
464 int certerr_expired = 0;
465 int certerr_notyetvalid = 0;
466 int certerr_hostname = 0;
467 int certerr_nottrusted = 0;
468 int certerr_revoked = 0;
469 int certerr_signernotca = 0;
471 if (gnutls_auth_get_type (state) != GNUTLS_CRD_CERTIFICATE) {
472 mutt_error (_("Unable to get certificate from peer"));
477 certstat = gnutls_certificate_verify_peers (state);
479 if (certstat == GNUTLS_E_NO_CERTIFICATE_FOUND) {
480 mutt_error (_("Unable to get certificate from peer"));
485 mutt_error (_("Certificate verification error (%s)"),
486 gnutls_strerror (certstat));
491 /* We only support X.509 certificates (not OpenPGP) at the moment */
492 if (gnutls_certificate_type_get (state) != GNUTLS_CRT_X509) {
493 mutt_error (_("Certificate is not X.509"));
498 if (gnutls_x509_crt_init (&cert) < 0) {
499 mutt_error (_("Error initialising gnutls certificate data"));
504 cert_list = gnutls_certificate_get_peers (state, &cert_list_size);
506 mutt_error (_("Unable to get certificate from peer"));
511 /* FIXME: Currently only check first certificate in chain. */
512 if (gnutls_x509_crt_import (cert, &cert_list[0], GNUTLS_X509_FMT_DER) < 0) {
513 mutt_error (_("Error processing certificate data"));
518 if (gnutls_x509_crt_get_expiration_time (cert) < time (NULL)) {
522 if (gnutls_x509_crt_get_activation_time (cert) > time (NULL)) {
523 certerr_notyetvalid = 1;
526 if (!gnutls_x509_crt_check_hostname (cert, conn->account.host) &&
527 !tls_check_stored_hostname (&cert_list[0], conn->account.host)) {
528 certerr_hostname = 1;
531 /* see whether certificate is in our cache (certificates file) */
532 if (tls_compare_certificates (&cert_list[0])) {
533 if (certstat & GNUTLS_CERT_INVALID) {
534 /* doesn't matter - have decided is valid because server
535 certificate is in our trusted cache */
536 certstat ^= GNUTLS_CERT_INVALID;
539 if (certstat & GNUTLS_CERT_SIGNER_NOT_FOUND) {
540 /* doesn't matter that we haven't found the signer, since
541 certificate is in our trusted cache */
542 certstat ^= GNUTLS_CERT_SIGNER_NOT_FOUND;
545 if (certstat & GNUTLS_CERT_SIGNER_NOT_CA) {
546 /* Hmm. Not really sure how to handle this, but let's say
547 that we don't care if the CA certificate hasn't got the
548 correct X.509 basic constraints if server certificate is
550 certstat ^= GNUTLS_CERT_SIGNER_NOT_CA;
555 if (certstat & GNUTLS_CERT_REVOKED) {
557 certstat ^= GNUTLS_CERT_REVOKED;
560 if (certstat & GNUTLS_CERT_INVALID) {
561 certerr_nottrusted = 1;
562 certstat ^= GNUTLS_CERT_INVALID;
565 if (certstat & GNUTLS_CERT_SIGNER_NOT_FOUND) {
566 /* NB: already cleared if cert in cache */
567 certerr_nottrusted = 1;
568 certstat ^= GNUTLS_CERT_SIGNER_NOT_FOUND;
571 if (certstat & GNUTLS_CERT_SIGNER_NOT_CA) {
572 /* NB: already cleared if cert in cache */
573 certerr_signernotca = 1;
574 certstat ^= GNUTLS_CERT_SIGNER_NOT_CA;
577 /* OK if signed by (or is) a trusted certificate */
578 /* we've been zeroing the interesting bits in certstat -
579 don't return OK if there are any unhandled bits we don't
581 if (!(certerr_expired || certerr_notyetvalid ||
582 certerr_hostname || certerr_nottrusted) && certstat == 0) {
583 gnutls_x509_crt_deinit (cert);
588 /* interactive check from user */
589 menu = mutt_new_menu ();
591 menu->dialog = p_new(char*, menu->max);
592 for (i = 0; i < menu->max; i++)
593 menu->dialog[i] = p_new(char, SHORT_STRING);
596 m_strcpy(menu->dialog[row], SHORT_STRING,
597 _("This certificate belongs to:"));
600 buflen = sizeof (dn_common_name);
601 if (gnutls_x509_crt_get_dn_by_oid (cert, GNUTLS_OID_X520_COMMON_NAME, 0, 0,
602 dn_common_name, &buflen) != 0)
603 dn_common_name[0] = '\0';
604 buflen = sizeof (dn_email);
605 if (gnutls_x509_crt_get_dn_by_oid (cert, GNUTLS_OID_PKCS9_EMAIL, 0, 0,
606 dn_email, &buflen) != 0)
608 buflen = sizeof (dn_organization);
609 if (gnutls_x509_crt_get_dn_by_oid
610 (cert, GNUTLS_OID_X520_ORGANIZATION_NAME, 0, 0, dn_organization,
612 dn_organization[0] = '\0';
613 buflen = sizeof (dn_organizational_unit);
614 if (gnutls_x509_crt_get_dn_by_oid
615 (cert, GNUTLS_OID_X520_ORGANIZATIONAL_UNIT_NAME, 0, 0,
616 dn_organizational_unit, &buflen) != 0)
617 dn_organizational_unit[0] = '\0';
618 buflen = sizeof (dn_locality);
619 if (gnutls_x509_crt_get_dn_by_oid
620 (cert, GNUTLS_OID_X520_LOCALITY_NAME, 0, 0, dn_locality, &buflen) != 0)
621 dn_locality[0] = '\0';
622 buflen = sizeof (dn_province);
623 if (gnutls_x509_crt_get_dn_by_oid
624 (cert, GNUTLS_OID_X520_STATE_OR_PROVINCE_NAME, 0, 0, dn_province,
626 dn_province[0] = '\0';
627 buflen = sizeof (dn_country);
628 if (gnutls_x509_crt_get_dn_by_oid (cert, GNUTLS_OID_X520_COUNTRY_NAME, 0, 0,
629 dn_country, &buflen) != 0)
630 dn_country[0] = '\0';
632 snprintf (menu->dialog[row++], SHORT_STRING, " %s %s", dn_common_name,
634 snprintf (menu->dialog[row++], SHORT_STRING, " %s", dn_organization);
635 snprintf (menu->dialog[row++], SHORT_STRING, " %s",
636 dn_organizational_unit);
637 snprintf (menu->dialog[row++], SHORT_STRING, " %s %s %s", dn_locality,
638 dn_province, dn_country);
641 m_strcpy(menu->dialog[row], SHORT_STRING,
642 _("This certificate was issued by:"));
645 buflen = sizeof (dn_common_name);
646 if (gnutls_x509_crt_get_issuer_dn_by_oid
647 (cert, GNUTLS_OID_X520_COMMON_NAME, 0, 0, dn_common_name, &buflen) != 0)
648 dn_common_name[0] = '\0';
649 buflen = sizeof (dn_email);
650 if (gnutls_x509_crt_get_issuer_dn_by_oid
651 (cert, GNUTLS_OID_PKCS9_EMAIL, 0, 0, dn_email, &buflen) != 0)
653 buflen = sizeof (dn_organization);
654 if (gnutls_x509_crt_get_issuer_dn_by_oid
655 (cert, GNUTLS_OID_X520_ORGANIZATION_NAME, 0, 0, dn_organization,
657 dn_organization[0] = '\0';
658 buflen = sizeof (dn_organizational_unit);
659 if (gnutls_x509_crt_get_issuer_dn_by_oid
660 (cert, GNUTLS_OID_X520_ORGANIZATIONAL_UNIT_NAME, 0, 0,
661 dn_organizational_unit, &buflen) != 0)
662 dn_organizational_unit[0] = '\0';
663 buflen = sizeof (dn_locality);
664 if (gnutls_x509_crt_get_issuer_dn_by_oid
665 (cert, GNUTLS_OID_X520_LOCALITY_NAME, 0, 0, dn_locality, &buflen) != 0)
666 dn_locality[0] = '\0';
667 buflen = sizeof (dn_province);
668 if (gnutls_x509_crt_get_issuer_dn_by_oid
669 (cert, GNUTLS_OID_X520_STATE_OR_PROVINCE_NAME, 0, 0, dn_province,
671 dn_province[0] = '\0';
672 buflen = sizeof (dn_country);
673 if (gnutls_x509_crt_get_issuer_dn_by_oid
674 (cert, GNUTLS_OID_X520_COUNTRY_NAME, 0, 0, dn_country, &buflen) != 0)
675 dn_country[0] = '\0';
677 snprintf (menu->dialog[row++], SHORT_STRING, " %s %s", dn_common_name,
679 snprintf (menu->dialog[row++], SHORT_STRING, " %s", dn_organization);
680 snprintf (menu->dialog[row++], SHORT_STRING, " %s",
681 dn_organizational_unit);
682 snprintf (menu->dialog[row++], SHORT_STRING, " %s %s %s", dn_locality,
683 dn_province, dn_country);
686 snprintf (menu->dialog[row++], SHORT_STRING,
687 _("This certificate is valid"));
689 t = gnutls_x509_crt_get_activation_time (cert);
690 snprintf (menu->dialog[row++], SHORT_STRING, _(" from %s"),
691 tls_make_date (t, datestr, 30));
693 t = gnutls_x509_crt_get_expiration_time (cert);
694 snprintf (menu->dialog[row++], SHORT_STRING, _(" to %s"),
695 tls_make_date (t, datestr, 30));
698 tls_fingerprint (GNUTLS_DIG_SHA, fpbuf, sizeof (fpbuf), &cert_list[0]);
699 snprintf (menu->dialog[row++], SHORT_STRING, _("SHA1 Fingerprint: %s"),
702 tls_fingerprint (GNUTLS_DIG_MD5, fpbuf, sizeof (fpbuf), &cert_list[0]);
703 snprintf (menu->dialog[row++], SHORT_STRING, _("MD5 Fingerprint: %s"),
706 if (certerr_notyetvalid) {
708 m_strcpy(menu->dialog[row], SHORT_STRING,
709 _("WARNING: Server certificate is not yet valid"));
711 if (certerr_expired) {
713 m_strcpy(menu->dialog[row], SHORT_STRING,
714 _("WARNING: Server certificate has expired"));
716 if (certerr_revoked) {
718 m_strcpy(menu->dialog[row], SHORT_STRING,
719 _("WARNING: Server certificate has been revoked"));
721 if (certerr_hostname) {
723 m_strcpy(menu->dialog[row], SHORT_STRING,
724 _("WARNING: Server hostname does not match certificate"));
726 if (certerr_signernotca) {
728 m_strcpy(menu->dialog[row], SHORT_STRING,
729 _("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 */