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"
26 #include "mutt_regex.h"
31 typedef struct _tlssockdata {
33 gnutls_certificate_credentials xcred;
36 /* local prototypes */
37 static int tls_socket_read (CONNECTION * conn, char *buf, size_t len);
38 static int tls_socket_write (CONNECTION * conn, const char *buf, size_t len);
39 static int tls_socket_open (CONNECTION * conn);
40 static int tls_socket_close (CONNECTION * conn);
41 static int tls_starttls_close (CONNECTION * conn);
43 static int tls_init (void);
44 static int tls_negotiate (CONNECTION * conn);
45 static int tls_check_certificate (CONNECTION * conn);
48 static int tls_init (void)
50 static unsigned char init_complete = 0;
56 err = gnutls_global_init ();
58 mutt_error ("gnutls_global_init: %s", gnutls_strerror (err));
67 int mutt_gnutls_socket_setup (CONNECTION * conn)
72 conn->conn_open = tls_socket_open;
73 conn->conn_read = tls_socket_read;
74 conn->conn_write = tls_socket_write;
75 conn->conn_close = tls_socket_close;
80 static int tls_socket_read (CONNECTION * conn, char *buf, size_t len)
82 tlssockdata *data = conn->sockdata;
86 mutt_error ("Error: no TLS socket open");
91 ret = gnutls_record_recv (data->state, buf, len);
92 if (gnutls_error_is_fatal (ret) == 1) {
93 mutt_error ("tls_socket_read (%s)", gnutls_strerror (ret));
100 static int tls_socket_write (CONNECTION * conn, const char *buf, size_t len)
102 tlssockdata *data = conn->sockdata;
106 mutt_error ("Error: no TLS socket open");
111 ret = gnutls_record_send (data->state, buf, len);
112 if (gnutls_error_is_fatal (ret) == 1) {
113 mutt_error ("tls_socket_write (%s)", gnutls_strerror (ret));
120 static int tls_socket_open (CONNECTION * conn)
122 if (raw_socket_open (conn) < 0)
125 if (tls_negotiate (conn) < 0) {
126 tls_socket_close (conn);
133 int mutt_gnutls_starttls (CONNECTION * conn)
138 if (tls_negotiate (conn) < 0)
141 conn->conn_read = tls_socket_read;
142 conn->conn_write = tls_socket_write;
143 conn->conn_close = tls_starttls_close;
148 static int protocol_priority[] = { GNUTLS_TLS1, GNUTLS_SSL3, 0 };
150 /* tls_negotiate: After TLS state has been initialised, attempt to negotiate
151 * TLS over the wire, including certificate checks. */
152 static int tls_negotiate (CONNECTION * conn)
157 data = (tlssockdata *) safe_calloc (1, sizeof (tlssockdata));
158 conn->sockdata = data;
159 err = gnutls_certificate_allocate_credentials (&data->xcred);
161 FREE (&conn->sockdata);
162 mutt_error ("gnutls_certificate_allocate_credentials: %s",
163 gnutls_strerror (err));
168 gnutls_certificate_set_x509_trust_file (data->xcred, SslCertFile,
169 GNUTLS_X509_FMT_PEM);
170 /* ignore errors, maybe file doesn't exist yet */
173 gnutls_certificate_set_x509_trust_file (data->xcred, SslCACertFile,
174 GNUTLS_X509_FMT_PEM);
178 gnutls_set_x509_client_key (data->xcred, "", "");
179 gnutls_set_x509_cert_callback (data->xcred, cert_callback);
182 gnutls_init (&data->state, GNUTLS_CLIENT);
185 gnutls_transport_set_ptr (data->state, (gnutls_transport_ptr) conn->fd);
187 /* disable TLS/SSL protocols as needed */
188 if (!option (OPTTLSV1) && !option (OPTSSLV3)) {
189 mutt_error (_("All available protocols for TLS/SSL connection disabled"));
192 else if (!option (OPTTLSV1)) {
193 protocol_priority[0] = GNUTLS_SSL3;
194 protocol_priority[1] = 0;
196 else if (!option (OPTSSLV3)) {
197 protocol_priority[0] = GNUTLS_TLS1;
198 protocol_priority[1] = 0;
202 use the list set above
205 /* We use default priorities (see gnutls documentation),
206 except for protocol version */
207 gnutls_set_default_priority (data->state);
208 gnutls_protocol_set_priority (data->state, protocol_priority);
210 if (SslDHPrimeBits > 0) {
211 gnutls_dh_set_prime_bits (data->state, SslDHPrimeBits);
215 gnutls_set_cred (data->state, GNUTLS_ANON, NULL);
218 gnutls_credentials_set (data->state, GNUTLS_CRD_CERTIFICATE, data->xcred);
220 err = gnutls_handshake (data->state);
222 while (err == GNUTLS_E_AGAIN || err == GNUTLS_E_INTERRUPTED) {
223 err = gnutls_handshake (data->state);
226 if (err == GNUTLS_E_FATAL_ALERT_RECEIVED) {
227 mutt_error ("gnutls_handshake: %s(%s)", gnutls_strerror (err),
228 gnutls_alert_get_name (gnutls_alert_get (data->state)));
231 mutt_error ("gnutls_handshake: %s", gnutls_strerror (err));
237 if (!tls_check_certificate (conn))
240 /* set Security Strength Factor (SSF) for SASL */
241 /* NB: gnutls_cipher_get_key_size() returns key length in bytes */
243 gnutls_cipher_get_key_size (gnutls_cipher_get (data->state)) * 8;
245 mutt_message (_("SSL/TLS connection using %s (%s/%s/%s)"),
246 gnutls_protocol_get_name (gnutls_protocol_get_version
248 gnutls_kx_get_name (gnutls_kx_get (data->state)),
249 gnutls_cipher_get_name (gnutls_cipher_get (data->state)),
250 gnutls_mac_get_name (gnutls_mac_get (data->state)));
256 gnutls_certificate_free_credentials (data->xcred);
257 gnutls_deinit (data->state);
258 FREE (&conn->sockdata);
262 static int tls_socket_close (CONNECTION * conn)
264 tlssockdata *data = conn->sockdata;
267 gnutls_bye (data->state, GNUTLS_SHUT_RDWR);
269 gnutls_certificate_free_credentials (data->xcred);
270 gnutls_deinit (data->state);
271 FREE(&conn->sockdata);
274 return raw_socket_close (conn);
277 static int tls_starttls_close (CONNECTION * conn)
281 rc = tls_socket_close (conn);
282 conn->conn_read = raw_socket_read;
283 conn->conn_write = raw_socket_write;
284 conn->conn_close = raw_socket_close;
289 #define CERT_SEP "-----BEGIN"
291 /* this bit is based on read_ca_file() in gnutls */
292 static int tls_compare_certificates (const gnutls_datum * peercert)
298 gnutls_datum b64_data;
299 unsigned char *b64_data_data;
300 struct stat filestat;
302 if (stat (SslCertFile, &filestat) == -1)
305 b64_data.size = filestat.st_size + 1;
306 b64_data_data = (unsigned char *) safe_calloc (1, b64_data.size);
307 b64_data_data[b64_data.size - 1] = '\0';
308 b64_data.data = b64_data_data;
310 fd1 = fopen (SslCertFile, "r");
315 b64_data.size = fread (b64_data.data, 1, b64_data.size, fd1);
319 ret = gnutls_pem_base64_decode_alloc (NULL, &b64_data, &cert);
321 FREE (&b64_data_data);
325 ptr = (unsigned char *) strstr (b64_data.data, CERT_SEP) + 1;
326 ptr = (unsigned char *) strstr (ptr, CERT_SEP);
328 b64_data.size = b64_data.size - (ptr - b64_data.data);
331 if (cert.size == peercert->size) {
332 if (memcmp (cert.data, peercert->data, cert.size) == 0) {
334 gnutls_free (cert.data);
335 FREE (&b64_data_data);
340 gnutls_free (cert.data);
341 } while (ptr != NULL);
344 FREE (&b64_data_data);
348 static void tls_fingerprint (gnutls_digest_algorithm algo,
349 char *s, int l, const gnutls_datum * data)
351 unsigned char md[36];
357 if (gnutls_fingerprint (algo, data, (char *) md, &n) < 0) {
358 snprintf (s, l, _("[unable to calculate]"));
361 for (j = 0; j < (int) n; j++) {
364 snprintf (ch, 8, "%02X%s", md[j], (j % 2 ? " " : ""));
367 s[2 * n + n / 2 - 1] = '\0'; /* don't want trailing space */
371 static char *tls_make_date (time_t t, char *s, size_t len)
373 struct tm *l = gmtime (&t);
376 snprintf (s, len, "%s, %d %s %d %02d:%02d:%02d UTC",
377 Weekdays[l->tm_wday], l->tm_mday, Months[l->tm_mon],
378 l->tm_year + 1900, l->tm_hour, l->tm_min, l->tm_sec);
380 strfcpy (s, _("[invalid date]"), len);
385 static int tls_check_stored_hostname (const gnutls_datum * cert,
386 const char *hostname)
390 char *linestr = NULL;
394 regmatch_t pmatch[3];
396 /* try checking against names stored in stored certs file */
397 if ((fp = fopen (SslCertFile, "r"))) {
400 "^#H ([a-zA-Z0-9_\\.-]+) ([0-9A-F]{4}( [0-9A-F]{4}){7})[ \t]*$",
401 REG_ICASE | REG_EXTENDED) != 0) {
407 tls_fingerprint (GNUTLS_DIG_MD5, buf, sizeof (buf), cert);
409 mutt_read_line (linestr, &linestrsize, fp, &linenum)) != NULL) {
410 if (linestr[0] == '#' && linestr[1] == 'H') {
411 if (regexec (&preg, linestr, 3, pmatch, 0) == 0) {
412 linestr[pmatch[1].rm_eo] = '\0';
413 linestr[pmatch[2].rm_eo] = '\0';
414 if (strcmp (linestr + pmatch[1].rm_so, hostname) == 0 &&
415 strcmp (linestr + pmatch[2].rm_so, buf) == 0) {
429 /* not found a matching name */
433 static int tls_check_certificate (CONNECTION * conn)
435 tlssockdata *data = conn->sockdata;
436 gnutls_session state = data->state;
437 char helpstr[SHORT_STRING];
438 char buf[SHORT_STRING];
439 char fpbuf[SHORT_STRING];
441 char dn_common_name[SHORT_STRING];
442 char dn_email[SHORT_STRING];
443 char dn_organization[SHORT_STRING];
444 char dn_organizational_unit[SHORT_STRING];
445 char dn_locality[SHORT_STRING];
446 char dn_province[SHORT_STRING];
447 char dn_country[SHORT_STRING];
449 int done, row, i, ret;
453 const gnutls_datum *cert_list;
454 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 (_("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 **) safe_calloc (1, menu->max * sizeof (char *));
587 for (i = 0; i < menu->max; i++)
588 menu->dialog[i] = (char *) safe_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;
751 switch (mutt_menuLoop (menu)) {
753 case OP_MAX + 1: /* reject */
757 case OP_MAX + 3: /* accept always */
759 if ((fp = fopen (SslCertFile, "a"))) {
760 /* save hostname if necessary */
761 if (certerr_hostname) {
762 fprintf (fp, "#H %s %s\n", conn->account.host, fpbuf);
765 if (certerr_nottrusted) {
767 ret = gnutls_pem_base64_encode_alloc ("CERTIFICATE", &cert_list[0],
770 if (fwrite (pemdata.data, pemdata.size, 1, fp) == 1) {
773 gnutls_free (pemdata.data);
779 mutt_error (_("Warning: Couldn't save certificate"));
783 mutt_message (_("Certificate saved"));
787 case OP_MAX + 2: /* accept once */
792 mutt_menuDestroy (&menu);
793 gnutls_x509_crt_deinit (cert);