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 *) safe_calloc (1, sizeof (tlssockdata));
159 conn->sockdata = data;
160 err = gnutls_certificate_allocate_credentials (&data->xcred);
162 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 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 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 *) safe_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 FREE (&b64_data_data);
326 ptr = (unsigned char *) strstr (b64_data.data, CERT_SEP) + 1;
327 ptr = (unsigned char *) strstr (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 FREE (&b64_data_data);
341 gnutls_free (cert.data);
342 } while (ptr != NULL);
345 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 (mutt_strcmp (linestr + pmatch[1].rm_so, hostname) == 0 &&
416 mutt_strcmp (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;
454 const gnutls_datum *cert_list;
455 int cert_list_size = 0;
456 gnutls_certificate_status certstat;
458 gnutls_x509_crt cert;
459 gnutls_datum pemdata;
460 int certerr_expired = 0;
461 int certerr_notyetvalid = 0;
462 int certerr_hostname = 0;
463 int certerr_nottrusted = 0;
464 int certerr_revoked = 0;
465 int certerr_signernotca = 0;
467 if (gnutls_auth_get_type (state) != GNUTLS_CRD_CERTIFICATE) {
468 mutt_error (_("Unable to get certificate from peer"));
473 certstat = gnutls_certificate_verify_peers (state);
475 if (certstat == GNUTLS_E_NO_CERTIFICATE_FOUND) {
476 mutt_error (_("Unable to get certificate from peer"));
481 mutt_error (_("Certificate verification error (%s)"),
482 gnutls_strerror (certstat));
487 /* We only support X.509 certificates (not OpenPGP) at the moment */
488 if (gnutls_certificate_type_get (state) != GNUTLS_CRT_X509) {
489 mutt_error (_("Error certificate is not X.509"));
494 if (gnutls_x509_crt_init (&cert) < 0) {
495 mutt_error (_("Error initialising gnutls certificate data"));
500 cert_list = gnutls_certificate_get_peers (state, &cert_list_size);
502 mutt_error (_("Unable to get certificate from peer"));
507 /* FIXME: Currently only check first certificate in chain. */
508 if (gnutls_x509_crt_import (cert, &cert_list[0], GNUTLS_X509_FMT_DER) < 0) {
509 mutt_error (_("Error processing certificate data"));
514 if (gnutls_x509_crt_get_expiration_time (cert) < time (NULL)) {
518 if (gnutls_x509_crt_get_activation_time (cert) > time (NULL)) {
519 certerr_notyetvalid = 1;
522 if (!gnutls_x509_crt_check_hostname (cert, conn->account.host) &&
523 !tls_check_stored_hostname (&cert_list[0], conn->account.host)) {
524 certerr_hostname = 1;
527 /* see whether certificate is in our cache (certificates file) */
528 if (tls_compare_certificates (&cert_list[0])) {
529 if (certstat & GNUTLS_CERT_INVALID) {
530 /* doesn't matter - have decided is valid because server
531 certificate is in our trusted cache */
532 certstat ^= GNUTLS_CERT_INVALID;
535 if (certstat & GNUTLS_CERT_SIGNER_NOT_FOUND) {
536 /* doesn't matter that we haven't found the signer, since
537 certificate is in our trusted cache */
538 certstat ^= GNUTLS_CERT_SIGNER_NOT_FOUND;
541 if (certstat & GNUTLS_CERT_SIGNER_NOT_CA) {
542 /* Hmm. Not really sure how to handle this, but let's say
543 that we don't care if the CA certificate hasn't got the
544 correct X.509 basic constraints if server certificate is
546 certstat ^= GNUTLS_CERT_SIGNER_NOT_CA;
551 if (certstat & GNUTLS_CERT_REVOKED) {
553 certstat ^= GNUTLS_CERT_REVOKED;
556 if (certstat & GNUTLS_CERT_INVALID) {
557 certerr_nottrusted = 1;
558 certstat ^= GNUTLS_CERT_INVALID;
561 if (certstat & GNUTLS_CERT_SIGNER_NOT_FOUND) {
562 /* NB: already cleared if cert in cache */
563 certerr_nottrusted = 1;
564 certstat ^= GNUTLS_CERT_SIGNER_NOT_FOUND;
567 if (certstat & GNUTLS_CERT_SIGNER_NOT_CA) {
568 /* NB: already cleared if cert in cache */
569 certerr_signernotca = 1;
570 certstat ^= GNUTLS_CERT_SIGNER_NOT_CA;
573 /* OK if signed by (or is) a trusted certificate */
574 /* we've been zeroing the interesting bits in certstat -
575 don't return OK if there are any unhandled bits we don't
577 if (!(certerr_expired || certerr_notyetvalid ||
578 certerr_hostname || certerr_nottrusted) && certstat == 0) {
579 gnutls_x509_crt_deinit (cert);
584 /* interactive check from user */
585 menu = mutt_new_menu ();
587 menu->dialog = (char **) safe_calloc (1, menu->max * sizeof (char *));
588 for (i = 0; i < menu->max; i++)
589 menu->dialog[i] = (char *) safe_calloc (1, SHORT_STRING * sizeof (char));
592 strfcpy (menu->dialog[row], _("This certificate belongs to:"),
596 buflen = sizeof (dn_common_name);
597 if (gnutls_x509_crt_get_dn_by_oid (cert, GNUTLS_OID_X520_COMMON_NAME, 0, 0,
598 dn_common_name, &buflen) != 0)
599 dn_common_name[0] = '\0';
600 buflen = sizeof (dn_email);
601 if (gnutls_x509_crt_get_dn_by_oid (cert, GNUTLS_OID_PKCS9_EMAIL, 0, 0,
602 dn_email, &buflen) != 0)
604 buflen = sizeof (dn_organization);
605 if (gnutls_x509_crt_get_dn_by_oid
606 (cert, GNUTLS_OID_X520_ORGANIZATION_NAME, 0, 0, dn_organization,
608 dn_organization[0] = '\0';
609 buflen = sizeof (dn_organizational_unit);
610 if (gnutls_x509_crt_get_dn_by_oid
611 (cert, GNUTLS_OID_X520_ORGANIZATIONAL_UNIT_NAME, 0, 0,
612 dn_organizational_unit, &buflen) != 0)
613 dn_organizational_unit[0] = '\0';
614 buflen = sizeof (dn_locality);
615 if (gnutls_x509_crt_get_dn_by_oid
616 (cert, GNUTLS_OID_X520_LOCALITY_NAME, 0, 0, dn_locality, &buflen) != 0)
617 dn_locality[0] = '\0';
618 buflen = sizeof (dn_province);
619 if (gnutls_x509_crt_get_dn_by_oid
620 (cert, GNUTLS_OID_X520_STATE_OR_PROVINCE_NAME, 0, 0, dn_province,
622 dn_province[0] = '\0';
623 buflen = sizeof (dn_country);
624 if (gnutls_x509_crt_get_dn_by_oid (cert, GNUTLS_OID_X520_COUNTRY_NAME, 0, 0,
625 dn_country, &buflen) != 0)
626 dn_country[0] = '\0';
628 snprintf (menu->dialog[row++], SHORT_STRING, " %s %s", dn_common_name,
630 snprintf (menu->dialog[row++], SHORT_STRING, " %s", dn_organization);
631 snprintf (menu->dialog[row++], SHORT_STRING, " %s",
632 dn_organizational_unit);
633 snprintf (menu->dialog[row++], SHORT_STRING, " %s %s %s", dn_locality,
634 dn_province, dn_country);
637 strfcpy (menu->dialog[row], _("This certificate was issued by:"),
641 buflen = sizeof (dn_common_name);
642 if (gnutls_x509_crt_get_issuer_dn_by_oid
643 (cert, GNUTLS_OID_X520_COMMON_NAME, 0, 0, dn_common_name, &buflen) != 0)
644 dn_common_name[0] = '\0';
645 buflen = sizeof (dn_email);
646 if (gnutls_x509_crt_get_issuer_dn_by_oid
647 (cert, GNUTLS_OID_PKCS9_EMAIL, 0, 0, dn_email, &buflen) != 0)
649 buflen = sizeof (dn_organization);
650 if (gnutls_x509_crt_get_issuer_dn_by_oid
651 (cert, GNUTLS_OID_X520_ORGANIZATION_NAME, 0, 0, dn_organization,
653 dn_organization[0] = '\0';
654 buflen = sizeof (dn_organizational_unit);
655 if (gnutls_x509_crt_get_issuer_dn_by_oid
656 (cert, GNUTLS_OID_X520_ORGANIZATIONAL_UNIT_NAME, 0, 0,
657 dn_organizational_unit, &buflen) != 0)
658 dn_organizational_unit[0] = '\0';
659 buflen = sizeof (dn_locality);
660 if (gnutls_x509_crt_get_issuer_dn_by_oid
661 (cert, GNUTLS_OID_X520_LOCALITY_NAME, 0, 0, dn_locality, &buflen) != 0)
662 dn_locality[0] = '\0';
663 buflen = sizeof (dn_province);
664 if (gnutls_x509_crt_get_issuer_dn_by_oid
665 (cert, GNUTLS_OID_X520_STATE_OR_PROVINCE_NAME, 0, 0, dn_province,
667 dn_province[0] = '\0';
668 buflen = sizeof (dn_country);
669 if (gnutls_x509_crt_get_issuer_dn_by_oid
670 (cert, GNUTLS_OID_X520_COUNTRY_NAME, 0, 0, dn_country, &buflen) != 0)
671 dn_country[0] = '\0';
673 snprintf (menu->dialog[row++], SHORT_STRING, " %s %s", dn_common_name,
675 snprintf (menu->dialog[row++], SHORT_STRING, " %s", dn_organization);
676 snprintf (menu->dialog[row++], SHORT_STRING, " %s",
677 dn_organizational_unit);
678 snprintf (menu->dialog[row++], SHORT_STRING, " %s %s %s", dn_locality,
679 dn_province, dn_country);
682 snprintf (menu->dialog[row++], SHORT_STRING,
683 _("This certificate is valid"));
685 t = gnutls_x509_crt_get_activation_time (cert);
686 snprintf (menu->dialog[row++], SHORT_STRING, _(" from %s"),
687 tls_make_date (t, datestr, 30));
689 t = gnutls_x509_crt_get_expiration_time (cert);
690 snprintf (menu->dialog[row++], SHORT_STRING, _(" to %s"),
691 tls_make_date (t, datestr, 30));
694 tls_fingerprint (GNUTLS_DIG_SHA, fpbuf, sizeof (fpbuf), &cert_list[0]);
695 snprintf (menu->dialog[row++], SHORT_STRING, _("SHA1 Fingerprint: %s"),
698 tls_fingerprint (GNUTLS_DIG_MD5, fpbuf, sizeof (fpbuf), &cert_list[0]);
699 snprintf (menu->dialog[row++], SHORT_STRING, _("MD5 Fingerprint: %s"),
702 if (certerr_notyetvalid) {
704 strfcpy (menu->dialog[row],
705 _("WARNING: Server certificate is not yet valid"), SHORT_STRING);
707 if (certerr_expired) {
709 strfcpy (menu->dialog[row], _("WARNING: Server certificate has expired"),
712 if (certerr_revoked) {
714 strfcpy (menu->dialog[row],
715 _("WARNING: Server certificate has been revoked"), SHORT_STRING);
717 if (certerr_hostname) {
719 strfcpy (menu->dialog[row],
720 _("WARNING: Server hostname does not match certificate"),
723 if (certerr_signernotca) {
725 strfcpy (menu->dialog[row],
726 _("WARNING: Signer of server certificate is not a CA"),
730 menu->title = _("TLS/SSL Certificate check");
731 /* certificates with bad dates, or that are revoked, must be
732 accepted manually each and every time */
733 if (SslCertFile && !certerr_expired && !certerr_notyetvalid
734 && !certerr_revoked) {
735 menu->prompt = _("(r)eject, accept (o)nce, (a)ccept always");
736 menu->keys = _("roa");
739 menu->prompt = _("(r)eject, accept (o)nce");
740 menu->keys = _("ro");
744 mutt_make_help (buf, sizeof (buf), _("Exit "), MENU_GENERIC, OP_EXIT);
745 strncat (helpstr, buf, sizeof (helpstr));
746 mutt_make_help (buf, sizeof (buf), _("Help"), MENU_GENERIC, OP_HELP);
747 strncat (helpstr, buf, sizeof (helpstr));
748 menu->help = helpstr;
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 mutt_menuDestroy (&menu);
794 gnutls_x509_crt_deinit (cert);