ssl-proxy-openssl.c revision a8c5a86d183db25a57bf193c06b41e092ec2e151
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch/* Copyright (c) 2002-2014 Dovecot authors, see the included COPYING file */
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch#if !defined(OPENSSL_NO_ECDH) && OPENSSL_VERSION_NUMBER >= 0x10000000L
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch/* Check every 30 minutes if parameters file has been updated */
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch#ifndef SSL_CTRL_SET_TLSEXT_HOSTNAME /* FIXME: this may be unnecessary.. */
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch const struct master_service_ssl_settings *ssl_set;
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch struct io *io_ssl_read, *io_ssl_write, *io_plain_read, *io_plain_write;
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch const char *key;
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch const char *ca;
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Boschstatic unsigned int ssl_proxy_count;
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Boschstatic void plain_read(struct ssl_proxy *proxy);
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Boschstatic void ssl_write(struct ssl_proxy *proxy);
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Boschstatic void ssl_proxy_destroy(struct ssl_proxy *proxy);
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Boschstatic void ssl_proxy_unref(struct ssl_proxy *proxy);
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Boschssl_server_context_init(const struct login_settings *login_set,
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch const struct master_service_ssl_settings *ssl_set);
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Boschstatic void ssl_server_context_deinit(struct ssl_server_context **_ctx);
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Boschstatic void ssl_proxy_ctx_set_crypto_params(SSL_CTX *ssl_ctx,
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch const struct master_service_ssl_settings *set);
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch#if defined(HAVE_ECDH) && OPENSSL_VERSION_NUMBER < 0x10002000L
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Boschstatic int ssl_proxy_ctx_get_pkey_ec_curve_name(const struct master_service_ssl_settings *set);
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Boschstatic unsigned int ssl_server_context_hash(const struct ssl_server_context *ctx)
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch unsigned int i, g, h = 0;
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch /* checking for different certs is typically good enough,
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch and it should be enough to check only the first few bytes. */
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch for (i = 0; i < 16 && ctx->cert[i] != '\0'; i++) {
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch if ((g = h & 0xf0000000UL)) {
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch h = h ^ (g >> 24);
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Boschstatic int ssl_server_context_cmp(const struct ssl_server_context *ctx1,
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch if (null_strcmp(ctx1->cipher_list, ctx2->cipher_list) != 0)
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch if (null_strcmp(ctx1->protocols, ctx2->protocols) != 0)
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch return ctx1->verify_client_cert == ctx2->verify_client_cert ? 0 : 1;
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Boschstatic void ssl_params_corrupted(const char *reason)
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch i_fatal("Corrupted SSL ssl-parameters.dat in state_dir: %s", reason);
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Boschstatic void read_next(struct ssl_parameters *params, void *data, size_t size)
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch if ((ret = read_full(params->fd, data, size)) < 0)
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Boschstatic bool read_dh_parameters_next(struct ssl_parameters *params)
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch unsigned char *buf;
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch const unsigned char *cbuf;
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch unsigned int len;
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch /* read bit size. 0 ends the DH parameters list. */
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch /* read data size. */
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch ssl_params_corrupted("Duplicate 512bit parameters");
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch params->dh_512 = d2i_DHparams(NULL, &cbuf, len);
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch ssl_params_corrupted("Duplicate default parameters");
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch params->dh_default = d2i_DHparams(NULL, &cbuf, len);
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Boschstatic void ssl_free_parameters(struct ssl_parameters *params)
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Boschstatic void ssl_refresh_parameters(struct ssl_parameters *params)
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch if (params->last_refresh > ioloop_time - SSL_PARAMFILE_CHECK_INTERVAL)
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch i_error("connect(%s) failed: %m", params->path);
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch else if (ret != 0) {
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch /* more data than expected */
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch ssl_params_corrupted("More data than expected");
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch i_error("close(%s) failed: %m", params->path);
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Boschstatic void ssl_set_io(struct ssl_proxy *proxy, enum ssl_io_action action)
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch proxy->io_ssl_read = io_add(proxy->fd_ssl, IO_READ,
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch proxy->io_ssl_write = io_add(proxy->fd_ssl, IO_WRITE,
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Boschstatic void plain_block_input(struct ssl_proxy *proxy, bool block)
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch proxy->io_plain_read = io_add(proxy->fd_plain, IO_READ,
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Boschstatic void plain_read(struct ssl_proxy *proxy)
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch if (proxy->sslout_size == sizeof(proxy->sslout_buf)) {
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch /* buffer full, block input until it's written */
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch while (proxy->sslout_size < sizeof(proxy->sslout_buf) &&
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Boschstatic void plain_write(struct ssl_proxy *proxy)
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch ret = net_transmit(proxy->fd_plain, proxy->plainout_buf,
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch memmove(proxy->plainout_buf, proxy->plainout_buf + ret,
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Boschstatic const char *ssl_err2str(unsigned long err, const char *data, int flags)
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch const char *ret;
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Boschstatic const char *ssl_last_error(void)
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch unsigned long err;
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch err = ERR_get_error_line_data(NULL, NULL, &data, &flags);
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch return "Unknown error";
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Boschstatic void ssl_handle_error(struct ssl_proxy *proxy, int ret,
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch /* eat up the error queue */
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch else if (ret != 0)
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch errstr = t_strdup_printf("%s syscall failed: %s",
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch /* clean connection closing */
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch if (ERR_GET_REASON(ERR_peek_error()) == ERR_R_MALLOC_FAILURE) {
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch "You may need to increase service %s { vsz_limit }",
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Bosch errstr = t_strdup_printf("%s failed: unknown failure %d (%s)",
57e3b63a75335f45cf6cf9cd89317e2e6cec249dStephan Boschstatic void ssl_handshake(struct ssl_proxy *proxy)
int ret;
if (ret <= 0) {
int ret;
if (ret <= 0)
if (!ssl_initialized) {
static struct ssl_server_context *
return ctx;
int ret;
if (ret < 0)
return ret;
char *name;
int len;
return NULL;
if (len < 0)
const char *comp_str;
#ifdef HAVE_SSL_COMPRESSION
return NULL;
case SSL_AD_CLOSE_NOTIFY:
} else if (ret == 0) {
if (!preverify_ok)
if (preverify_ok)
void *userdata)
unsigned int ssl_proxy_get_count(void)
return ssl_proxy_count;
bool load_xnames)
#ifdef SSL_MODE_RELEASE_BUFFERS
return xnames;
int nid;
const char *curve_name;
#ifdef HAVE_ECDH
unsigned long err;
return ssl_last_error();
static const char *ssl_key_load_error(void)
return ssl_last_error();
char *dup_password;
return pkey;
const char *password;
int nid = 0;
const char *password;
return nid;
X509 *x;
int ret = 0;
if (x == NULL)
goto end;
if (ERR_peek_error() != 0)
ret = 0;
if (ret != 0) {
unsigned long err;
ret = 0;
goto end;
end:
return ret;
#ifdef HAVE_SSL_GET_SERVERNAME
const char *host;
void **other_sets;
&other_sets);
static struct ssl_server_context *
#ifdef HAVE_SSL_GET_SERVERNAME
return ctx;
void ssl_proxy_init(void)
unsigned char buf;
ssl_proxy_count = 0;
void ssl_proxy_deinit(void)
if (!ssl_initialized)
EVP_cleanup();