1 /* Copyright (C) 2001 Marco d'Itri <md@linux.it>
2 * Copyright (C) 2001-2004 Andrew McDonald <andrew@mcdonald.org.uk>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
23 #include <gnutls/gnutls.h>
24 #include <gnutls/x509.h>
25 #ifdef HAVE_GNUTLS_OPENSSL_H
26 #include <gnutls/openssl.h>
30 #include "mutt_socket.h"
31 #include "mutt_curses.h"
32 #include "mutt_menu.h"
34 #include "mutt_regex.h"
36 typedef struct _tlssockdata {
38 gnutls_certificate_credentials xcred;
41 /* local prototypes */
42 static int tls_socket_read (CONNECTION * conn, char *buf, size_t len);
43 static int tls_socket_write (CONNECTION * conn, const char *buf, size_t len);
44 static int tls_socket_open (CONNECTION * conn);
45 static int tls_socket_close (CONNECTION * conn);
46 static int tls_starttls_close (CONNECTION * conn);
48 static int tls_init (void);
49 static int tls_negotiate (CONNECTION * conn);
50 static int tls_check_certificate (CONNECTION * conn);
53 static int tls_init (void)
55 static unsigned char init_complete = 0;
61 err = gnutls_global_init ();
63 mutt_error ("gnutls_global_init: %s", gnutls_strerror (err));
72 int mutt_gnutls_socket_setup (CONNECTION * conn)
77 conn->conn_open = tls_socket_open;
78 conn->conn_read = tls_socket_read;
79 conn->conn_write = tls_socket_write;
80 conn->conn_close = tls_socket_close;
85 static int tls_socket_read (CONNECTION * conn, char *buf, size_t len)
87 tlssockdata *data = conn->sockdata;
91 mutt_error ("Error: no TLS socket open");
96 ret = gnutls_record_recv (data->state, buf, len);
97 if (gnutls_error_is_fatal (ret) == 1) {
98 mutt_error ("tls_socket_read (%s)", gnutls_strerror (ret));
105 static int tls_socket_write (CONNECTION * conn, const char *buf, size_t len)
107 tlssockdata *data = conn->sockdata;
111 mutt_error ("Error: no TLS socket open");
116 ret = gnutls_record_send (data->state, buf, len);
117 if (gnutls_error_is_fatal (ret) == 1) {
118 mutt_error ("tls_socket_write (%s)", gnutls_strerror (ret));
125 static int tls_socket_open (CONNECTION * conn)
127 if (raw_socket_open (conn) < 0)
130 if (tls_negotiate (conn) < 0) {
131 tls_socket_close (conn);
138 int mutt_gnutls_starttls (CONNECTION * conn)
143 if (tls_negotiate (conn) < 0)
146 conn->conn_read = tls_socket_read;
147 conn->conn_write = tls_socket_write;
148 conn->conn_close = tls_starttls_close;
153 static int protocol_priority[] = { GNUTLS_TLS1, GNUTLS_SSL3, 0 };
155 /* tls_negotiate: After TLS state has been initialised, attempt to negotiate
156 * TLS over the wire, including certificate checks. */
157 static int tls_negotiate (CONNECTION * conn)
162 data = (tlssockdata *) safe_calloc (1, sizeof (tlssockdata));
163 conn->sockdata = data;
164 err = gnutls_certificate_allocate_credentials (&data->xcred);
166 FREE (&conn->sockdata);
167 mutt_error ("gnutls_certificate_allocate_credentials: %s",
168 gnutls_strerror (err));
173 gnutls_certificate_set_x509_trust_file (data->xcred, SslCertFile,
174 GNUTLS_X509_FMT_PEM);
175 /* ignore errors, maybe file doesn't exist yet */
178 gnutls_certificate_set_x509_trust_file (data->xcred, SslCACertFile,
179 GNUTLS_X509_FMT_PEM);
183 gnutls_set_x509_client_key (data->xcred, "", "");
184 gnutls_set_x509_cert_callback (data->xcred, cert_callback);
187 gnutls_init (&data->state, GNUTLS_CLIENT);
190 gnutls_transport_set_ptr (data->state, (gnutls_transport_ptr) conn->fd);
192 /* disable TLS/SSL protocols as needed */
193 if (!option (OPTTLSV1) && !option (OPTSSLV3)) {
194 mutt_error (_("All available protocols for TLS/SSL connection disabled"));
197 else if (!option (OPTTLSV1)) {
198 protocol_priority[0] = GNUTLS_SSL3;
199 protocol_priority[1] = 0;
201 else if (!option (OPTSSLV3)) {
202 protocol_priority[0] = GNUTLS_TLS1;
203 protocol_priority[1] = 0;
207 use the list set above
210 /* We use default priorities (see gnutls documentation),
211 except for protocol version */
212 gnutls_set_default_priority (data->state);
213 gnutls_protocol_set_priority (data->state, protocol_priority);
215 if (SslDHPrimeBits > 0) {
216 gnutls_dh_set_prime_bits (data->state, SslDHPrimeBits);
220 gnutls_set_cred (data->state, GNUTLS_ANON, NULL);
223 gnutls_credentials_set (data->state, GNUTLS_CRD_CERTIFICATE, data->xcred);
225 err = gnutls_handshake (data->state);
227 while (err == GNUTLS_E_AGAIN || err == GNUTLS_E_INTERRUPTED) {
228 err = gnutls_handshake (data->state);
231 if (err == GNUTLS_E_FATAL_ALERT_RECEIVED) {
232 mutt_error ("gnutls_handshake: %s(%s)", gnutls_strerror (err),
233 gnutls_alert_get_name (gnutls_alert_get (data->state)));
236 mutt_error ("gnutls_handshake: %s", gnutls_strerror (err));
242 if (!tls_check_certificate (conn))
245 /* set Security Strength Factor (SSF) for SASL */
246 /* NB: gnutls_cipher_get_key_size() returns key length in bytes */
248 gnutls_cipher_get_key_size (gnutls_cipher_get (data->state)) * 8;
250 mutt_message (_("SSL/TLS connection using %s (%s/%s/%s)"),
251 gnutls_protocol_get_name (gnutls_protocol_get_version
253 gnutls_kx_get_name (gnutls_kx_get (data->state)),
254 gnutls_cipher_get_name (gnutls_cipher_get (data->state)),
255 gnutls_mac_get_name (gnutls_mac_get (data->state)));
261 gnutls_certificate_free_credentials (data->xcred);
262 gnutls_deinit (data->state);
263 FREE (&conn->sockdata);
267 static int tls_socket_close (CONNECTION * conn)
269 tlssockdata *data = conn->sockdata;
272 gnutls_bye (data->state, GNUTLS_SHUT_RDWR);
274 gnutls_certificate_free_credentials (data->xcred);
275 gnutls_deinit (data->state);
276 safe_free ((void **) &conn->sockdata);
279 return raw_socket_close (conn);
282 static int tls_starttls_close (CONNECTION * conn)
286 rc = tls_socket_close (conn);
287 conn->conn_read = raw_socket_read;
288 conn->conn_write = raw_socket_write;
289 conn->conn_close = raw_socket_close;
294 #define CERT_SEP "-----BEGIN"
296 /* this bit is based on read_ca_file() in gnutls */
297 static int tls_compare_certificates (const gnutls_datum * peercert)
303 gnutls_datum b64_data;
304 unsigned char *b64_data_data;
305 struct stat filestat;
307 if (stat (SslCertFile, &filestat) == -1)
310 b64_data.size = filestat.st_size + 1;
311 b64_data_data = (unsigned char *) safe_calloc (1, b64_data.size);
312 b64_data_data[b64_data.size - 1] = '\0';
313 b64_data.data = b64_data_data;
315 fd1 = fopen (SslCertFile, "r");
320 b64_data.size = fread (b64_data.data, 1, b64_data.size, fd1);
324 ret = gnutls_pem_base64_decode_alloc (NULL, &b64_data, &cert);
326 FREE (&b64_data_data);
330 ptr = (unsigned char *) strstr (b64_data.data, CERT_SEP) + 1;
331 ptr = (unsigned char *) strstr (ptr, CERT_SEP);
333 b64_data.size = b64_data.size - (ptr - b64_data.data);
336 if (cert.size == peercert->size) {
337 if (memcmp (cert.data, peercert->data, cert.size) == 0) {
339 gnutls_free (cert.data);
340 FREE (&b64_data_data);
345 gnutls_free (cert.data);
346 } while (ptr != NULL);
349 FREE (&b64_data_data);
353 static void tls_fingerprint (gnutls_digest_algorithm algo,
354 char *s, int l, const gnutls_datum * data)
356 unsigned char md[36];
362 if (gnutls_fingerprint (algo, data, (char *) md, &n) < 0) {
363 snprintf (s, l, _("[unable to calculate]"));
366 for (j = 0; j < (int) n; j++) {
369 snprintf (ch, 8, "%02X%s", md[j], (j % 2 ? " " : ""));
372 s[2 * n + n / 2 - 1] = '\0'; /* don't want trailing space */
376 static char *tls_make_date (time_t t, char *s, size_t len)
378 struct tm *l = gmtime (&t);
381 snprintf (s, len, "%s, %d %s %d %02d:%02d:%02d UTC",
382 Weekdays[l->tm_wday], l->tm_mday, Months[l->tm_mon],
383 l->tm_year + 1900, l->tm_hour, l->tm_min, l->tm_sec);
385 strfcpy (s, _("[invalid date]"), len);
390 static int tls_check_stored_hostname (const gnutls_datum * cert,
391 const char *hostname)
395 char *linestr = NULL;
399 regmatch_t pmatch[3];
401 /* try checking against names stored in stored certs file */
402 if ((fp = fopen (SslCertFile, "r"))) {
405 "^#H ([a-zA-Z0-9_\\.-]+) ([0-9A-F]{4}( [0-9A-F]{4}){7})[ \t]*$",
406 REG_ICASE | REG_EXTENDED) != 0) {
412 tls_fingerprint (GNUTLS_DIG_MD5, buf, sizeof (buf), cert);
414 mutt_read_line (linestr, &linestrsize, fp, &linenum)) != NULL) {
415 if (linestr[0] == '#' && linestr[1] == 'H') {
416 if (regexec (&preg, linestr, 3, pmatch, 0) == 0) {
417 linestr[pmatch[1].rm_eo] = '\0';
418 linestr[pmatch[2].rm_eo] = '\0';
419 if (strcmp (linestr + pmatch[1].rm_so, hostname) == 0 &&
420 strcmp (linestr + pmatch[2].rm_so, buf) == 0) {
422 safe_free ((void **) &linestr);
434 /* not found a matching name */
438 static int tls_check_certificate (CONNECTION * conn)
440 tlssockdata *data = conn->sockdata;
441 gnutls_session state = data->state;
442 char helpstr[SHORT_STRING];
443 char buf[SHORT_STRING];
444 char fpbuf[SHORT_STRING];
446 char dn_common_name[SHORT_STRING];
447 char dn_email[SHORT_STRING];
448 char dn_organization[SHORT_STRING];
449 char dn_organizational_unit[SHORT_STRING];
450 char dn_locality[SHORT_STRING];
451 char dn_province[SHORT_STRING];
452 char dn_country[SHORT_STRING];
454 int done, row, i, ret;
458 const gnutls_datum *cert_list;
459 int cert_list_size = 0;
460 gnutls_certificate_status certstat;
462 gnutls_x509_crt cert;
463 gnutls_datum pemdata;
464 int certerr_expired = 0;
465 int certerr_notyetvalid = 0;
466 int certerr_hostname = 0;
467 int certerr_nottrusted = 0;
468 int certerr_revoked = 0;
469 int certerr_signernotca = 0;
471 if (gnutls_auth_get_type (state) != GNUTLS_CRD_CERTIFICATE) {
472 mutt_error (_("Unable to get certificate from peer"));
477 certstat = gnutls_certificate_verify_peers (state);
479 if (certstat == GNUTLS_E_NO_CERTIFICATE_FOUND) {
480 mutt_error (_("Unable to get certificate from peer"));
485 mutt_error (_("Certificate verification error (%s)"),
486 gnutls_strerror (certstat));
491 /* We only support X.509 certificates (not OpenPGP) at the moment */
492 if (gnutls_certificate_type_get (state) != GNUTLS_CRT_X509) {
493 mutt_error (_("Error certificate is not X.509"));
498 if (gnutls_x509_crt_init (&cert) < 0) {
499 mutt_error (_("Error initialising gnutls certificate data"));
504 cert_list = gnutls_certificate_get_peers (state, &cert_list_size);
506 mutt_error (_("Unable to get certificate from peer"));
511 /* FIXME: Currently only check first certificate in chain. */
512 if (gnutls_x509_crt_import (cert, &cert_list[0], GNUTLS_X509_FMT_DER) < 0) {
513 mutt_error (_("Error processing certificate data"));
518 if (gnutls_x509_crt_get_expiration_time (cert) < time (NULL)) {
522 if (gnutls_x509_crt_get_activation_time (cert) > time (NULL)) {
523 certerr_notyetvalid = 1;
526 if (!gnutls_x509_crt_check_hostname (cert, conn->account.host) &&
527 !tls_check_stored_hostname (&cert_list[0], conn->account.host)) {
528 certerr_hostname = 1;
531 /* see whether certificate is in our cache (certificates file) */
532 if (tls_compare_certificates (&cert_list[0])) {
533 if (certstat & GNUTLS_CERT_INVALID) {
534 /* doesn't matter - have decided is valid because server
535 certificate is in our trusted cache */
536 certstat ^= GNUTLS_CERT_INVALID;
539 if (certstat & GNUTLS_CERT_SIGNER_NOT_FOUND) {
540 /* doesn't matter that we haven't found the signer, since
541 certificate is in our trusted cache */
542 certstat ^= GNUTLS_CERT_SIGNER_NOT_FOUND;
545 if (certstat & GNUTLS_CERT_SIGNER_NOT_CA) {
546 /* Hmm. Not really sure how to handle this, but let's say
547 that we don't care if the CA certificate hasn't got the
548 correct X.509 basic constraints if server certificate is
550 certstat ^= GNUTLS_CERT_SIGNER_NOT_CA;
555 if (certstat & GNUTLS_CERT_REVOKED) {
557 certstat ^= GNUTLS_CERT_REVOKED;
560 if (certstat & GNUTLS_CERT_INVALID) {
561 certerr_nottrusted = 1;
562 certstat ^= GNUTLS_CERT_INVALID;
565 if (certstat & GNUTLS_CERT_SIGNER_NOT_FOUND) {
566 /* NB: already cleared if cert in cache */
567 certerr_nottrusted = 1;
568 certstat ^= GNUTLS_CERT_SIGNER_NOT_FOUND;
571 if (certstat & GNUTLS_CERT_SIGNER_NOT_CA) {
572 /* NB: already cleared if cert in cache */
573 certerr_signernotca = 1;
574 certstat ^= GNUTLS_CERT_SIGNER_NOT_CA;
577 /* OK if signed by (or is) a trusted certificate */
578 /* we've been zeroing the interesting bits in certstat -
579 don't return OK if there are any unhandled bits we don't
581 if (!(certerr_expired || certerr_notyetvalid ||
582 certerr_hostname || certerr_nottrusted) && certstat == 0) {
583 gnutls_x509_crt_deinit (cert);
588 /* interactive check from user */
589 menu = mutt_new_menu ();
591 menu->dialog = (char **) safe_calloc (1, menu->max * sizeof (char *));
592 for (i = 0; i < menu->max; i++)
593 menu->dialog[i] = (char *) safe_calloc (1, SHORT_STRING * sizeof (char));
596 strfcpy (menu->dialog[row], _("This certificate belongs to:"),
600 buflen = sizeof (dn_common_name);
601 if (gnutls_x509_crt_get_dn_by_oid (cert, GNUTLS_OID_X520_COMMON_NAME, 0, 0,
602 dn_common_name, &buflen) != 0)
603 dn_common_name[0] = '\0';
604 buflen = sizeof (dn_email);
605 if (gnutls_x509_crt_get_dn_by_oid (cert, GNUTLS_OID_PKCS9_EMAIL, 0, 0,
606 dn_email, &buflen) != 0)
608 buflen = sizeof (dn_organization);
609 if (gnutls_x509_crt_get_dn_by_oid
610 (cert, GNUTLS_OID_X520_ORGANIZATION_NAME, 0, 0, dn_organization,
612 dn_organization[0] = '\0';
613 buflen = sizeof (dn_organizational_unit);
614 if (gnutls_x509_crt_get_dn_by_oid
615 (cert, GNUTLS_OID_X520_ORGANIZATIONAL_UNIT_NAME, 0, 0,
616 dn_organizational_unit, &buflen) != 0)
617 dn_organizational_unit[0] = '\0';
618 buflen = sizeof (dn_locality);
619 if (gnutls_x509_crt_get_dn_by_oid
620 (cert, GNUTLS_OID_X520_LOCALITY_NAME, 0, 0, dn_locality, &buflen) != 0)
621 dn_locality[0] = '\0';
622 buflen = sizeof (dn_province);
623 if (gnutls_x509_crt_get_dn_by_oid
624 (cert, GNUTLS_OID_X520_STATE_OR_PROVINCE_NAME, 0, 0, dn_province,
626 dn_province[0] = '\0';
627 buflen = sizeof (dn_country);
628 if (gnutls_x509_crt_get_dn_by_oid (cert, GNUTLS_OID_X520_COUNTRY_NAME, 0, 0,
629 dn_country, &buflen) != 0)
630 dn_country[0] = '\0';
632 snprintf (menu->dialog[row++], SHORT_STRING, " %s %s", dn_common_name,
634 snprintf (menu->dialog[row++], SHORT_STRING, " %s", dn_organization);
635 snprintf (menu->dialog[row++], SHORT_STRING, " %s",
636 dn_organizational_unit);
637 snprintf (menu->dialog[row++], SHORT_STRING, " %s %s %s", dn_locality,
638 dn_province, dn_country);
641 strfcpy (menu->dialog[row], _("This certificate was issued by:"),
645 buflen = sizeof (dn_common_name);
646 if (gnutls_x509_crt_get_issuer_dn_by_oid
647 (cert, GNUTLS_OID_X520_COMMON_NAME, 0, 0, dn_common_name, &buflen) != 0)
648 dn_common_name[0] = '\0';
649 buflen = sizeof (dn_email);
650 if (gnutls_x509_crt_get_issuer_dn_by_oid
651 (cert, GNUTLS_OID_PKCS9_EMAIL, 0, 0, dn_email, &buflen) != 0)
653 buflen = sizeof (dn_organization);
654 if (gnutls_x509_crt_get_issuer_dn_by_oid
655 (cert, GNUTLS_OID_X520_ORGANIZATION_NAME, 0, 0, dn_organization,
657 dn_organization[0] = '\0';
658 buflen = sizeof (dn_organizational_unit);
659 if (gnutls_x509_crt_get_issuer_dn_by_oid
660 (cert, GNUTLS_OID_X520_ORGANIZATIONAL_UNIT_NAME, 0, 0,
661 dn_organizational_unit, &buflen) != 0)
662 dn_organizational_unit[0] = '\0';
663 buflen = sizeof (dn_locality);
664 if (gnutls_x509_crt_get_issuer_dn_by_oid
665 (cert, GNUTLS_OID_X520_LOCALITY_NAME, 0, 0, dn_locality, &buflen) != 0)
666 dn_locality[0] = '\0';
667 buflen = sizeof (dn_province);
668 if (gnutls_x509_crt_get_issuer_dn_by_oid
669 (cert, GNUTLS_OID_X520_STATE_OR_PROVINCE_NAME, 0, 0, dn_province,
671 dn_province[0] = '\0';
672 buflen = sizeof (dn_country);
673 if (gnutls_x509_crt_get_issuer_dn_by_oid
674 (cert, GNUTLS_OID_X520_COUNTRY_NAME, 0, 0, dn_country, &buflen) != 0)
675 dn_country[0] = '\0';
677 snprintf (menu->dialog[row++], SHORT_STRING, " %s %s", dn_common_name,
679 snprintf (menu->dialog[row++], SHORT_STRING, " %s", dn_organization);
680 snprintf (menu->dialog[row++], SHORT_STRING, " %s",
681 dn_organizational_unit);
682 snprintf (menu->dialog[row++], SHORT_STRING, " %s %s %s", dn_locality,
683 dn_province, dn_country);
686 snprintf (menu->dialog[row++], SHORT_STRING,
687 _("This certificate is valid"));
689 t = gnutls_x509_crt_get_activation_time (cert);
690 snprintf (menu->dialog[row++], SHORT_STRING, _(" from %s"),
691 tls_make_date (t, datestr, 30));
693 t = gnutls_x509_crt_get_expiration_time (cert);
694 snprintf (menu->dialog[row++], SHORT_STRING, _(" to %s"),
695 tls_make_date (t, datestr, 30));
698 tls_fingerprint (GNUTLS_DIG_SHA, fpbuf, sizeof (fpbuf), &cert_list[0]);
699 snprintf (menu->dialog[row++], SHORT_STRING, _("SHA1 Fingerprint: %s"),
702 tls_fingerprint (GNUTLS_DIG_MD5, fpbuf, sizeof (fpbuf), &cert_list[0]);
703 snprintf (menu->dialog[row++], SHORT_STRING, _("MD5 Fingerprint: %s"),
706 if (certerr_notyetvalid) {
708 strfcpy (menu->dialog[row],
709 _("WARNING: Server certificate is not yet valid"), SHORT_STRING);
711 if (certerr_expired) {
713 strfcpy (menu->dialog[row], _("WARNING: Server certificate has expired"),
716 if (certerr_revoked) {
718 strfcpy (menu->dialog[row],
719 _("WARNING: Server certificate has been revoked"), SHORT_STRING);
721 if (certerr_hostname) {
723 strfcpy (menu->dialog[row],
724 _("WARNING: Server hostname does not match certificate"),
727 if (certerr_signernotca) {
729 strfcpy (menu->dialog[row],
730 _("WARNING: Signer of server certificate is not a CA"),
734 menu->title = _("TLS/SSL Certificate check");
735 /* certificates with bad dates, or that are revoked, must be
736 accepted manually each and every time */
737 if (SslCertFile && !certerr_expired && !certerr_notyetvalid
738 && !certerr_revoked) {
739 menu->prompt = _("(r)eject, accept (o)nce, (a)ccept always");
740 menu->keys = _("roa");
743 menu->prompt = _("(r)eject, accept (o)nce");
744 menu->keys = _("ro");
748 mutt_make_help (buf, sizeof (buf), _("Exit "), MENU_GENERIC, OP_EXIT);
749 strncat (helpstr, buf, sizeof (helpstr));
750 mutt_make_help (buf, sizeof (buf), _("Help"), MENU_GENERIC, OP_HELP);
751 strncat (helpstr, buf, sizeof (helpstr));
752 menu->help = helpstr;
756 switch (mutt_menuLoop (menu)) {
758 case OP_MAX + 1: /* reject */
762 case OP_MAX + 3: /* accept always */
764 if ((fp = fopen (SslCertFile, "a"))) {
765 /* save hostname if necessary */
766 if (certerr_hostname) {
767 fprintf (fp, "#H %s %s\n", conn->account.host, fpbuf);
770 if (certerr_nottrusted) {
772 ret = gnutls_pem_base64_encode_alloc ("CERTIFICATE", &cert_list[0],
775 if (fwrite (pemdata.data, pemdata.size, 1, fp) == 1) {
778 gnutls_free (pemdata.data);
784 mutt_error (_("Warning: Couldn't save certificate"));
788 mutt_message (_("Certificate saved"));
792 case OP_MAX + 2: /* accept once */
797 mutt_menuDestroy (&menu);
798 gnutls_x509_crt_deinit (cert);