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"
28 typedef struct _tlssockdata {
30 gnutls_certificate_credentials xcred;
33 /* local prototypes */
34 static int tls_socket_read (CONNECTION * conn, char *buf, size_t len);
35 static int tls_socket_write (CONNECTION * conn, const char *buf, size_t len);
36 static int tls_socket_open (CONNECTION * conn);
37 static int tls_socket_close (CONNECTION * conn);
38 static int tls_starttls_close (CONNECTION * conn);
40 static int tls_init (void);
41 static int tls_negotiate (CONNECTION * conn);
42 static int tls_check_certificate (CONNECTION * conn);
45 static int tls_init (void)
47 static unsigned char init_complete = 0;
53 err = gnutls_global_init ();
55 mutt_error ("gnutls_global_init: %s", gnutls_strerror (err));
64 int mutt_gnutls_socket_setup (CONNECTION * conn)
69 conn->conn_open = tls_socket_open;
70 conn->conn_read = tls_socket_read;
71 conn->conn_write = tls_socket_write;
72 conn->conn_close = tls_socket_close;
77 static int tls_socket_read (CONNECTION * conn, char *buf, size_t len)
79 tlssockdata *data = conn->sockdata;
83 mutt_error ("Error: no TLS socket open");
88 ret = gnutls_record_recv (data->state, buf, len);
89 if (gnutls_error_is_fatal (ret) == 1) {
90 mutt_error ("tls_socket_read (%s)", gnutls_strerror (ret));
97 static int tls_socket_write (CONNECTION * conn, const char *buf, size_t len)
99 tlssockdata *data = conn->sockdata;
103 mutt_error ("Error: no TLS socket open");
108 ret = gnutls_record_send (data->state, buf, len);
109 if (gnutls_error_is_fatal (ret) == 1) {
110 mutt_error ("tls_socket_write (%s)", gnutls_strerror (ret));
117 static int tls_socket_open (CONNECTION * conn)
119 if (raw_socket_open (conn) < 0)
122 if (tls_negotiate (conn) < 0) {
123 tls_socket_close (conn);
130 int mutt_gnutls_starttls (CONNECTION * conn)
135 if (tls_negotiate (conn) < 0)
138 conn->conn_read = tls_socket_read;
139 conn->conn_write = tls_socket_write;
140 conn->conn_close = tls_starttls_close;
145 static int protocol_priority[] = { GNUTLS_TLS1, GNUTLS_SSL3, 0 };
147 /* tls_negotiate: After TLS state has been initialised, attempt to negotiate
148 * TLS over the wire, including certificate checks. */
149 static int tls_negotiate (CONNECTION * conn)
154 data = (tlssockdata *) safe_calloc (1, sizeof (tlssockdata));
155 conn->sockdata = data;
156 err = gnutls_certificate_allocate_credentials (&data->xcred);
158 FREE (&conn->sockdata);
159 mutt_error ("gnutls_certificate_allocate_credentials: %s",
160 gnutls_strerror (err));
165 gnutls_certificate_set_x509_trust_file (data->xcred, SslCertFile,
166 GNUTLS_X509_FMT_PEM);
167 /* ignore errors, maybe file doesn't exist yet */
170 gnutls_certificate_set_x509_trust_file (data->xcred, SslCACertFile,
171 GNUTLS_X509_FMT_PEM);
175 gnutls_set_x509_client_key (data->xcred, "", "");
176 gnutls_set_x509_cert_callback (data->xcred, cert_callback);
179 gnutls_init (&data->state, GNUTLS_CLIENT);
182 gnutls_transport_set_ptr (data->state, (gnutls_transport_ptr) conn->fd);
184 /* disable TLS/SSL protocols as needed */
185 if (!option (OPTTLSV1) && !option (OPTSSLV3)) {
186 mutt_error (_("All available protocols for TLS/SSL connection disabled"));
189 else if (!option (OPTTLSV1)) {
190 protocol_priority[0] = GNUTLS_SSL3;
191 protocol_priority[1] = 0;
193 else if (!option (OPTSSLV3)) {
194 protocol_priority[0] = GNUTLS_TLS1;
195 protocol_priority[1] = 0;
199 use the list set above
202 /* We use default priorities (see gnutls documentation),
203 except for protocol version */
204 gnutls_set_default_priority (data->state);
205 gnutls_protocol_set_priority (data->state, protocol_priority);
207 if (SslDHPrimeBits > 0) {
208 gnutls_dh_set_prime_bits (data->state, SslDHPrimeBits);
212 gnutls_set_cred (data->state, GNUTLS_ANON, NULL);
215 gnutls_credentials_set (data->state, GNUTLS_CRD_CERTIFICATE, data->xcred);
217 err = gnutls_handshake (data->state);
219 while (err == GNUTLS_E_AGAIN || err == GNUTLS_E_INTERRUPTED) {
220 err = gnutls_handshake (data->state);
223 if (err == GNUTLS_E_FATAL_ALERT_RECEIVED) {
224 mutt_error ("gnutls_handshake: %s(%s)", gnutls_strerror (err),
225 gnutls_alert_get_name (gnutls_alert_get (data->state)));
228 mutt_error ("gnutls_handshake: %s", gnutls_strerror (err));
234 if (!tls_check_certificate (conn))
237 /* set Security Strength Factor (SSF) for SASL */
238 /* NB: gnutls_cipher_get_key_size() returns key length in bytes */
240 gnutls_cipher_get_key_size (gnutls_cipher_get (data->state)) * 8;
242 mutt_message (_("SSL/TLS connection using %s (%s/%s/%s)"),
243 gnutls_protocol_get_name (gnutls_protocol_get_version
245 gnutls_kx_get_name (gnutls_kx_get (data->state)),
246 gnutls_cipher_get_name (gnutls_cipher_get (data->state)),
247 gnutls_mac_get_name (gnutls_mac_get (data->state)));
253 gnutls_certificate_free_credentials (data->xcred);
254 gnutls_deinit (data->state);
255 FREE (&conn->sockdata);
259 static int tls_socket_close (CONNECTION * conn)
261 tlssockdata *data = conn->sockdata;
264 gnutls_bye (data->state, GNUTLS_SHUT_RDWR);
266 gnutls_certificate_free_credentials (data->xcred);
267 gnutls_deinit (data->state);
268 safe_free ((void **) &conn->sockdata);
271 return raw_socket_close (conn);
274 static int tls_starttls_close (CONNECTION * conn)
278 rc = tls_socket_close (conn);
279 conn->conn_read = raw_socket_read;
280 conn->conn_write = raw_socket_write;
281 conn->conn_close = raw_socket_close;
286 #define CERT_SEP "-----BEGIN"
288 /* this bit is based on read_ca_file() in gnutls */
289 static int tls_compare_certificates (const gnutls_datum * peercert)
295 gnutls_datum b64_data;
296 unsigned char *b64_data_data;
297 struct stat filestat;
299 if (stat (SslCertFile, &filestat) == -1)
302 b64_data.size = filestat.st_size + 1;
303 b64_data_data = (unsigned char *) safe_calloc (1, b64_data.size);
304 b64_data_data[b64_data.size - 1] = '\0';
305 b64_data.data = b64_data_data;
307 fd1 = fopen (SslCertFile, "r");
312 b64_data.size = fread (b64_data.data, 1, b64_data.size, fd1);
316 ret = gnutls_pem_base64_decode_alloc (NULL, &b64_data, &cert);
318 FREE (&b64_data_data);
322 ptr = (unsigned char *) strstr (b64_data.data, CERT_SEP) + 1;
323 ptr = (unsigned char *) strstr (ptr, CERT_SEP);
325 b64_data.size = b64_data.size - (ptr - b64_data.data);
328 if (cert.size == peercert->size) {
329 if (memcmp (cert.data, peercert->data, cert.size) == 0) {
331 gnutls_free (cert.data);
332 FREE (&b64_data_data);
337 gnutls_free (cert.data);
338 } while (ptr != NULL);
341 FREE (&b64_data_data);
345 static void tls_fingerprint (gnutls_digest_algorithm algo,
346 char *s, int l, const gnutls_datum * data)
348 unsigned char md[36];
354 if (gnutls_fingerprint (algo, data, (char *) md, &n) < 0) {
355 snprintf (s, l, _("[unable to calculate]"));
358 for (j = 0; j < (int) n; j++) {
361 snprintf (ch, 8, "%02X%s", md[j], (j % 2 ? " " : ""));
364 s[2 * n + n / 2 - 1] = '\0'; /* don't want trailing space */
368 static char *tls_make_date (time_t t, char *s, size_t len)
370 struct tm *l = gmtime (&t);
373 snprintf (s, len, "%s, %d %s %d %02d:%02d:%02d UTC",
374 Weekdays[l->tm_wday], l->tm_mday, Months[l->tm_mon],
375 l->tm_year + 1900, l->tm_hour, l->tm_min, l->tm_sec);
377 strfcpy (s, _("[invalid date]"), len);
382 static int tls_check_stored_hostname (const gnutls_datum * cert,
383 const char *hostname)
387 char *linestr = NULL;
391 regmatch_t pmatch[3];
393 /* try checking against names stored in stored certs file */
394 if ((fp = fopen (SslCertFile, "r"))) {
397 "^#H ([a-zA-Z0-9_\\.-]+) ([0-9A-F]{4}( [0-9A-F]{4}){7})[ \t]*$",
398 REG_ICASE | REG_EXTENDED) != 0) {
404 tls_fingerprint (GNUTLS_DIG_MD5, buf, sizeof (buf), cert);
406 mutt_read_line (linestr, &linestrsize, fp, &linenum)) != NULL) {
407 if (linestr[0] == '#' && linestr[1] == 'H') {
408 if (regexec (&preg, linestr, 3, pmatch, 0) == 0) {
409 linestr[pmatch[1].rm_eo] = '\0';
410 linestr[pmatch[2].rm_eo] = '\0';
411 if (strcmp (linestr + pmatch[1].rm_so, hostname) == 0 &&
412 strcmp (linestr + pmatch[2].rm_so, buf) == 0) {
414 safe_free ((void **) &linestr);
426 /* not found a matching name */
430 static int tls_check_certificate (CONNECTION * conn)
432 tlssockdata *data = conn->sockdata;
433 gnutls_session state = data->state;
434 char helpstr[SHORT_STRING];
435 char buf[SHORT_STRING];
436 char fpbuf[SHORT_STRING];
438 char dn_common_name[SHORT_STRING];
439 char dn_email[SHORT_STRING];
440 char dn_organization[SHORT_STRING];
441 char dn_organizational_unit[SHORT_STRING];
442 char dn_locality[SHORT_STRING];
443 char dn_province[SHORT_STRING];
444 char dn_country[SHORT_STRING];
446 int done, row, i, ret;
450 const gnutls_datum *cert_list;
451 int cert_list_size = 0;
452 gnutls_certificate_status certstat;
454 gnutls_x509_crt cert;
455 gnutls_datum pemdata;
456 int certerr_expired = 0;
457 int certerr_notyetvalid = 0;
458 int certerr_hostname = 0;
459 int certerr_nottrusted = 0;
460 int certerr_revoked = 0;
461 int certerr_signernotca = 0;
463 if (gnutls_auth_get_type (state) != GNUTLS_CRD_CERTIFICATE) {
464 mutt_error (_("Unable to get certificate from peer"));
469 certstat = gnutls_certificate_verify_peers (state);
471 if (certstat == GNUTLS_E_NO_CERTIFICATE_FOUND) {
472 mutt_error (_("Unable to get certificate from peer"));
477 mutt_error (_("Certificate verification error (%s)"),
478 gnutls_strerror (certstat));
483 /* We only support X.509 certificates (not OpenPGP) at the moment */
484 if (gnutls_certificate_type_get (state) != GNUTLS_CRT_X509) {
485 mutt_error (_("Error certificate is not X.509"));
490 if (gnutls_x509_crt_init (&cert) < 0) {
491 mutt_error (_("Error initialising gnutls certificate data"));
496 cert_list = gnutls_certificate_get_peers (state, &cert_list_size);
498 mutt_error (_("Unable to get certificate from peer"));
503 /* FIXME: Currently only check first certificate in chain. */
504 if (gnutls_x509_crt_import (cert, &cert_list[0], GNUTLS_X509_FMT_DER) < 0) {
505 mutt_error (_("Error processing certificate data"));
510 if (gnutls_x509_crt_get_expiration_time (cert) < time (NULL)) {
514 if (gnutls_x509_crt_get_activation_time (cert) > time (NULL)) {
515 certerr_notyetvalid = 1;
518 if (!gnutls_x509_crt_check_hostname (cert, conn->account.host) &&
519 !tls_check_stored_hostname (&cert_list[0], conn->account.host)) {
520 certerr_hostname = 1;
523 /* see whether certificate is in our cache (certificates file) */
524 if (tls_compare_certificates (&cert_list[0])) {
525 if (certstat & GNUTLS_CERT_INVALID) {
526 /* doesn't matter - have decided is valid because server
527 certificate is in our trusted cache */
528 certstat ^= GNUTLS_CERT_INVALID;
531 if (certstat & GNUTLS_CERT_SIGNER_NOT_FOUND) {
532 /* doesn't matter that we haven't found the signer, since
533 certificate is in our trusted cache */
534 certstat ^= GNUTLS_CERT_SIGNER_NOT_FOUND;
537 if (certstat & GNUTLS_CERT_SIGNER_NOT_CA) {
538 /* Hmm. Not really sure how to handle this, but let's say
539 that we don't care if the CA certificate hasn't got the
540 correct X.509 basic constraints if server certificate is
542 certstat ^= GNUTLS_CERT_SIGNER_NOT_CA;
547 if (certstat & GNUTLS_CERT_REVOKED) {
549 certstat ^= GNUTLS_CERT_REVOKED;
552 if (certstat & GNUTLS_CERT_INVALID) {
553 certerr_nottrusted = 1;
554 certstat ^= GNUTLS_CERT_INVALID;
557 if (certstat & GNUTLS_CERT_SIGNER_NOT_FOUND) {
558 /* NB: already cleared if cert in cache */
559 certerr_nottrusted = 1;
560 certstat ^= GNUTLS_CERT_SIGNER_NOT_FOUND;
563 if (certstat & GNUTLS_CERT_SIGNER_NOT_CA) {
564 /* NB: already cleared if cert in cache */
565 certerr_signernotca = 1;
566 certstat ^= GNUTLS_CERT_SIGNER_NOT_CA;
569 /* OK if signed by (or is) a trusted certificate */
570 /* we've been zeroing the interesting bits in certstat -
571 don't return OK if there are any unhandled bits we don't
573 if (!(certerr_expired || certerr_notyetvalid ||
574 certerr_hostname || certerr_nottrusted) && certstat == 0) {
575 gnutls_x509_crt_deinit (cert);
580 /* interactive check from user */
581 menu = mutt_new_menu ();
583 menu->dialog = (char **) safe_calloc (1, menu->max * sizeof (char *));
584 for (i = 0; i < menu->max; i++)
585 menu->dialog[i] = (char *) safe_calloc (1, SHORT_STRING * sizeof (char));
588 strfcpy (menu->dialog[row], _("This certificate belongs to:"),
592 buflen = sizeof (dn_common_name);
593 if (gnutls_x509_crt_get_dn_by_oid (cert, GNUTLS_OID_X520_COMMON_NAME, 0, 0,
594 dn_common_name, &buflen) != 0)
595 dn_common_name[0] = '\0';
596 buflen = sizeof (dn_email);
597 if (gnutls_x509_crt_get_dn_by_oid (cert, GNUTLS_OID_PKCS9_EMAIL, 0, 0,
598 dn_email, &buflen) != 0)
600 buflen = sizeof (dn_organization);
601 if (gnutls_x509_crt_get_dn_by_oid
602 (cert, GNUTLS_OID_X520_ORGANIZATION_NAME, 0, 0, dn_organization,
604 dn_organization[0] = '\0';
605 buflen = sizeof (dn_organizational_unit);
606 if (gnutls_x509_crt_get_dn_by_oid
607 (cert, GNUTLS_OID_X520_ORGANIZATIONAL_UNIT_NAME, 0, 0,
608 dn_organizational_unit, &buflen) != 0)
609 dn_organizational_unit[0] = '\0';
610 buflen = sizeof (dn_locality);
611 if (gnutls_x509_crt_get_dn_by_oid
612 (cert, GNUTLS_OID_X520_LOCALITY_NAME, 0, 0, dn_locality, &buflen) != 0)
613 dn_locality[0] = '\0';
614 buflen = sizeof (dn_province);
615 if (gnutls_x509_crt_get_dn_by_oid
616 (cert, GNUTLS_OID_X520_STATE_OR_PROVINCE_NAME, 0, 0, dn_province,
618 dn_province[0] = '\0';
619 buflen = sizeof (dn_country);
620 if (gnutls_x509_crt_get_dn_by_oid (cert, GNUTLS_OID_X520_COUNTRY_NAME, 0, 0,
621 dn_country, &buflen) != 0)
622 dn_country[0] = '\0';
624 snprintf (menu->dialog[row++], SHORT_STRING, " %s %s", dn_common_name,
626 snprintf (menu->dialog[row++], SHORT_STRING, " %s", dn_organization);
627 snprintf (menu->dialog[row++], SHORT_STRING, " %s",
628 dn_organizational_unit);
629 snprintf (menu->dialog[row++], SHORT_STRING, " %s %s %s", dn_locality,
630 dn_province, dn_country);
633 strfcpy (menu->dialog[row], _("This certificate was issued by:"),
637 buflen = sizeof (dn_common_name);
638 if (gnutls_x509_crt_get_issuer_dn_by_oid
639 (cert, GNUTLS_OID_X520_COMMON_NAME, 0, 0, dn_common_name, &buflen) != 0)
640 dn_common_name[0] = '\0';
641 buflen = sizeof (dn_email);
642 if (gnutls_x509_crt_get_issuer_dn_by_oid
643 (cert, GNUTLS_OID_PKCS9_EMAIL, 0, 0, dn_email, &buflen) != 0)
645 buflen = sizeof (dn_organization);
646 if (gnutls_x509_crt_get_issuer_dn_by_oid
647 (cert, GNUTLS_OID_X520_ORGANIZATION_NAME, 0, 0, dn_organization,
649 dn_organization[0] = '\0';
650 buflen = sizeof (dn_organizational_unit);
651 if (gnutls_x509_crt_get_issuer_dn_by_oid
652 (cert, GNUTLS_OID_X520_ORGANIZATIONAL_UNIT_NAME, 0, 0,
653 dn_organizational_unit, &buflen) != 0)
654 dn_organizational_unit[0] = '\0';
655 buflen = sizeof (dn_locality);
656 if (gnutls_x509_crt_get_issuer_dn_by_oid
657 (cert, GNUTLS_OID_X520_LOCALITY_NAME, 0, 0, dn_locality, &buflen) != 0)
658 dn_locality[0] = '\0';
659 buflen = sizeof (dn_province);
660 if (gnutls_x509_crt_get_issuer_dn_by_oid
661 (cert, GNUTLS_OID_X520_STATE_OR_PROVINCE_NAME, 0, 0, dn_province,
663 dn_province[0] = '\0';
664 buflen = sizeof (dn_country);
665 if (gnutls_x509_crt_get_issuer_dn_by_oid
666 (cert, GNUTLS_OID_X520_COUNTRY_NAME, 0, 0, dn_country, &buflen) != 0)
667 dn_country[0] = '\0';
669 snprintf (menu->dialog[row++], SHORT_STRING, " %s %s", dn_common_name,
671 snprintf (menu->dialog[row++], SHORT_STRING, " %s", dn_organization);
672 snprintf (menu->dialog[row++], SHORT_STRING, " %s",
673 dn_organizational_unit);
674 snprintf (menu->dialog[row++], SHORT_STRING, " %s %s %s", dn_locality,
675 dn_province, dn_country);
678 snprintf (menu->dialog[row++], SHORT_STRING,
679 _("This certificate is valid"));
681 t = gnutls_x509_crt_get_activation_time (cert);
682 snprintf (menu->dialog[row++], SHORT_STRING, _(" from %s"),
683 tls_make_date (t, datestr, 30));
685 t = gnutls_x509_crt_get_expiration_time (cert);
686 snprintf (menu->dialog[row++], SHORT_STRING, _(" to %s"),
687 tls_make_date (t, datestr, 30));
690 tls_fingerprint (GNUTLS_DIG_SHA, fpbuf, sizeof (fpbuf), &cert_list[0]);
691 snprintf (menu->dialog[row++], SHORT_STRING, _("SHA1 Fingerprint: %s"),
694 tls_fingerprint (GNUTLS_DIG_MD5, fpbuf, sizeof (fpbuf), &cert_list[0]);
695 snprintf (menu->dialog[row++], SHORT_STRING, _("MD5 Fingerprint: %s"),
698 if (certerr_notyetvalid) {
700 strfcpy (menu->dialog[row],
701 _("WARNING: Server certificate is not yet valid"), SHORT_STRING);
703 if (certerr_expired) {
705 strfcpy (menu->dialog[row], _("WARNING: Server certificate has expired"),
708 if (certerr_revoked) {
710 strfcpy (menu->dialog[row],
711 _("WARNING: Server certificate has been revoked"), SHORT_STRING);
713 if (certerr_hostname) {
715 strfcpy (menu->dialog[row],
716 _("WARNING: Server hostname does not match certificate"),
719 if (certerr_signernotca) {
721 strfcpy (menu->dialog[row],
722 _("WARNING: Signer of server certificate is not a CA"),
726 menu->title = _("TLS/SSL Certificate check");
727 /* certificates with bad dates, or that are revoked, must be
728 accepted manually each and every time */
729 if (SslCertFile && !certerr_expired && !certerr_notyetvalid
730 && !certerr_revoked) {
731 menu->prompt = _("(r)eject, accept (o)nce, (a)ccept always");
732 menu->keys = _("roa");
735 menu->prompt = _("(r)eject, accept (o)nce");
736 menu->keys = _("ro");
740 mutt_make_help (buf, sizeof (buf), _("Exit "), MENU_GENERIC, OP_EXIT);
741 strncat (helpstr, buf, sizeof (helpstr));
742 mutt_make_help (buf, sizeof (buf), _("Help"), MENU_GENERIC, OP_HELP);
743 strncat (helpstr, buf, sizeof (helpstr));
744 menu->help = helpstr;
748 switch (mutt_menuLoop (menu)) {
750 case OP_MAX + 1: /* reject */
754 case OP_MAX + 3: /* accept always */
756 if ((fp = fopen (SslCertFile, "a"))) {
757 /* save hostname if necessary */
758 if (certerr_hostname) {
759 fprintf (fp, "#H %s %s\n", conn->account.host, fpbuf);
762 if (certerr_nottrusted) {
764 ret = gnutls_pem_base64_encode_alloc ("CERTIFICATE", &cert_list[0],
767 if (fwrite (pemdata.data, pemdata.size, 1, fp) == 1) {
770 gnutls_free (pemdata.data);
776 mutt_error (_("Warning: Couldn't save certificate"));
780 mutt_message (_("Certificate saved"));
784 case OP_MAX + 2: /* accept once */
789 mutt_menuDestroy (&menu);
790 gnutls_x509_crt_deinit (cert);