ssl-proxy-openssl.c revision 678d0463849ba777106eb7875f27db07a5d8e3df
02c335c23bf5fa225a467c19f2c063fb0dc7b8c3Timo Sirainen/* Copyright (c) 2002-2012 Dovecot authors, see the included COPYING file */
862ec874f9373e3e499e237d3b9f71fdf1413feeTimo Sirainen/* Check every 30 minutes if parameters file has been updated */
862ec874f9373e3e499e237d3b9f71fdf1413feeTimo Sirainen#ifndef SSL_CTRL_SET_TLSEXT_HOSTNAME /* FIXME: this may be unnecessary.. */
2d2ebe91d56e9a158de000c9d0026f65600fbcfaTimo Sirainen const struct master_service_ssl_settings *ssl_set;
e2ce8d4a6ac5d82a906178148453e7613fab9ba0Timo Sirainen struct io *io_ssl_read, *io_ssl_write, *io_plain_read, *io_plain_write;
eb98a038ca8b0ef33d1d11794803ce09547496faTimo Sirainen const char *key;
eb98a038ca8b0ef33d1d11794803ce09547496faTimo Sirainen const char *ca;
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainenstatic unsigned int ssl_proxy_count;
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainenstatic void plain_read(struct ssl_proxy *proxy);
211ed7806d8715ec2280ffbf5d10f0d6e4f1beb2Timo Sirainenstatic void ssl_write(struct ssl_proxy *proxy);
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainenstatic void ssl_proxy_destroy(struct ssl_proxy *proxy);
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainenstatic void ssl_proxy_unref(struct ssl_proxy *proxy);
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainenssl_server_context_init(const struct login_settings *login_set,
59151b71059df1190acd75d8717ed04a7920c862Timo Sirainen const struct master_service_ssl_settings *ssl_set);
59151b71059df1190acd75d8717ed04a7920c862Timo Sirainenstatic void ssl_server_context_deinit(struct ssl_server_context **_ctx);
a10ed8c47534b4c6b6bf2711ccfe577e720a47b4Timo Sirainenstatic unsigned int ssl_server_context_hash(const struct ssl_server_context *ctx)
59151b71059df1190acd75d8717ed04a7920c862Timo Sirainen unsigned int i, g, h = 0;
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen /* checking for different certs is typically good enough,
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen and it should be enough to check only the first few bytes. */
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen for (i = 0; i < 16 && ctx->cert[i] != '\0'; i++) {
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen if ((g = h & 0xf0000000UL)) {
556f95092c3bc850517d5ab2bb502024a55645f1Timo Sirainen h = h ^ (g >> 24);
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainenstatic int ssl_server_context_cmp(const struct ssl_server_context *ctx1,
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen if (null_strcmp(ctx1->cipher_list, ctx2->cipher_list) != 0)
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen if (null_strcmp(ctx1->protocols, ctx2->protocols) != 0)
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen return ctx1->verify_client_cert == ctx2->verify_client_cert ? 0 : 1;
10ff47d5d6146995e16da00d36eca7d162064a7bTimo Sirainenstatic void read_next(struct ssl_parameters *params, void *data, size_t size)
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen if ((ret = read_full(params->fd, data, size)) < 0)
d66ef20c30fee728899ee168c75fcc5ff8fbdac1Timo Sirainenstatic bool read_dh_parameters_next(struct ssl_parameters *params)
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen unsigned char *buf;
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen const unsigned char *cbuf;
d66ef20c30fee728899ee168c75fcc5ff8fbdac1Timo Sirainen unsigned int len;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen /* read bit size. 0 ends the DH parameters list. */
3d77cc0d502dc69ffe2afe318605964dd40b7b20Timo Sirainen /* read data size. */
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen params->dh_512 = d2i_DHparams(NULL, &cbuf, len);
de954ff15b495be13007a8aca2c09fd1d356a283Timo Sirainen params->dh_1024 = d2i_DHparams(NULL, &cbuf, len);
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainenstatic void ssl_free_parameters(struct ssl_parameters *params)
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainenstatic void ssl_refresh_parameters(struct ssl_parameters *params)
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen if (params->last_refresh > ioloop_time - SSL_PARAMFILE_CHECK_INTERVAL)
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen i_error("connect(%s) failed: %m", params->path);
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen else if (ret != 0) {
9de176ef7f3d28ff486c2a8805110b84389e4f19Timo Sirainen /* more data than expected */
0dbcf4026ff8471b4d6d2e14f7e52321396bf087Timo Sirainen i_error("close(%s) failed: %m", params->path);
6cc0546c058f3e6253c6f99727b28dd602712974Timo Sirainenstatic void ssl_set_io(struct ssl_proxy *proxy, enum ssl_io_action action)
9de176ef7f3d28ff486c2a8805110b84389e4f19Timo Sirainen proxy->io_ssl_read = io_add(proxy->fd_ssl, IO_READ,
9de176ef7f3d28ff486c2a8805110b84389e4f19Timo Sirainen proxy->io_ssl_write = io_add(proxy->fd_ssl, IO_WRITE,
555ebb032f9b8f0cdb66f27ce7374734833e7cacTimo Sirainenstatic void plain_block_input(struct ssl_proxy *proxy, bool block)
efe78d3ba24fc866af1c79b9223dc0809ba26cadStephan Bosch proxy->io_plain_read = io_add(proxy->fd_plain, IO_READ,
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainenstatic void plain_read(struct ssl_proxy *proxy)
9de176ef7f3d28ff486c2a8805110b84389e4f19Timo Sirainen if (proxy->sslout_size == sizeof(proxy->sslout_buf)) {
9de176ef7f3d28ff486c2a8805110b84389e4f19Timo Sirainen /* buffer full, block input until it's written */
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen while (proxy->sslout_size < sizeof(proxy->sslout_buf) &&
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainenstatic void plain_write(struct ssl_proxy *proxy)
98a73e104c7b9c3747053e63113451e24daf7f36Timo Sirainen ret = net_transmit(proxy->fd_plain, proxy->plainout_buf,
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen memmove(proxy->plainout_buf, proxy->plainout_buf + ret,
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainenstatic const char *ssl_err2str(unsigned long err, const char *data, int flags)
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen const char *ret;
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainenstatic const char *ssl_last_error(void)
d66ef20c30fee728899ee168c75fcc5ff8fbdac1Timo Sirainen unsigned long err;
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen err = ERR_get_error_line_data(NULL, NULL, &data, &flags);
b90fb7f78aca271243c26074ddd6587cce112a1eTimo Sirainen return "Unknown error";
b90fb7f78aca271243c26074ddd6587cce112a1eTimo Sirainenstatic void ssl_handle_error(struct ssl_proxy *proxy, int ret,
3d77cc0d502dc69ffe2afe318605964dd40b7b20Timo Sirainen /* eat up the error queue */
d66ef20c30fee728899ee168c75fcc5ff8fbdac1Timo Sirainen else if (ret != 0)
8cdb3234fe3c77e477c7a0e6934678f58fc54d4dTimo Sirainen errstr = t_strdup_printf("%s syscall failed: %s",
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen /* clean connection closing */
8ab3d88c56dff2f567193f80cc29821a64e576d1Timo Sirainen if (ERR_GET_REASON(ERR_peek_error()) == ERR_R_MALLOC_FAILURE) {
8ab3d88c56dff2f567193f80cc29821a64e576d1Timo Sirainen "You may need to increase login_process_size");
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen errstr = t_strdup_printf("%s failed: unknown failure %d (%s)",
71da447014454c84828d9dface77219875554d7dTimo Sirainenstatic void ssl_handshake(struct ssl_proxy *proxy)
e2ce8d4a6ac5d82a906178148453e7613fab9ba0Timo Sirainen ssl_handle_error(proxy, ret, "SSL_connect()");
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen if (proxy->handshake_callback(proxy->handshake_context) < 0)
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen while (proxy->plainout_size < sizeof(proxy->plainout_buf) &&
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen ret = SSL_write(proxy->ssl, proxy->sslout_buf, proxy->sslout_size);
d691782ae9eb4f6ab06bc42f1617e889e7c18a3bTimo Sirainen memmove(proxy->sslout_buf, proxy->sslout_buf + ret,
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen if (proxy->plainout_size == sizeof(proxy->plainout_buf))
f28583935a4509d3f7932738bba6c548ef455c82Timo Sirainenssl_proxy_alloc_common(SSL_CTX *ssl_ctx, int fd, const struct ip_addr *ip,
f28583935a4509d3f7932738bba6c548ef455c82Timo Sirainen pool_t set_pool, const struct login_settings *login_set,
f28583935a4509d3f7932738bba6c548ef455c82Timo Sirainen const struct master_service_ssl_settings *ssl_set,
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen i_error("SSL support not enabled in configuration");
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen i_error("SSL_new() failed: %s", ssl_last_error());
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen i_error("SSL_set_fd() failed: %s", ssl_last_error());
981139bb2e446bb2050c1158614725f8413fd709Timo Sirainen if (socketpair(AF_UNIX, SOCK_STREAM, 0, sfd) < 0) {
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainenssl_server_context_get(const struct login_settings *login_set,
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen const struct master_service_ssl_settings *set)
8b2cf1c1bd8ddcea0525b62fd35ba76e136828a1Timo Sirainen lookup_ctx.cipher_list = set->ssl_cipher_list;
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen lookup_ctx.verify_client_cert = set->ssl_verify_client_cert ||
f28583935a4509d3f7932738bba6c548ef455c82Timo Sirainen ctx = hash_table_lookup(ssl_servers, &lookup_ctx);
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen ctx = ssl_server_context_init(login_set, set);
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainenint ssl_proxy_alloc(int fd, const struct ip_addr *ip, pool_t set_pool,
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen const struct master_service_ssl_settings *ssl_set,
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen ctx = ssl_server_context_get(login_set, ssl_set);
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen return ssl_proxy_alloc_common(ctx->ctx, fd, ip,
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainenint ssl_proxy_client_alloc(int fd, struct ip_addr *ip, pool_t set_pool,
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen const struct master_service_ssl_settings *ssl_set,
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen ssl_handshake_callback_t *callback, void *context,
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen ret = ssl_proxy_alloc_common(ssl_client_ctx, fd, ip,
0c1835a90dd1dcedaeaedd1cd91672299cbeb5beTimo Sirainenvoid ssl_proxy_set_client(struct ssl_proxy *proxy, struct client *client)
0c1835a90dd1dcedaeaedd1cd91672299cbeb5beTimo Sirainenbool ssl_proxy_has_valid_client_cert(const struct ssl_proxy *proxy)
f4735bf7ec2019fdc730e9ebdb39e5a4ea580405Timo Sirainen return proxy->cert_received && !proxy->cert_broken;
0c1835a90dd1dcedaeaedd1cd91672299cbeb5beTimo Sirainenbool ssl_proxy_has_broken_client_cert(struct ssl_proxy *proxy)
f4735bf7ec2019fdc730e9ebdb39e5a4ea580405Timo Sirainen return proxy->cert_received && proxy->cert_broken;
f4735bf7ec2019fdc730e9ebdb39e5a4ea580405Timo Sirainenint ssl_proxy_cert_match_name(struct ssl_proxy *proxy, const char *verify_name)
f4735bf7ec2019fdc730e9ebdb39e5a4ea580405Timo Sirainen return openssl_cert_match_name(proxy->ssl, verify_name);
f4735bf7ec2019fdc730e9ebdb39e5a4ea580405Timo Sirainenconst char *ssl_proxy_get_peer_name(struct ssl_proxy *proxy)
if (len < 0)
const char *comp_str;
#ifdef HAVE_SSL_COMPRESSION
return NULL;
} 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;
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;
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();