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>
23 #include <lib-lib/lib-lib.h>
25 #include <lib-ui/curses.h>
26 #include <lib-ui/menu.h>
29 #include "mutt_socket.h"
32 typedef struct _tlssockdata {
34 gnutls_certificate_credentials xcred;
37 /* local prototypes */
38 static int tls_socket_read (CONNECTION * conn, char *buf, ssize_t len);
39 static int tls_socket_write (CONNECTION * conn, const char *buf, ssize_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_ssl_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, ssize_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, ssize_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_ssl_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 = p_new(tlssockdata, 1);
159 conn->sockdata = data;
160 err = gnutls_certificate_allocate_credentials (&data->xcred);
162 p_delete(&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)(intptr_t)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 p_delete(&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 p_delete(&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 = p_new(unsigned char, 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 p_delete(&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 p_delete(&b64_data_data);
341 gnutls_free (cert.data);
342 } while (ptr != NULL);
345 p_delete(&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, ssize_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 m_strcpy(s, len, _("[invalid date]"));
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 (m_strcmp(linestr + pmatch[1].rm_so, hostname) == 0 &&
416 m_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;
453 const gnutls_datum *cert_list;
454 unsigned int cert_list_size = 0;
455 gnutls_certificate_status_t 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 if (gnutls_certificate_verify_peers2(state, &certstat) < 0) {
473 mutt_error (_("Certificate verification error (%s)"),
474 gnutls_strerror(certstat));
479 /* We only support X.509 certificates (not OpenPGP) at the moment */
480 if (gnutls_certificate_type_get (state) != GNUTLS_CRT_X509) {
481 mutt_error (_("Certificate is not X.509"));
486 if (gnutls_x509_crt_init (&cert) < 0) {
487 mutt_error (_("Error initialising gnutls certificate data"));
492 cert_list = gnutls_certificate_get_peers (state, &cert_list_size);
494 mutt_error (_("Unable to get certificate from peer"));
499 /* FIXME: Currently only check first certificate in chain. */
500 if (gnutls_x509_crt_import (cert, &cert_list[0], GNUTLS_X509_FMT_DER) < 0) {
501 mutt_error (_("Error processing certificate data"));
506 if (gnutls_x509_crt_get_expiration_time (cert) < time (NULL)) {
510 if (gnutls_x509_crt_get_activation_time (cert) > time (NULL)) {
511 certerr_notyetvalid = 1;
514 if (!gnutls_x509_crt_check_hostname (cert, conn->account.host) &&
515 !tls_check_stored_hostname (&cert_list[0], conn->account.host)) {
516 certerr_hostname = 1;
519 /* see whether certificate is in our cache (certificates file) */
520 if (tls_compare_certificates (&cert_list[0])) {
521 if (certstat & GNUTLS_CERT_INVALID) {
522 /* doesn't matter - have decided is valid because server
523 certificate is in our trusted cache */
524 certstat ^= GNUTLS_CERT_INVALID;
527 if (certstat & GNUTLS_CERT_SIGNER_NOT_FOUND) {
528 /* doesn't matter that we haven't found the signer, since
529 certificate is in our trusted cache */
530 certstat ^= GNUTLS_CERT_SIGNER_NOT_FOUND;
533 if (certstat & GNUTLS_CERT_SIGNER_NOT_CA) {
534 /* Hmm. Not really sure how to handle this, but let's say
535 that we don't care if the CA certificate hasn't got the
536 correct X.509 basic constraints if server certificate is
538 certstat ^= GNUTLS_CERT_SIGNER_NOT_CA;
543 if (certstat & GNUTLS_CERT_REVOKED) {
545 certstat ^= GNUTLS_CERT_REVOKED;
548 if (certstat & GNUTLS_CERT_INVALID) {
549 certerr_nottrusted = 1;
550 certstat ^= GNUTLS_CERT_INVALID;
553 if (certstat & GNUTLS_CERT_SIGNER_NOT_FOUND) {
554 /* NB: already cleared if cert in cache */
555 certerr_nottrusted = 1;
556 certstat ^= GNUTLS_CERT_SIGNER_NOT_FOUND;
559 if (certstat & GNUTLS_CERT_SIGNER_NOT_CA) {
560 /* NB: already cleared if cert in cache */
561 certerr_signernotca = 1;
562 certstat ^= GNUTLS_CERT_SIGNER_NOT_CA;
565 /* OK if signed by (or is) a trusted certificate */
566 /* we've been zeroing the interesting bits in certstat -
567 don't return OK if there are any unhandled bits we don't
569 if (!(certerr_expired || certerr_notyetvalid ||
570 certerr_hostname || certerr_nottrusted) && certstat == 0) {
571 gnutls_x509_crt_deinit (cert);
576 /* interactive check from user */
577 menu = mutt_new_menu ();
579 menu->dialog = p_new(char*, menu->max);
580 for (i = 0; i < menu->max; i++)
581 menu->dialog[i] = p_new(char, SHORT_STRING);
584 m_strcpy(menu->dialog[row], SHORT_STRING,
585 _("This certificate belongs to:"));
588 buflen = sizeof (dn_common_name);
589 if (gnutls_x509_crt_get_dn_by_oid (cert, GNUTLS_OID_X520_COMMON_NAME, 0, 0,
590 dn_common_name, &buflen) != 0)
591 dn_common_name[0] = '\0';
592 buflen = sizeof (dn_email);
593 if (gnutls_x509_crt_get_dn_by_oid (cert, GNUTLS_OID_PKCS9_EMAIL, 0, 0,
594 dn_email, &buflen) != 0)
596 buflen = sizeof (dn_organization);
597 if (gnutls_x509_crt_get_dn_by_oid
598 (cert, GNUTLS_OID_X520_ORGANIZATION_NAME, 0, 0, dn_organization,
600 dn_organization[0] = '\0';
601 buflen = sizeof (dn_organizational_unit);
602 if (gnutls_x509_crt_get_dn_by_oid
603 (cert, GNUTLS_OID_X520_ORGANIZATIONAL_UNIT_NAME, 0, 0,
604 dn_organizational_unit, &buflen) != 0)
605 dn_organizational_unit[0] = '\0';
606 buflen = sizeof (dn_locality);
607 if (gnutls_x509_crt_get_dn_by_oid
608 (cert, GNUTLS_OID_X520_LOCALITY_NAME, 0, 0, dn_locality, &buflen) != 0)
609 dn_locality[0] = '\0';
610 buflen = sizeof (dn_province);
611 if (gnutls_x509_crt_get_dn_by_oid
612 (cert, GNUTLS_OID_X520_STATE_OR_PROVINCE_NAME, 0, 0, dn_province,
614 dn_province[0] = '\0';
615 buflen = sizeof (dn_country);
616 if (gnutls_x509_crt_get_dn_by_oid (cert, GNUTLS_OID_X520_COUNTRY_NAME, 0, 0,
617 dn_country, &buflen) != 0)
618 dn_country[0] = '\0';
620 snprintf (menu->dialog[row++], SHORT_STRING, " %s %s", dn_common_name,
622 snprintf (menu->dialog[row++], SHORT_STRING, " %s", dn_organization);
623 snprintf (menu->dialog[row++], SHORT_STRING, " %s",
624 dn_organizational_unit);
625 snprintf (menu->dialog[row++], SHORT_STRING, " %s %s %s", dn_locality,
626 dn_province, dn_country);
629 m_strcpy(menu->dialog[row], SHORT_STRING,
630 _("This certificate was issued by:"));
633 buflen = sizeof (dn_common_name);
634 if (gnutls_x509_crt_get_issuer_dn_by_oid
635 (cert, GNUTLS_OID_X520_COMMON_NAME, 0, 0, dn_common_name, &buflen) != 0)
636 dn_common_name[0] = '\0';
637 buflen = sizeof (dn_email);
638 if (gnutls_x509_crt_get_issuer_dn_by_oid
639 (cert, GNUTLS_OID_PKCS9_EMAIL, 0, 0, dn_email, &buflen) != 0)
641 buflen = sizeof (dn_organization);
642 if (gnutls_x509_crt_get_issuer_dn_by_oid
643 (cert, GNUTLS_OID_X520_ORGANIZATION_NAME, 0, 0, dn_organization,
645 dn_organization[0] = '\0';
646 buflen = sizeof (dn_organizational_unit);
647 if (gnutls_x509_crt_get_issuer_dn_by_oid
648 (cert, GNUTLS_OID_X520_ORGANIZATIONAL_UNIT_NAME, 0, 0,
649 dn_organizational_unit, &buflen) != 0)
650 dn_organizational_unit[0] = '\0';
651 buflen = sizeof (dn_locality);
652 if (gnutls_x509_crt_get_issuer_dn_by_oid
653 (cert, GNUTLS_OID_X520_LOCALITY_NAME, 0, 0, dn_locality, &buflen) != 0)
654 dn_locality[0] = '\0';
655 buflen = sizeof (dn_province);
656 if (gnutls_x509_crt_get_issuer_dn_by_oid
657 (cert, GNUTLS_OID_X520_STATE_OR_PROVINCE_NAME, 0, 0, dn_province,
659 dn_province[0] = '\0';
660 buflen = sizeof (dn_country);
661 if (gnutls_x509_crt_get_issuer_dn_by_oid
662 (cert, GNUTLS_OID_X520_COUNTRY_NAME, 0, 0, dn_country, &buflen) != 0)
663 dn_country[0] = '\0';
665 snprintf (menu->dialog[row++], SHORT_STRING, " %s %s", dn_common_name,
667 snprintf (menu->dialog[row++], SHORT_STRING, " %s", dn_organization);
668 snprintf (menu->dialog[row++], SHORT_STRING, " %s",
669 dn_organizational_unit);
670 snprintf (menu->dialog[row++], SHORT_STRING, " %s %s %s", dn_locality,
671 dn_province, dn_country);
674 snprintf (menu->dialog[row++], SHORT_STRING,
675 _("This certificate is valid"));
677 t = gnutls_x509_crt_get_activation_time (cert);
678 snprintf (menu->dialog[row++], SHORT_STRING, _(" from %s"),
679 tls_make_date (t, datestr, 30));
681 t = gnutls_x509_crt_get_expiration_time (cert);
682 snprintf (menu->dialog[row++], SHORT_STRING, _(" to %s"),
683 tls_make_date (t, datestr, 30));
686 tls_fingerprint (GNUTLS_DIG_SHA, fpbuf, sizeof (fpbuf), &cert_list[0]);
687 snprintf (menu->dialog[row++], SHORT_STRING, _("SHA1 Fingerprint: %s"),
690 tls_fingerprint (GNUTLS_DIG_MD5, fpbuf, sizeof (fpbuf), &cert_list[0]);
691 snprintf (menu->dialog[row++], SHORT_STRING, _("MD5 Fingerprint: %s"),
694 if (certerr_notyetvalid) {
696 m_strcpy(menu->dialog[row], SHORT_STRING,
697 _("WARNING: Server certificate is not yet valid"));
699 if (certerr_expired) {
701 m_strcpy(menu->dialog[row], SHORT_STRING,
702 _("WARNING: Server certificate has expired"));
704 if (certerr_revoked) {
706 m_strcpy(menu->dialog[row], SHORT_STRING,
707 _("WARNING: Server certificate has been revoked"));
709 if (certerr_hostname) {
711 m_strcpy(menu->dialog[row], SHORT_STRING,
712 _("WARNING: Server hostname does not match certificate"));
714 if (certerr_signernotca) {
716 m_strcpy(menu->dialog[row], SHORT_STRING,
717 _("WARNING: Signer of server certificate is not a CA"));
720 menu->title = _("TLS/SSL Certificate check");
721 /* certificates with bad dates, or that are revoked, must be
722 accepted manually each and every time */
723 if (SslCertFile && !certerr_expired && !certerr_notyetvalid
724 && !certerr_revoked) {
725 menu->prompt = _("(r)eject, accept (o)nce, (a)ccept always");
726 menu->keys = _("roa");
729 menu->prompt = _("(r)eject, accept (o)nce");
730 menu->keys = _("ro");
734 mutt_make_help (buf, sizeof (buf), _("Exit "), MENU_GENERIC, OP_EXIT);
735 strncat (helpstr, buf, sizeof (helpstr));
736 mutt_make_help (buf, sizeof (buf), _("Help"), MENU_GENERIC, OP_HELP);
737 strncat (helpstr, buf, sizeof (helpstr));
738 menu->help = helpstr;
741 set_option (OPTUNBUFFEREDINPUT);
743 switch (mutt_menuLoop (menu)) {
745 case OP_MAX + 1: /* reject */
749 case OP_MAX + 3: /* accept always */
751 if ((fp = fopen (SslCertFile, "a"))) {
752 /* save hostname if necessary */
753 if (certerr_hostname) {
754 fprintf (fp, "#H %s %s\n", conn->account.host, fpbuf);
757 if (certerr_nottrusted) {
759 ret = gnutls_pem_base64_encode_alloc ("CERTIFICATE", &cert_list[0],
762 if (fwrite (pemdata.data, pemdata.size, 1, fp) == 1) {
765 gnutls_free (pemdata.data);
771 mutt_error (_("Warning: Couldn't save certificate"));
775 mutt_message (_("Certificate saved"));
779 case OP_MAX + 2: /* accept once */
784 unset_option (OPTUNBUFFEREDINPUT);
785 mutt_menuDestroy (&menu);
786 gnutls_x509_crt_deinit (cert);
790 #endif /* USE_GNUTLS */