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>
24 #include "mutt_socket.h"
25 #include "mutt_curses.h"
26 #include "mutt_menu.h"
34 typedef struct _tlssockdata {
36 gnutls_certificate_credentials xcred;
39 /* local prototypes */
40 static int tls_socket_read (CONNECTION * conn, char *buf, size_t len);
41 static int tls_socket_write (CONNECTION * conn, const char *buf, size_t len);
42 static int tls_socket_open (CONNECTION * conn);
43 static int tls_socket_close (CONNECTION * conn);
44 static int tls_starttls_close (CONNECTION * conn);
46 static int tls_init (void);
47 static int tls_negotiate (CONNECTION * conn);
48 static int tls_check_certificate (CONNECTION * conn);
51 static int tls_init (void)
53 static unsigned char init_complete = 0;
59 err = gnutls_global_init ();
61 mutt_error (_("gnutls_global_init: %s"), gnutls_strerror (err));
70 int mutt_ssl_socket_setup (CONNECTION * conn)
75 conn->conn_open = tls_socket_open;
76 conn->conn_read = tls_socket_read;
77 conn->conn_write = tls_socket_write;
78 conn->conn_close = tls_socket_close;
83 static int tls_socket_read (CONNECTION * conn, char *buf, size_t len)
85 tlssockdata *data = conn->sockdata;
89 mutt_error (_("Error: no TLS socket open"));
94 ret = gnutls_record_recv (data->state, buf, len);
95 if (gnutls_error_is_fatal (ret) == 1) {
96 mutt_error (_("tls_socket_read (%s)"), gnutls_strerror (ret));
103 static int tls_socket_write (CONNECTION * conn, const char *buf, size_t len)
105 tlssockdata *data = conn->sockdata;
109 mutt_error (_("Error: no TLS socket open"));
114 ret = gnutls_record_send (data->state, buf, len);
115 if (gnutls_error_is_fatal (ret) == 1) {
116 mutt_error (_("tls_socket_write (%s)"), gnutls_strerror (ret));
123 static int tls_socket_open (CONNECTION * conn)
125 if (raw_socket_open (conn) < 0)
128 if (tls_negotiate (conn) < 0) {
129 tls_socket_close (conn);
136 int mutt_ssl_starttls (CONNECTION * conn)
141 if (tls_negotiate (conn) < 0)
144 conn->conn_read = tls_socket_read;
145 conn->conn_write = tls_socket_write;
146 conn->conn_close = tls_starttls_close;
151 static int protocol_priority[] = { GNUTLS_TLS1, GNUTLS_SSL3, 0 };
153 /* tls_negotiate: After TLS state has been initialised, attempt to negotiate
154 * TLS over the wire, including certificate checks. */
155 static int tls_negotiate (CONNECTION * conn)
160 data = (tlssockdata *) mem_calloc (1, sizeof (tlssockdata));
161 conn->sockdata = data;
162 err = gnutls_certificate_allocate_credentials (&data->xcred);
164 mem_free (&conn->sockdata);
165 mutt_error (_("gnutls_certificate_allocate_credentials: %s"),
166 gnutls_strerror (err));
171 gnutls_certificate_set_x509_trust_file (data->xcred, SslCertFile,
172 GNUTLS_X509_FMT_PEM);
173 /* ignore errors, maybe file doesn't exist yet */
176 gnutls_certificate_set_x509_trust_file (data->xcred, SslCACertFile,
177 GNUTLS_X509_FMT_PEM);
181 gnutls_set_x509_client_key (data->xcred, "", "");
182 gnutls_set_x509_cert_callback (data->xcred, cert_callback);
185 gnutls_init (&data->state, GNUTLS_CLIENT);
188 gnutls_transport_set_ptr (data->state, (gnutls_transport_ptr) conn->fd);
190 /* disable TLS/SSL protocols as needed */
191 if (!option (OPTTLSV1) && !option (OPTSSLV3)) {
192 mutt_error (_("All available protocols for TLS/SSL connection disabled"));
195 else if (!option (OPTTLSV1)) {
196 protocol_priority[0] = GNUTLS_SSL3;
197 protocol_priority[1] = 0;
199 else if (!option (OPTSSLV3)) {
200 protocol_priority[0] = GNUTLS_TLS1;
201 protocol_priority[1] = 0;
205 use the list set above
208 /* We use default priorities (see gnutls documentation),
209 except for protocol version */
210 gnutls_set_default_priority (data->state);
211 gnutls_protocol_set_priority (data->state, protocol_priority);
213 if (SslDHPrimeBits > 0) {
214 gnutls_dh_set_prime_bits (data->state, SslDHPrimeBits);
218 gnutls_set_cred (data->state, GNUTLS_ANON, NULL);
221 gnutls_credentials_set (data->state, GNUTLS_CRD_CERTIFICATE, data->xcred);
223 err = gnutls_handshake (data->state);
225 while (err == GNUTLS_E_AGAIN || err == GNUTLS_E_INTERRUPTED) {
226 err = gnutls_handshake (data->state);
229 if (err == GNUTLS_E_FATAL_ALERT_RECEIVED) {
230 mutt_error (_("gnutls_handshake: %s(%s)"), gnutls_strerror (err),
231 gnutls_alert_get_name (gnutls_alert_get (data->state)));
234 mutt_error (_("gnutls_handshake: %s"), gnutls_strerror (err));
240 if (!tls_check_certificate (conn))
243 /* set Security Strength Factor (SSF) for SASL */
244 /* NB: gnutls_cipher_get_key_size() returns key length in bytes */
246 gnutls_cipher_get_key_size (gnutls_cipher_get (data->state)) * 8;
248 mutt_message (_("SSL/TLS connection using %s (%s/%s/%s)"),
249 gnutls_protocol_get_name (gnutls_protocol_get_version
251 gnutls_kx_get_name (gnutls_kx_get (data->state)),
252 gnutls_cipher_get_name (gnutls_cipher_get (data->state)),
253 gnutls_mac_get_name (gnutls_mac_get (data->state)));
259 gnutls_certificate_free_credentials (data->xcred);
260 gnutls_deinit (data->state);
261 mem_free (&conn->sockdata);
265 static int tls_socket_close (CONNECTION * conn)
267 tlssockdata *data = conn->sockdata;
270 gnutls_bye (data->state, GNUTLS_SHUT_RDWR);
272 gnutls_certificate_free_credentials (data->xcred);
273 gnutls_deinit (data->state);
274 mem_free(&conn->sockdata);
277 return raw_socket_close (conn);
280 static int tls_starttls_close (CONNECTION * conn)
284 rc = tls_socket_close (conn);
285 conn->conn_read = raw_socket_read;
286 conn->conn_write = raw_socket_write;
287 conn->conn_close = raw_socket_close;
292 #define CERT_SEP "-----BEGIN"
294 /* this bit is based on read_ca_file() in gnutls */
295 static int tls_compare_certificates (const gnutls_datum * peercert)
301 gnutls_datum b64_data;
302 unsigned char *b64_data_data;
303 struct stat filestat;
305 if (stat (SslCertFile, &filestat) == -1)
308 b64_data.size = filestat.st_size + 1;
309 b64_data_data = (unsigned char *) mem_calloc (1, b64_data.size);
310 b64_data_data[b64_data.size - 1] = '\0';
311 b64_data.data = b64_data_data;
313 fd1 = fopen (SslCertFile, "r");
318 b64_data.size = fread (b64_data.data, 1, b64_data.size, fd1);
322 ret = gnutls_pem_base64_decode_alloc (NULL, &b64_data, &cert);
324 mem_free (&b64_data_data);
328 ptr = (unsigned char *) strstr ((char*) b64_data.data, CERT_SEP) + 1;
329 ptr = (unsigned char *) strstr ((char*) ptr, CERT_SEP);
331 b64_data.size = b64_data.size - (ptr - b64_data.data);
334 if (cert.size == peercert->size) {
335 if (memcmp (cert.data, peercert->data, cert.size) == 0) {
337 gnutls_free (cert.data);
338 mem_free (&b64_data_data);
343 gnutls_free (cert.data);
344 } while (ptr != NULL);
347 mem_free (&b64_data_data);
351 static void tls_fingerprint (gnutls_digest_algorithm algo,
352 char *s, int l, const gnutls_datum * data)
354 unsigned char md[36];
360 if (gnutls_fingerprint (algo, data, (char *) md, &n) < 0) {
361 snprintf (s, l, _("[unable to calculate]"));
364 for (j = 0; j < (int) n; j++) {
367 snprintf (ch, 8, "%02X%s", md[j], (j % 2 ? " " : ""));
370 s[2 * n + n / 2 - 1] = '\0'; /* don't want trailing space */
374 static char *tls_make_date (time_t t, char *s, size_t len)
376 struct tm *l = gmtime (&t);
379 snprintf (s, len, "%s, %d %s %d %02d:%02d:%02d UTC",
380 Weekdays[l->tm_wday], l->tm_mday, Months[l->tm_mon],
381 l->tm_year + 1900, l->tm_hour, l->tm_min, l->tm_sec);
383 strfcpy (s, _("[invalid date]"), len);
388 static int tls_check_stored_hostname (const gnutls_datum * cert,
389 const char *hostname)
393 char *linestr = NULL;
397 regmatch_t pmatch[3];
399 /* try checking against names stored in stored certs file */
400 if ((fp = fopen (SslCertFile, "r"))) {
403 "^#H ([a-zA-Z0-9_\\.-]+) ([0-9A-F]{4}( [0-9A-F]{4}){7})[ \t]*$",
404 REG_ICASE | REG_EXTENDED) != 0) {
410 tls_fingerprint (GNUTLS_DIG_MD5, buf, sizeof (buf), cert);
412 mutt_read_line (linestr, &linestrsize, fp, &linenum)) != NULL) {
413 if (linestr[0] == '#' && linestr[1] == 'H') {
414 if (regexec (&preg, linestr, 3, pmatch, 0) == 0) {
415 linestr[pmatch[1].rm_eo] = '\0';
416 linestr[pmatch[2].rm_eo] = '\0';
417 if (str_cmp (linestr + pmatch[1].rm_so, hostname) == 0 &&
418 str_cmp (linestr + pmatch[2].rm_so, buf) == 0) {
432 /* not found a matching name */
436 static int tls_check_certificate (CONNECTION * conn)
438 tlssockdata *data = conn->sockdata;
439 gnutls_session state = data->state;
440 char helpstr[SHORT_STRING];
441 char buf[SHORT_STRING];
442 char fpbuf[SHORT_STRING];
444 char dn_common_name[SHORT_STRING];
445 char dn_email[SHORT_STRING];
446 char dn_organization[SHORT_STRING];
447 char dn_organizational_unit[SHORT_STRING];
448 char dn_locality[SHORT_STRING];
449 char dn_province[SHORT_STRING];
450 char dn_country[SHORT_STRING];
452 int done, row, i, ret;
455 const gnutls_datum *cert_list;
456 unsigned int cert_list_size = 0;
457 gnutls_certificate_status certstat;
459 gnutls_x509_crt cert;
460 gnutls_datum pemdata;
461 int certerr_expired = 0;
462 int certerr_notyetvalid = 0;
463 int certerr_hostname = 0;
464 int certerr_nottrusted = 0;
465 int certerr_revoked = 0;
466 int certerr_signernotca = 0;
468 if (gnutls_auth_get_type (state) != GNUTLS_CRD_CERTIFICATE) {
469 mutt_error (_("Unable to get certificate from peer"));
474 certstat = gnutls_certificate_verify_peers (state);
476 if (certstat == GNUTLS_E_NO_CERTIFICATE_FOUND) {
477 mutt_error (_("Unable to get certificate from peer"));
482 mutt_error (_("Certificate verification error (%s)"),
483 gnutls_strerror (certstat));
488 /* We only support X.509 certificates (not OpenPGP) at the moment */
489 if (gnutls_certificate_type_get (state) != GNUTLS_CRT_X509) {
490 mutt_error (_("Certificate is not X.509"));
495 if (gnutls_x509_crt_init (&cert) < 0) {
496 mutt_error (_("Error initialising gnutls certificate data"));
501 cert_list = gnutls_certificate_get_peers (state, &cert_list_size);
503 mutt_error (_("Unable to get certificate from peer"));
508 /* FIXME: Currently only check first certificate in chain. */
509 if (gnutls_x509_crt_import (cert, &cert_list[0], GNUTLS_X509_FMT_DER) < 0) {
510 mutt_error (_("Error processing certificate data"));
515 if (gnutls_x509_crt_get_expiration_time (cert) < time (NULL)) {
519 if (gnutls_x509_crt_get_activation_time (cert) > time (NULL)) {
520 certerr_notyetvalid = 1;
523 if (!gnutls_x509_crt_check_hostname (cert, conn->account.host) &&
524 !tls_check_stored_hostname (&cert_list[0], conn->account.host)) {
525 certerr_hostname = 1;
528 /* see whether certificate is in our cache (certificates file) */
529 if (tls_compare_certificates (&cert_list[0])) {
530 if (certstat & GNUTLS_CERT_INVALID) {
531 /* doesn't matter - have decided is valid because server
532 certificate is in our trusted cache */
533 certstat ^= GNUTLS_CERT_INVALID;
536 if (certstat & GNUTLS_CERT_SIGNER_NOT_FOUND) {
537 /* doesn't matter that we haven't found the signer, since
538 certificate is in our trusted cache */
539 certstat ^= GNUTLS_CERT_SIGNER_NOT_FOUND;
542 if (certstat & GNUTLS_CERT_SIGNER_NOT_CA) {
543 /* Hmm. Not really sure how to handle this, but let's say
544 that we don't care if the CA certificate hasn't got the
545 correct X.509 basic constraints if server certificate is
547 certstat ^= GNUTLS_CERT_SIGNER_NOT_CA;
552 if (certstat & GNUTLS_CERT_REVOKED) {
554 certstat ^= GNUTLS_CERT_REVOKED;
557 if (certstat & GNUTLS_CERT_INVALID) {
558 certerr_nottrusted = 1;
559 certstat ^= GNUTLS_CERT_INVALID;
562 if (certstat & GNUTLS_CERT_SIGNER_NOT_FOUND) {
563 /* NB: already cleared if cert in cache */
564 certerr_nottrusted = 1;
565 certstat ^= GNUTLS_CERT_SIGNER_NOT_FOUND;
568 if (certstat & GNUTLS_CERT_SIGNER_NOT_CA) {
569 /* NB: already cleared if cert in cache */
570 certerr_signernotca = 1;
571 certstat ^= GNUTLS_CERT_SIGNER_NOT_CA;
574 /* OK if signed by (or is) a trusted certificate */
575 /* we've been zeroing the interesting bits in certstat -
576 don't return OK if there are any unhandled bits we don't
578 if (!(certerr_expired || certerr_notyetvalid ||
579 certerr_hostname || certerr_nottrusted) && certstat == 0) {
580 gnutls_x509_crt_deinit (cert);
585 /* interactive check from user */
586 menu = mutt_new_menu ();
588 menu->dialog = (char **) mem_calloc (1, menu->max * sizeof (char *));
589 for (i = 0; i < menu->max; i++)
590 menu->dialog[i] = (char *) mem_calloc (1, SHORT_STRING * sizeof (char));
593 strfcpy (menu->dialog[row], _("This certificate belongs to:"),
597 buflen = sizeof (dn_common_name);
598 if (gnutls_x509_crt_get_dn_by_oid (cert, GNUTLS_OID_X520_COMMON_NAME, 0, 0,
599 dn_common_name, &buflen) != 0)
600 dn_common_name[0] = '\0';
601 buflen = sizeof (dn_email);
602 if (gnutls_x509_crt_get_dn_by_oid (cert, GNUTLS_OID_PKCS9_EMAIL, 0, 0,
603 dn_email, &buflen) != 0)
605 buflen = sizeof (dn_organization);
606 if (gnutls_x509_crt_get_dn_by_oid
607 (cert, GNUTLS_OID_X520_ORGANIZATION_NAME, 0, 0, dn_organization,
609 dn_organization[0] = '\0';
610 buflen = sizeof (dn_organizational_unit);
611 if (gnutls_x509_crt_get_dn_by_oid
612 (cert, GNUTLS_OID_X520_ORGANIZATIONAL_UNIT_NAME, 0, 0,
613 dn_organizational_unit, &buflen) != 0)
614 dn_organizational_unit[0] = '\0';
615 buflen = sizeof (dn_locality);
616 if (gnutls_x509_crt_get_dn_by_oid
617 (cert, GNUTLS_OID_X520_LOCALITY_NAME, 0, 0, dn_locality, &buflen) != 0)
618 dn_locality[0] = '\0';
619 buflen = sizeof (dn_province);
620 if (gnutls_x509_crt_get_dn_by_oid
621 (cert, GNUTLS_OID_X520_STATE_OR_PROVINCE_NAME, 0, 0, dn_province,
623 dn_province[0] = '\0';
624 buflen = sizeof (dn_country);
625 if (gnutls_x509_crt_get_dn_by_oid (cert, GNUTLS_OID_X520_COUNTRY_NAME, 0, 0,
626 dn_country, &buflen) != 0)
627 dn_country[0] = '\0';
629 snprintf (menu->dialog[row++], SHORT_STRING, " %s %s", dn_common_name,
631 snprintf (menu->dialog[row++], SHORT_STRING, " %s", dn_organization);
632 snprintf (menu->dialog[row++], SHORT_STRING, " %s",
633 dn_organizational_unit);
634 snprintf (menu->dialog[row++], SHORT_STRING, " %s %s %s", dn_locality,
635 dn_province, dn_country);
638 strfcpy (menu->dialog[row], _("This certificate was issued by:"),
642 buflen = sizeof (dn_common_name);
643 if (gnutls_x509_crt_get_issuer_dn_by_oid
644 (cert, GNUTLS_OID_X520_COMMON_NAME, 0, 0, dn_common_name, &buflen) != 0)
645 dn_common_name[0] = '\0';
646 buflen = sizeof (dn_email);
647 if (gnutls_x509_crt_get_issuer_dn_by_oid
648 (cert, GNUTLS_OID_PKCS9_EMAIL, 0, 0, dn_email, &buflen) != 0)
650 buflen = sizeof (dn_organization);
651 if (gnutls_x509_crt_get_issuer_dn_by_oid
652 (cert, GNUTLS_OID_X520_ORGANIZATION_NAME, 0, 0, dn_organization,
654 dn_organization[0] = '\0';
655 buflen = sizeof (dn_organizational_unit);
656 if (gnutls_x509_crt_get_issuer_dn_by_oid
657 (cert, GNUTLS_OID_X520_ORGANIZATIONAL_UNIT_NAME, 0, 0,
658 dn_organizational_unit, &buflen) != 0)
659 dn_organizational_unit[0] = '\0';
660 buflen = sizeof (dn_locality);
661 if (gnutls_x509_crt_get_issuer_dn_by_oid
662 (cert, GNUTLS_OID_X520_LOCALITY_NAME, 0, 0, dn_locality, &buflen) != 0)
663 dn_locality[0] = '\0';
664 buflen = sizeof (dn_province);
665 if (gnutls_x509_crt_get_issuer_dn_by_oid
666 (cert, GNUTLS_OID_X520_STATE_OR_PROVINCE_NAME, 0, 0, dn_province,
668 dn_province[0] = '\0';
669 buflen = sizeof (dn_country);
670 if (gnutls_x509_crt_get_issuer_dn_by_oid
671 (cert, GNUTLS_OID_X520_COUNTRY_NAME, 0, 0, dn_country, &buflen) != 0)
672 dn_country[0] = '\0';
674 snprintf (menu->dialog[row++], SHORT_STRING, " %s %s", dn_common_name,
676 snprintf (menu->dialog[row++], SHORT_STRING, " %s", dn_organization);
677 snprintf (menu->dialog[row++], SHORT_STRING, " %s",
678 dn_organizational_unit);
679 snprintf (menu->dialog[row++], SHORT_STRING, " %s %s %s", dn_locality,
680 dn_province, dn_country);
683 snprintf (menu->dialog[row++], SHORT_STRING,
684 _("This certificate is valid"));
686 t = gnutls_x509_crt_get_activation_time (cert);
687 snprintf (menu->dialog[row++], SHORT_STRING, _(" from %s"),
688 tls_make_date (t, datestr, 30));
690 t = gnutls_x509_crt_get_expiration_time (cert);
691 snprintf (menu->dialog[row++], SHORT_STRING, _(" to %s"),
692 tls_make_date (t, datestr, 30));
695 tls_fingerprint (GNUTLS_DIG_SHA, fpbuf, sizeof (fpbuf), &cert_list[0]);
696 snprintf (menu->dialog[row++], SHORT_STRING, _("SHA1 Fingerprint: %s"),
699 tls_fingerprint (GNUTLS_DIG_MD5, fpbuf, sizeof (fpbuf), &cert_list[0]);
700 snprintf (menu->dialog[row++], SHORT_STRING, _("MD5 Fingerprint: %s"),
703 if (certerr_notyetvalid) {
705 strfcpy (menu->dialog[row],
706 _("WARNING: Server certificate is not yet valid"), SHORT_STRING);
708 if (certerr_expired) {
710 strfcpy (menu->dialog[row], _("WARNING: Server certificate has expired"),
713 if (certerr_revoked) {
715 strfcpy (menu->dialog[row],
716 _("WARNING: Server certificate has been revoked"), SHORT_STRING);
718 if (certerr_hostname) {
720 strfcpy (menu->dialog[row],
721 _("WARNING: Server hostname does not match certificate"),
724 if (certerr_signernotca) {
726 strfcpy (menu->dialog[row],
727 _("WARNING: Signer of server certificate is not a CA"),
731 menu->title = _("TLS/SSL Certificate check");
732 /* certificates with bad dates, or that are revoked, must be
733 accepted manually each and every time */
734 if (SslCertFile && !certerr_expired && !certerr_notyetvalid
735 && !certerr_revoked) {
736 menu->prompt = _("(r)eject, accept (o)nce, (a)ccept always");
737 menu->keys = _("roa");
740 menu->prompt = _("(r)eject, accept (o)nce");
741 menu->keys = _("ro");
745 mutt_make_help (buf, sizeof (buf), _("Exit "), MENU_GENERIC, OP_EXIT);
746 strncat (helpstr, buf, sizeof (helpstr));
747 mutt_make_help (buf, sizeof (buf), _("Help"), MENU_GENERIC, OP_HELP);
748 strncat (helpstr, buf, sizeof (helpstr));
749 menu->help = helpstr;
752 set_option (OPTUNBUFFEREDINPUT);
754 switch (mutt_menuLoop (menu)) {
756 case OP_MAX + 1: /* reject */
760 case OP_MAX + 3: /* accept always */
762 if ((fp = fopen (SslCertFile, "a"))) {
763 /* save hostname if necessary */
764 if (certerr_hostname) {
765 fprintf (fp, "#H %s %s\n", conn->account.host, fpbuf);
768 if (certerr_nottrusted) {
770 ret = gnutls_pem_base64_encode_alloc ("CERTIFICATE", &cert_list[0],
773 if (fwrite (pemdata.data, pemdata.size, 1, fp) == 1) {
776 gnutls_free (pemdata.data);
782 mutt_error (_("Warning: Couldn't save certificate"));
786 mutt_message (_("Certificate saved"));
790 case OP_MAX + 2: /* accept once */
795 unset_option (OPTUNBUFFEREDINPUT);
796 mutt_menuDestroy (&menu);
797 gnutls_x509_crt_deinit (cert);
801 #endif /* USE_GNUTLS */