📄 mutt_ssl.c
字号:
/* * Copyright (C) 1999-2001 Tommi Komulainen <Tommi.Komulainen@iki.fi> * * 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 <openssl/ssl.h>#include <openssl/x509.h>#include <openssl/err.h>#include <openssl/rand.h>#undef _#include <string.h>#include "mutt.h"#include "mutt_socket.h"#include "mutt_menu.h"#include "mutt_curses.h"#include "mutt_ssl.h"#if OPENSSL_VERSION_NUMBER >= 0x00904000L#define READ_X509_KEY(fp, key) PEM_read_X509(fp, key, NULL, NULL)#else#define READ_X509_KEY(fp, key) PEM_read_X509(fp, key, NULL)#endif/* Just in case OpenSSL doesn't define DEVRANDOM */#ifndef DEVRANDOM#define DEVRANDOM "/dev/urandom"#endif/* This is ugly, but as RAND_status came in on OpenSSL version 0.9.5 * and the code has to support older versions too, this is seemed to * be cleaner way compared to having even uglier #ifdefs all around. */#ifdef HAVE_RAND_STATUS#define HAVE_ENTROPY() (RAND_status() == 1)#elsestatic int entropy_byte_count = 0;/* OpenSSL fills the entropy pool from /dev/urandom if it exists */#define HAVE_ENTROPY() (!access(DEVRANDOM, R_OK) || entropy_byte_count >= 16)#endiftypedef struct _sslsockdata{ SSL_CTX *ctx; SSL *ssl; X509 *cert;}sslsockdata;/* local prototypes */int ssl_init (void);static int add_entropy (const char *file);static int ssl_socket_read (CONNECTION* conn, char* buf, size_t len);static int ssl_socket_write (CONNECTION* conn, const char* buf, size_t len);static int ssl_socket_open (CONNECTION * conn);static int ssl_socket_close (CONNECTION * conn);static int tls_close (CONNECTION* conn);static int ssl_check_certificate (sslsockdata * data);static void ssl_get_client_cert(sslsockdata *ssldata, CONNECTION *conn);static int ssl_passwd_cb(char *buf, int size, int rwflag, void *userdata);static int ssl_negotiate (sslsockdata*);/* mutt_ssl_starttls: Negotiate TLS over an already opened connection. * TODO: Merge this code better with ssl_socket_open. */int mutt_ssl_starttls (CONNECTION* conn){ sslsockdata* ssldata; int maxbits; if (ssl_init()) goto bail; ssldata = (sslsockdata*) safe_calloc (1, sizeof (sslsockdata)); /* the ssl_use_xxx protocol options don't apply. We must use TLS in TLS. */ if (! (ssldata->ctx = SSL_CTX_new (TLSv1_client_method ()))) { dprint (1, (debugfile, "mutt_ssl_starttls: Error allocating SSL_CTX\n")); goto bail_ssldata; } ssl_get_client_cert(ssldata, conn); if (! (ssldata->ssl = SSL_new (ssldata->ctx))) { dprint (1, (debugfile, "mutt_ssl_starttls: Error allocating SSL\n")); goto bail_ctx; } if (SSL_set_fd (ssldata->ssl, conn->fd) != 1) { dprint (1, (debugfile, "mutt_ssl_starttls: Error setting fd\n")); goto bail_ssl; } if (ssl_negotiate (ssldata)) goto bail_ssl; /* hmm. watch out if we're starting TLS over any method other than raw. */ conn->sockdata = ssldata; conn->conn_read = ssl_socket_read; conn->conn_write = ssl_socket_write; conn->conn_close = tls_close; conn->ssf = SSL_CIPHER_get_bits (SSL_get_current_cipher (ssldata->ssl), &maxbits); return 0; bail_ssl: FREE (&ssldata->ssl); bail_ctx: FREE (&ssldata->ctx); bail_ssldata: FREE (&ssldata); bail: return -1;}/* * OpenSSL library needs to be fed with sufficient entropy. On systems * with /dev/urandom, this is done transparently by the library itself, * on other systems we need to fill the entropy pool ourselves. * * Even though only OpenSSL 0.9.5 and later will complain about the * lack of entropy, we try to our best and fill the pool with older * versions also. (That's the reason for the ugly #ifdefs and macros, * otherwise I could have simply #ifdef'd the whole ssl_init funcion) */int ssl_init (void){ char path[_POSIX_PATH_MAX]; static unsigned char init_complete = 0; if (init_complete) return 0; if (! HAVE_ENTROPY()) { /* load entropy from files */ add_entropy (SslEntropyFile); add_entropy (RAND_file_name (path, sizeof (path))); /* load entropy from egd sockets */#ifdef HAVE_RAND_EGD add_entropy (getenv ("EGDSOCKET")); snprintf (path, sizeof(path), "%s/.entropy", NONULL(Homedir)); add_entropy (path); add_entropy ("/tmp/entropy");#endif /* shuffle $RANDFILE (or ~/.rnd if unset) */ RAND_write_file (RAND_file_name (path, sizeof (path))); mutt_clear_error (); if (! HAVE_ENTROPY()) { mutt_error (_("Failed to find enough entropy on your system")); mutt_sleep (2); return -1; } } /* I don't think you can do this just before reading the error. The call * itself might clobber the last SSL error. */ SSL_load_error_strings(); SSL_library_init(); init_complete = 1; return 0;}static int add_entropy (const char *file){ struct stat st; int n = -1; if (!file) return 0; if (stat (file, &st) == -1) return errno == ENOENT ? 0 : -1; mutt_message (_("Filling entropy pool: %s...\n"), file); /* check that the file permissions are secure */ if (st.st_uid != getuid () || ((st.st_mode & (S_IWGRP | S_IRGRP)) != 0) || ((st.st_mode & (S_IWOTH | S_IROTH)) != 0)) { mutt_error (_("%s has insecure permissions!"), file); mutt_sleep (2); return -1; }#ifdef HAVE_RAND_EGD n = RAND_egd (file);#endif if (n <= 0) n = RAND_load_file (file, -1);#ifndef HAVE_RAND_STATUS if (n > 0) entropy_byte_count += n;#endif return n;}static int ssl_socket_open_err (CONNECTION *conn){ mutt_error (_("SSL disabled due the lack of entropy")); mutt_sleep (2); return -1;}int mutt_ssl_socket_setup (CONNECTION * conn){ if (ssl_init() < 0) { conn->conn_open = ssl_socket_open_err; return -1; } conn->conn_open = ssl_socket_open; conn->conn_read = ssl_socket_read; conn->conn_write = ssl_socket_write; conn->conn_close = ssl_socket_close; conn->conn_poll = raw_socket_poll; return 0;}static int ssl_socket_read (CONNECTION* conn, char* buf, size_t len){ sslsockdata *data = conn->sockdata; return SSL_read (data->ssl, buf, len);}static int ssl_socket_write (CONNECTION* conn, const char* buf, size_t len){ sslsockdata *data = conn->sockdata; return SSL_write (data->ssl, buf, len);}static int ssl_socket_open (CONNECTION * conn){ sslsockdata *data; int maxbits; if (raw_socket_open (conn) < 0) return -1; data = (sslsockdata *) safe_calloc (1, sizeof (sslsockdata)); conn->sockdata = data; data->ctx = SSL_CTX_new (SSLv23_client_method ()); /* disable SSL protocols as needed */ if (!option(OPTTLSV1)) { SSL_CTX_set_options(data->ctx, SSL_OP_NO_TLSv1); } if (!option(OPTSSLV2)) { SSL_CTX_set_options(data->ctx, SSL_OP_NO_SSLv2); } if (!option(OPTSSLV3)) { SSL_CTX_set_options(data->ctx, SSL_OP_NO_SSLv3); } ssl_get_client_cert(data, conn); data->ssl = SSL_new (data->ctx); SSL_set_fd (data->ssl, conn->fd); if (ssl_negotiate(data)) { mutt_socket_close (conn); return -1; } conn->ssf = SSL_CIPHER_get_bits (SSL_get_current_cipher (data->ssl), &maxbits); return 0;}/* ssl_negotiate: After SSL state has been initialised, attempt to negotiate * SSL over the wire, including certificate checks. */static int ssl_negotiate (sslsockdata* ssldata){ int err; const char* errmsg;#if OPENSSL_VERSION_NUMBER >= 0x00906000L /* This only exists in 0.9.6 and above. Without it we may get interrupted * reads or writes. Bummer. */ SSL_set_mode (ssldata->ssl, SSL_MODE_AUTO_RETRY);#endif if ((err = SSL_connect (ssldata->ssl)) != 1) { switch (SSL_get_error (ssldata->ssl, err)) { case SSL_ERROR_SYSCALL: errmsg = _("I/O error"); break; case SSL_ERROR_SSL: errmsg = ERR_error_string (ERR_get_error (), NULL); break; default: errmsg = _("unknown error"); } mutt_error (_("SSL failed: %s"), errmsg); mutt_sleep (1); return -1; } ssldata->cert = SSL_get_peer_certificate (ssldata->ssl); if (!ssldata->cert) { mutt_error (_("Unable to get certificate from peer")); mutt_sleep (1); return -1; } if (!ssl_check_certificate (ssldata)) return -1; mutt_message (_("SSL connection using %s (%s)"), SSL_get_cipher_version (ssldata->ssl), SSL_get_cipher_name (ssldata->ssl)); mutt_sleep (0); return 0;}static int ssl_socket_close (CONNECTION * conn){ sslsockdata *data = conn->sockdata; if (data) { SSL_shutdown (data->ssl); /* hold onto this for the life of mutt, in case we want to reconnect. * The purist in me wants a mutt_exit hook. */#if 0 X509_free (data->cert);#endif SSL_free (data->ssl); SSL_CTX_free (data->ctx); FREE (&conn->sockdata); } return raw_socket_close (conn);}static int tls_close (CONNECTION* conn){
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -