ssl-proxy-openssl.c revision 78c27af9d04b830afe3df6495d7a1efee556ecb8
199767f8919635c4928607450d9e0abb932109ceToomas Soome/* Copyright (c) 2002-2015 Dovecot authors, see the included COPYING file */
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soome#include "login-common.h"
199767f8919635c4928607450d9e0abb932109ceToomas Soome#include "array.h"
199767f8919635c4928607450d9e0abb932109ceToomas Soome#include "ioloop.h"
199767f8919635c4928607450d9e0abb932109ceToomas Soome#include "net.h"
199767f8919635c4928607450d9e0abb932109ceToomas Soome#include "ostream.h"
199767f8919635c4928607450d9e0abb932109ceToomas Soome#include "read-full.h"
199767f8919635c4928607450d9e0abb932109ceToomas Soome#include "safe-memset.h"
199767f8919635c4928607450d9e0abb932109ceToomas Soome#include "hash.h"
199767f8919635c4928607450d9e0abb932109ceToomas Soome#include "llist.h"
199767f8919635c4928607450d9e0abb932109ceToomas Soome#include "master-interface.h"
199767f8919635c4928607450d9e0abb932109ceToomas Soome#include "master-service-ssl-settings.h"
199767f8919635c4928607450d9e0abb932109ceToomas Soome#include "client-common.h"
199767f8919635c4928607450d9e0abb932109ceToomas Soome#include "ssl-proxy.h"
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soome#include <fcntl.h>
199767f8919635c4928607450d9e0abb932109ceToomas Soome#include <unistd.h>
199767f8919635c4928607450d9e0abb932109ceToomas Soome#include <sys/stat.h>
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soome#ifdef HAVE_OPENSSL
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soome#include "iostream-openssl.h"
199767f8919635c4928607450d9e0abb932109ceToomas Soome#include <openssl/crypto.h>
199767f8919635c4928607450d9e0abb932109ceToomas Soome#include <openssl/engine.h>
199767f8919635c4928607450d9e0abb932109ceToomas Soome#include <openssl/x509.h>
199767f8919635c4928607450d9e0abb932109ceToomas Soome#include <openssl/pem.h>
199767f8919635c4928607450d9e0abb932109ceToomas Soome#include <openssl/ssl.h>
199767f8919635c4928607450d9e0abb932109ceToomas Soome#include <openssl/err.h>
199767f8919635c4928607450d9e0abb932109ceToomas Soome#include <openssl/rand.h>
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soome#if !defined(OPENSSL_NO_ECDH) && OPENSSL_VERSION_NUMBER >= 0x10000000L
199767f8919635c4928607450d9e0abb932109ceToomas Soome# define HAVE_ECDH
199767f8919635c4928607450d9e0abb932109ceToomas Soome#endif
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soome/* Check every 30 minutes if parameters file has been updated */
199767f8919635c4928607450d9e0abb932109ceToomas Soome#define SSL_PARAMFILE_CHECK_INTERVAL (60*30)
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soome#define SSL_PARAMETERS_PATH "ssl-params"
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soome#ifndef SSL_CTRL_SET_TLSEXT_HOSTNAME /* FIXME: this may be unnecessary.. */
199767f8919635c4928607450d9e0abb932109ceToomas Soome# undef HAVE_SSL_GET_SERVERNAME
199767f8919635c4928607450d9e0abb932109ceToomas Soome#endif
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soomeenum ssl_io_action {
199767f8919635c4928607450d9e0abb932109ceToomas Soome SSL_ADD_INPUT,
199767f8919635c4928607450d9e0abb932109ceToomas Soome SSL_REMOVE_INPUT,
199767f8919635c4928607450d9e0abb932109ceToomas Soome SSL_ADD_OUTPUT,
199767f8919635c4928607450d9e0abb932109ceToomas Soome SSL_REMOVE_OUTPUT
199767f8919635c4928607450d9e0abb932109ceToomas Soome};
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soomestruct ssl_proxy {
199767f8919635c4928607450d9e0abb932109ceToomas Soome int refcount;
199767f8919635c4928607450d9e0abb932109ceToomas Soome struct ssl_proxy *prev, *next;
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soome SSL *ssl;
199767f8919635c4928607450d9e0abb932109ceToomas Soome struct client *client;
199767f8919635c4928607450d9e0abb932109ceToomas Soome struct ip_addr ip;
199767f8919635c4928607450d9e0abb932109ceToomas Soome const struct login_settings *login_set;
199767f8919635c4928607450d9e0abb932109ceToomas Soome const struct master_service_ssl_settings *ssl_set;
199767f8919635c4928607450d9e0abb932109ceToomas Soome pool_t set_pool;
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soome int fd_ssl, fd_plain;
199767f8919635c4928607450d9e0abb932109ceToomas Soome struct io *io_ssl_read, *io_ssl_write, *io_plain_read, *io_plain_write;
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soome unsigned char plainout_buf[1024];
199767f8919635c4928607450d9e0abb932109ceToomas Soome unsigned int plainout_size;
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soome unsigned char sslout_buf[1024];
199767f8919635c4928607450d9e0abb932109ceToomas Soome unsigned int sslout_size;
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soome ssl_handshake_callback_t *handshake_callback;
199767f8919635c4928607450d9e0abb932109ceToomas Soome void *handshake_context;
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soome const char *cert_error;
199767f8919635c4928607450d9e0abb932109ceToomas Soome char *last_error;
199767f8919635c4928607450d9e0abb932109ceToomas Soome unsigned int handshaked:1;
199767f8919635c4928607450d9e0abb932109ceToomas Soome unsigned int destroyed:1;
199767f8919635c4928607450d9e0abb932109ceToomas Soome unsigned int cert_received:1;
199767f8919635c4928607450d9e0abb932109ceToomas Soome unsigned int cert_broken:1;
199767f8919635c4928607450d9e0abb932109ceToomas Soome unsigned int client_proxy:1;
199767f8919635c4928607450d9e0abb932109ceToomas Soome unsigned int flushing:1;
199767f8919635c4928607450d9e0abb932109ceToomas Soome unsigned int failed:1;
199767f8919635c4928607450d9e0abb932109ceToomas Soome};
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soomestruct ssl_parameters {
199767f8919635c4928607450d9e0abb932109ceToomas Soome const char *path;
199767f8919635c4928607450d9e0abb932109ceToomas Soome time_t last_refresh;
199767f8919635c4928607450d9e0abb932109ceToomas Soome int fd;
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soome DH *dh_512, *dh_default;
199767f8919635c4928607450d9e0abb932109ceToomas Soome};
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soomestruct ssl_server_context {
199767f8919635c4928607450d9e0abb932109ceToomas Soome SSL_CTX *ctx;
199767f8919635c4928607450d9e0abb932109ceToomas Soome pool_t pool;
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soome const char *cert;
199767f8919635c4928607450d9e0abb932109ceToomas Soome const char *key;
199767f8919635c4928607450d9e0abb932109ceToomas Soome const char *ca;
199767f8919635c4928607450d9e0abb932109ceToomas Soome const char *cipher_list;
199767f8919635c4928607450d9e0abb932109ceToomas Soome const char *protocols;
199767f8919635c4928607450d9e0abb932109ceToomas Soome bool verify_client_cert;
199767f8919635c4928607450d9e0abb932109ceToomas Soome bool prefer_server_ciphers;
199767f8919635c4928607450d9e0abb932109ceToomas Soome bool compression;
199767f8919635c4928607450d9e0abb932109ceToomas Soome bool tickets;
199767f8919635c4928607450d9e0abb932109ceToomas Soome};
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soomestatic int extdata_index;
199767f8919635c4928607450d9e0abb932109ceToomas Soomestatic HASH_TABLE(struct ssl_server_context *,
199767f8919635c4928607450d9e0abb932109ceToomas Soome struct ssl_server_context *) ssl_servers;
199767f8919635c4928607450d9e0abb932109ceToomas Soomestatic SSL_CTX *ssl_client_ctx;
199767f8919635c4928607450d9e0abb932109ceToomas Soomestatic unsigned int ssl_proxy_count;
199767f8919635c4928607450d9e0abb932109ceToomas Soomestatic struct ssl_proxy *ssl_proxies;
199767f8919635c4928607450d9e0abb932109ceToomas Soomestatic struct ssl_parameters ssl_params;
199767f8919635c4928607450d9e0abb932109ceToomas Soomestatic int ssl_username_nid;
199767f8919635c4928607450d9e0abb932109ceToomas Soomestatic ENGINE *ssl_engine;
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soomestatic void plain_read(struct ssl_proxy *proxy);
199767f8919635c4928607450d9e0abb932109ceToomas Soomestatic void ssl_read(struct ssl_proxy *proxy);
199767f8919635c4928607450d9e0abb932109ceToomas Soomestatic void ssl_write(struct ssl_proxy *proxy);
199767f8919635c4928607450d9e0abb932109ceToomas Soomestatic void ssl_step(struct ssl_proxy *proxy);
199767f8919635c4928607450d9e0abb932109ceToomas Soomestatic void ssl_proxy_unref(struct ssl_proxy *proxy);
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soomestatic struct ssl_server_context *
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 Soome
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 Soome#endif
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soomestatic void ssl_proxy_destroy_failed(struct ssl_proxy *proxy)
199767f8919635c4928607450d9e0abb932109ceToomas Soome{
199767f8919635c4928607450d9e0abb932109ceToomas Soome proxy->failed = TRUE;
199767f8919635c4928607450d9e0abb932109ceToomas Soome ssl_proxy_destroy(proxy);
199767f8919635c4928607450d9e0abb932109ceToomas Soome}
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soomestatic unsigned int ssl_server_context_hash(const struct ssl_server_context *ctx)
199767f8919635c4928607450d9e0abb932109ceToomas Soome{
199767f8919635c4928607450d9e0abb932109ceToomas Soome unsigned int i, g, h = 0;
199767f8919635c4928607450d9e0abb932109ceToomas Soome
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 h = (h << 4) + ctx->cert[i];
199767f8919635c4928607450d9e0abb932109ceToomas Soome if ((g = h & 0xf0000000UL)) {
199767f8919635c4928607450d9e0abb932109ceToomas Soome h = h ^ (g >> 24);
199767f8919635c4928607450d9e0abb932109ceToomas Soome h = h ^ g;
199767f8919635c4928607450d9e0abb932109ceToomas Soome }
199767f8919635c4928607450d9e0abb932109ceToomas Soome }
199767f8919635c4928607450d9e0abb932109ceToomas Soome return h;
199767f8919635c4928607450d9e0abb932109ceToomas Soome}
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soomestatic int ssl_server_context_cmp(const struct ssl_server_context *ctx1,
199767f8919635c4928607450d9e0abb932109ceToomas Soome const struct ssl_server_context *ctx2)
199767f8919635c4928607450d9e0abb932109ceToomas Soome{
199767f8919635c4928607450d9e0abb932109ceToomas Soome if (strcmp(ctx1->cert, ctx2->cert) != 0)
199767f8919635c4928607450d9e0abb932109ceToomas Soome return 1;
199767f8919635c4928607450d9e0abb932109ceToomas Soome if (strcmp(ctx1->key, ctx2->key) != 0)
199767f8919635c4928607450d9e0abb932109ceToomas Soome return 1;
199767f8919635c4928607450d9e0abb932109ceToomas Soome if (null_strcmp(ctx1->ca, ctx2->ca) != 0)
199767f8919635c4928607450d9e0abb932109ceToomas Soome return 1;
199767f8919635c4928607450d9e0abb932109ceToomas Soome if (null_strcmp(ctx1->cipher_list, ctx2->cipher_list) != 0)
199767f8919635c4928607450d9e0abb932109ceToomas Soome return 1;
199767f8919635c4928607450d9e0abb932109ceToomas Soome if (null_strcmp(ctx1->protocols, ctx2->protocols) != 0)
199767f8919635c4928607450d9e0abb932109ceToomas Soome return 1;
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soome return ctx1->verify_client_cert == ctx2->verify_client_cert ? 0 : 1;
199767f8919635c4928607450d9e0abb932109ceToomas Soome}
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soomestatic void ssl_params_corrupted(const char *reason)
199767f8919635c4928607450d9e0abb932109ceToomas Soome{
199767f8919635c4928607450d9e0abb932109ceToomas Soome i_fatal("Corrupted SSL ssl-parameters.dat in state_dir: %s", reason);
199767f8919635c4928607450d9e0abb932109ceToomas Soome}
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soomestatic void read_next(struct ssl_parameters *params, void *data, size_t size)
199767f8919635c4928607450d9e0abb932109ceToomas Soome{
199767f8919635c4928607450d9e0abb932109ceToomas Soome int ret;
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soome if ((ret = read_full(params->fd, data, size)) < 0)
199767f8919635c4928607450d9e0abb932109ceToomas Soome i_fatal("read(%s) failed: %m", params->path);
199767f8919635c4928607450d9e0abb932109ceToomas Soome if (ret == 0)
199767f8919635c4928607450d9e0abb932109ceToomas Soome ssl_params_corrupted("Truncated file");
199767f8919635c4928607450d9e0abb932109ceToomas Soome}
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soomestatic bool read_dh_parameters_next(struct ssl_parameters *params)
199767f8919635c4928607450d9e0abb932109ceToomas Soome{
199767f8919635c4928607450d9e0abb932109ceToomas Soome unsigned char *buf;
199767f8919635c4928607450d9e0abb932109ceToomas Soome const unsigned char *cbuf;
199767f8919635c4928607450d9e0abb932109ceToomas Soome unsigned int len;
199767f8919635c4928607450d9e0abb932109ceToomas Soome int bits;
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soome /* read bit size. 0 ends the DH parameters list. */
199767f8919635c4928607450d9e0abb932109ceToomas Soome read_next(params, &bits, sizeof(bits));
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soome if (bits == 0)
199767f8919635c4928607450d9e0abb932109ceToomas Soome return FALSE;
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soome /* read data size. */
199767f8919635c4928607450d9e0abb932109ceToomas Soome read_next(params, &len, sizeof(len));
199767f8919635c4928607450d9e0abb932109ceToomas Soome if (len > 1024*100) /* should be enough? */
199767f8919635c4928607450d9e0abb932109ceToomas Soome ssl_params_corrupted("File too large");
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soome buf = i_malloc(len);
199767f8919635c4928607450d9e0abb932109ceToomas Soome read_next(params, buf, len);
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soome cbuf = buf;
199767f8919635c4928607450d9e0abb932109ceToomas Soome switch (bits) {
199767f8919635c4928607450d9e0abb932109ceToomas Soome case 512:
199767f8919635c4928607450d9e0abb932109ceToomas Soome if (params->dh_512 != NULL)
199767f8919635c4928607450d9e0abb932109ceToomas Soome ssl_params_corrupted("Duplicate 512bit parameters");
199767f8919635c4928607450d9e0abb932109ceToomas Soome params->dh_512 = d2i_DHparams(NULL, &cbuf, len);
199767f8919635c4928607450d9e0abb932109ceToomas Soome break;
199767f8919635c4928607450d9e0abb932109ceToomas Soome default:
199767f8919635c4928607450d9e0abb932109ceToomas Soome if (params->dh_default != NULL)
199767f8919635c4928607450d9e0abb932109ceToomas Soome ssl_params_corrupted("Duplicate default parameters");
199767f8919635c4928607450d9e0abb932109ceToomas Soome params->dh_default = d2i_DHparams(NULL, &cbuf, len);
199767f8919635c4928607450d9e0abb932109ceToomas Soome break;
199767f8919635c4928607450d9e0abb932109ceToomas Soome }
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soome i_free(buf);
199767f8919635c4928607450d9e0abb932109ceToomas Soome return TRUE;
199767f8919635c4928607450d9e0abb932109ceToomas Soome}
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soomestatic void ssl_free_parameters(struct ssl_parameters *params)
199767f8919635c4928607450d9e0abb932109ceToomas Soome{
199767f8919635c4928607450d9e0abb932109ceToomas Soome if (params->dh_512 != NULL) {
199767f8919635c4928607450d9e0abb932109ceToomas Soome DH_free(params->dh_512);
199767f8919635c4928607450d9e0abb932109ceToomas Soome params->dh_512 = NULL;
199767f8919635c4928607450d9e0abb932109ceToomas Soome }
199767f8919635c4928607450d9e0abb932109ceToomas Soome if (params->dh_default != NULL) {
199767f8919635c4928607450d9e0abb932109ceToomas Soome DH_free(params->dh_default);
199767f8919635c4928607450d9e0abb932109ceToomas Soome params->dh_default = NULL;
199767f8919635c4928607450d9e0abb932109ceToomas Soome }
199767f8919635c4928607450d9e0abb932109ceToomas Soome}
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soomestatic void ssl_refresh_parameters(struct ssl_parameters *params)
199767f8919635c4928607450d9e0abb932109ceToomas Soome{
199767f8919635c4928607450d9e0abb932109ceToomas Soome char c;
199767f8919635c4928607450d9e0abb932109ceToomas Soome int ret;
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soome if (params->last_refresh > ioloop_time - SSL_PARAMFILE_CHECK_INTERVAL)
199767f8919635c4928607450d9e0abb932109ceToomas Soome return;
199767f8919635c4928607450d9e0abb932109ceToomas Soome params->last_refresh = ioloop_time;
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soome params->fd = net_connect_unix(params->path);
199767f8919635c4928607450d9e0abb932109ceToomas Soome if (params->fd == -1) {
199767f8919635c4928607450d9e0abb932109ceToomas Soome i_error("connect(%s) failed: %m", params->path);
199767f8919635c4928607450d9e0abb932109ceToomas Soome return;
199767f8919635c4928607450d9e0abb932109ceToomas Soome }
199767f8919635c4928607450d9e0abb932109ceToomas Soome net_set_nonblock(params->fd, FALSE);
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soome ssl_free_parameters(params);
199767f8919635c4928607450d9e0abb932109ceToomas Soome while (read_dh_parameters_next(params)) ;
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soome if ((ret = read_full(params->fd, &c, 1)) < 0)
199767f8919635c4928607450d9e0abb932109ceToomas Soome i_fatal("read(%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 Soome }
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soome if (close(params->fd) < 0)
199767f8919635c4928607450d9e0abb932109ceToomas Soome i_error("close(%s) failed: %m", params->path);
199767f8919635c4928607450d9e0abb932109ceToomas Soome params->fd = -1;
199767f8919635c4928607450d9e0abb932109ceToomas Soome}
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soomestatic void ssl_set_io(struct ssl_proxy *proxy, enum ssl_io_action action)
199767f8919635c4928607450d9e0abb932109ceToomas Soome{
199767f8919635c4928607450d9e0abb932109ceToomas Soome switch (action) {
199767f8919635c4928607450d9e0abb932109ceToomas Soome case SSL_ADD_INPUT:
199767f8919635c4928607450d9e0abb932109ceToomas Soome if (proxy->io_ssl_read != NULL)
199767f8919635c4928607450d9e0abb932109ceToomas Soome break;
199767f8919635c4928607450d9e0abb932109ceToomas Soome proxy->io_ssl_read = io_add(proxy->fd_ssl, IO_READ,
199767f8919635c4928607450d9e0abb932109ceToomas Soome ssl_step, proxy);
199767f8919635c4928607450d9e0abb932109ceToomas Soome break;
199767f8919635c4928607450d9e0abb932109ceToomas Soome case SSL_REMOVE_INPUT:
199767f8919635c4928607450d9e0abb932109ceToomas Soome if (proxy->io_ssl_read != NULL)
199767f8919635c4928607450d9e0abb932109ceToomas Soome io_remove(&proxy->io_ssl_read);
199767f8919635c4928607450d9e0abb932109ceToomas Soome break;
199767f8919635c4928607450d9e0abb932109ceToomas Soome case SSL_ADD_OUTPUT:
199767f8919635c4928607450d9e0abb932109ceToomas Soome if (proxy->io_ssl_write != NULL)
199767f8919635c4928607450d9e0abb932109ceToomas Soome break;
199767f8919635c4928607450d9e0abb932109ceToomas Soome proxy->io_ssl_write = io_add(proxy->fd_ssl, IO_WRITE,
199767f8919635c4928607450d9e0abb932109ceToomas Soome ssl_step, proxy);
199767f8919635c4928607450d9e0abb932109ceToomas Soome break;
199767f8919635c4928607450d9e0abb932109ceToomas Soome case SSL_REMOVE_OUTPUT:
199767f8919635c4928607450d9e0abb932109ceToomas Soome if (proxy->io_ssl_write != NULL)
199767f8919635c4928607450d9e0abb932109ceToomas Soome io_remove(&proxy->io_ssl_write);
199767f8919635c4928607450d9e0abb932109ceToomas Soome break;
199767f8919635c4928607450d9e0abb932109ceToomas Soome }
199767f8919635c4928607450d9e0abb932109ceToomas Soome}
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soomestatic void plain_block_input(struct ssl_proxy *proxy, bool block)
199767f8919635c4928607450d9e0abb932109ceToomas Soome{
199767f8919635c4928607450d9e0abb932109ceToomas Soome if (block) {
199767f8919635c4928607450d9e0abb932109ceToomas Soome if (proxy->io_plain_read != NULL)
199767f8919635c4928607450d9e0abb932109ceToomas Soome io_remove(&proxy->io_plain_read);
199767f8919635c4928607450d9e0abb932109ceToomas Soome } else {
199767f8919635c4928607450d9e0abb932109ceToomas Soome if (proxy->io_plain_read == NULL) {
199767f8919635c4928607450d9e0abb932109ceToomas Soome proxy->io_plain_read = io_add(proxy->fd_plain, IO_READ,
199767f8919635c4928607450d9e0abb932109ceToomas Soome plain_read, proxy);
199767f8919635c4928607450d9e0abb932109ceToomas Soome }
199767f8919635c4928607450d9e0abb932109ceToomas Soome }
199767f8919635c4928607450d9e0abb932109ceToomas Soome}
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soomestatic void plain_read(struct ssl_proxy *proxy)
199767f8919635c4928607450d9e0abb932109ceToomas Soome{
199767f8919635c4928607450d9e0abb932109ceToomas Soome ssize_t ret;
199767f8919635c4928607450d9e0abb932109ceToomas Soome bool corked = FALSE;
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soome if (proxy->sslout_size == sizeof(proxy->sslout_buf)) {
199767f8919635c4928607450d9e0abb932109ceToomas Soome /* buffer full, block input until it's written */
199767f8919635c4928607450d9e0abb932109ceToomas Soome plain_block_input(proxy, TRUE);
199767f8919635c4928607450d9e0abb932109ceToomas Soome return;
199767f8919635c4928607450d9e0abb932109ceToomas Soome }
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soome proxy->refcount++;
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soome while (proxy->sslout_size < sizeof(proxy->sslout_buf) &&
199767f8919635c4928607450d9e0abb932109ceToomas Soome !proxy->destroyed) {
199767f8919635c4928607450d9e0abb932109ceToomas Soome ret = net_receive(proxy->fd_plain,
199767f8919635c4928607450d9e0abb932109ceToomas Soome proxy->sslout_buf + proxy->sslout_size,
199767f8919635c4928607450d9e0abb932109ceToomas Soome sizeof(proxy->sslout_buf) -
199767f8919635c4928607450d9e0abb932109ceToomas Soome proxy->sslout_size);
199767f8919635c4928607450d9e0abb932109ceToomas Soome if (ret <= 0) {
199767f8919635c4928607450d9e0abb932109ceToomas Soome if (ret < 0)
199767f8919635c4928607450d9e0abb932109ceToomas Soome ssl_proxy_destroy(proxy);
199767f8919635c4928607450d9e0abb932109ceToomas Soome break;
199767f8919635c4928607450d9e0abb932109ceToomas Soome } else {
199767f8919635c4928607450d9e0abb932109ceToomas Soome proxy->sslout_size += ret;
199767f8919635c4928607450d9e0abb932109ceToomas Soome if (!corked) {
199767f8919635c4928607450d9e0abb932109ceToomas Soome if (net_set_cork(proxy->fd_ssl, TRUE) == 0)
199767f8919635c4928607450d9e0abb932109ceToomas Soome corked = TRUE;
199767f8919635c4928607450d9e0abb932109ceToomas Soome }
199767f8919635c4928607450d9e0abb932109ceToomas Soome ssl_write(proxy);
199767f8919635c4928607450d9e0abb932109ceToomas Soome }
199767f8919635c4928607450d9e0abb932109ceToomas Soome }
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soome if (corked)
199767f8919635c4928607450d9e0abb932109ceToomas Soome (void)net_set_cork(proxy->fd_ssl, FALSE);
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soome ssl_proxy_unref(proxy);
199767f8919635c4928607450d9e0abb932109ceToomas Soome}
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soomestatic void plain_write(struct ssl_proxy *proxy)
199767f8919635c4928607450d9e0abb932109ceToomas Soome{
199767f8919635c4928607450d9e0abb932109ceToomas Soome ssize_t ret;
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soome proxy->refcount++;
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soome ret = net_transmit(proxy->fd_plain, proxy->plainout_buf,
199767f8919635c4928607450d9e0abb932109ceToomas Soome proxy->plainout_size);
199767f8919635c4928607450d9e0abb932109ceToomas Soome if (ret < 0)
199767f8919635c4928607450d9e0abb932109ceToomas Soome ssl_proxy_destroy(proxy);
199767f8919635c4928607450d9e0abb932109ceToomas Soome else {
199767f8919635c4928607450d9e0abb932109ceToomas Soome proxy->plainout_size -= ret;
199767f8919635c4928607450d9e0abb932109ceToomas Soome memmove(proxy->plainout_buf, proxy->plainout_buf + ret,
199767f8919635c4928607450d9e0abb932109ceToomas Soome proxy->plainout_size);
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soome if (proxy->plainout_size > 0) {
199767f8919635c4928607450d9e0abb932109ceToomas Soome if (proxy->io_plain_write == NULL) {
199767f8919635c4928607450d9e0abb932109ceToomas Soome proxy->io_plain_write =
199767f8919635c4928607450d9e0abb932109ceToomas Soome io_add(proxy->fd_plain, IO_WRITE,
199767f8919635c4928607450d9e0abb932109ceToomas Soome plain_write, proxy);
199767f8919635c4928607450d9e0abb932109ceToomas Soome }
199767f8919635c4928607450d9e0abb932109ceToomas Soome } else {
199767f8919635c4928607450d9e0abb932109ceToomas Soome if (proxy->io_plain_write != NULL)
199767f8919635c4928607450d9e0abb932109ceToomas Soome io_remove(&proxy->io_plain_write);
199767f8919635c4928607450d9e0abb932109ceToomas Soome }
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soome ssl_set_io(proxy, SSL_ADD_INPUT);
199767f8919635c4928607450d9e0abb932109ceToomas Soome if (SSL_pending(proxy->ssl) > 0)
199767f8919635c4928607450d9e0abb932109ceToomas Soome ssl_read(proxy);
199767f8919635c4928607450d9e0abb932109ceToomas Soome }
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soome ssl_proxy_unref(proxy);
199767f8919635c4928607450d9e0abb932109ceToomas Soome}
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soomestatic void ssl_handle_error(struct ssl_proxy *proxy, int ret,
199767f8919635c4928607450d9e0abb932109ceToomas Soome const char *func_name)
199767f8919635c4928607450d9e0abb932109ceToomas Soome{
199767f8919635c4928607450d9e0abb932109ceToomas Soome const char *errstr = NULL;
199767f8919635c4928607450d9e0abb932109ceToomas Soome int err;
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soome proxy->refcount++;
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soome i_free_and_null(proxy->last_error);
199767f8919635c4928607450d9e0abb932109ceToomas Soome err = SSL_get_error(proxy->ssl, ret);
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soome switch (err) {
199767f8919635c4928607450d9e0abb932109ceToomas Soome case SSL_ERROR_WANT_READ:
199767f8919635c4928607450d9e0abb932109ceToomas Soome ssl_set_io(proxy, SSL_ADD_INPUT);
199767f8919635c4928607450d9e0abb932109ceToomas Soome break;
199767f8919635c4928607450d9e0abb932109ceToomas Soome case SSL_ERROR_WANT_WRITE:
199767f8919635c4928607450d9e0abb932109ceToomas Soome ssl_set_io(proxy, SSL_ADD_OUTPUT);
199767f8919635c4928607450d9e0abb932109ceToomas Soome break;
199767f8919635c4928607450d9e0abb932109ceToomas Soome case SSL_ERROR_SYSCALL:
199767f8919635c4928607450d9e0abb932109ceToomas Soome /* eat up the error queue */
199767f8919635c4928607450d9e0abb932109ceToomas Soome if (ERR_peek_error() != 0)
199767f8919635c4928607450d9e0abb932109ceToomas Soome errstr = openssl_iostream_error();
199767f8919635c4928607450d9e0abb932109ceToomas Soome else if (ret != 0)
199767f8919635c4928607450d9e0abb932109ceToomas Soome errstr = strerror(errno);
199767f8919635c4928607450d9e0abb932109ceToomas Soome else {
199767f8919635c4928607450d9e0abb932109ceToomas Soome /* EOF. */
199767f8919635c4928607450d9e0abb932109ceToomas Soome errstr = "Disconnected";
199767f8919635c4928607450d9e0abb932109ceToomas Soome break;
199767f8919635c4928607450d9e0abb932109ceToomas Soome }
199767f8919635c4928607450d9e0abb932109ceToomas Soome errstr = t_strdup_printf("%s syscall failed: %s",
199767f8919635c4928607450d9e0abb932109ceToomas Soome func_name, errstr);
199767f8919635c4928607450d9e0abb932109ceToomas Soome break;
199767f8919635c4928607450d9e0abb932109ceToomas Soome case SSL_ERROR_ZERO_RETURN:
199767f8919635c4928607450d9e0abb932109ceToomas Soome /* clean connection closing */
199767f8919635c4928607450d9e0abb932109ceToomas Soome ssl_proxy_destroy(proxy);
199767f8919635c4928607450d9e0abb932109ceToomas Soome break;
199767f8919635c4928607450d9e0abb932109ceToomas Soome case SSL_ERROR_SSL:
199767f8919635c4928607450d9e0abb932109ceToomas Soome if (ERR_GET_REASON(ERR_peek_error()) == ERR_R_MALLOC_FAILURE) {
199767f8919635c4928607450d9e0abb932109ceToomas Soome i_error("OpenSSL malloc() failed. "
199767f8919635c4928607450d9e0abb932109ceToomas Soome "You may need to increase service %s { vsz_limit }",
199767f8919635c4928607450d9e0abb932109ceToomas Soome login_binary->process_name);
199767f8919635c4928607450d9e0abb932109ceToomas Soome }
199767f8919635c4928607450d9e0abb932109ceToomas Soome errstr = t_strdup_printf("%s failed: %s",
199767f8919635c4928607450d9e0abb932109ceToomas Soome func_name, openssl_iostream_error());
199767f8919635c4928607450d9e0abb932109ceToomas Soome break;
199767f8919635c4928607450d9e0abb932109ceToomas Soome default:
199767f8919635c4928607450d9e0abb932109ceToomas Soome errstr = t_strdup_printf("%s failed: unknown failure %d (%s)",
199767f8919635c4928607450d9e0abb932109ceToomas Soome func_name, err, openssl_iostream_error());
199767f8919635c4928607450d9e0abb932109ceToomas Soome break;
199767f8919635c4928607450d9e0abb932109ceToomas Soome }
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soome if (errstr != NULL) {
199767f8919635c4928607450d9e0abb932109ceToomas Soome proxy->last_error = i_strdup(errstr);
199767f8919635c4928607450d9e0abb932109ceToomas Soome ssl_proxy_destroy_failed(proxy);
199767f8919635c4928607450d9e0abb932109ceToomas Soome }
199767f8919635c4928607450d9e0abb932109ceToomas Soome ssl_proxy_unref(proxy);
199767f8919635c4928607450d9e0abb932109ceToomas Soome}
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soomestatic void ssl_handshake(struct ssl_proxy *proxy)
199767f8919635c4928607450d9e0abb932109ceToomas Soome{
199767f8919635c4928607450d9e0abb932109ceToomas Soome int ret;
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soome if (proxy->client_proxy) {
199767f8919635c4928607450d9e0abb932109ceToomas Soome ret = SSL_connect(proxy->ssl);
199767f8919635c4928607450d9e0abb932109ceToomas Soome if (ret != 1) {
199767f8919635c4928607450d9e0abb932109ceToomas Soome ssl_handle_error(proxy, ret, "SSL_connect()");
199767f8919635c4928607450d9e0abb932109ceToomas Soome return;
199767f8919635c4928607450d9e0abb932109ceToomas Soome }
199767f8919635c4928607450d9e0abb932109ceToomas Soome } else {
199767f8919635c4928607450d9e0abb932109ceToomas Soome ret = SSL_accept(proxy->ssl);
199767f8919635c4928607450d9e0abb932109ceToomas Soome if (ret != 1) {
199767f8919635c4928607450d9e0abb932109ceToomas Soome ssl_handle_error(proxy, ret, "SSL_accept()");
199767f8919635c4928607450d9e0abb932109ceToomas Soome return;
199767f8919635c4928607450d9e0abb932109ceToomas Soome }
199767f8919635c4928607450d9e0abb932109ceToomas Soome }
199767f8919635c4928607450d9e0abb932109ceToomas Soome i_free_and_null(proxy->last_error);
199767f8919635c4928607450d9e0abb932109ceToomas Soome proxy->handshaked = TRUE;
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soome ssl_set_io(proxy, SSL_ADD_INPUT);
199767f8919635c4928607450d9e0abb932109ceToomas Soome plain_block_input(proxy, FALSE);
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soome if (proxy->handshake_callback != NULL) {
199767f8919635c4928607450d9e0abb932109ceToomas Soome if (proxy->handshake_callback(proxy->handshake_context) < 0)
199767f8919635c4928607450d9e0abb932109ceToomas Soome ssl_proxy_destroy_failed(proxy);
199767f8919635c4928607450d9e0abb932109ceToomas Soome }
199767f8919635c4928607450d9e0abb932109ceToomas Soome}
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soomestatic void ssl_read(struct ssl_proxy *proxy)
199767f8919635c4928607450d9e0abb932109ceToomas Soome{
199767f8919635c4928607450d9e0abb932109ceToomas Soome int ret;
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soome while (proxy->plainout_size < sizeof(proxy->plainout_buf) &&
199767f8919635c4928607450d9e0abb932109ceToomas Soome !proxy->destroyed) {
199767f8919635c4928607450d9e0abb932109ceToomas Soome ret = SSL_read(proxy->ssl,
199767f8919635c4928607450d9e0abb932109ceToomas Soome proxy->plainout_buf + proxy->plainout_size,
199767f8919635c4928607450d9e0abb932109ceToomas Soome sizeof(proxy->plainout_buf) -
199767f8919635c4928607450d9e0abb932109ceToomas Soome proxy->plainout_size);
199767f8919635c4928607450d9e0abb932109ceToomas Soome if (ret <= 0) {
199767f8919635c4928607450d9e0abb932109ceToomas Soome ssl_handle_error(proxy, ret, "SSL_read()");
199767f8919635c4928607450d9e0abb932109ceToomas Soome break;
199767f8919635c4928607450d9e0abb932109ceToomas Soome } else {
199767f8919635c4928607450d9e0abb932109ceToomas Soome i_free_and_null(proxy->last_error);
199767f8919635c4928607450d9e0abb932109ceToomas Soome proxy->plainout_size += ret;
199767f8919635c4928607450d9e0abb932109ceToomas Soome plain_write(proxy);
199767f8919635c4928607450d9e0abb932109ceToomas Soome }
199767f8919635c4928607450d9e0abb932109ceToomas Soome }
199767f8919635c4928607450d9e0abb932109ceToomas Soome}
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soomestatic void ssl_write(struct ssl_proxy *proxy)
199767f8919635c4928607450d9e0abb932109ceToomas Soome{
199767f8919635c4928607450d9e0abb932109ceToomas Soome int ret;
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soome ret = SSL_write(proxy->ssl, proxy->sslout_buf, proxy->sslout_size);
199767f8919635c4928607450d9e0abb932109ceToomas Soome if (ret <= 0)
199767f8919635c4928607450d9e0abb932109ceToomas Soome ssl_handle_error(proxy, ret, "SSL_write()");
199767f8919635c4928607450d9e0abb932109ceToomas Soome else {
199767f8919635c4928607450d9e0abb932109ceToomas Soome i_free_and_null(proxy->last_error);
199767f8919635c4928607450d9e0abb932109ceToomas Soome proxy->sslout_size -= ret;
199767f8919635c4928607450d9e0abb932109ceToomas Soome memmove(proxy->sslout_buf, proxy->sslout_buf + ret,
199767f8919635c4928607450d9e0abb932109ceToomas Soome proxy->sslout_size);
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soome ssl_set_io(proxy, proxy->sslout_size > 0 ?
199767f8919635c4928607450d9e0abb932109ceToomas Soome SSL_ADD_OUTPUT : SSL_REMOVE_OUTPUT);
199767f8919635c4928607450d9e0abb932109ceToomas Soome plain_block_input(proxy, FALSE);
199767f8919635c4928607450d9e0abb932109ceToomas Soome }
199767f8919635c4928607450d9e0abb932109ceToomas Soome}
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soomestatic void ssl_step(struct ssl_proxy *proxy)
199767f8919635c4928607450d9e0abb932109ceToomas Soome{
199767f8919635c4928607450d9e0abb932109ceToomas Soome proxy->refcount++;
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soome if (!proxy->handshaked) {
199767f8919635c4928607450d9e0abb932109ceToomas Soome ssl_set_io(proxy, SSL_REMOVE_OUTPUT);
199767f8919635c4928607450d9e0abb932109ceToomas Soome ssl_handshake(proxy);
199767f8919635c4928607450d9e0abb932109ceToomas Soome }
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soome if (proxy->handshaked) {
199767f8919635c4928607450d9e0abb932109ceToomas Soome if (proxy->plainout_size == sizeof(proxy->plainout_buf))
199767f8919635c4928607450d9e0abb932109ceToomas Soome ssl_set_io(proxy, SSL_REMOVE_INPUT);
199767f8919635c4928607450d9e0abb932109ceToomas Soome else
199767f8919635c4928607450d9e0abb932109ceToomas Soome ssl_read(proxy);
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soome if (proxy->sslout_size == 0)
199767f8919635c4928607450d9e0abb932109ceToomas Soome ssl_set_io(proxy, SSL_REMOVE_OUTPUT);
199767f8919635c4928607450d9e0abb932109ceToomas Soome else {
199767f8919635c4928607450d9e0abb932109ceToomas Soome (void)net_set_cork(proxy->fd_ssl, TRUE);
199767f8919635c4928607450d9e0abb932109ceToomas Soome ssl_write(proxy);
199767f8919635c4928607450d9e0abb932109ceToomas Soome (void)net_set_cork(proxy->fd_ssl, FALSE);
199767f8919635c4928607450d9e0abb932109ceToomas Soome }
199767f8919635c4928607450d9e0abb932109ceToomas Soome }
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soome ssl_proxy_unref(proxy);
199767f8919635c4928607450d9e0abb932109ceToomas Soome}
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soomestatic int
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 struct ssl_proxy **proxy_r)
199767f8919635c4928607450d9e0abb932109ceToomas Soome{
199767f8919635c4928607450d9e0abb932109ceToomas Soome struct ssl_proxy *proxy;
199767f8919635c4928607450d9e0abb932109ceToomas Soome SSL *ssl;
199767f8919635c4928607450d9e0abb932109ceToomas Soome int sfd[2];
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soome i_assert(fd != -1);
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soome *proxy_r = NULL;
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soome if (!ssl_initialized) {
199767f8919635c4928607450d9e0abb932109ceToomas Soome i_error("SSL support not enabled in configuration");
199767f8919635c4928607450d9e0abb932109ceToomas Soome return -1;
199767f8919635c4928607450d9e0abb932109ceToomas Soome }
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soome ssl_refresh_parameters(&ssl_params);
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soome ssl = SSL_new(ssl_ctx);
199767f8919635c4928607450d9e0abb932109ceToomas Soome if (ssl == NULL) {
199767f8919635c4928607450d9e0abb932109ceToomas Soome i_error("SSL_new() failed: %s", openssl_iostream_error());
199767f8919635c4928607450d9e0abb932109ceToomas Soome return -1;
199767f8919635c4928607450d9e0abb932109ceToomas Soome }
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soome if (SSL_set_fd(ssl, fd) != 1) {
199767f8919635c4928607450d9e0abb932109ceToomas Soome i_error("SSL_set_fd() failed: %s", openssl_iostream_error());
199767f8919635c4928607450d9e0abb932109ceToomas Soome SSL_free(ssl);
199767f8919635c4928607450d9e0abb932109ceToomas Soome return -1;
199767f8919635c4928607450d9e0abb932109ceToomas Soome }
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soome if (socketpair(AF_UNIX, SOCK_STREAM, 0, sfd) < 0) {
199767f8919635c4928607450d9e0abb932109ceToomas Soome i_error("socketpair() failed: %m");
199767f8919635c4928607450d9e0abb932109ceToomas Soome SSL_free(ssl);
199767f8919635c4928607450d9e0abb932109ceToomas Soome return -1;
199767f8919635c4928607450d9e0abb932109ceToomas Soome }
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soome net_set_nonblock(sfd[0], TRUE);
199767f8919635c4928607450d9e0abb932109ceToomas Soome net_set_nonblock(sfd[1], TRUE);
199767f8919635c4928607450d9e0abb932109ceToomas Soome net_set_nonblock(fd, TRUE);
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soome proxy = i_new(struct ssl_proxy, 1);
199767f8919635c4928607450d9e0abb932109ceToomas Soome proxy->refcount = 2;
199767f8919635c4928607450d9e0abb932109ceToomas Soome proxy->ssl = ssl;
199767f8919635c4928607450d9e0abb932109ceToomas Soome proxy->login_set = login_set;
199767f8919635c4928607450d9e0abb932109ceToomas Soome proxy->ssl_set = ssl_set;
199767f8919635c4928607450d9e0abb932109ceToomas Soome proxy->fd_ssl = fd;
199767f8919635c4928607450d9e0abb932109ceToomas Soome proxy->fd_plain = sfd[0];
199767f8919635c4928607450d9e0abb932109ceToomas Soome proxy->ip = *ip;
199767f8919635c4928607450d9e0abb932109ceToomas Soome proxy->set_pool = set_pool;
199767f8919635c4928607450d9e0abb932109ceToomas Soome pool_ref(set_pool);
199767f8919635c4928607450d9e0abb932109ceToomas Soome SSL_set_ex_data(ssl, extdata_index, proxy);
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soome ssl_proxy_count++;
199767f8919635c4928607450d9e0abb932109ceToomas Soome DLLIST_PREPEND(&ssl_proxies, proxy);
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soome *proxy_r = proxy;
199767f8919635c4928607450d9e0abb932109ceToomas Soome return sfd[1];
199767f8919635c4928607450d9e0abb932109ceToomas Soome}
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soomestatic struct ssl_server_context *
199767f8919635c4928607450d9e0abb932109ceToomas Soomessl_server_context_get(const struct login_settings *login_set,
199767f8919635c4928607450d9e0abb932109ceToomas Soome const struct master_service_ssl_settings *set)
199767f8919635c4928607450d9e0abb932109ceToomas Soome{
199767f8919635c4928607450d9e0abb932109ceToomas Soome struct ssl_server_context *ctx, lookup_ctx;
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soome memset(&lookup_ctx, 0, sizeof(lookup_ctx));
199767f8919635c4928607450d9e0abb932109ceToomas Soome lookup_ctx.cert = set->ssl_cert;
199767f8919635c4928607450d9e0abb932109ceToomas Soome lookup_ctx.key = set->ssl_key;
199767f8919635c4928607450d9e0abb932109ceToomas Soome lookup_ctx.ca = set->ssl_ca;
199767f8919635c4928607450d9e0abb932109ceToomas Soome lookup_ctx.cipher_list = set->ssl_cipher_list;
199767f8919635c4928607450d9e0abb932109ceToomas Soome lookup_ctx.protocols = set->ssl_protocols;
199767f8919635c4928607450d9e0abb932109ceToomas Soome lookup_ctx.verify_client_cert = set->ssl_verify_client_cert ||
199767f8919635c4928607450d9e0abb932109ceToomas Soome login_set->auth_ssl_require_client_cert ||
199767f8919635c4928607450d9e0abb932109ceToomas Soome login_set->auth_ssl_username_from_cert;
199767f8919635c4928607450d9e0abb932109ceToomas Soome lookup_ctx.prefer_server_ciphers = set->ssl_prefer_server_ciphers;
199767f8919635c4928607450d9e0abb932109ceToomas Soome lookup_ctx.compression = set->parsed_opts.compression;
199767f8919635c4928607450d9e0abb932109ceToomas Soome lookup_ctx.tickets = set->parsed_opts.tickets;
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soome ctx = hash_table_lookup(ssl_servers, &lookup_ctx);
199767f8919635c4928607450d9e0abb932109ceToomas Soome if (ctx == NULL)
199767f8919635c4928607450d9e0abb932109ceToomas Soome ctx = ssl_server_context_init(login_set, set);
199767f8919635c4928607450d9e0abb932109ceToomas Soome return ctx;
199767f8919635c4928607450d9e0abb932109ceToomas Soome}
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soomeint ssl_proxy_alloc(int fd, const struct ip_addr *ip, pool_t set_pool,
199767f8919635c4928607450d9e0abb932109ceToomas Soome const struct login_settings *login_set,
199767f8919635c4928607450d9e0abb932109ceToomas Soome const struct master_service_ssl_settings *ssl_set,
199767f8919635c4928607450d9e0abb932109ceToomas Soome struct ssl_proxy **proxy_r)
199767f8919635c4928607450d9e0abb932109ceToomas Soome{
199767f8919635c4928607450d9e0abb932109ceToomas Soome struct ssl_server_context *ctx;
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soome ctx = ssl_server_context_get(login_set, ssl_set);
199767f8919635c4928607450d9e0abb932109ceToomas Soome return ssl_proxy_alloc_common(ctx->ctx, fd, ip,
199767f8919635c4928607450d9e0abb932109ceToomas Soome set_pool, login_set, ssl_set, proxy_r);
199767f8919635c4928607450d9e0abb932109ceToomas Soome}
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soomeint ssl_proxy_client_alloc(int fd, struct ip_addr *ip, pool_t set_pool,
199767f8919635c4928607450d9e0abb932109ceToomas Soome const struct login_settings *login_set,
199767f8919635c4928607450d9e0abb932109ceToomas Soome const struct master_service_ssl_settings *ssl_set,
199767f8919635c4928607450d9e0abb932109ceToomas Soome ssl_handshake_callback_t *callback, void *context,
199767f8919635c4928607450d9e0abb932109ceToomas Soome struct ssl_proxy **proxy_r)
199767f8919635c4928607450d9e0abb932109ceToomas Soome{
199767f8919635c4928607450d9e0abb932109ceToomas Soome int ret;
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soome ret = ssl_proxy_alloc_common(ssl_client_ctx, fd, ip,
199767f8919635c4928607450d9e0abb932109ceToomas Soome set_pool, login_set, ssl_set, proxy_r);
199767f8919635c4928607450d9e0abb932109ceToomas Soome if (ret < 0)
199767f8919635c4928607450d9e0abb932109ceToomas Soome return -1;
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soome (*proxy_r)->handshake_callback = callback;
199767f8919635c4928607450d9e0abb932109ceToomas Soome (*proxy_r)->handshake_context = context;
199767f8919635c4928607450d9e0abb932109ceToomas Soome (*proxy_r)->client_proxy = TRUE;
199767f8919635c4928607450d9e0abb932109ceToomas Soome return ret;
199767f8919635c4928607450d9e0abb932109ceToomas Soome}
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soomevoid ssl_proxy_start(struct ssl_proxy *proxy)
199767f8919635c4928607450d9e0abb932109ceToomas Soome{
199767f8919635c4928607450d9e0abb932109ceToomas Soome ssl_step(proxy);
199767f8919635c4928607450d9e0abb932109ceToomas Soome}
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soomevoid ssl_proxy_set_client(struct ssl_proxy *proxy, struct client *client)
199767f8919635c4928607450d9e0abb932109ceToomas Soome{
199767f8919635c4928607450d9e0abb932109ceToomas Soome i_assert(proxy->client == NULL);
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soome client_ref(client);
199767f8919635c4928607450d9e0abb932109ceToomas Soome proxy->client = client;
199767f8919635c4928607450d9e0abb932109ceToomas Soome}
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soomebool ssl_proxy_has_valid_client_cert(const struct ssl_proxy *proxy)
199767f8919635c4928607450d9e0abb932109ceToomas Soome{
199767f8919635c4928607450d9e0abb932109ceToomas Soome return proxy->cert_received && !proxy->cert_broken;
199767f8919635c4928607450d9e0abb932109ceToomas Soome}
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soomebool ssl_proxy_has_broken_client_cert(struct ssl_proxy *proxy)
199767f8919635c4928607450d9e0abb932109ceToomas Soome{
199767f8919635c4928607450d9e0abb932109ceToomas Soome return proxy->cert_received && proxy->cert_broken;
199767f8919635c4928607450d9e0abb932109ceToomas Soome}
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soomeint ssl_proxy_cert_match_name(struct ssl_proxy *proxy, const char *verify_name)
199767f8919635c4928607450d9e0abb932109ceToomas Soome{
199767f8919635c4928607450d9e0abb932109ceToomas Soome return openssl_cert_match_name(proxy->ssl, verify_name);
199767f8919635c4928607450d9e0abb932109ceToomas Soome}
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soomeconst char *ssl_proxy_get_peer_name(struct ssl_proxy *proxy)
199767f8919635c4928607450d9e0abb932109ceToomas Soome{
199767f8919635c4928607450d9e0abb932109ceToomas Soome X509 *x509;
199767f8919635c4928607450d9e0abb932109ceToomas Soome char *name;
199767f8919635c4928607450d9e0abb932109ceToomas Soome int len;
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soome if (!ssl_proxy_has_valid_client_cert(proxy))
199767f8919635c4928607450d9e0abb932109ceToomas Soome return NULL;
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soome x509 = SSL_get_peer_certificate(proxy->ssl);
199767f8919635c4928607450d9e0abb932109ceToomas Soome if (x509 == NULL)
199767f8919635c4928607450d9e0abb932109ceToomas Soome return NULL; /* we should have had it.. */
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soome len = X509_NAME_get_text_by_NID(X509_get_subject_name(x509),
199767f8919635c4928607450d9e0abb932109ceToomas Soome ssl_username_nid, NULL, 0);
199767f8919635c4928607450d9e0abb932109ceToomas Soome if (len < 0)
199767f8919635c4928607450d9e0abb932109ceToomas Soome name = "";
199767f8919635c4928607450d9e0abb932109ceToomas Soome else {
199767f8919635c4928607450d9e0abb932109ceToomas Soome name = t_malloc(len + 1);
199767f8919635c4928607450d9e0abb932109ceToomas Soome if (X509_NAME_get_text_by_NID(X509_get_subject_name(x509),
199767f8919635c4928607450d9e0abb932109ceToomas Soome ssl_username_nid, name, len + 1) < 0)
199767f8919635c4928607450d9e0abb932109ceToomas Soome name = "";
199767f8919635c4928607450d9e0abb932109ceToomas Soome else if (strlen(name) != (size_t)len) {
199767f8919635c4928607450d9e0abb932109ceToomas Soome /* NUL characters in name. Someone's trying to fake
199767f8919635c4928607450d9e0abb932109ceToomas Soome being another user? Don't allow it. */
199767f8919635c4928607450d9e0abb932109ceToomas Soome name = "";
199767f8919635c4928607450d9e0abb932109ceToomas Soome }
199767f8919635c4928607450d9e0abb932109ceToomas Soome }
199767f8919635c4928607450d9e0abb932109ceToomas Soome X509_free(x509);
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soome return *name == '\0' ? NULL : name;
199767f8919635c4928607450d9e0abb932109ceToomas Soome}
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soomebool ssl_proxy_is_handshaked(const struct ssl_proxy *proxy)
199767f8919635c4928607450d9e0abb932109ceToomas Soome{
199767f8919635c4928607450d9e0abb932109ceToomas Soome return proxy->handshaked;
199767f8919635c4928607450d9e0abb932109ceToomas Soome}
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soomeconst char *ssl_proxy_get_last_error(const struct ssl_proxy *proxy)
199767f8919635c4928607450d9e0abb932109ceToomas Soome{
199767f8919635c4928607450d9e0abb932109ceToomas Soome return proxy->last_error;
199767f8919635c4928607450d9e0abb932109ceToomas Soome}
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soomeconst char *ssl_proxy_get_security_string(struct ssl_proxy *proxy)
199767f8919635c4928607450d9e0abb932109ceToomas Soome{
199767f8919635c4928607450d9e0abb932109ceToomas Soome const SSL_CIPHER *cipher;
199767f8919635c4928607450d9e0abb932109ceToomas Soome int bits, alg_bits;
199767f8919635c4928607450d9e0abb932109ceToomas Soome const char *comp_str;
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soome if (!proxy->handshaked)
199767f8919635c4928607450d9e0abb932109ceToomas Soome return "";
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soome cipher = SSL_get_current_cipher(proxy->ssl);
199767f8919635c4928607450d9e0abb932109ceToomas Soome bits = SSL_CIPHER_get_bits(cipher, &alg_bits);
199767f8919635c4928607450d9e0abb932109ceToomas Soome comp_str = ssl_proxy_get_compression(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 Soome SSL_get_version(proxy->ssl),
199767f8919635c4928607450d9e0abb932109ceToomas Soome SSL_CIPHER_get_name(cipher),
199767f8919635c4928607450d9e0abb932109ceToomas Soome bits, alg_bits, comp_str);
199767f8919635c4928607450d9e0abb932109ceToomas Soome}
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soomeconst char *ssl_proxy_get_compression(struct ssl_proxy *proxy ATTR_UNUSED)
199767f8919635c4928607450d9e0abb932109ceToomas Soome{
199767f8919635c4928607450d9e0abb932109ceToomas Soome#if defined(HAVE_SSL_COMPRESSION) && !defined(OPENSSL_NO_COMP)
199767f8919635c4928607450d9e0abb932109ceToomas Soome const COMP_METHOD *comp;
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soome comp = SSL_get_current_compression(proxy->ssl);
199767f8919635c4928607450d9e0abb932109ceToomas Soome return comp == NULL ? NULL : SSL_COMP_get_name(comp);
199767f8919635c4928607450d9e0abb932109ceToomas Soome#else
199767f8919635c4928607450d9e0abb932109ceToomas Soome return NULL;
199767f8919635c4928607450d9e0abb932109ceToomas Soome#endif
199767f8919635c4928607450d9e0abb932109ceToomas Soome}
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soomeconst char *ssl_proxy_get_cert_error(struct ssl_proxy *proxy)
199767f8919635c4928607450d9e0abb932109ceToomas Soome{
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_flush(struct ssl_proxy *proxy)
{
/* this is pretty kludgy. mainly this is just for flushing the final
LOGOUT command output. */
plain_read(proxy);
ssl_step(proxy);
}
void ssl_proxy_destroy(struct ssl_proxy *proxy)
{
if (proxy->destroyed || proxy->flushing)
return;
proxy->flushing = TRUE;
if (!proxy->failed && proxy->handshaked)
ssl_proxy_flush(proxy);
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)
{
if (is_export && keylength == 512 && ssl_params.dh_512 != NULL)
return ssl_params.dh_512;
return ssl_params.dh_default;
}
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) {
switch (ret & 0xff) {
case SSL_AD_CLOSE_NOTIFY:
i_debug("SSL alert: %s [%s]",
SSL_alert_desc_string_long(ret),
net_ip2addr(&proxy->ip));
break;
default:
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));
break;
}
} 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 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", openssl_iostream_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 */
long ssl_ops = SSL_OP_ALL & ~SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS;
#ifdef SSL_OP_NO_COMPRESSION
if (!set->parsed_opts.compression)
ssl_ops |= SSL_OP_NO_COMPRESSION;
#endif
#ifdef SSL_OP_NO_TICKET
if (!set->parsed_opts.tickets)
ssl_ops |= SSL_OP_NO_TICKET;
#endif
SSL_CTX_set_options(ssl_ctx, ssl_ops);
#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_proxy_ctx_set_crypto_params(ssl_ctx, set);
SSL_CTX_set_info_callback(ssl_ctx, ssl_info_callback);
return xnames;
}
static void
ssl_proxy_ctx_set_crypto_params(SSL_CTX *ssl_ctx,
const struct master_service_ssl_settings *set ATTR_UNUSED)
{
#if defined(HAVE_ECDH) && !defined(SSL_CTRL_SET_ECDH_AUTO)
EC_KEY *ecdh;
int nid;
const char *curve_name;
#endif
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);
#ifdef HAVE_ECDH
/* In the non-recommended situation where ECDH cipher suites are being
used instead of ECDHE, do not reuse the same ECDH key pair for
different sessions. This option improves forward secrecy. */
SSL_CTX_set_options(ssl_ctx, SSL_OP_SINGLE_ECDH_USE);
#ifdef SSL_CTRL_SET_ECDH_AUTO
/* OpenSSL >= 1.0.2 automatically handles ECDH temporary key parameter
selection. */
SSL_CTX_set_ecdh_auto(ssl_ctx, 1);
#else
/* For OpenSSL < 1.0.2, ECDH temporary key parameter selection must be
performed manually. Attempt to select the same curve as that used
in the server's private EC key file. Otherwise fall back to the
NIST P-384 (secp384r1) curve to be compliant with RFC 6460 when
AES-256 TLS cipher suites are in use. This fall back option does
however make Dovecot non-compliant with RFC 6460 which requires
curve NIST P-256 (prime256v1) be used when AES-128 TLS cipher
suites are in use. At least the non-compliance is in the form of
providing too much security rather than too little. */
nid = ssl_proxy_ctx_get_pkey_ec_curve_name(set);
ecdh = EC_KEY_new_by_curve_name(nid);
if (ecdh == NULL) {
/* Fall back option */
nid = NID_secp384r1;
ecdh = EC_KEY_new_by_curve_name(nid);
}
if ((curve_name = OBJ_nid2sn(nid)) != NULL && set->verbose_ssl)
i_debug("SSL: elliptic curve %s will be used for ECDH and"
" ECDHE key exchanges", curve_name);
if (ecdh != NULL) {
SSL_CTX_set_tmp_ecdh(ssl_ctx, ecdh);
EC_KEY_free(ecdh);
}
#endif
#endif
}
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 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",
openssl_iostream_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", openssl_iostream_key_load_error());
EVP_PKEY_free(pkey);
}
#if defined(HAVE_ECDH) && !defined(SSL_CTRL_SET_ECDH_AUTO)
static int
ssl_proxy_ctx_get_pkey_ec_curve_name(const struct master_service_ssl_settings *set)
{
int nid = 0;
EVP_PKEY *pkey;
const char *password;
EC_KEY *eckey;
const EC_GROUP *ecgrp;
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 (pkey != NULL &&
(eckey = EVP_PKEY_get1_EC_KEY(pkey)) != NULL &&
(ecgrp = EC_KEY_get0_group(eckey)) != NULL)
nid = EC_GROUP_get_curve_name(ecgrp);
else {
/* clear errors added by the above calls */
openssl_iostream_clear_errors();
}
EVP_PKEY_free(pkey);
return nid;
}
#endif
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->prefer_server_ciphers = ssl_set->ssl_prefer_server_ciphers;
ctx->compression = ssl_set->parsed_opts.compression;
ctx->tickets = ssl_set->parsed_opts.tickets;
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, openssl_iostream_error());
}
if (ctx->prefer_server_ciphers)
SSL_CTX_set_options(ssl_ctx, SSL_OP_CIPHER_SERVER_PREFERENCE);
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",
openssl_iostream_use_certificate_error(ctx->cert, "ssl_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);
if (ctx->verify_client_cert)
ssl_proxy_ctx_verify_client(ctx->ctx, xnames);
i_assert(hash_table_lookup(ssl_servers, ctx) == NULL);
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",
openssl_iostream_use_certificate_error(
set->ssl_client_cert, "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",
openssl_iostream_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(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