ssl-proxy-openssl.c revision 678d0463849ba777106eb7875f27db07a5d8e3df
02c335c23bf5fa225a467c19f2c063fb0dc7b8c3Timo Sirainen/* Copyright (c) 2002-2012 Dovecot authors, see the included COPYING file */
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen#include "login-common.h"
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen#include "array.h"
0536ccb51d41e3078c3a9fa33e509fb4b2420f95Timo Sirainen#include "ioloop.h"
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen#include "network.h"
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen#include "ostream.h"
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen#include "read-full.h"
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen#include "safe-memset.h"
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen#include "hash.h"
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen#include "llist.h"
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen#include "master-interface.h"
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen#include "master-service-ssl-settings.h"
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen#include "client-common.h"
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen#include "ssl-proxy.h"
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen#include <fcntl.h>
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen#include <unistd.h>
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen#include <sys/stat.h>
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen#ifdef HAVE_OPENSSL
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen
8b247780e911909a9fdc47f69ce6d1478902ad98Timo Sirainen#include "iostream-openssl.h"
8b247780e911909a9fdc47f69ce6d1478902ad98Timo Sirainen#include <openssl/crypto.h>
8b247780e911909a9fdc47f69ce6d1478902ad98Timo Sirainen#include <openssl/engine.h>
8b247780e911909a9fdc47f69ce6d1478902ad98Timo Sirainen#include <openssl/x509.h>
8b247780e911909a9fdc47f69ce6d1478902ad98Timo Sirainen#include <openssl/pem.h>
862ec874f9373e3e499e237d3b9f71fdf1413feeTimo Sirainen#include <openssl/ssl.h>
862ec874f9373e3e499e237d3b9f71fdf1413feeTimo Sirainen#include <openssl/err.h>
862ec874f9373e3e499e237d3b9f71fdf1413feeTimo Sirainen#include <openssl/rand.h>
862ec874f9373e3e499e237d3b9f71fdf1413feeTimo Sirainen
862ec874f9373e3e499e237d3b9f71fdf1413feeTimo Sirainen/* Check every 30 minutes if parameters file has been updated */
862ec874f9373e3e499e237d3b9f71fdf1413feeTimo Sirainen#define SSL_PARAMFILE_CHECK_INTERVAL (60*30)
862ec874f9373e3e499e237d3b9f71fdf1413feeTimo Sirainen
862ec874f9373e3e499e237d3b9f71fdf1413feeTimo Sirainen#define SSL_PARAMETERS_PATH "ssl-params"
862ec874f9373e3e499e237d3b9f71fdf1413feeTimo Sirainen
862ec874f9373e3e499e237d3b9f71fdf1413feeTimo Sirainen#ifndef SSL_CTRL_SET_TLSEXT_HOSTNAME /* FIXME: this may be unnecessary.. */
862ec874f9373e3e499e237d3b9f71fdf1413feeTimo Sirainen# undef HAVE_SSL_GET_SERVERNAME
862ec874f9373e3e499e237d3b9f71fdf1413feeTimo Sirainen#endif
862ec874f9373e3e499e237d3b9f71fdf1413feeTimo Sirainen
862ec874f9373e3e499e237d3b9f71fdf1413feeTimo Sirainenenum ssl_io_action {
862ec874f9373e3e499e237d3b9f71fdf1413feeTimo Sirainen SSL_ADD_INPUT,
862ec874f9373e3e499e237d3b9f71fdf1413feeTimo Sirainen SSL_REMOVE_INPUT,
862ec874f9373e3e499e237d3b9f71fdf1413feeTimo Sirainen SSL_ADD_OUTPUT,
862ec874f9373e3e499e237d3b9f71fdf1413feeTimo Sirainen SSL_REMOVE_OUTPUT
e2ce8d4a6ac5d82a906178148453e7613fab9ba0Timo Sirainen};
e2ce8d4a6ac5d82a906178148453e7613fab9ba0Timo Sirainen
9a48c2243fe98ca8393be7908f84d20c634bcdf9Timo Sirainenstruct ssl_proxy {
9a48c2243fe98ca8393be7908f84d20c634bcdf9Timo Sirainen int refcount;
9a48c2243fe98ca8393be7908f84d20c634bcdf9Timo Sirainen struct ssl_proxy *prev, *next;
9a48c2243fe98ca8393be7908f84d20c634bcdf9Timo Sirainen
9a48c2243fe98ca8393be7908f84d20c634bcdf9Timo Sirainen SSL *ssl;
9a48c2243fe98ca8393be7908f84d20c634bcdf9Timo Sirainen struct client *client;
59e26ff34b05cd971a111f8a42fc60c13d9f688bTimo Sirainen struct ip_addr ip;
9a48c2243fe98ca8393be7908f84d20c634bcdf9Timo Sirainen const struct login_settings *login_set;
2d2ebe91d56e9a158de000c9d0026f65600fbcfaTimo Sirainen const struct master_service_ssl_settings *ssl_set;
e2ce8d4a6ac5d82a906178148453e7613fab9ba0Timo Sirainen pool_t set_pool;
f988b93c2ef773987bcdcbfb4cca39b955e3a392Timo Sirainen
e2ce8d4a6ac5d82a906178148453e7613fab9ba0Timo Sirainen int fd_ssl, fd_plain;
e2ce8d4a6ac5d82a906178148453e7613fab9ba0Timo Sirainen struct io *io_ssl_read, *io_ssl_write, *io_plain_read, *io_plain_write;
e2ce8d4a6ac5d82a906178148453e7613fab9ba0Timo Sirainen
cd56a23e21f1df3f79648cf07e2f4385e2fadebbTimo Sirainen unsigned char plainout_buf[1024];
cd56a23e21f1df3f79648cf07e2f4385e2fadebbTimo Sirainen unsigned int plainout_size;
e2ce8d4a6ac5d82a906178148453e7613fab9ba0Timo Sirainen
cd56a23e21f1df3f79648cf07e2f4385e2fadebbTimo Sirainen unsigned char sslout_buf[1024];
cd56a23e21f1df3f79648cf07e2f4385e2fadebbTimo Sirainen unsigned int sslout_size;
cd56a23e21f1df3f79648cf07e2f4385e2fadebbTimo Sirainen
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen ssl_handshake_callback_t *handshake_callback;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen void *handshake_context;
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen const char *cert_error;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen char *last_error;
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen unsigned int handshaked:1;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen unsigned int destroyed:1;
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen unsigned int cert_received:1;
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen unsigned int cert_broken:1;
571fd6ff94570ee11a72a20b649acfdac2495919Timo Sirainen unsigned int client_proxy:1;
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen};
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainenstruct ssl_parameters {
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen const char *path;
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen time_t last_refresh;
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen int fd;
5a1b498b646b5c5dbd1b3f3861df766f560578c5Timo Sirainen
5a1b498b646b5c5dbd1b3f3861df766f560578c5Timo Sirainen DH *dh_512, *dh_1024;
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen};
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainenstruct ssl_server_context {
eb98a038ca8b0ef33d1d11794803ce09547496faTimo Sirainen SSL_CTX *ctx;
eb98a038ca8b0ef33d1d11794803ce09547496faTimo Sirainen pool_t pool;
eb98a038ca8b0ef33d1d11794803ce09547496faTimo Sirainen
eb98a038ca8b0ef33d1d11794803ce09547496faTimo Sirainen const char *cert;
eb98a038ca8b0ef33d1d11794803ce09547496faTimo Sirainen const char *key;
eb98a038ca8b0ef33d1d11794803ce09547496faTimo Sirainen const char *ca;
eb98a038ca8b0ef33d1d11794803ce09547496faTimo Sirainen const char *cipher_list;
eb98a038ca8b0ef33d1d11794803ce09547496faTimo Sirainen const char *protocols;
eb98a038ca8b0ef33d1d11794803ce09547496faTimo Sirainen bool verify_client_cert;
eb98a038ca8b0ef33d1d11794803ce09547496faTimo Sirainen};
eb98a038ca8b0ef33d1d11794803ce09547496faTimo Sirainen
eb98a038ca8b0ef33d1d11794803ce09547496faTimo Sirainenstatic int extdata_index;
eb98a038ca8b0ef33d1d11794803ce09547496faTimo Sirainenstatic HASH_TABLE(struct ssl_server_context *,
eb98a038ca8b0ef33d1d11794803ce09547496faTimo Sirainen struct ssl_server_context *) ssl_servers;
eb98a038ca8b0ef33d1d11794803ce09547496faTimo Sirainenstatic SSL_CTX *ssl_client_ctx;
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainenstatic unsigned int ssl_proxy_count;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainenstatic struct ssl_proxy *ssl_proxies;
e2ce8d4a6ac5d82a906178148453e7613fab9ba0Timo Sirainenstatic struct ssl_parameters ssl_params;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainenstatic int ssl_username_nid;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainenstatic ENGINE *ssl_engine;
59151b71059df1190acd75d8717ed04a7920c862Timo Sirainen
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainenstatic void plain_read(struct ssl_proxy *proxy);
211ed7806d8715ec2280ffbf5d10f0d6e4f1beb2Timo Sirainenstatic void ssl_read(struct ssl_proxy *proxy);
211ed7806d8715ec2280ffbf5d10f0d6e4f1beb2Timo Sirainenstatic void ssl_write(struct ssl_proxy *proxy);
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainenstatic void ssl_step(struct ssl_proxy *proxy);
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainenstatic void ssl_proxy_destroy(struct ssl_proxy *proxy);
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainenstatic void ssl_proxy_unref(struct ssl_proxy *proxy);
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainenstatic struct ssl_server_context *
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 Sirainen
a10ed8c47534b4c6b6bf2711ccfe577e720a47b4Timo Sirainenstatic unsigned int ssl_server_context_hash(const struct ssl_server_context *ctx)
a10ed8c47534b4c6b6bf2711ccfe577e720a47b4Timo Sirainen{
59151b71059df1190acd75d8717ed04a7920c862Timo Sirainen unsigned int i, g, h = 0;
59151b71059df1190acd75d8717ed04a7920c862Timo Sirainen
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 h = (h << 4) + ctx->cert[i];
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen if ((g = h & 0xf0000000UL)) {
556f95092c3bc850517d5ab2bb502024a55645f1Timo Sirainen h = h ^ (g >> 24);
556f95092c3bc850517d5ab2bb502024a55645f1Timo Sirainen h = h ^ g;
556f95092c3bc850517d5ab2bb502024a55645f1Timo Sirainen }
556f95092c3bc850517d5ab2bb502024a55645f1Timo Sirainen }
556f95092c3bc850517d5ab2bb502024a55645f1Timo Sirainen return h;
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen}
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainenstatic int ssl_server_context_cmp(const struct ssl_server_context *ctx1,
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen const struct ssl_server_context *ctx2)
3d77cc0d502dc69ffe2afe318605964dd40b7b20Timo Sirainen{
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen if (strcmp(ctx1->cert, ctx2->cert) != 0)
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen return 1;
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen if (strcmp(ctx1->key, ctx2->key) != 0)
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen return 1;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen if (null_strcmp(ctx1->ca, ctx2->ca) != 0)
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen return 1;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen if (null_strcmp(ctx1->cipher_list, ctx2->cipher_list) != 0)
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen return 1;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen if (null_strcmp(ctx1->protocols, ctx2->protocols) != 0)
3d77cc0d502dc69ffe2afe318605964dd40b7b20Timo Sirainen return 1;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen return ctx1->verify_client_cert == ctx2->verify_client_cert ? 0 : 1;
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen}
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainenstatic void ssl_params_corrupted(void)
10ff47d5d6146995e16da00d36eca7d162064a7bTimo Sirainen{
10ff47d5d6146995e16da00d36eca7d162064a7bTimo Sirainen i_fatal("Corrupted SSL parameters file: "
10ff47d5d6146995e16da00d36eca7d162064a7bTimo Sirainen PKG_STATEDIR"/ssl-parameters.dat");
10ff47d5d6146995e16da00d36eca7d162064a7bTimo Sirainen}
10ff47d5d6146995e16da00d36eca7d162064a7bTimo Sirainen
10ff47d5d6146995e16da00d36eca7d162064a7bTimo Sirainenstatic void read_next(struct ssl_parameters *params, void *data, size_t size)
10ff47d5d6146995e16da00d36eca7d162064a7bTimo Sirainen{
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen int ret;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen if ((ret = read_full(params->fd, data, size)) < 0)
8f32e59200da904613506f5649ffa4d9f5989cebTimo Sirainen i_fatal("read(%s) failed: %m", params->path);
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen if (ret == 0)
3d77cc0d502dc69ffe2afe318605964dd40b7b20Timo Sirainen ssl_params_corrupted();
d66ef20c30fee728899ee168c75fcc5ff8fbdac1Timo Sirainen}
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
d66ef20c30fee728899ee168c75fcc5ff8fbdac1Timo Sirainenstatic bool read_dh_parameters_next(struct ssl_parameters *params)
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen{
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen unsigned char *buf;
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen const unsigned char *cbuf;
d66ef20c30fee728899ee168c75fcc5ff8fbdac1Timo Sirainen unsigned int len;
6cc0546c058f3e6253c6f99727b28dd602712974Timo Sirainen int bits;
6cc0546c058f3e6253c6f99727b28dd602712974Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen /* read bit size. 0 ends the DH parameters list. */
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen read_next(params, &bits, sizeof(bits));
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen
5238111c460098d9cc8cc22527026138a278b9a4Timo Sirainen if (bits == 0)
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen return FALSE;
5238111c460098d9cc8cc22527026138a278b9a4Timo Sirainen
3d77cc0d502dc69ffe2afe318605964dd40b7b20Timo Sirainen /* read data size. */
c9bf63e9094761767a63ac6b189bcf60bcffdc44Timo Sirainen read_next(params, &len, sizeof(len));
c9bf63e9094761767a63ac6b189bcf60bcffdc44Timo Sirainen if (len > 1024*100) /* should be enough? */
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen ssl_params_corrupted();
5238111c460098d9cc8cc22527026138a278b9a4Timo Sirainen
5238111c460098d9cc8cc22527026138a278b9a4Timo Sirainen buf = i_malloc(len);
68a4946b12583b88fa802e52ebee45cd96056772Timo Sirainen read_next(params, buf, len);
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
68a4946b12583b88fa802e52ebee45cd96056772Timo Sirainen cbuf = buf;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen switch (bits) {
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen case 512:
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen params->dh_512 = d2i_DHparams(NULL, &cbuf, len);
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen break;
de954ff15b495be13007a8aca2c09fd1d356a283Timo Sirainen case 1024:
de954ff15b495be13007a8aca2c09fd1d356a283Timo Sirainen params->dh_1024 = d2i_DHparams(NULL, &cbuf, len);
de954ff15b495be13007a8aca2c09fd1d356a283Timo Sirainen break;
de954ff15b495be13007a8aca2c09fd1d356a283Timo Sirainen default:
de954ff15b495be13007a8aca2c09fd1d356a283Timo Sirainen ssl_params_corrupted();
de954ff15b495be13007a8aca2c09fd1d356a283Timo Sirainen }
de954ff15b495be13007a8aca2c09fd1d356a283Timo Sirainen
de954ff15b495be13007a8aca2c09fd1d356a283Timo Sirainen i_free(buf);
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen return TRUE;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen}
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainenstatic void ssl_free_parameters(struct ssl_parameters *params)
3d77cc0d502dc69ffe2afe318605964dd40b7b20Timo Sirainen{
d66ef20c30fee728899ee168c75fcc5ff8fbdac1Timo Sirainen if (params->dh_512 != NULL) {
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen DH_free(params->dh_512);
d66ef20c30fee728899ee168c75fcc5ff8fbdac1Timo Sirainen params->dh_512 = NULL;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen }
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen if (params->dh_1024 != NULL) {
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen DH_free(params->dh_1024);
d66ef20c30fee728899ee168c75fcc5ff8fbdac1Timo Sirainen params->dh_1024 = NULL;
8f32e59200da904613506f5649ffa4d9f5989cebTimo Sirainen }
6cc0546c058f3e6253c6f99727b28dd602712974Timo Sirainen}
6cc0546c058f3e6253c6f99727b28dd602712974Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainenstatic void ssl_refresh_parameters(struct ssl_parameters *params)
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen{
b92e979748a22925b0770d3004eaab043ed69574Timo Sirainen char c;
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen int ret;
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen if (params->last_refresh > ioloop_time - SSL_PARAMFILE_CHECK_INTERVAL)
efe78d3ba24fc866af1c79b9223dc0809ba26cadStephan Bosch return;
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen params->last_refresh = ioloop_time;
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen params->fd = net_connect_unix(params->path);
35283613d4c04ce18836e9fc431582c87b3710a0Timo Sirainen if (params->fd == -1) {
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen i_error("connect(%s) failed: %m", params->path);
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen return;
9de176ef7f3d28ff486c2a8805110b84389e4f19Timo Sirainen }
9de176ef7f3d28ff486c2a8805110b84389e4f19Timo Sirainen net_set_nonblock(params->fd, FALSE);
9de176ef7f3d28ff486c2a8805110b84389e4f19Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen ssl_free_parameters(params);
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen while (read_dh_parameters_next(params)) ;
0ce5f96804e81cb0f857e7df32c0272f1eed9377Timo Sirainen
0ce5f96804e81cb0f857e7df32c0272f1eed9377Timo Sirainen if ((ret = read_full(params->fd, &c, 1)) < 0)
0ce5f96804e81cb0f857e7df32c0272f1eed9377Timo Sirainen i_fatal("read(%s) failed: %m", params->path);
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen else if (ret != 0) {
9de176ef7f3d28ff486c2a8805110b84389e4f19Timo Sirainen /* more data than expected */
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen ssl_params_corrupted();
0ce5f96804e81cb0f857e7df32c0272f1eed9377Timo Sirainen }
0ce5f96804e81cb0f857e7df32c0272f1eed9377Timo Sirainen
0dbcf4026ff8471b4d6d2e14f7e52321396bf087Timo Sirainen if (close(params->fd) < 0)
0dbcf4026ff8471b4d6d2e14f7e52321396bf087Timo Sirainen i_error("close(%s) failed: %m", params->path);
0ce5f96804e81cb0f857e7df32c0272f1eed9377Timo Sirainen params->fd = -1;
0ce5f96804e81cb0f857e7df32c0272f1eed9377Timo Sirainen}
6cc0546c058f3e6253c6f99727b28dd602712974Timo Sirainen
6cc0546c058f3e6253c6f99727b28dd602712974Timo Sirainenstatic void ssl_set_io(struct ssl_proxy *proxy, enum ssl_io_action action)
6cc0546c058f3e6253c6f99727b28dd602712974Timo Sirainen{
d66ef20c30fee728899ee168c75fcc5ff8fbdac1Timo Sirainen switch (action) {
6cc0546c058f3e6253c6f99727b28dd602712974Timo Sirainen case SSL_ADD_INPUT:
8b2cf1c1bd8ddcea0525b62fd35ba76e136828a1Timo Sirainen if (proxy->io_ssl_read != NULL)
6cc0546c058f3e6253c6f99727b28dd602712974Timo Sirainen break;
9de176ef7f3d28ff486c2a8805110b84389e4f19Timo Sirainen proxy->io_ssl_read = io_add(proxy->fd_ssl, IO_READ,
6cc0546c058f3e6253c6f99727b28dd602712974Timo Sirainen ssl_step, proxy);
6cc0546c058f3e6253c6f99727b28dd602712974Timo Sirainen break;
0ce5f96804e81cb0f857e7df32c0272f1eed9377Timo Sirainen case SSL_REMOVE_INPUT:
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen if (proxy->io_ssl_read != NULL)
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen io_remove(&proxy->io_ssl_read);
9de176ef7f3d28ff486c2a8805110b84389e4f19Timo Sirainen break;
9de176ef7f3d28ff486c2a8805110b84389e4f19Timo Sirainen case SSL_ADD_OUTPUT:
9de176ef7f3d28ff486c2a8805110b84389e4f19Timo Sirainen if (proxy->io_ssl_write != NULL)
9de176ef7f3d28ff486c2a8805110b84389e4f19Timo Sirainen break;
9de176ef7f3d28ff486c2a8805110b84389e4f19Timo Sirainen proxy->io_ssl_write = io_add(proxy->fd_ssl, IO_WRITE,
9de176ef7f3d28ff486c2a8805110b84389e4f19Timo Sirainen ssl_step, proxy);
9de176ef7f3d28ff486c2a8805110b84389e4f19Timo Sirainen break;
9de176ef7f3d28ff486c2a8805110b84389e4f19Timo Sirainen case SSL_REMOVE_OUTPUT:
9de176ef7f3d28ff486c2a8805110b84389e4f19Timo Sirainen if (proxy->io_ssl_write != NULL)
9de176ef7f3d28ff486c2a8805110b84389e4f19Timo Sirainen io_remove(&proxy->io_ssl_write);
9de176ef7f3d28ff486c2a8805110b84389e4f19Timo Sirainen break;
9de176ef7f3d28ff486c2a8805110b84389e4f19Timo Sirainen }
b92e979748a22925b0770d3004eaab043ed69574Timo Sirainen}
555ebb032f9b8f0cdb66f27ce7374734833e7cacTimo Sirainen
555ebb032f9b8f0cdb66f27ce7374734833e7cacTimo Sirainenstatic void plain_block_input(struct ssl_proxy *proxy, bool block)
555ebb032f9b8f0cdb66f27ce7374734833e7cacTimo Sirainen{
555ebb032f9b8f0cdb66f27ce7374734833e7cacTimo Sirainen if (block) {
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen if (proxy->io_plain_read != NULL)
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen io_remove(&proxy->io_plain_read);
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen } else {
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen if (proxy->io_plain_read == NULL) {
efe78d3ba24fc866af1c79b9223dc0809ba26cadStephan Bosch proxy->io_plain_read = io_add(proxy->fd_plain, IO_READ,
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen plain_read, proxy);
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen }
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen }
4e8d6d03c2ff85448df79b181a2ea850fb5d4199Timo Sirainen}
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainenstatic void plain_read(struct ssl_proxy *proxy)
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen{
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen ssize_t ret;
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen bool corked = FALSE;
9de176ef7f3d28ff486c2a8805110b84389e4f19Timo Sirainen
9de176ef7f3d28ff486c2a8805110b84389e4f19Timo Sirainen if (proxy->sslout_size == sizeof(proxy->sslout_buf)) {
9de176ef7f3d28ff486c2a8805110b84389e4f19Timo Sirainen /* buffer full, block input until it's written */
9de176ef7f3d28ff486c2a8805110b84389e4f19Timo Sirainen plain_block_input(proxy, TRUE);
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen return;
9de176ef7f3d28ff486c2a8805110b84389e4f19Timo Sirainen }
9de176ef7f3d28ff486c2a8805110b84389e4f19Timo Sirainen
9de176ef7f3d28ff486c2a8805110b84389e4f19Timo Sirainen proxy->refcount++;
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen while (proxy->sslout_size < sizeof(proxy->sslout_buf) &&
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen !proxy->destroyed) {
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen ret = net_receive(proxy->fd_plain,
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen proxy->sslout_buf + proxy->sslout_size,
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen sizeof(proxy->sslout_buf) -
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen proxy->sslout_size);
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen if (ret <= 0) {
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen if (ret < 0)
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen ssl_proxy_destroy(proxy);
3d77cc0d502dc69ffe2afe318605964dd40b7b20Timo Sirainen break;
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen } else {
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen proxy->sslout_size += ret;
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen if (!corked) {
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen if (net_set_cork(proxy->fd_ssl, TRUE) == 0)
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen corked = TRUE;
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen }
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen ssl_write(proxy);
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen }
98a73e104c7b9c3747053e63113451e24daf7f36Timo Sirainen }
9de176ef7f3d28ff486c2a8805110b84389e4f19Timo Sirainen
9de176ef7f3d28ff486c2a8805110b84389e4f19Timo Sirainen if (corked)
9de176ef7f3d28ff486c2a8805110b84389e4f19Timo Sirainen net_set_cork(proxy->fd_ssl, FALSE);
9de176ef7f3d28ff486c2a8805110b84389e4f19Timo Sirainen
9de176ef7f3d28ff486c2a8805110b84389e4f19Timo Sirainen ssl_proxy_unref(proxy);
9de176ef7f3d28ff486c2a8805110b84389e4f19Timo Sirainen}
f988b93c2ef773987bcdcbfb4cca39b955e3a392Timo Sirainen
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainenstatic void plain_write(struct ssl_proxy *proxy)
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen{
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen ssize_t ret;
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen
98a73e104c7b9c3747053e63113451e24daf7f36Timo Sirainen proxy->refcount++;
98a73e104c7b9c3747053e63113451e24daf7f36Timo Sirainen
98a73e104c7b9c3747053e63113451e24daf7f36Timo Sirainen ret = net_transmit(proxy->fd_plain, proxy->plainout_buf,
98a73e104c7b9c3747053e63113451e24daf7f36Timo Sirainen proxy->plainout_size);
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen if (ret < 0)
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen ssl_proxy_destroy(proxy);
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen else {
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen proxy->plainout_size -= ret;
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen memmove(proxy->plainout_buf, proxy->plainout_buf + ret,
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen proxy->plainout_size);
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen if (proxy->plainout_size > 0) {
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen if (proxy->io_plain_write == NULL) {
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen proxy->io_plain_write =
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen io_add(proxy->fd_plain, IO_WRITE,
6adf683655750bcb809275cd65dc75fd12214198Timo Sirainen plain_write, proxy);
6adf683655750bcb809275cd65dc75fd12214198Timo Sirainen }
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen } else {
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen if (proxy->io_plain_write != NULL)
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen io_remove(&proxy->io_plain_write);
6adf683655750bcb809275cd65dc75fd12214198Timo Sirainen }
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen ssl_set_io(proxy, SSL_ADD_INPUT);
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen if (SSL_pending(proxy->ssl) > 0)
6adf683655750bcb809275cd65dc75fd12214198Timo Sirainen ssl_read(proxy);
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen }
6adf683655750bcb809275cd65dc75fd12214198Timo Sirainen
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen ssl_proxy_unref(proxy);
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen}
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainenstatic const char *ssl_err2str(unsigned long err, const char *data, int flags)
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen{
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen const char *ret;
6adf683655750bcb809275cd65dc75fd12214198Timo Sirainen char *buf;
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen size_t err_size = 256;
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen buf = t_malloc(err_size);
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen buf[err_size-1] = '\0';
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen ERR_error_string_n(err, buf, err_size-1);
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen ret = buf;
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen if ((flags & ERR_TXT_STRING) != 0)
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen ret = t_strdup_printf("%s: %s", buf, data);
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen return ret;
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen}
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainenstatic const char *ssl_last_error(void)
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen{
d66ef20c30fee728899ee168c75fcc5ff8fbdac1Timo Sirainen unsigned long err;
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen const char *data;
6adf683655750bcb809275cd65dc75fd12214198Timo Sirainen int flags;
6adf683655750bcb809275cd65dc75fd12214198Timo Sirainen
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen err = ERR_get_error_line_data(NULL, NULL, &data, &flags);
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen while (err != 0 && ERR_peek_error() != 0) {
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen i_error("SSL: Stacked error: %s",
b90fb7f78aca271243c26074ddd6587cce112a1eTimo Sirainen ssl_err2str(err, data, flags));
b90fb7f78aca271243c26074ddd6587cce112a1eTimo Sirainen err = ERR_get_error();
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen }
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen if (err == 0) {
b90fb7f78aca271243c26074ddd6587cce112a1eTimo Sirainen if (errno != 0)
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen return strerror(errno);
b90fb7f78aca271243c26074ddd6587cce112a1eTimo Sirainen return "Unknown error";
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen }
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen return ssl_err2str(err, data, flags);
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen}
b90fb7f78aca271243c26074ddd6587cce112a1eTimo Sirainen
b90fb7f78aca271243c26074ddd6587cce112a1eTimo Sirainenstatic void ssl_handle_error(struct ssl_proxy *proxy, int ret,
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen const char *func_name)
b90fb7f78aca271243c26074ddd6587cce112a1eTimo Sirainen{
b90fb7f78aca271243c26074ddd6587cce112a1eTimo Sirainen const char *errstr = NULL;
b90fb7f78aca271243c26074ddd6587cce112a1eTimo Sirainen int err;
b90fb7f78aca271243c26074ddd6587cce112a1eTimo Sirainen
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen proxy->refcount++;
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen i_free_and_null(proxy->last_error);
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen err = SSL_get_error(proxy->ssl, ret);
b90fb7f78aca271243c26074ddd6587cce112a1eTimo Sirainen
b90fb7f78aca271243c26074ddd6587cce112a1eTimo Sirainen switch (err) {
b90fb7f78aca271243c26074ddd6587cce112a1eTimo Sirainen case SSL_ERROR_WANT_READ:
b90fb7f78aca271243c26074ddd6587cce112a1eTimo Sirainen ssl_set_io(proxy, SSL_ADD_INPUT);
8cdb3234fe3c77e477c7a0e6934678f58fc54d4dTimo Sirainen break;
8cdb3234fe3c77e477c7a0e6934678f58fc54d4dTimo Sirainen case SSL_ERROR_WANT_WRITE:
8cdb3234fe3c77e477c7a0e6934678f58fc54d4dTimo Sirainen ssl_set_io(proxy, SSL_ADD_OUTPUT);
8cdb3234fe3c77e477c7a0e6934678f58fc54d4dTimo Sirainen break;
8cdb3234fe3c77e477c7a0e6934678f58fc54d4dTimo Sirainen case SSL_ERROR_SYSCALL:
3d77cc0d502dc69ffe2afe318605964dd40b7b20Timo Sirainen /* eat up the error queue */
d66ef20c30fee728899ee168c75fcc5ff8fbdac1Timo Sirainen if (ERR_peek_error() != 0)
8cdb3234fe3c77e477c7a0e6934678f58fc54d4dTimo Sirainen errstr = ssl_last_error();
d66ef20c30fee728899ee168c75fcc5ff8fbdac1Timo Sirainen else if (ret != 0)
8cdb3234fe3c77e477c7a0e6934678f58fc54d4dTimo Sirainen errstr = strerror(errno);
8cdb3234fe3c77e477c7a0e6934678f58fc54d4dTimo Sirainen else {
8cdb3234fe3c77e477c7a0e6934678f58fc54d4dTimo Sirainen /* EOF. */
8cdb3234fe3c77e477c7a0e6934678f58fc54d4dTimo Sirainen errstr = "Disconnected";
8cdb3234fe3c77e477c7a0e6934678f58fc54d4dTimo Sirainen break;
d66ef20c30fee728899ee168c75fcc5ff8fbdac1Timo Sirainen }
8cdb3234fe3c77e477c7a0e6934678f58fc54d4dTimo Sirainen errstr = t_strdup_printf("%s syscall failed: %s",
8cdb3234fe3c77e477c7a0e6934678f58fc54d4dTimo Sirainen func_name, errstr);
8cdb3234fe3c77e477c7a0e6934678f58fc54d4dTimo Sirainen break;
8cdb3234fe3c77e477c7a0e6934678f58fc54d4dTimo Sirainen case SSL_ERROR_ZERO_RETURN:
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen /* clean connection closing */
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen ssl_proxy_destroy(proxy);
8ab3d88c56dff2f567193f80cc29821a64e576d1Timo Sirainen break;
8ab3d88c56dff2f567193f80cc29821a64e576d1Timo Sirainen case SSL_ERROR_SSL:
8ab3d88c56dff2f567193f80cc29821a64e576d1Timo Sirainen if (ERR_GET_REASON(ERR_peek_error()) == ERR_R_MALLOC_FAILURE) {
8ab3d88c56dff2f567193f80cc29821a64e576d1Timo Sirainen i_error("OpenSSL malloc() failed. "
8ab3d88c56dff2f567193f80cc29821a64e576d1Timo Sirainen "You may need to increase login_process_size");
6adf683655750bcb809275cd65dc75fd12214198Timo Sirainen }
8ab3d88c56dff2f567193f80cc29821a64e576d1Timo Sirainen errstr = t_strdup_printf("%s failed: %s",
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen func_name, ssl_last_error());
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen break;
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen default:
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen errstr = t_strdup_printf("%s failed: unknown failure %d (%s)",
8ab3d88c56dff2f567193f80cc29821a64e576d1Timo Sirainen func_name, err, ssl_last_error());
6adf683655750bcb809275cd65dc75fd12214198Timo Sirainen break;
8ab3d88c56dff2f567193f80cc29821a64e576d1Timo Sirainen }
6adf683655750bcb809275cd65dc75fd12214198Timo Sirainen
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen if (errstr != NULL) {
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen proxy->last_error = i_strdup(errstr);
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen ssl_proxy_destroy(proxy);
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen }
8ab3d88c56dff2f567193f80cc29821a64e576d1Timo Sirainen ssl_proxy_unref(proxy);
71da447014454c84828d9dface77219875554d7dTimo Sirainen}
71da447014454c84828d9dface77219875554d7dTimo Sirainen
71da447014454c84828d9dface77219875554d7dTimo Sirainenstatic void ssl_handshake(struct ssl_proxy *proxy)
71da447014454c84828d9dface77219875554d7dTimo Sirainen{
71da447014454c84828d9dface77219875554d7dTimo Sirainen int ret;
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen if (proxy->client_proxy) {
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen ret = SSL_connect(proxy->ssl);
e2ce8d4a6ac5d82a906178148453e7613fab9ba0Timo Sirainen if (ret != 1) {
e2ce8d4a6ac5d82a906178148453e7613fab9ba0Timo Sirainen ssl_handle_error(proxy, ret, "SSL_connect()");
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen return;
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen }
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen } else {
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen ret = SSL_accept(proxy->ssl);
e2ce8d4a6ac5d82a906178148453e7613fab9ba0Timo Sirainen if (ret != 1) {
e2ce8d4a6ac5d82a906178148453e7613fab9ba0Timo Sirainen ssl_handle_error(proxy, ret, "SSL_accept()");
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen return;
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen }
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen }
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen i_free_and_null(proxy->last_error);
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen proxy->handshaked = TRUE;
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen ssl_set_io(proxy, SSL_ADD_INPUT);
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen plain_block_input(proxy, FALSE);
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen if (proxy->handshake_callback != NULL) {
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen if (proxy->handshake_callback(proxy->handshake_context) < 0)
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen ssl_proxy_destroy(proxy);
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen }
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen}
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainenstatic void ssl_read(struct ssl_proxy *proxy)
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen{
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen int ret;
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen while (proxy->plainout_size < sizeof(proxy->plainout_buf) &&
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen !proxy->destroyed) {
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen ret = SSL_read(proxy->ssl,
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen proxy->plainout_buf + proxy->plainout_size,
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen sizeof(proxy->plainout_buf) -
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen proxy->plainout_size);
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen if (ret <= 0) {
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen ssl_handle_error(proxy, ret, "SSL_read()");
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen break;
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen } else {
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen i_free_and_null(proxy->last_error);
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen proxy->plainout_size += ret;
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen plain_write(proxy);
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen }
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen }
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen}
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainenstatic void ssl_write(struct ssl_proxy *proxy)
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen{
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen int ret;
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen ret = SSL_write(proxy->ssl, proxy->sslout_buf, proxy->sslout_size);
2d2ebe91d56e9a158de000c9d0026f65600fbcfaTimo Sirainen if (ret <= 0)
2d2ebe91d56e9a158de000c9d0026f65600fbcfaTimo Sirainen ssl_handle_error(proxy, ret, "SSL_write()");
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen else {
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen i_free_and_null(proxy->last_error);
d691782ae9eb4f6ab06bc42f1617e889e7c18a3bTimo Sirainen proxy->sslout_size -= ret;
d691782ae9eb4f6ab06bc42f1617e889e7c18a3bTimo Sirainen memmove(proxy->sslout_buf, proxy->sslout_buf + ret,
d691782ae9eb4f6ab06bc42f1617e889e7c18a3bTimo Sirainen proxy->sslout_size);
d691782ae9eb4f6ab06bc42f1617e889e7c18a3bTimo Sirainen
d691782ae9eb4f6ab06bc42f1617e889e7c18a3bTimo Sirainen ssl_set_io(proxy, proxy->sslout_size > 0 ?
d691782ae9eb4f6ab06bc42f1617e889e7c18a3bTimo Sirainen SSL_ADD_OUTPUT : SSL_REMOVE_OUTPUT);
d691782ae9eb4f6ab06bc42f1617e889e7c18a3bTimo Sirainen plain_block_input(proxy, FALSE);
d691782ae9eb4f6ab06bc42f1617e889e7c18a3bTimo Sirainen }
d691782ae9eb4f6ab06bc42f1617e889e7c18a3bTimo Sirainen}
d691782ae9eb4f6ab06bc42f1617e889e7c18a3bTimo Sirainen
d691782ae9eb4f6ab06bc42f1617e889e7c18a3bTimo Sirainenstatic void ssl_step(struct ssl_proxy *proxy)
d691782ae9eb4f6ab06bc42f1617e889e7c18a3bTimo Sirainen{
d691782ae9eb4f6ab06bc42f1617e889e7c18a3bTimo Sirainen proxy->refcount++;
d691782ae9eb4f6ab06bc42f1617e889e7c18a3bTimo Sirainen
d691782ae9eb4f6ab06bc42f1617e889e7c18a3bTimo Sirainen if (!proxy->handshaked)
d691782ae9eb4f6ab06bc42f1617e889e7c18a3bTimo Sirainen ssl_handshake(proxy);
d691782ae9eb4f6ab06bc42f1617e889e7c18a3bTimo Sirainen
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen if (proxy->handshaked) {
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen if (proxy->plainout_size == sizeof(proxy->plainout_buf))
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen ssl_set_io(proxy, SSL_REMOVE_INPUT);
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen else
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen ssl_read(proxy);
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen if (proxy->sslout_size == 0)
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen ssl_set_io(proxy, SSL_REMOVE_OUTPUT);
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen else {
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen net_set_cork(proxy->fd_ssl, TRUE);
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen ssl_write(proxy);
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen net_set_cork(proxy->fd_ssl, FALSE);
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen }
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen }
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen ssl_proxy_unref(proxy);
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen}
f28583935a4509d3f7932738bba6c548ef455c82Timo Sirainen
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainenstatic int
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 struct ssl_proxy **proxy_r)
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen{
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen struct ssl_proxy *proxy;
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen SSL *ssl;
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen int sfd[2];
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen i_assert(fd != -1);
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen *proxy_r = NULL;
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen if (!ssl_initialized) {
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen i_error("SSL support not enabled in configuration");
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen return -1;
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen }
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen ssl_refresh_parameters(&ssl_params);
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen ssl = SSL_new(ssl_ctx);
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen if (ssl == NULL) {
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen i_error("SSL_new() failed: %s", ssl_last_error());
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen return -1;
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen }
d78be924ad150840e018eda6a8a7d5e46a39bda2Timo Sirainen
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen if (SSL_set_fd(ssl, fd) != 1) {
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen i_error("SSL_set_fd() failed: %s", ssl_last_error());
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen SSL_free(ssl);
981139bb2e446bb2050c1158614725f8413fd709Timo Sirainen return -1;
981139bb2e446bb2050c1158614725f8413fd709Timo Sirainen }
981139bb2e446bb2050c1158614725f8413fd709Timo Sirainen
981139bb2e446bb2050c1158614725f8413fd709Timo Sirainen if (socketpair(AF_UNIX, SOCK_STREAM, 0, sfd) < 0) {
981139bb2e446bb2050c1158614725f8413fd709Timo Sirainen i_error("socketpair() failed: %m");
981139bb2e446bb2050c1158614725f8413fd709Timo Sirainen SSL_free(ssl);
981139bb2e446bb2050c1158614725f8413fd709Timo Sirainen return -1;
981139bb2e446bb2050c1158614725f8413fd709Timo Sirainen }
981139bb2e446bb2050c1158614725f8413fd709Timo Sirainen
981139bb2e446bb2050c1158614725f8413fd709Timo Sirainen net_set_nonblock(sfd[0], TRUE);
981139bb2e446bb2050c1158614725f8413fd709Timo Sirainen net_set_nonblock(sfd[1], TRUE);
981139bb2e446bb2050c1158614725f8413fd709Timo Sirainen net_set_nonblock(fd, TRUE);
981139bb2e446bb2050c1158614725f8413fd709Timo Sirainen
981139bb2e446bb2050c1158614725f8413fd709Timo Sirainen proxy = i_new(struct ssl_proxy, 1);
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen proxy->refcount = 2;
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen proxy->ssl = ssl;
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen proxy->login_set = login_set;
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen proxy->ssl_set = ssl_set;
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen proxy->fd_ssl = fd;
d78be924ad150840e018eda6a8a7d5e46a39bda2Timo Sirainen proxy->fd_plain = sfd[0];
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen proxy->ip = *ip;
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen proxy->set_pool = set_pool;
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen pool_ref(set_pool);
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen SSL_set_ex_data(ssl, extdata_index, proxy);
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen ssl_proxy_count++;
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen DLLIST_PREPEND(&ssl_proxies, proxy);
46631c1d903c409444b1b1c4a1d41a033c09ee37Timo Sirainen
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen *proxy_r = proxy;
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen return sfd[1];
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen}
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainenstatic struct ssl_server_context *
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainenssl_server_context_get(const struct login_settings *login_set,
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen const struct master_service_ssl_settings *set)
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen{
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen struct ssl_server_context *ctx, lookup_ctx;
8b247780e911909a9fdc47f69ce6d1478902ad98Timo Sirainen
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen memset(&lookup_ctx, 0, sizeof(lookup_ctx));
8b247780e911909a9fdc47f69ce6d1478902ad98Timo Sirainen lookup_ctx.cert = set->ssl_cert;
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen lookup_ctx.key = set->ssl_key;
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen lookup_ctx.ca = set->ssl_ca;
8b2cf1c1bd8ddcea0525b62fd35ba76e136828a1Timo Sirainen lookup_ctx.cipher_list = set->ssl_cipher_list;
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen lookup_ctx.protocols = set->ssl_protocols;
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen lookup_ctx.verify_client_cert = set->ssl_verify_client_cert ||
f28583935a4509d3f7932738bba6c548ef455c82Timo Sirainen login_set->auth_ssl_require_client_cert ||
f28583935a4509d3f7932738bba6c548ef455c82Timo Sirainen login_set->auth_ssl_username_from_cert;
f28583935a4509d3f7932738bba6c548ef455c82Timo Sirainen
f28583935a4509d3f7932738bba6c548ef455c82Timo Sirainen ctx = hash_table_lookup(ssl_servers, &lookup_ctx);
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen if (ctx == NULL)
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen ctx = ssl_server_context_init(login_set, set);
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen return ctx;
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen}
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainenint ssl_proxy_alloc(int fd, const struct ip_addr *ip, pool_t set_pool,
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen const struct login_settings *login_set,
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen const struct master_service_ssl_settings *ssl_set,
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen struct ssl_proxy **proxy_r)
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen{
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen struct ssl_server_context *ctx;
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen ctx = ssl_server_context_get(login_set, ssl_set);
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen return ssl_proxy_alloc_common(ctx->ctx, fd, ip,
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen set_pool, login_set, ssl_set, proxy_r);
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen}
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainenint ssl_proxy_client_alloc(int fd, struct ip_addr *ip, pool_t set_pool,
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen const struct login_settings *login_set,
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen const struct master_service_ssl_settings *ssl_set,
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen ssl_handshake_callback_t *callback, void *context,
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen struct ssl_proxy **proxy_r)
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen{
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen int ret;
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen ret = ssl_proxy_alloc_common(ssl_client_ctx, fd, ip,
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen set_pool, login_set, ssl_set, proxy_r);
981139bb2e446bb2050c1158614725f8413fd709Timo Sirainen if (ret < 0)
981139bb2e446bb2050c1158614725f8413fd709Timo Sirainen return -1;
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen (*proxy_r)->handshake_callback = callback;
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen (*proxy_r)->handshake_context = context;
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen (*proxy_r)->client_proxy = TRUE;
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen return ret;
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen}
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainenvoid ssl_proxy_start(struct ssl_proxy *proxy)
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen{
71da447014454c84828d9dface77219875554d7dTimo Sirainen ssl_step(proxy);
0c1835a90dd1dcedaeaedd1cd91672299cbeb5beTimo Sirainen}
0c1835a90dd1dcedaeaedd1cd91672299cbeb5beTimo Sirainen
0c1835a90dd1dcedaeaedd1cd91672299cbeb5beTimo Sirainenvoid ssl_proxy_set_client(struct ssl_proxy *proxy, struct client *client)
0c1835a90dd1dcedaeaedd1cd91672299cbeb5beTimo Sirainen{
0c1835a90dd1dcedaeaedd1cd91672299cbeb5beTimo Sirainen i_assert(proxy->client == NULL);
0c1835a90dd1dcedaeaedd1cd91672299cbeb5beTimo Sirainen
0c1835a90dd1dcedaeaedd1cd91672299cbeb5beTimo Sirainen client_ref(client);
8b2cf1c1bd8ddcea0525b62fd35ba76e136828a1Timo Sirainen proxy->client = client;
0c1835a90dd1dcedaeaedd1cd91672299cbeb5beTimo Sirainen}
0c1835a90dd1dcedaeaedd1cd91672299cbeb5beTimo Sirainen
0c1835a90dd1dcedaeaedd1cd91672299cbeb5beTimo Sirainenbool ssl_proxy_has_valid_client_cert(const struct ssl_proxy *proxy)
0c1835a90dd1dcedaeaedd1cd91672299cbeb5beTimo Sirainen{
f4735bf7ec2019fdc730e9ebdb39e5a4ea580405Timo Sirainen return proxy->cert_received && !proxy->cert_broken;
0c1835a90dd1dcedaeaedd1cd91672299cbeb5beTimo Sirainen}
0c1835a90dd1dcedaeaedd1cd91672299cbeb5beTimo Sirainen
0c1835a90dd1dcedaeaedd1cd91672299cbeb5beTimo Sirainenbool ssl_proxy_has_broken_client_cert(struct ssl_proxy *proxy)
f4735bf7ec2019fdc730e9ebdb39e5a4ea580405Timo Sirainen{
f4735bf7ec2019fdc730e9ebdb39e5a4ea580405Timo Sirainen return proxy->cert_received && proxy->cert_broken;
f4735bf7ec2019fdc730e9ebdb39e5a4ea580405Timo Sirainen}
f4735bf7ec2019fdc730e9ebdb39e5a4ea580405Timo Sirainen
f4735bf7ec2019fdc730e9ebdb39e5a4ea580405Timo Sirainenint ssl_proxy_cert_match_name(struct ssl_proxy *proxy, const char *verify_name)
f4735bf7ec2019fdc730e9ebdb39e5a4ea580405Timo Sirainen{
f4735bf7ec2019fdc730e9ebdb39e5a4ea580405Timo Sirainen return openssl_cert_match_name(proxy->ssl, verify_name);
f4735bf7ec2019fdc730e9ebdb39e5a4ea580405Timo Sirainen}
f4735bf7ec2019fdc730e9ebdb39e5a4ea580405Timo Sirainen
f4735bf7ec2019fdc730e9ebdb39e5a4ea580405Timo Sirainenconst char *ssl_proxy_get_peer_name(struct ssl_proxy *proxy)
f4735bf7ec2019fdc730e9ebdb39e5a4ea580405Timo Sirainen{
f4735bf7ec2019fdc730e9ebdb39e5a4ea580405Timo Sirainen X509 *x509;
f4735bf7ec2019fdc730e9ebdb39e5a4ea580405Timo Sirainen char *name;
981139bb2e446bb2050c1158614725f8413fd709Timo Sirainen int len;
981139bb2e446bb2050c1158614725f8413fd709Timo Sirainen
981139bb2e446bb2050c1158614725f8413fd709Timo Sirainen if (!ssl_proxy_has_valid_client_cert(proxy))
981139bb2e446bb2050c1158614725f8413fd709Timo Sirainen return NULL;
981139bb2e446bb2050c1158614725f8413fd709Timo Sirainen
981139bb2e446bb2050c1158614725f8413fd709Timo Sirainen x509 = SSL_get_peer_certificate(proxy->ssl);
981139bb2e446bb2050c1158614725f8413fd709Timo Sirainen if (x509 == NULL)
981139bb2e446bb2050c1158614725f8413fd709Timo Sirainen return NULL; /* we should have had it.. */
len = X509_NAME_get_text_by_NID(X509_get_subject_name(x509),
ssl_username_nid, NULL, 0);
if (len < 0)
name = "";
else {
name = t_malloc(len + 1);
if (X509_NAME_get_text_by_NID(X509_get_subject_name(x509),
ssl_username_nid, name, len + 1) < 0)
name = "";
else if (strlen(name) != (size_t)len) {
/* NUL characters in name. Someone's trying to fake
being another user? Don't allow it. */
name = "";
}
}
X509_free(x509);
return *name == '\0' ? NULL : name;
}
bool ssl_proxy_is_handshaked(const struct ssl_proxy *proxy)
{
return proxy->handshaked;
}
const char *ssl_proxy_get_last_error(const struct ssl_proxy *proxy)
{
return proxy->last_error;
}
const char *ssl_proxy_get_security_string(struct ssl_proxy *proxy)
{
const SSL_CIPHER *cipher;
int bits, alg_bits;
const char *comp_str;
if (!proxy->handshaked)
return "";
cipher = SSL_get_current_cipher(proxy->ssl);
bits = SSL_CIPHER_get_bits(cipher, &alg_bits);
comp_str = ssl_proxy_get_compression(proxy);
comp_str = comp_str == NULL ? "" : t_strconcat(" ", comp_str, NULL);
return t_strdup_printf("%s with cipher %s (%d/%d bits)%s",
SSL_get_version(proxy->ssl),
SSL_CIPHER_get_name(cipher),
bits, alg_bits, comp_str);
}
const char *ssl_proxy_get_compression(struct ssl_proxy *proxy ATTR_UNUSED)
{
#ifdef HAVE_SSL_COMPRESSION
const COMP_METHOD *comp;
comp = SSL_get_current_compression(proxy->ssl);
return comp == NULL ? NULL : SSL_COMP_get_name(comp);
#else
return NULL;
#endif
}
const char *ssl_proxy_get_cert_error(struct ssl_proxy *proxy)
{
return proxy->cert_error != NULL ? proxy->cert_error :
"(Unknown error)";
}
void ssl_proxy_free(struct ssl_proxy **_proxy)
{
struct ssl_proxy *proxy = *_proxy;
*_proxy = NULL;
ssl_proxy_unref(proxy);
}
static void ssl_proxy_unref(struct ssl_proxy *proxy)
{
if (--proxy->refcount > 0)
return;
i_assert(proxy->refcount == 0);
SSL_free(proxy->ssl);
pool_unref(&proxy->set_pool);
i_free(proxy->last_error);
i_free(proxy);
}
static void ssl_proxy_destroy(struct ssl_proxy *proxy)
{
if (proxy->destroyed)
return;
proxy->destroyed = TRUE;
ssl_proxy_count--;
DLLIST_REMOVE(&ssl_proxies, proxy);
if (proxy->io_ssl_read != NULL)
io_remove(&proxy->io_ssl_read);
if (proxy->io_ssl_write != NULL)
io_remove(&proxy->io_ssl_write);
if (proxy->io_plain_read != NULL)
io_remove(&proxy->io_plain_read);
if (proxy->io_plain_write != NULL)
io_remove(&proxy->io_plain_write);
(void)SSL_shutdown(proxy->ssl);
net_disconnect(proxy->fd_ssl);
net_disconnect(proxy->fd_plain);
if (proxy->client != NULL)
client_unref(&proxy->client);
ssl_proxy_unref(proxy);
}
static RSA *ssl_gen_rsa_key(SSL *ssl ATTR_UNUSED,
int is_export ATTR_UNUSED, int keylength)
{
return RSA_generate_key(keylength, RSA_F4, NULL, NULL);
}
static DH *ssl_tmp_dh_callback(SSL *ssl ATTR_UNUSED,
int is_export, int keylength)
{
/* Well, I'm not exactly sure why the logic in here is this.
It's the same as in Postfix, so it can't be too wrong. */
if (is_export && keylength == 512 && ssl_params.dh_512 != NULL)
return ssl_params.dh_512;
return ssl_params.dh_1024;
}
static void ssl_info_callback(const SSL *ssl, int where, int ret)
{
struct ssl_proxy *proxy;
proxy = SSL_get_ex_data(ssl, extdata_index);
if (!proxy->ssl_set->verbose_ssl)
return;
if ((where & SSL_CB_ALERT) != 0) {
i_warning("SSL alert: where=0x%x, ret=%d: %s %s [%s]",
where, ret, SSL_alert_type_string_long(ret),
SSL_alert_desc_string_long(ret),
net_ip2addr(&proxy->ip));
} else if (ret == 0) {
i_warning("SSL failed: where=0x%x: %s [%s]",
where, SSL_state_string_long(ssl),
net_ip2addr(&proxy->ip));
} else {
i_debug("SSL: where=0x%x, ret=%d: %s [%s]",
where, ret, SSL_state_string_long(ssl),
net_ip2addr(&proxy->ip));
}
}
static int ssl_verify_client_cert(int preverify_ok, X509_STORE_CTX *ctx)
{
SSL *ssl;
struct ssl_proxy *proxy;
char buf[1024];
X509_NAME *subject;
ssl = X509_STORE_CTX_get_ex_data(ctx,
SSL_get_ex_data_X509_STORE_CTX_idx());
proxy = SSL_get_ex_data(ssl, extdata_index);
proxy->cert_received = TRUE;
if (proxy->client_proxy && !proxy->login_set->ssl_require_crl &&
(ctx->error == X509_V_ERR_UNABLE_TO_GET_CRL ||
ctx->error == X509_V_ERR_CRL_HAS_EXPIRED)) {
/* no CRL given with the CA list. don't worry about it. */
preverify_ok = 1;
}
if (!preverify_ok)
proxy->cert_broken = TRUE;
subject = X509_get_subject_name(ctx->current_cert);
(void)X509_NAME_oneline(subject, buf, sizeof(buf));
buf[sizeof(buf)-1] = '\0'; /* just in case.. */
if (proxy->cert_error == NULL) {
proxy->cert_error = p_strdup_printf(proxy->client->pool, "%s: %s",
X509_verify_cert_error_string(ctx->error), buf);
}
if (proxy->ssl_set->verbose_ssl ||
(proxy->login_set->auth_verbose && !preverify_ok)) {
if (preverify_ok)
i_info("Valid certificate: %s", buf);
else {
i_info("Invalid certificate: %s: %s",
X509_verify_cert_error_string(ctx->error), buf);
}
}
/* Return success anyway, because if ssl_require_client_cert=no we
could still allow authentication. */
return 1;
}
static int
pem_password_callback(char *buf, int size, int rwflag ATTR_UNUSED,
void *userdata)
{
if (userdata == NULL) {
i_error("SSL private key file is password protected, "
"but password isn't given");
return 0;
}
if (i_strocpy(buf, userdata, size) < 0)
return 0;
return strlen(buf);
}
unsigned int ssl_proxy_get_count(void)
{
return ssl_proxy_count;
}
static bool is_pem_key(const char *cert)
{
return strstr(cert, "PRIVATE KEY---") != NULL;
}
static void load_ca(X509_STORE *store, const char *ca,
STACK_OF(X509_NAME) **xnames_r)
{
/* mostly just copy&pasted from X509_load_cert_crl_file() */
STACK_OF(X509_INFO) *inf;
X509_INFO *itmp;
X509_NAME *xname;
BIO *bio;
int i;
bio = BIO_new_mem_buf(t_strdup_noconst(ca), strlen(ca));
if (bio == NULL)
i_fatal("BIO_new_mem_buf() failed");
inf = PEM_X509_INFO_read_bio(bio, NULL, NULL, NULL);
if (inf == NULL)
i_fatal("Couldn't parse ssl_ca: %s", ssl_last_error());
BIO_free(bio);
if (xnames_r != NULL) {
*xnames_r = sk_X509_NAME_new_null();
if (*xnames_r == NULL)
i_fatal_status(FATAL_OUTOFMEM, "sk_X509_NAME_new_null() failed");
}
for(i = 0; i < sk_X509_INFO_num(inf); i++) {
itmp = sk_X509_INFO_value(inf, i);
if(itmp->x509) {
X509_STORE_add_cert(store, itmp->x509);
xname = X509_get_subject_name(itmp->x509);
if (xname != NULL && xnames_r != NULL) {
xname = X509_NAME_dup(xname);
if (xname == NULL)
i_fatal_status(FATAL_OUTOFMEM, "X509_NAME_dup() failed");
sk_X509_NAME_push(*xnames_r, xname);
}
}
if(itmp->crl)
X509_STORE_add_crl(store, itmp->crl);
}
sk_X509_INFO_pop_free(inf, X509_INFO_free);
}
static STACK_OF(X509_NAME) *
ssl_proxy_ctx_init(SSL_CTX *ssl_ctx, const struct master_service_ssl_settings *set,
bool load_xnames)
{
X509_STORE *store;
STACK_OF(X509_NAME) *xnames = NULL;
/* enable all SSL workarounds, except empty fragments as it
makes SSL more vulnerable against attacks */
SSL_CTX_set_options(ssl_ctx, SSL_OP_ALL &
~SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS);
#ifdef SSL_MODE_RELEASE_BUFFERS
SSL_CTX_set_mode(ssl_ctx, SSL_MODE_RELEASE_BUFFERS);
#endif
if (*set->ssl_ca != '\0') {
/* set trusted CA certs */
store = SSL_CTX_get_cert_store(ssl_ctx);
load_ca(store, set->ssl_ca, load_xnames ? &xnames : NULL);
}
SSL_CTX_set_info_callback(ssl_ctx, ssl_info_callback);
if (SSL_CTX_need_tmp_RSA(ssl_ctx))
SSL_CTX_set_tmp_rsa_callback(ssl_ctx, ssl_gen_rsa_key);
SSL_CTX_set_tmp_dh_callback(ssl_ctx, ssl_tmp_dh_callback);
return xnames;
}
static void
ssl_proxy_ctx_verify_client(SSL_CTX *ssl_ctx, STACK_OF(X509_NAME) *ca_names)
{
#if OPENSSL_VERSION_NUMBER >= 0x00907000L
X509_STORE *store;
store = SSL_CTX_get_cert_store(ssl_ctx);
X509_STORE_set_flags(store, X509_V_FLAG_CRL_CHECK |
X509_V_FLAG_CRL_CHECK_ALL);
#endif
SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE,
ssl_verify_client_cert);
/* set list of CA names that are sent to client */
SSL_CTX_set_client_CA_list(ssl_ctx, ca_names);
}
static const char *ssl_proxy_get_use_certificate_error(const char *cert)
{
unsigned long err;
err = ERR_peek_error();
if (ERR_GET_LIB(err) != ERR_LIB_PEM ||
ERR_GET_REASON(err) != PEM_R_NO_START_LINE)
return ssl_last_error();
else if (is_pem_key(cert)) {
return "The file contains a private key "
"(you've mixed ssl_cert and ssl_key settings)";
} else if (strchr(cert, '\n') == NULL) {
return t_strdup_printf("There is no valid PEM certificate. "
"(You probably forgot '<' from ssl_cert=<%s)", cert);
} else {
return "There is no valid PEM certificate.";
}
}
static const char *ssl_key_load_error(void)
{
unsigned long err = ERR_peek_error();
if (ERR_GET_LIB(err) == ERR_LIB_X509 &&
ERR_GET_REASON(err) == X509_R_KEY_VALUES_MISMATCH)
return "Key is for a different cert than ssl_cert";
else
return ssl_last_error();
}
static EVP_PKEY * ATTR_NULL(2)
ssl_proxy_load_key(const char *key, const char *password)
{
EVP_PKEY *pkey;
BIO *bio;
char *dup_password;
bio = BIO_new_mem_buf(t_strdup_noconst(key), strlen(key));
if (bio == NULL)
i_fatal("BIO_new_mem_buf() failed");
dup_password = t_strdup_noconst(password);
pkey = PEM_read_bio_PrivateKey(bio, NULL, pem_password_callback,
dup_password);
if (pkey == NULL) {
i_fatal("Couldn't parse private ssl_key: %s",
ssl_key_load_error());
}
BIO_free(bio);
return pkey;
}
static void
ssl_proxy_ctx_use_key(SSL_CTX *ctx,
const struct master_service_ssl_settings *set)
{
EVP_PKEY *pkey;
const char *password;
password = *set->ssl_key_password != '\0' ? set->ssl_key_password :
getenv(MASTER_SSL_KEY_PASSWORD_ENV);
pkey = ssl_proxy_load_key(set->ssl_key, password);
if (SSL_CTX_use_PrivateKey(ctx, pkey) != 1)
i_fatal("Can't load private ssl_key: %s", ssl_key_load_error());
EVP_PKEY_free(pkey);
}
static int
ssl_proxy_ctx_use_certificate_chain(SSL_CTX *ctx, const char *cert)
{
/* mostly just copy&pasted from SSL_CTX_use_certificate_chain_file() */
BIO *in;
X509 *x;
int ret = 0;
in = BIO_new_mem_buf(t_strdup_noconst(cert), strlen(cert));
if (in == NULL)
i_fatal("BIO_new_mem_buf() failed");
x = PEM_read_bio_X509(in, NULL, NULL, NULL);
if (x == NULL)
goto end;
ret = SSL_CTX_use_certificate(ctx, x);
#if 0
/* This is in OpenSSL code, but it seems to cause failures.. */
if (ERR_peek_error() != 0)
ret = 0;
#endif
if (ret != 0) {
/* If we could set up our certificate, now proceed to
* the CA certificates.
*/
X509 *ca;
int r;
unsigned long err;
while ((ca = PEM_read_bio_X509(in,NULL,NULL,NULL)) != NULL) {
r = SSL_CTX_add_extra_chain_cert(ctx, ca);
if (!r) {
X509_free(ca);
ret = 0;
goto end;
}
}
/* When the while loop ends, it's usually just EOF. */
err = ERR_peek_last_error();
if (ERR_GET_LIB(err) == ERR_LIB_PEM && ERR_GET_REASON(err) == PEM_R_NO_START_LINE)
ERR_clear_error();
else
ret = 0; /* some real error */
}
end:
if (x != NULL) X509_free(x);
BIO_free(in);
return ret;
}
#ifdef HAVE_SSL_GET_SERVERNAME
static void ssl_servername_callback(SSL *ssl, int *al ATTR_UNUSED,
void *context ATTR_UNUSED)
{
struct ssl_server_context *ctx;
struct ssl_proxy *proxy;
struct client *client;
const char *host;
void **other_sets;
proxy = SSL_get_ex_data(ssl, extdata_index);
host = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
client = proxy->client;
if (!client->ssl_servername_settings_read) {
client->ssl_servername_settings_read = TRUE;
client->set = login_settings_read(client->pool,
&client->local_ip,
&client->ip, host,
&client->ssl_set,
&other_sets);
}
ctx = ssl_server_context_get(client->set, client->ssl_set);
SSL_set_SSL_CTX(ssl, ctx->ctx);
}
#endif
static struct ssl_server_context *
ssl_server_context_init(const struct login_settings *login_set,
const struct master_service_ssl_settings *ssl_set)
{
struct ssl_server_context *ctx;
SSL_CTX *ssl_ctx;
pool_t pool;
STACK_OF(X509_NAME) *xnames;
pool = pool_alloconly_create("ssl server context", 4096);
ctx = p_new(pool, struct ssl_server_context, 1);
ctx->pool = pool;
ctx->cert = p_strdup(pool, ssl_set->ssl_cert);
ctx->key = p_strdup(pool, ssl_set->ssl_key);
ctx->ca = p_strdup(pool, ssl_set->ssl_ca);
ctx->cipher_list = p_strdup(pool, ssl_set->ssl_cipher_list);
ctx->protocols = p_strdup(pool, ssl_set->ssl_protocols);
ctx->verify_client_cert = ssl_set->ssl_verify_client_cert ||
login_set->auth_ssl_require_client_cert ||
login_set->auth_ssl_username_from_cert;
ctx->ctx = ssl_ctx = SSL_CTX_new(SSLv23_server_method());
if (ssl_ctx == NULL)
i_fatal("SSL_CTX_new() failed");
xnames = ssl_proxy_ctx_init(ssl_ctx, ssl_set, ctx->verify_client_cert);
if (SSL_CTX_set_cipher_list(ssl_ctx, ctx->cipher_list) != 1) {
i_fatal("Can't set cipher list to '%s': %s",
ctx->cipher_list, ssl_last_error());
}
SSL_CTX_set_options(ssl_ctx, openssl_get_protocol_options(ctx->protocols));
if (ssl_proxy_ctx_use_certificate_chain(ctx->ctx, ctx->cert) != 1) {
i_fatal("Can't load ssl_cert: %s",
ssl_proxy_get_use_certificate_error(ctx->cert));
}
#ifdef HAVE_SSL_GET_SERVERNAME
if (SSL_CTX_set_tlsext_servername_callback(ctx->ctx,
ssl_servername_callback) != 1) {
if (ssl_set->verbose_ssl)
i_debug("OpenSSL library doesn't support SNI");
}
#endif
ssl_proxy_ctx_use_key(ctx->ctx, ssl_set);
SSL_CTX_set_info_callback(ctx->ctx, ssl_info_callback);
if (ctx->verify_client_cert)
ssl_proxy_ctx_verify_client(ctx->ctx, xnames);
hash_table_insert(ssl_servers, ctx, ctx);
return ctx;
}
static void ssl_server_context_deinit(struct ssl_server_context **_ctx)
{
struct ssl_server_context *ctx = *_ctx;
SSL_CTX_free(ctx->ctx);
pool_unref(&ctx->pool);
}
static void
ssl_proxy_client_ctx_set_client_cert(SSL_CTX *ctx,
const struct login_settings *set)
{
EVP_PKEY *pkey;
if (*set->ssl_client_cert == '\0')
return;
if (ssl_proxy_ctx_use_certificate_chain(ctx, set->ssl_client_cert) != 1) {
i_fatal("Can't load ssl_client_cert: %s",
ssl_proxy_get_use_certificate_error(set->ssl_client_cert));
}
pkey = ssl_proxy_load_key(set->ssl_client_key, NULL);
if (SSL_CTX_use_PrivateKey(ctx, pkey) != 1) {
i_fatal("Can't load private ssl_client_key: %s",
ssl_key_load_error());
}
EVP_PKEY_free(pkey);
}
static void
ssl_proxy_init_client(const struct login_settings *login_set,
const struct master_service_ssl_settings *ssl_set)
{
STACK_OF(X509_NAME) *xnames;
if ((ssl_client_ctx = SSL_CTX_new(SSLv23_client_method())) == NULL)
i_fatal("SSL_CTX_new() failed");
xnames = ssl_proxy_ctx_init(ssl_client_ctx, ssl_set, TRUE);
ssl_proxy_ctx_verify_client(ssl_client_ctx, xnames);
ssl_proxy_client_ctx_set_client_cert(ssl_client_ctx, login_set);
}
void ssl_proxy_init(void)
{
const struct login_settings *login_set = global_login_settings;
const struct master_service_ssl_settings *ssl_set = global_ssl_settings;
static char dovecot[] = "dovecot";
unsigned char buf;
if (strcmp(ssl_set->ssl, "no") == 0)
return;
SSL_library_init();
SSL_load_error_strings();
OpenSSL_add_all_algorithms();
if (*ssl_set->ssl_crypto_device != '\0') {
ENGINE_load_builtin_engines();
ssl_engine = ENGINE_by_id(ssl_set->ssl_crypto_device);
if (ssl_engine == NULL) {
i_fatal("Unknown ssl_crypto_device: %s",
ssl_set->ssl_crypto_device);
}
ENGINE_init(ssl_engine);
ENGINE_set_default_RSA(ssl_engine);
ENGINE_set_default_DSA(ssl_engine);
ENGINE_set_default_ciphers(ssl_engine);
}
extdata_index = SSL_get_ex_new_index(0, dovecot, NULL, NULL, NULL);
hash_table_create(&ssl_servers, default_pool, 0,
ssl_server_context_hash, ssl_server_context_cmp);
(void)ssl_server_context_init(login_set, ssl_set);
ssl_proxy_init_client(login_set, ssl_set);
ssl_username_nid = OBJ_txt2nid(ssl_set->ssl_cert_username_field);
if (ssl_username_nid == NID_undef) {
i_fatal("Invalid ssl_cert_username_field: %s",
ssl_set->ssl_cert_username_field);
}
/* PRNG initialization might want to use /dev/urandom, make sure it
does it before chrooting. We might not have enough entropy at
the first try, so this function may fail. It's still been
initialized though. */
(void)RAND_bytes(&buf, 1);
memset(&ssl_params, 0, sizeof(ssl_params));
ssl_params.path = SSL_PARAMETERS_PATH;
ssl_proxy_count = 0;
ssl_proxies = NULL;
ssl_initialized = TRUE;
}
void ssl_proxy_deinit(void)
{
struct hash_iterate_context *iter;
struct ssl_server_context *ctx;
if (!ssl_initialized)
return;
while (ssl_proxies != NULL)
ssl_proxy_destroy(ssl_proxies);
iter = hash_table_iterate_init(ssl_servers);
while (hash_table_iterate_t(iter, ssl_servers, &ctx, &ctx))
ssl_server_context_deinit(&ctx);
hash_table_iterate_deinit(&iter);
hash_table_destroy(&ssl_servers);
ssl_free_parameters(&ssl_params);
SSL_CTX_free(ssl_client_ctx);
if (ssl_engine != NULL) {
ENGINE_finish(ssl_engine);
ENGINE_cleanup();
}
EVP_cleanup();
ERR_free_strings();
}
#endif