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.
15 #include <gnutls/gnutls.h>
16 #include <gnutls/x509.h>
17 #ifdef HAVE_GNUTLS_OPENSSL_H
18 #include <gnutls/openssl.h>
22 #include "mutt_socket.h"
23 #include "mutt_curses.h"
24 #include "mutt_menu.h"
32 typedef struct _tlssockdata {
34 gnutls_certificate_credentials xcred;
37 /* local prototypes */
38 static int tls_socket_read (CONNECTION * conn, char *buf, size_t len);
39 static int tls_socket_write (CONNECTION * conn, const char *buf, size_t len);
40 static int tls_socket_open (CONNECTION * conn);
41 static int tls_socket_close (CONNECTION * conn);
42 static int tls_starttls_close (CONNECTION * conn);
44 static int tls_init (void);
45 static int tls_negotiate (CONNECTION * conn);
46 static int tls_check_certificate (CONNECTION * conn);
49 static int tls_init (void)
51 static unsigned char init_complete = 0;
57 err = gnutls_global_init ();
59 mutt_error (_("gnutls_global_init: %s"), gnutls_strerror (err));
68 int mutt_gnutls_socket_setup (CONNECTION * conn)
73 conn->conn_open = tls_socket_open;
74 conn->conn_read = tls_socket_read;
75 conn->conn_write = tls_socket_write;
76 conn->conn_close = tls_socket_close;
81 static int tls_socket_read (CONNECTION * conn, char *buf, size_t len)
83 tlssockdata *data = conn->sockdata;
87 mutt_error (_("Error: no TLS socket open"));
92 ret = gnutls_record_recv (data->state, buf, len);
93 if (gnutls_error_is_fatal (ret) == 1) {
94 mutt_error (_("tls_socket_read (%s)"), gnutls_strerror (ret));
101 static int tls_socket_write (CONNECTION * conn, const char *buf, size_t len)
103 tlssockdata *data = conn->sockdata;
107 mutt_error (_("Error: no TLS socket open"));
112 ret = gnutls_record_send (data->state, buf, len);
113 if (gnutls_error_is_fatal (ret) == 1) {
114 mutt_error (_("tls_socket_write (%s)"), gnutls_strerror (ret));
121 static int tls_socket_open (CONNECTION * conn)
123 if (raw_socket_open (conn) < 0)
126 if (tls_negotiate (conn) < 0) {
127 tls_socket_close (conn);
134 int mutt_gnutls_starttls (CONNECTION * conn)
139 if (tls_negotiate (conn) < 0)
142 conn->conn_read = tls_socket_read;
143 conn->conn_write = tls_socket_write;
144 conn->conn_close = tls_starttls_close;
149 static int protocol_priority[] = { GNUTLS_TLS1, GNUTLS_SSL3, 0 };
151 /* tls_negotiate: After TLS state has been initialised, attempt to negotiate
152 * TLS over the wire, including certificate checks. */
153 static int tls_negotiate (CONNECTION * conn)
158 data = (tlssockdata *) mem_calloc (1, sizeof (tlssockdata));
159 conn->sockdata = data;
160 err = gnutls_certificate_allocate_credentials (&data->xcred);
162 mem_free (&conn->sockdata);
163 mutt_error (_("gnutls_certificate_allocate_credentials: %s"),
164 gnutls_strerror (err));
169 gnutls_certificate_set_x509_trust_file (data->xcred, SslCertFile,
170 GNUTLS_X509_FMT_PEM);
171 /* ignore errors, maybe file doesn't exist yet */
174 gnutls_certificate_set_x509_trust_file (data->xcred, SslCACertFile,
175 GNUTLS_X509_FMT_PEM);
179 gnutls_set_x509_client_key (data->xcred, "", "");
180 gnutls_set_x509_cert_callback (data->xcred, cert_callback);
183 gnutls_init (&data->state, GNUTLS_CLIENT);
186 gnutls_transport_set_ptr (data->state, (gnutls_transport_ptr) conn->fd);
188 /* disable TLS/SSL protocols as needed */
189 if (!option (OPTTLSV1) && !option (OPTSSLV3)) {
190 mutt_error (_("All available protocols for TLS/SSL connection disabled"));
193 else if (!option (OPTTLSV1)) {
194 protocol_priority[0] = GNUTLS_SSL3;
195 protocol_priority[1] = 0;
197 else if (!option (OPTSSLV3)) {
198 protocol_priority[0] = GNUTLS_TLS1;
199 protocol_priority[1] = 0;
203 use the list set above
206 /* We use default priorities (see gnutls documentation),
207 except for protocol version */
208 gnutls_set_default_priority (data->state);
209 gnutls_protocol_set_priority (data->state, protocol_priority);
211 if (SslDHPrimeBits > 0) {
212 gnutls_dh_set_prime_bits (data->state, SslDHPrimeBits);
216 gnutls_set_cred (data->state, GNUTLS_ANON, NULL);
219 gnutls_credentials_set (data->state, GNUTLS_CRD_CERTIFICATE, data->xcred);
221 err = gnutls_handshake (data->state);
223 while (err == GNUTLS_E_AGAIN || err == GNUTLS_E_INTERRUPTED) {
224 err = gnutls_handshake (data->state);
227 if (err == GNUTLS_E_FATAL_ALERT_RECEIVED) {
228 mutt_error (_("gnutls_handshake: %s(%s)"), gnutls_strerror (err),
229 gnutls_alert_get_name (gnutls_alert_get (data->state)));
232 mutt_error (_("gnutls_handshake: %s"), gnutls_strerror (err));
238 if (!tls_check_certificate (conn))
241 /* set Security Strength Factor (SSF) for SASL */
242 /* NB: gnutls_cipher_get_key_size() returns key length in bytes */
244 gnutls_cipher_get_key_size (gnutls_cipher_get (data->state)) * 8;
246 mutt_message (_("SSL/TLS connection using %s (%s/%s/%s)"),
247 gnutls_protocol_get_name (gnutls_protocol_get_version
249 gnutls_kx_get_name (gnutls_kx_get (data->state)),
250 gnutls_cipher_get_name (gnutls_cipher_get (data->state)),
251 gnutls_mac_get_name (gnutls_mac_get (data->state)));
257 gnutls_certificate_free_credentials (data->xcred);
258 gnutls_deinit (data->state);
259 mem_free (&conn->sockdata);
263 static int tls_socket_close (CONNECTION * conn)
265 tlssockdata *data = conn->sockdata;
268 gnutls_bye (data->state, GNUTLS_SHUT_RDWR);
270 gnutls_certificate_free_credentials (data->xcred);
271 gnutls_deinit (data->state);
272 mem_free(&conn->sockdata);
275 return raw_socket_close (conn);
278 static int tls_starttls_close (CONNECTION * conn)
282 rc = tls_socket_close (conn);
283 conn->conn_read = raw_socket_read;
284 conn->conn_write = raw_socket_write;
285 conn->conn_close = raw_socket_close;
290 #define CERT_SEP "-----BEGIN"
292 /* this bit is based on read_ca_file() in gnutls */
293 static int tls_compare_certificates (const gnutls_datum * peercert)
299 gnutls_datum b64_data;
300 unsigned char *b64_data_data;
301 struct stat filestat;
303 if (stat (SslCertFile, &filestat) == -1)
306 b64_data.size = filestat.st_size + 1;
307 b64_data_data = (unsigned char *) mem_calloc (1, b64_data.size);
308 b64_data_data[b64_data.size - 1] = '\0';
309 b64_data.data = b64_data_data;
311 fd1 = fopen (SslCertFile, "r");
316 b64_data.size = fread (b64_data.data, 1, b64_data.size, fd1);
320 ret = gnutls_pem_base64_decode_alloc (NULL, &b64_data, &cert);
322 mem_free (&b64_data_data);
326 ptr = (unsigned char *) strstr ((char*) b64_data.data, CERT_SEP) + 1;
327 ptr = (unsigned char *) strstr ((char*) ptr, CERT_SEP);
329 b64_data.size = b64_data.size - (ptr - b64_data.data);
332 if (cert.size == peercert->size) {
333 if (memcmp (cert.data, peercert->data, cert.size) == 0) {
335 gnutls_free (cert.data);
336 mem_free (&b64_data_data);
341 gnutls_free (cert.data);
342 } while (ptr != NULL);
345 mem_free (&b64_data_data);
349 static void tls_fingerprint (gnutls_digest_algorithm algo,
350 char *s, int l, const gnutls_datum * data)
352 unsigned char md[36];
358 if (gnutls_fingerprint (algo, data, (char *) md, &n) < 0) {
359 snprintf (s, l, _("[unable to calculate]"));
362 for (j = 0; j < (int) n; j++) {
365 snprintf (ch, 8, "%02X%s", md[j], (j % 2 ? " " : ""));
368 s[2 * n + n / 2 - 1] = '\0'; /* don't want trailing space */
372 static char *tls_make_date (time_t t, char *s, size_t len)
374 struct tm *l = gmtime (&t);
377 snprintf (s, len, "%s, %d %s %d %02d:%02d:%02d UTC",
378 Weekdays[l->tm_wday], l->tm_mday, Months[l->tm_mon],
379 l->tm_year + 1900, l->tm_hour, l->tm_min, l->tm_sec);
381 strfcpy (s, _("[invalid date]"), len);
386 static int tls_check_stored_hostname (const gnutls_datum * cert,
387 const char *hostname)
391 char *linestr = NULL;
395 regmatch_t pmatch[3];
397 /* try checking against names stored in stored certs file */
398 if ((fp = fopen (SslCertFile, "r"))) {
401 "^#H ([a-zA-Z0-9_\\.-]+) ([0-9A-F]{4}( [0-9A-F]{4}){7})[ \t]*$",
402 REG_ICASE | REG_EXTENDED) != 0) {
408 tls_fingerprint (GNUTLS_DIG_MD5, buf, sizeof (buf), cert);
410 mutt_read_line (linestr, &linestrsize, fp, &linenum)) != NULL) {
411 if (linestr[0] == '#' && linestr[1] == 'H') {
412 if (regexec (&preg, linestr, 3, pmatch, 0) == 0) {
413 linestr[pmatch[1].rm_eo] = '\0';
414 linestr[pmatch[2].rm_eo] = '\0';
415 if (str_cmp (linestr + pmatch[1].rm_so, hostname) == 0 &&
416 str_cmp (linestr + pmatch[2].rm_so, buf) == 0) {
430 /* not found a matching name */
434 static int tls_check_certificate (CONNECTION * conn)
436 tlssockdata *data = conn->sockdata;
437 gnutls_session state = data->state;
438 char helpstr[SHORT_STRING];
439 char buf[SHORT_STRING];
440 char fpbuf[SHORT_STRING];
442 char dn_common_name[SHORT_STRING];
443 char dn_email[SHORT_STRING];
444 char dn_organization[SHORT_STRING];
445 char dn_organizational_unit[SHORT_STRING];
446 char dn_locality[SHORT_STRING];
447 char dn_province[SHORT_STRING];
448 char dn_country[SHORT_STRING];
450 int done, row, i, ret;
453 const gnutls_datum *cert_list;
454 unsigned int cert_list_size = 0;
455 gnutls_certificate_status certstat;
457 gnutls_x509_crt cert;
458 gnutls_datum pemdata;
459 int certerr_expired = 0;
460 int certerr_notyetvalid = 0;
461 int certerr_hostname = 0;
462 int certerr_nottrusted = 0;
463 int certerr_revoked = 0;
464 int certerr_signernotca = 0;
466 if (gnutls_auth_get_type (state) != GNUTLS_CRD_CERTIFICATE) {
467 mutt_error (_("Unable to get certificate from peer"));
472 certstat = gnutls_certificate_verify_peers (state);
474 if (certstat == GNUTLS_E_NO_CERTIFICATE_FOUND) {
475 mutt_error (_("Unable to get certificate from peer"));
480 mutt_error (_("Certificate verification error (%s)"),
481 gnutls_strerror (certstat));
486 /* We only support X.509 certificates (not OpenPGP) at the moment */
487 if (gnutls_certificate_type_get (state) != GNUTLS_CRT_X509) {
488 mutt_error (_("Certificate is not X.509"));
493 if (gnutls_x509_crt_init (&cert) < 0) {
494 mutt_error (_("Error initialising gnutls certificate data"));
499 cert_list = gnutls_certificate_get_peers (state, &cert_list_size);
501 mutt_error (_("Unable to get certificate from peer"));
506 /* FIXME: Currently only check first certificate in chain. */
507 if (gnutls_x509_crt_import (cert, &cert_list[0], GNUTLS_X509_FMT_DER) < 0) {
508 mutt_error (_("Error processing certificate data"));
513 if (gnutls_x509_crt_get_expiration_time (cert) < time (NULL)) {
517 if (gnutls_x509_crt_get_activation_time (cert) > time (NULL)) {
518 certerr_notyetvalid = 1;
521 if (!gnutls_x509_crt_check_hostname (cert, conn->account.host) &&
522 !tls_check_stored_hostname (&cert_list[0], conn->account.host)) {
523 certerr_hostname = 1;
526 /* see whether certificate is in our cache (certificates file) */
527 if (tls_compare_certificates (&cert_list[0])) {
528 if (certstat & GNUTLS_CERT_INVALID) {
529 /* doesn't matter - have decided is valid because server
530 certificate is in our trusted cache */
531 certstat ^= GNUTLS_CERT_INVALID;
534 if (certstat & GNUTLS_CERT_SIGNER_NOT_FOUND) {
535 /* doesn't matter that we haven't found the signer, since
536 certificate is in our trusted cache */
537 certstat ^= GNUTLS_CERT_SIGNER_NOT_FOUND;
540 if (certstat & GNUTLS_CERT_SIGNER_NOT_CA) {
541 /* Hmm. Not really sure how to handle this, but let's say
542 that we don't care if the CA certificate hasn't got the
543 correct X.509 basic constraints if server certificate is
545 certstat ^= GNUTLS_CERT_SIGNER_NOT_CA;
550 if (certstat & GNUTLS_CERT_REVOKED) {
552 certstat ^= GNUTLS_CERT_REVOKED;
555 if (certstat & GNUTLS_CERT_INVALID) {
556 certerr_nottrusted = 1;
557 certstat ^= GNUTLS_CERT_INVALID;
560 if (certstat & GNUTLS_CERT_SIGNER_NOT_FOUND) {
561 /* NB: already cleared if cert in cache */
562 certerr_nottrusted = 1;
563 certstat ^= GNUTLS_CERT_SIGNER_NOT_FOUND;
566 if (certstat & GNUTLS_CERT_SIGNER_NOT_CA) {
567 /* NB: already cleared if cert in cache */
568 certerr_signernotca = 1;
569 certstat ^= GNUTLS_CERT_SIGNER_NOT_CA;
572 /* OK if signed by (or is) a trusted certificate */
573 /* we've been zeroing the interesting bits in certstat -
574 don't return OK if there are any unhandled bits we don't
576 if (!(certerr_expired || certerr_notyetvalid ||
577 certerr_hostname || certerr_nottrusted) && certstat == 0) {
578 gnutls_x509_crt_deinit (cert);
583 /* interactive check from user */
584 menu = mutt_new_menu ();
586 menu->dialog = (char **) mem_calloc (1, menu->max * sizeof (char *));
587 for (i = 0; i < menu->max; i++)
588 menu->dialog[i] = (char *) mem_calloc (1, SHORT_STRING * sizeof (char));
591 strfcpy (menu->dialog[row], _("This certificate belongs to:"),
595 buflen = sizeof (dn_common_name);
596 if (gnutls_x509_crt_get_dn_by_oid (cert, GNUTLS_OID_X520_COMMON_NAME, 0, 0,
597 dn_common_name, &buflen) != 0)
598 dn_common_name[0] = '\0';
599 buflen = sizeof (dn_email);
600 if (gnutls_x509_crt_get_dn_by_oid (cert, GNUTLS_OID_PKCS9_EMAIL, 0, 0,
601 dn_email, &buflen) != 0)
603 buflen = sizeof (dn_organization);
604 if (gnutls_x509_crt_get_dn_by_oid
605 (cert, GNUTLS_OID_X520_ORGANIZATION_NAME, 0, 0, dn_organization,
607 dn_organization[0] = '\0';
608 buflen = sizeof (dn_organizational_unit);
609 if (gnutls_x509_crt_get_dn_by_oid
610 (cert, GNUTLS_OID_X520_ORGANIZATIONAL_UNIT_NAME, 0, 0,
611 dn_organizational_unit, &buflen) != 0)
612 dn_organizational_unit[0] = '\0';
613 buflen = sizeof (dn_locality);
614 if (gnutls_x509_crt_get_dn_by_oid
615 (cert, GNUTLS_OID_X520_LOCALITY_NAME, 0, 0, dn_locality, &buflen) != 0)
616 dn_locality[0] = '\0';
617 buflen = sizeof (dn_province);
618 if (gnutls_x509_crt_get_dn_by_oid
619 (cert, GNUTLS_OID_X520_STATE_OR_PROVINCE_NAME, 0, 0, dn_province,
621 dn_province[0] = '\0';
622 buflen = sizeof (dn_country);
623 if (gnutls_x509_crt_get_dn_by_oid (cert, GNUTLS_OID_X520_COUNTRY_NAME, 0, 0,
624 dn_country, &buflen) != 0)
625 dn_country[0] = '\0';
627 snprintf (menu->dialog[row++], SHORT_STRING, " %s %s", dn_common_name,
629 snprintf (menu->dialog[row++], SHORT_STRING, " %s", dn_organization);
630 snprintf (menu->dialog[row++], SHORT_STRING, " %s",
631 dn_organizational_unit);
632 snprintf (menu->dialog[row++], SHORT_STRING, " %s %s %s", dn_locality,
633 dn_province, dn_country);
636 strfcpy (menu->dialog[row], _("This certificate was issued by:"),
640 buflen = sizeof (dn_common_name);
641 if (gnutls_x509_crt_get_issuer_dn_by_oid
642 (cert, GNUTLS_OID_X520_COMMON_NAME, 0, 0, dn_common_name, &buflen) != 0)
643 dn_common_name[0] = '\0';
644 buflen = sizeof (dn_email);
645 if (gnutls_x509_crt_get_issuer_dn_by_oid
646 (cert, GNUTLS_OID_PKCS9_EMAIL, 0, 0, dn_email, &buflen) != 0)
648 buflen = sizeof (dn_organization);
649 if (gnutls_x509_crt_get_issuer_dn_by_oid
650 (cert, GNUTLS_OID_X520_ORGANIZATION_NAME, 0, 0, dn_organization,
652 dn_organization[0] = '\0';
653 buflen = sizeof (dn_organizational_unit);
654 if (gnutls_x509_crt_get_issuer_dn_by_oid
655 (cert, GNUTLS_OID_X520_ORGANIZATIONAL_UNIT_NAME, 0, 0,
656 dn_organizational_unit, &buflen) != 0)
657 dn_organizational_unit[0] = '\0';
658 buflen = sizeof (dn_locality);
659 if (gnutls_x509_crt_get_issuer_dn_by_oid
660 (cert, GNUTLS_OID_X520_LOCALITY_NAME, 0, 0, dn_locality, &buflen) != 0)
661 dn_locality[0] = '\0';
662 buflen = sizeof (dn_province);
663 if (gnutls_x509_crt_get_issuer_dn_by_oid
664 (cert, GNUTLS_OID_X520_STATE_OR_PROVINCE_NAME, 0, 0, dn_province,
666 dn_province[0] = '\0';
667 buflen = sizeof (dn_country);
668 if (gnutls_x509_crt_get_issuer_dn_by_oid
669 (cert, GNUTLS_OID_X520_COUNTRY_NAME, 0, 0, dn_country, &buflen) != 0)
670 dn_country[0] = '\0';
672 snprintf (menu->dialog[row++], SHORT_STRING, " %s %s", dn_common_name,
674 snprintf (menu->dialog[row++], SHORT_STRING, " %s", dn_organization);
675 snprintf (menu->dialog[row++], SHORT_STRING, " %s",
676 dn_organizational_unit);
677 snprintf (menu->dialog[row++], SHORT_STRING, " %s %s %s", dn_locality,
678 dn_province, dn_country);
681 snprintf (menu->dialog[row++], SHORT_STRING,
682 _("This certificate is valid"));
684 t = gnutls_x509_crt_get_activation_time (cert);
685 snprintf (menu->dialog[row++], SHORT_STRING, _(" from %s"),
686 tls_make_date (t, datestr, 30));
688 t = gnutls_x509_crt_get_expiration_time (cert);
689 snprintf (menu->dialog[row++], SHORT_STRING, _(" to %s"),
690 tls_make_date (t, datestr, 30));
693 tls_fingerprint (GNUTLS_DIG_SHA, fpbuf, sizeof (fpbuf), &cert_list[0]);
694 snprintf (menu->dialog[row++], SHORT_STRING, _("SHA1 Fingerprint: %s"),
697 tls_fingerprint (GNUTLS_DIG_MD5, fpbuf, sizeof (fpbuf), &cert_list[0]);
698 snprintf (menu->dialog[row++], SHORT_STRING, _("MD5 Fingerprint: %s"),
701 if (certerr_notyetvalid) {
703 strfcpy (menu->dialog[row],
704 _("WARNING: Server certificate is not yet valid"), SHORT_STRING);
706 if (certerr_expired) {
708 strfcpy (menu->dialog[row], _("WARNING: Server certificate has expired"),
711 if (certerr_revoked) {
713 strfcpy (menu->dialog[row],
714 _("WARNING: Server certificate has been revoked"), SHORT_STRING);
716 if (certerr_hostname) {
718 strfcpy (menu->dialog[row],
719 _("WARNING: Server hostname does not match certificate"),
722 if (certerr_signernotca) {
724 strfcpy (menu->dialog[row],
725 _("WARNING: Signer of server certificate is not a CA"),
729 menu->title = _("TLS/SSL Certificate check");
730 /* certificates with bad dates, or that are revoked, must be
731 accepted manually each and every time */
732 if (SslCertFile && !certerr_expired && !certerr_notyetvalid
733 && !certerr_revoked) {
734 menu->prompt = _("(r)eject, accept (o)nce, (a)ccept always");
735 menu->keys = _("roa");
738 menu->prompt = _("(r)eject, accept (o)nce");
739 menu->keys = _("ro");
743 mutt_make_help (buf, sizeof (buf), _("Exit "), MENU_GENERIC, OP_EXIT);
744 strncat (helpstr, buf, sizeof (helpstr));
745 mutt_make_help (buf, sizeof (buf), _("Help"), MENU_GENERIC, OP_HELP);
746 strncat (helpstr, buf, sizeof (helpstr));
747 menu->help = helpstr;
750 set_option (OPTUNBUFFEREDINPUT);
752 switch (mutt_menuLoop (menu)) {
754 case OP_MAX + 1: /* reject */
758 case OP_MAX + 3: /* accept always */
760 if ((fp = fopen (SslCertFile, "a"))) {
761 /* save hostname if necessary */
762 if (certerr_hostname) {
763 fprintf (fp, "#H %s %s\n", conn->account.host, fpbuf);
766 if (certerr_nottrusted) {
768 ret = gnutls_pem_base64_encode_alloc ("CERTIFICATE", &cert_list[0],
771 if (fwrite (pemdata.data, pemdata.size, 1, fp) == 1) {
774 gnutls_free (pemdata.data);
780 mutt_error (_("Warning: Couldn't save certificate"));
784 mutt_message (_("Certificate saved"));
788 case OP_MAX + 2: /* accept once */
793 unset_option (OPTUNBUFFEREDINPUT);
794 mutt_menuDestroy (&menu);
795 gnutls_x509_crt_deinit (cert);