ssl-proxy-openssl.c revision 78c27af9d04b830afe3df6495d7a1efee556ecb8
199767f8919635c4928607450d9e0abb932109ceToomas Soome/* Copyright (c) 2002-2015 Dovecot authors, see the included COPYING file */
199767f8919635c4928607450d9e0abb932109ceToomas Soome#if !defined(OPENSSL_NO_ECDH) && OPENSSL_VERSION_NUMBER >= 0x10000000L
199767f8919635c4928607450d9e0abb932109ceToomas Soome/* Check every 30 minutes if parameters file has been updated */
199767f8919635c4928607450d9e0abb932109ceToomas Soome#ifndef SSL_CTRL_SET_TLSEXT_HOSTNAME /* FIXME: this may be unnecessary.. */
199767f8919635c4928607450d9e0abb932109ceToomas Soome const struct master_service_ssl_settings *ssl_set;
199767f8919635c4928607450d9e0abb932109ceToomas Soome struct io *io_ssl_read, *io_ssl_write, *io_plain_read, *io_plain_write;
199767f8919635c4928607450d9e0abb932109ceToomas Soome const char *path;
199767f8919635c4928607450d9e0abb932109ceToomas Soome const char *cert;
199767f8919635c4928607450d9e0abb932109ceToomas Soome const char *key;
199767f8919635c4928607450d9e0abb932109ceToomas Soome const char *ca;
199767f8919635c4928607450d9e0abb932109ceToomas Soomestatic unsigned int ssl_proxy_count;
199767f8919635c4928607450d9e0abb932109ceToomas Soomestatic void plain_read(struct ssl_proxy *proxy);
199767f8919635c4928607450d9e0abb932109ceToomas Soomestatic void ssl_proxy_unref(struct ssl_proxy *proxy);
199767f8919635c4928607450d9e0abb932109ceToomas Soomessl_server_context_init(const struct login_settings *login_set,
199767f8919635c4928607450d9e0abb932109ceToomas Soome const struct master_service_ssl_settings *ssl_set);
199767f8919635c4928607450d9e0abb932109ceToomas Soomestatic void ssl_server_context_deinit(struct ssl_server_context **_ctx);
199767f8919635c4928607450d9e0abb932109ceToomas Soomestatic void ssl_proxy_ctx_set_crypto_params(SSL_CTX *ssl_ctx,
199767f8919635c4928607450d9e0abb932109ceToomas Soome const struct master_service_ssl_settings *set);
199767f8919635c4928607450d9e0abb932109ceToomas Soome#if defined(HAVE_ECDH) && !defined(SSL_CTRL_SET_ECDH_AUTO)
199767f8919635c4928607450d9e0abb932109ceToomas Soomestatic int ssl_proxy_ctx_get_pkey_ec_curve_name(const struct master_service_ssl_settings *set);
199767f8919635c4928607450d9e0abb932109ceToomas Soomestatic void ssl_proxy_destroy_failed(struct ssl_proxy *proxy)
199767f8919635c4928607450d9e0abb932109ceToomas Soomestatic unsigned int ssl_server_context_hash(const struct ssl_server_context *ctx)
199767f8919635c4928607450d9e0abb932109ceToomas Soome unsigned int i, g, h = 0;
199767f8919635c4928607450d9e0abb932109ceToomas Soome /* checking for different certs is typically good enough,
199767f8919635c4928607450d9e0abb932109ceToomas Soome and it should be enough to check only the first few bytes. */
199767f8919635c4928607450d9e0abb932109ceToomas Soome for (i = 0; i < 16 && ctx->cert[i] != '\0'; i++) {
199767f8919635c4928607450d9e0abb932109ceToomas Soome if ((g = h & 0xf0000000UL)) {
199767f8919635c4928607450d9e0abb932109ceToomas Soome h = h ^ (g >> 24);
199767f8919635c4928607450d9e0abb932109ceToomas Soomestatic int ssl_server_context_cmp(const struct ssl_server_context *ctx1,
199767f8919635c4928607450d9e0abb932109ceToomas Soome if (null_strcmp(ctx1->cipher_list, ctx2->cipher_list) != 0)
199767f8919635c4928607450d9e0abb932109ceToomas Soome if (null_strcmp(ctx1->protocols, ctx2->protocols) != 0)
199767f8919635c4928607450d9e0abb932109ceToomas Soome return ctx1->verify_client_cert == ctx2->verify_client_cert ? 0 : 1;
199767f8919635c4928607450d9e0abb932109ceToomas Soomestatic void ssl_params_corrupted(const char *reason)
199767f8919635c4928607450d9e0abb932109ceToomas Soome i_fatal("Corrupted SSL ssl-parameters.dat in state_dir: %s", reason);
199767f8919635c4928607450d9e0abb932109ceToomas Soomestatic void read_next(struct ssl_parameters *params, void *data, size_t size)
199767f8919635c4928607450d9e0abb932109ceToomas Soome if ((ret = read_full(params->fd, data, size)) < 0)
199767f8919635c4928607450d9e0abb932109ceToomas Soomestatic bool read_dh_parameters_next(struct ssl_parameters *params)
199767f8919635c4928607450d9e0abb932109ceToomas Soome unsigned char *buf;
199767f8919635c4928607450d9e0abb932109ceToomas Soome const unsigned char *cbuf;
199767f8919635c4928607450d9e0abb932109ceToomas Soome unsigned int len;
199767f8919635c4928607450d9e0abb932109ceToomas Soome /* read bit size. 0 ends the DH parameters list. */
199767f8919635c4928607450d9e0abb932109ceToomas Soome /* read data size. */
199767f8919635c4928607450d9e0abb932109ceToomas Soome ssl_params_corrupted("Duplicate 512bit parameters");
199767f8919635c4928607450d9e0abb932109ceToomas Soome params->dh_512 = d2i_DHparams(NULL, &cbuf, len);
199767f8919635c4928607450d9e0abb932109ceToomas Soome ssl_params_corrupted("Duplicate default parameters");
199767f8919635c4928607450d9e0abb932109ceToomas Soome params->dh_default = d2i_DHparams(NULL, &cbuf, len);
199767f8919635c4928607450d9e0abb932109ceToomas Soomestatic void ssl_free_parameters(struct ssl_parameters *params)
199767f8919635c4928607450d9e0abb932109ceToomas Soomestatic void ssl_refresh_parameters(struct ssl_parameters *params)
199767f8919635c4928607450d9e0abb932109ceToomas Soome if (params->last_refresh > ioloop_time - SSL_PARAMFILE_CHECK_INTERVAL)
199767f8919635c4928607450d9e0abb932109ceToomas Soome i_error("connect(%s) failed: %m", params->path);
199767f8919635c4928607450d9e0abb932109ceToomas Soome else if (ret != 0) {
199767f8919635c4928607450d9e0abb932109ceToomas Soome /* more data than expected */
199767f8919635c4928607450d9e0abb932109ceToomas Soome ssl_params_corrupted("More data than expected");
199767f8919635c4928607450d9e0abb932109ceToomas Soomestatic void ssl_set_io(struct ssl_proxy *proxy, enum ssl_io_action action)
199767f8919635c4928607450d9e0abb932109ceToomas Soome proxy->io_ssl_read = io_add(proxy->fd_ssl, IO_READ,
199767f8919635c4928607450d9e0abb932109ceToomas Soome proxy->io_ssl_write = io_add(proxy->fd_ssl, IO_WRITE,
199767f8919635c4928607450d9e0abb932109ceToomas Soomestatic void plain_block_input(struct ssl_proxy *proxy, bool block)
199767f8919635c4928607450d9e0abb932109ceToomas Soome proxy->io_plain_read = io_add(proxy->fd_plain, IO_READ,
199767f8919635c4928607450d9e0abb932109ceToomas Soome if (proxy->sslout_size == sizeof(proxy->sslout_buf)) {
199767f8919635c4928607450d9e0abb932109ceToomas Soome /* buffer full, block input until it's written */
199767f8919635c4928607450d9e0abb932109ceToomas Soome while (proxy->sslout_size < sizeof(proxy->sslout_buf) &&
199767f8919635c4928607450d9e0abb932109ceToomas Soomestatic void plain_write(struct ssl_proxy *proxy)
199767f8919635c4928607450d9e0abb932109ceToomas Soome ret = net_transmit(proxy->fd_plain, proxy->plainout_buf,
199767f8919635c4928607450d9e0abb932109ceToomas Soome memmove(proxy->plainout_buf, proxy->plainout_buf + ret,
199767f8919635c4928607450d9e0abb932109ceToomas Soomestatic void ssl_handle_error(struct ssl_proxy *proxy, int ret,
199767f8919635c4928607450d9e0abb932109ceToomas Soome /* eat up the error queue */
199767f8919635c4928607450d9e0abb932109ceToomas Soome else if (ret != 0)
199767f8919635c4928607450d9e0abb932109ceToomas Soome errstr = t_strdup_printf("%s syscall failed: %s",
199767f8919635c4928607450d9e0abb932109ceToomas Soome /* clean connection closing */
199767f8919635c4928607450d9e0abb932109ceToomas Soome if (ERR_GET_REASON(ERR_peek_error()) == ERR_R_MALLOC_FAILURE) {
199767f8919635c4928607450d9e0abb932109ceToomas Soome "You may need to increase service %s { vsz_limit }",
199767f8919635c4928607450d9e0abb932109ceToomas Soome errstr = t_strdup_printf("%s failed: unknown failure %d (%s)",
199767f8919635c4928607450d9e0abb932109ceToomas Soomestatic void ssl_handshake(struct ssl_proxy *proxy)
199767f8919635c4928607450d9e0abb932109ceToomas Soome if (proxy->handshake_callback(proxy->handshake_context) < 0)
199767f8919635c4928607450d9e0abb932109ceToomas Soome while (proxy->plainout_size < sizeof(proxy->plainout_buf) &&
199767f8919635c4928607450d9e0abb932109ceToomas Soome ret = SSL_write(proxy->ssl, proxy->sslout_buf, proxy->sslout_size);
199767f8919635c4928607450d9e0abb932109ceToomas Soome memmove(proxy->sslout_buf, proxy->sslout_buf + ret,
199767f8919635c4928607450d9e0abb932109ceToomas Soome if (proxy->plainout_size == sizeof(proxy->plainout_buf))
199767f8919635c4928607450d9e0abb932109ceToomas Soomessl_proxy_alloc_common(SSL_CTX *ssl_ctx, int fd, const struct ip_addr *ip,
199767f8919635c4928607450d9e0abb932109ceToomas Soome pool_t set_pool, const struct login_settings *login_set,
199767f8919635c4928607450d9e0abb932109ceToomas Soome const struct master_service_ssl_settings *ssl_set,
199767f8919635c4928607450d9e0abb932109ceToomas Soome i_error("SSL support not enabled in configuration");
199767f8919635c4928607450d9e0abb932109ceToomas Soome i_error("SSL_new() failed: %s", openssl_iostream_error());
199767f8919635c4928607450d9e0abb932109ceToomas Soome i_error("SSL_set_fd() failed: %s", openssl_iostream_error());
199767f8919635c4928607450d9e0abb932109ceToomas Soome if (socketpair(AF_UNIX, SOCK_STREAM, 0, sfd) < 0) {
199767f8919635c4928607450d9e0abb932109ceToomas Soomessl_server_context_get(const struct login_settings *login_set,
199767f8919635c4928607450d9e0abb932109ceToomas Soome lookup_ctx.verify_client_cert = set->ssl_verify_client_cert ||
199767f8919635c4928607450d9e0abb932109ceToomas Soome lookup_ctx.prefer_server_ciphers = set->ssl_prefer_server_ciphers;
199767f8919635c4928607450d9e0abb932109ceToomas Soome lookup_ctx.compression = set->parsed_opts.compression;
199767f8919635c4928607450d9e0abb932109ceToomas Soome ctx = hash_table_lookup(ssl_servers, &lookup_ctx);
199767f8919635c4928607450d9e0abb932109ceToomas Soomeint ssl_proxy_alloc(int fd, const struct ip_addr *ip, pool_t set_pool,
199767f8919635c4928607450d9e0abb932109ceToomas Soome const struct master_service_ssl_settings *ssl_set,
199767f8919635c4928607450d9e0abb932109ceToomas Soome ctx = ssl_server_context_get(login_set, ssl_set);
199767f8919635c4928607450d9e0abb932109ceToomas Soome return ssl_proxy_alloc_common(ctx->ctx, fd, ip,
199767f8919635c4928607450d9e0abb932109ceToomas Soomeint ssl_proxy_client_alloc(int fd, struct ip_addr *ip, pool_t set_pool,
199767f8919635c4928607450d9e0abb932109ceToomas Soome const struct master_service_ssl_settings *ssl_set,
199767f8919635c4928607450d9e0abb932109ceToomas Soome ssl_handshake_callback_t *callback, void *context,
199767f8919635c4928607450d9e0abb932109ceToomas Soome ret = ssl_proxy_alloc_common(ssl_client_ctx, fd, ip,
199767f8919635c4928607450d9e0abb932109ceToomas Soomevoid ssl_proxy_set_client(struct ssl_proxy *proxy, struct client *client)
199767f8919635c4928607450d9e0abb932109ceToomas Soomebool ssl_proxy_has_valid_client_cert(const struct ssl_proxy *proxy)
199767f8919635c4928607450d9e0abb932109ceToomas Soome return proxy->cert_received && !proxy->cert_broken;
199767f8919635c4928607450d9e0abb932109ceToomas Soomebool ssl_proxy_has_broken_client_cert(struct ssl_proxy *proxy)
199767f8919635c4928607450d9e0abb932109ceToomas Soome return proxy->cert_received && proxy->cert_broken;
199767f8919635c4928607450d9e0abb932109ceToomas Soomeint ssl_proxy_cert_match_name(struct ssl_proxy *proxy, const char *verify_name)
199767f8919635c4928607450d9e0abb932109ceToomas Soome return openssl_cert_match_name(proxy->ssl, verify_name);
199767f8919635c4928607450d9e0abb932109ceToomas Soomeconst char *ssl_proxy_get_peer_name(struct ssl_proxy *proxy)
199767f8919635c4928607450d9e0abb932109ceToomas Soome len = X509_NAME_get_text_by_NID(X509_get_subject_name(x509),
199767f8919635c4928607450d9e0abb932109ceToomas Soome if (X509_NAME_get_text_by_NID(X509_get_subject_name(x509),
199767f8919635c4928607450d9e0abb932109ceToomas Soome /* NUL characters in name. Someone's trying to fake
199767f8919635c4928607450d9e0abb932109ceToomas Soome being another user? Don't allow it. */
199767f8919635c4928607450d9e0abb932109ceToomas Soomebool ssl_proxy_is_handshaked(const struct ssl_proxy *proxy)
199767f8919635c4928607450d9e0abb932109ceToomas Soomeconst char *ssl_proxy_get_last_error(const struct ssl_proxy *proxy)
199767f8919635c4928607450d9e0abb932109ceToomas Soomeconst char *ssl_proxy_get_security_string(struct ssl_proxy *proxy)
199767f8919635c4928607450d9e0abb932109ceToomas Soome comp_str = comp_str == NULL ? "" : t_strconcat(" ", comp_str, NULL);
199767f8919635c4928607450d9e0abb932109ceToomas Soome return t_strdup_printf("%s with cipher %s (%d/%d bits)%s",
199767f8919635c4928607450d9e0abb932109ceToomas Soomeconst char *ssl_proxy_get_compression(struct ssl_proxy *proxy ATTR_UNUSED)
199767f8919635c4928607450d9e0abb932109ceToomas Soome#if defined(HAVE_SSL_COMPRESSION) && !defined(OPENSSL_NO_COMP)
199767f8919635c4928607450d9e0abb932109ceToomas Soome comp = SSL_get_current_compression(proxy->ssl);
199767f8919635c4928607450d9e0abb932109ceToomas Soome return comp == NULL ? NULL : SSL_COMP_get_name(comp);
199767f8919635c4928607450d9e0abb932109ceToomas Soomeconst char *ssl_proxy_get_cert_error(struct ssl_proxy *proxy)
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_OP_NO_COMPRESSION
#ifdef SSL_OP_NO_TICKET
#ifdef SSL_MODE_RELEASE_BUFFERS
return xnames;
int nid;
const char *curve_name;
#ifdef HAVE_ECDH
#ifdef SSL_CTRL_SET_ECDH_AUTO
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();