📄 mutt_ssl_gnutls.c
字号:
/* Copyright (C) 2001 Marco d'Itri <md@linux.it> * Copyright (C) 2001-2004 Andrew McDonald <andrew@mcdonald.org.uk> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */#if HAVE_CONFIG_H# include "config.h"#endif#include <gnutls/gnutls.h>#include <gnutls/x509.h>#ifdef HAVE_GNUTLS_OPENSSL_H#include <gnutls/openssl.h>#endif#include "mutt.h"#include "mutt_socket.h"#include "mutt_curses.h"#include "mutt_menu.h"#include "mutt_ssl.h"#include "mutt_regex.h"typedef struct _tlssockdata{ gnutls_session state; gnutls_certificate_credentials xcred;}tlssockdata;/* local prototypes */static int tls_socket_read (CONNECTION* conn, char* buf, size_t len);static int tls_socket_write (CONNECTION* conn, const char* buf, size_t len);static int tls_socket_open (CONNECTION* conn);static int tls_socket_close (CONNECTION* conn);static int tls_starttls_close (CONNECTION* conn);static int tls_init (void);static int tls_negotiate (CONNECTION* conn);static int tls_check_certificate (CONNECTION* conn);static int tls_init (void){ static unsigned char init_complete = 0; int err; if (init_complete) return 0; err = gnutls_global_init(); if (err < 0) { mutt_error ("gnutls_global_init: %s", gnutls_strerror(err)); mutt_sleep (2); return -1; } init_complete = 1; return 0;}int mutt_ssl_socket_setup (CONNECTION* conn){ if (tls_init() < 0) return -1; conn->conn_open = tls_socket_open; conn->conn_read = tls_socket_read; conn->conn_write = tls_socket_write; conn->conn_close = tls_socket_close; conn->conn_poll = raw_socket_poll; return 0;}static int tls_socket_read (CONNECTION* conn, char* buf, size_t len){ tlssockdata *data = conn->sockdata; int ret; if (!data) { mutt_error (_("Error: no TLS socket open")); mutt_sleep (2); return -1; } ret = gnutls_record_recv (data->state, buf, len); if (gnutls_error_is_fatal(ret) == 1) { mutt_error ("tls_socket_read (%s)", gnutls_strerror (ret)); mutt_sleep (4); return -1; } return ret;}static int tls_socket_write (CONNECTION* conn, const char* buf, size_t len){ tlssockdata *data = conn->sockdata; int ret; if (!data) { mutt_error (_("Error: no TLS socket open")); mutt_sleep (2); return -1; } ret = gnutls_record_send (data->state, buf, len); if (gnutls_error_is_fatal(ret) == 1) { mutt_error ("tls_socket_write (%s)", gnutls_strerror (ret)); mutt_sleep (4); return -1; } return ret;}static int tls_socket_open (CONNECTION* conn){ if (raw_socket_open (conn) < 0) return -1; if (tls_negotiate (conn) < 0) { tls_socket_close (conn); return -1; } return 0;}int mutt_ssl_starttls (CONNECTION* conn){ if (tls_init() < 0) return -1; if (tls_negotiate (conn) < 0) return -1; conn->conn_read = tls_socket_read; conn->conn_write = tls_socket_write; conn->conn_close = tls_starttls_close; return 0;}static int protocol_priority[] = {GNUTLS_TLS1, GNUTLS_SSL3, 0};/* tls_negotiate: After TLS state has been initialised, attempt to negotiate * TLS over the wire, including certificate checks. */static int tls_negotiate (CONNECTION * conn){ tlssockdata *data; int err; data = (tlssockdata *) safe_calloc (1, sizeof (tlssockdata)); conn->sockdata = data; err = gnutls_certificate_allocate_credentials (&data->xcred); if (err < 0) { FREE(&conn->sockdata); mutt_error ("gnutls_certificate_allocate_credentials: %s", gnutls_strerror(err)); mutt_sleep (2); return -1; } gnutls_certificate_set_x509_trust_file (data->xcred, SslCertFile, GNUTLS_X509_FMT_PEM); /* ignore errors, maybe file doesn't exist yet */ if (SslCACertFile) { gnutls_certificate_set_x509_trust_file (data->xcred, SslCACertFile, GNUTLS_X509_FMT_PEM); }/* gnutls_set_x509_client_key (data->xcred, "", ""); gnutls_set_x509_cert_callback (data->xcred, cert_callback);*/ gnutls_init(&data->state, GNUTLS_CLIENT); /* set socket */ gnutls_transport_set_ptr (data->state, (gnutls_transport_ptr)conn->fd); /* disable TLS/SSL protocols as needed */ if (!option(OPTTLSV1) && !option(OPTSSLV3)) { mutt_error (_("All available protocols for TLS/SSL connection disabled")); goto fail; } else if (!option(OPTTLSV1)) { protocol_priority[0] = GNUTLS_SSL3; protocol_priority[1] = 0; } else if (!option(OPTSSLV3)) { protocol_priority[0] = GNUTLS_TLS1; protocol_priority[1] = 0; } /* else use the list set above */ /* We use default priorities (see gnutls documentation), except for protocol version */ gnutls_set_default_priority (data->state); gnutls_protocol_set_priority (data->state, protocol_priority); if (SslDHPrimeBits > 0) { gnutls_dh_set_prime_bits (data->state, SslDHPrimeBits); }/* gnutls_set_cred (data->state, GNUTLS_ANON, NULL);*/ gnutls_credentials_set (data->state, GNUTLS_CRD_CERTIFICATE, data->xcred); err = gnutls_handshake(data->state); while (err == GNUTLS_E_AGAIN || err == GNUTLS_E_INTERRUPTED) { err = gnutls_handshake(data->state); } if (err < 0) { if (err == GNUTLS_E_FATAL_ALERT_RECEIVED) { mutt_error("gnutls_handshake: %s(%s)", gnutls_strerror(err), gnutls_alert_get_name(gnutls_alert_get(data->state))); } else { mutt_error("gnutls_handshake: %s", gnutls_strerror(err)); } mutt_sleep (2); goto fail; } if (!tls_check_certificate(conn)) goto fail; /* set Security Strength Factor (SSF) for SASL */ /* NB: gnutls_cipher_get_key_size() returns key length in bytes */ conn->ssf = gnutls_cipher_get_key_size (gnutls_cipher_get (data->state)) * 8; mutt_message (_("SSL/TLS connection using %s (%s/%s/%s)"), gnutls_protocol_get_name (gnutls_protocol_get_version (data->state)), gnutls_kx_get_name (gnutls_kx_get (data->state)), gnutls_cipher_get_name (gnutls_cipher_get (data->state)), gnutls_mac_get_name (gnutls_mac_get (data->state))); mutt_sleep (0); return 0; fail: gnutls_certificate_free_credentials (data->xcred); gnutls_deinit (data->state); FREE(&conn->sockdata); return -1;}static int tls_socket_close (CONNECTION* conn){ tlssockdata *data = conn->sockdata; if (data) { gnutls_bye (data->state, GNUTLS_SHUT_RDWR); gnutls_certificate_free_credentials (data->xcred); gnutls_deinit (data->state); FREE (&conn->sockdata); } return raw_socket_close (conn);}static int tls_starttls_close (CONNECTION* conn){ int rc; rc = tls_socket_close (conn); conn->conn_read = raw_socket_read; conn->conn_write = raw_socket_write; conn->conn_close = raw_socket_close; return rc;}#define CERT_SEP "-----BEGIN"/* this bit is based on read_ca_file() in gnutls */static int tls_compare_certificates (const gnutls_datum *peercert){ gnutls_datum cert; unsigned char *ptr; FILE *fd1; int ret; gnutls_datum b64_data; unsigned char *b64_data_data; struct stat filestat; if (stat(SslCertFile, &filestat) == -1) return 0; b64_data.size = filestat.st_size+1; b64_data_data = (unsigned char *) safe_calloc (1, b64_data.size); b64_data_data[b64_data.size-1] = '\0'; b64_data.data = b64_data_data; fd1 = fopen(SslCertFile, "r"); if (fd1 == NULL) { return 0; } b64_data.size = fread(b64_data.data, 1, b64_data.size, fd1); fclose(fd1); do { ret = gnutls_pem_base64_decode_alloc(NULL, &b64_data, &cert); if (ret != 0) { FREE (&b64_data_data); return 0; } ptr = (unsigned char *)strstr((char*)b64_data.data, CERT_SEP) + 1; ptr = (unsigned char *)strstr((char*)ptr, CERT_SEP); b64_data.size = b64_data.size - (ptr - b64_data.data); b64_data.data = ptr; if (cert.size == peercert->size) { if (memcmp (cert.data, peercert->data, cert.size) == 0) { /* match found */ gnutls_free(cert.data); FREE (&b64_data_data); return 1; } } gnutls_free(cert.data); } while (ptr != NULL); /* no match found */ FREE (&b64_data_data); return 0;}static void tls_fingerprint (gnutls_digest_algorithm algo, char* s, int l, const gnutls_datum* data){ unsigned char md[36]; size_t n; int j; n = 36; if (gnutls_fingerprint (algo, data, (char *)md, &n) < 0) { snprintf (s, l, _("[unable to calculate]")); } else { for (j = 0; j < (int) n; j++) { char ch[8]; snprintf (ch, 8, "%02X%s", md[j], (j % 2 ? " " : "")); safe_strcat (s, l, ch); } s[2*n+n/2-1] = '\0'; /* don't want trailing space */ }}static char *tls_make_date (time_t t, char *s, size_t len){ struct tm *l = gmtime (&t); if (l) snprintf (s, len, "%s, %d %s %d %02d:%02d:%02d UTC", Weekdays[l->tm_wday], l->tm_mday, Months[l->tm_mon], l->tm_year + 1900, l->tm_hour, l->tm_min, l->tm_sec); else strfcpy (s, _("[invalid date]"), len); return (s);}static int tls_check_stored_hostname (const gnutls_datum *cert, const char *hostname){ char buf[80]; FILE *fp; char *linestr = NULL; size_t linestrsize; int linenum = 0; regex_t preg;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -