/*
* 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/x509v3.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"
#include "mutt_idna.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)
#else
static 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)
#endif
/* keep a handle on accepted certificates in case we want to
* open up another connection to the same server in this session */
static STACK_OF(X509) *SslSessionCerts = NULL;
typedef struct
{
SSL_CTX *ctx;
SSL *ssl;
X509 *cert;
unsigned char isopen;
}
sslsockdata;
/* local prototypes */
static 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 void ssl_err (sslsockdata *data, int err);
static int ssl_cache_trusted_cert (X509 *cert);
static int ssl_check_certificate (CONNECTION *conn, sslsockdata * data);
static int interactive_check_cert (X509 *cert, int idx, int len);
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 (CONNECTION *conn, 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;
long ssl_options = 0;
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.
*
* However, we need to be able to negotiate amongst various TLS versions,
* which at present can only be done with the SSLv23_client_method;
* TLSv1_client_method gives us explicitly TLSv1.0, not 1.1 or 1.2 (True as
* of OpenSSL 1.0.1c)
*/
if (! (ssldata->ctx = SSL_CTX_new (SSLv23_client_method())))
{
dprint (1, (debugfile, "mutt_ssl_starttls: Error allocating SSL_CTX\n"));
goto bail_ssldata;
}
#ifdef SSL_OP_NO_TLSv1_2
if (!option(OPTTLSV1_2))
ssl_options |= SSL_OP_NO_TLSv1_2;
#endif
#ifdef SSL_OP_NO_TLSv1_1
if (!option(OPTTLSV1_1))
ssl_options |= SSL_OP_NO_TLSv1_1;
#endif
#ifdef SSL_OP_NO_TLSv1
if (!option(OPTTLSV1))
ssl_options |= SSL_OP_NO_TLSv1;
#endif
/* these are always set */
#ifdef SSL_OP_NO_SSLv3
ssl_options |= SSL_OP_NO_SSLv3;
#endif
#ifdef SSL_OP_NO_SSLv2
ssl_options |= SSL_OP_NO_SSLv2;
#endif
if (! SSL_CTX_set_options(ssldata->ctx, ssl_options))
{
dprint(1,