ssl-proxy-openssl.c revision fa02962b74d39e8d74c4c307c0210791b2f0a1ca
c25356d5978632df6203437e1953bcb29e0c736fTimo Sirainen/* Copyright (c) 2002-2012 Dovecot authors, see the included COPYING file */
a53cb86b4d733d9c48ee4d285bed477c80825804Timo Sirainen/* Check every 30 minutes if parameters file has been updated */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#ifndef SSL_CTRL_SET_TLSEXT_HOSTNAME /* FIXME: this may be unnecessary.. */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen struct io *io_ssl_read, *io_ssl_write, *io_plain_read, *io_plain_write;
8aacc9e7c84f8376822823ec98c2f551d4919b2eTimo Sirainen const char *key;
2a734f36105e33ab452d057df6bc7a2b7d9f96f0Timo Sirainen const char *ca;
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainenstatic unsigned int ssl_proxy_count;
84ed9f8f3d0e5ed47607ef417618e49e4f865557Timo Sirainenstatic void plain_read(struct ssl_proxy *proxy);
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainenstatic void ssl_write(struct ssl_proxy *proxy);
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainenstatic void ssl_proxy_destroy(struct ssl_proxy *proxy);
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainenstatic void ssl_proxy_unref(struct ssl_proxy *proxy);
b9c44feadade0481b957f2978640afb3317bd1dfTimo Sirainenssl_server_context_init(const struct login_settings *set);
db87d16551d1081ada01f787ea21aa3ed1402c31Timo Sirainenstatic void ssl_server_context_deinit(struct ssl_server_context **_ctx);
db87d16551d1081ada01f787ea21aa3ed1402c31Timo Sirainenstatic unsigned int ssl_server_context_hash(const void *p)
c8adec8db635f5efb13b9879a5f3fb523abdc969Timo Sirainen unsigned int i, g, h = 0;
807b48fe1f6a57b01ed2cc20247d5b5e3facc562Timo Sirainen /* checking for different certs is typically good enough,
807b48fe1f6a57b01ed2cc20247d5b5e3facc562Timo Sirainen and it should be enough to check only the first few bytes. */
807b48fe1f6a57b01ed2cc20247d5b5e3facc562Timo Sirainen for (i = 0; i < 16 && ctx->cert[i] != '\0'; i++) {
db87d16551d1081ada01f787ea21aa3ed1402c31Timo Sirainen if ((g = h & 0xf0000000UL)) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen h = h ^ (g >> 24);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstatic int ssl_server_context_cmp(const void *p1, const void *p2)
2d39dc1a453546892109b35c0d9770369011a13dTimo Sirainen const struct ssl_server_context *ctx1 = p1, *ctx2 = p2;
c8adec8db635f5efb13b9879a5f3fb523abdc969Timo Sirainen if (null_strcmp(ctx1->cipher_list, ctx2->cipher_list) != 0)
d6a1fa1d65c6d1996937802c2482c0f14dd821a7Timo Sirainen if (null_strcmp(ctx1->protocols, ctx2->protocols) != 0)
807b48fe1f6a57b01ed2cc20247d5b5e3facc562Timo Sirainen return ctx1->verify_client_cert == ctx2->verify_client_cert ? 0 : 1;
62f4a199b5c9a0862f486cbf18e195cc621bbe25Timo Sirainenstatic void read_next(struct ssl_parameters *params, void *data, size_t size)
659fe5d24825b160cae512538088020d97a60239Timo Sirainen if ((ret = read_full(params->fd, data, size)) < 0)
26ff8f8a4867bf8e9551a27a2de8c12cd138b065Timo Sirainenstatic bool read_dh_parameters_next(struct ssl_parameters *params)
26ff8f8a4867bf8e9551a27a2de8c12cd138b065Timo Sirainen unsigned char *buf;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen const unsigned char *cbuf;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen unsigned int len;
1f43c8ac132c153c224c4fffe34b2c3075d87ef7Timo Sirainen /* read bit size. 0 ends the DH parameters list. */
b9c44feadade0481b957f2978640afb3317bd1dfTimo Sirainen /* read data size. */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen params->dh_512 = d2i_DHparams(NULL, &cbuf, len);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen params->dh_1024 = d2i_DHparams(NULL, &cbuf, len);
a53cb86b4d733d9c48ee4d285bed477c80825804Timo Sirainenstatic void ssl_free_parameters(struct ssl_parameters *params)
a045c3aba2610c6ed0bf1c346df1c6d8f7b9fbfdTimo Sirainenstatic void ssl_refresh_parameters(struct ssl_parameters *params)
d30da25fb6be1f1c667d93767c9194000194b618Timo Sirainen if (params->last_refresh > ioloop_time - SSL_PARAMFILE_CHECK_INTERVAL)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen i_error("connect(%s) failed: %m", params->path);
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen else if (ret != 0) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* more data than expected */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen i_error("close(%s) failed: %m", params->path);
9404a7b90dcb80d31bd37ee2493f03751acdb1bdTimo Sirainenstatic void ssl_set_io(struct ssl_proxy *proxy, enum ssl_io_action action)
48270badadd82279bfe50ae3d187aea8b0b2b30eTimo Sirainen proxy->io_ssl_read = io_add(proxy->fd_ssl, IO_READ,
e376693bfa3985232c41df99c7010fca22612c89Timo Sirainen proxy->io_ssl_write = io_add(proxy->fd_ssl, IO_WRITE,
d6a1fa1d65c6d1996937802c2482c0f14dd821a7Timo Sirainenstatic void plain_block_input(struct ssl_proxy *proxy, bool block)
b92813e2f96d4b28f989528ed5dd6115da7d9bdbTimo Sirainen proxy->io_plain_read = io_add(proxy->fd_plain, IO_READ,
db5164c9a1129af0cfb11fc18d88da361a8011fbTimo Sirainenstatic void plain_read(struct ssl_proxy *proxy)
d152ccd0d29fae1bc6092bf198ee7eb843202f96Timo Sirainen if (proxy->sslout_size == sizeof(proxy->sslout_buf)) {
d152ccd0d29fae1bc6092bf198ee7eb843202f96Timo Sirainen /* buffer full, block input until it's written */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen while (proxy->sslout_size < sizeof(proxy->sslout_buf) &&
89caf81340a4da959ef18c5f9b9c99824a53066bTimo Sirainenstatic void plain_write(struct ssl_proxy *proxy)
d9de52132072d80b8c268094b879c0ef5a108db3Timo Sirainen ret = net_transmit(proxy->fd_plain, proxy->plainout_buf,
3b80595fcf2001cf7b2fcc6290823e38f4a142fcTimo Sirainen memmove(proxy->plainout_buf, proxy->plainout_buf + ret,
73b50eecfc31750a312e2f940023f522eb07178cTimo Sirainenstatic const char *ssl_err2str(unsigned long err, const char *data, int flags)
73b50eecfc31750a312e2f940023f522eb07178cTimo Sirainen const char *ret;
dd9712b013e5a14939deed84b2e391d89897d2cfTimo Sirainenstatic const char *ssl_last_error(void)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen unsigned long err;
3da614c39dd29f536c485089e67839b4cf89fed3Timo Sirainen err = ERR_get_error_line_data(NULL, NULL, &data, &flags);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return "Unknown error";
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstatic void ssl_handle_error(struct ssl_proxy *proxy, int ret,
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen /* eat up the error queue */
6825360d446542046757b06064282301c4c6b27cTimo Sirainen else if (ret != 0)
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainen errstr = t_strdup_printf("%s syscall failed: %s",
61f5256ef248d35459b53534ae428bf6d016e1c5Timo Sirainen /* clean connection closing */
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainen if (ERR_GET_REASON(ERR_peek_error()) == ERR_R_MALLOC_FAILURE) {
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen "You may need to increase login_process_size");
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen errstr = t_strdup_printf("%s failed: unknown failure %d (%s)",
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstatic void ssl_handshake(struct ssl_proxy *proxy)
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen ssl_handle_error(proxy, ret, "SSL_connect()");
bbf796c17f02538058d7559bfe96d677e5b55015Timo Sirainen if (proxy->handshake_callback(proxy->handshake_context) < 0)
6e07b4251bf6a3cf34019c351a32a65c08392e58Timo Sirainen while (proxy->plainout_size < sizeof(proxy->plainout_buf) &&
88187ee880b4829443e0d55ea7d145d9d5880217Timo Sirainen ret = SSL_write(proxy->ssl, proxy->sslout_buf, proxy->sslout_size);
73b50eecfc31750a312e2f940023f522eb07178cTimo Sirainen memmove(proxy->sslout_buf, proxy->sslout_buf + ret,
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen if (proxy->plainout_size == sizeof(proxy->plainout_buf))
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;
} else if (ret == 0) {
if (!preverify_ok)
if (!preverify_ok)
void *userdata)
unsigned int ssl_proxy_get_count(void)
return ssl_proxy_count;
return xnames;
#ifdef SSL_MODE_RELEASE_BUFFERS
return xnames;
unsigned long err;
return ssl_last_error();
static EVP_PKEY *
char *dup_password;
return pkey;
static const char *ssl_key_load_error(void)
return ssl_last_error();
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);
const char *protocols)
const char *const *tmp;
bool neg;
name++;
if (neg)
include |= proto;
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();