From: Timo Sirainen Date: Tue, 31 Oct 2017 23:51:14 +0000 (+0200) Subject: login-common: Remove ssl-proxy code X-Git-Tag: 2.3.0.rc1~509 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=71bd935dba8721d8607608bc73d04247a3732166;p=thirdparty%2Fdovecot%2Fcore.git login-common: Remove ssl-proxy code --- diff --git a/src/login-common/Makefile.am b/src/login-common/Makefile.am index 7340e6b306..806ddd235a 100644 --- a/src/login-common/Makefile.am +++ b/src/login-common/Makefile.am @@ -19,18 +19,7 @@ liblogin_la_SOURCES = \ login-proxy-state.c \ login-settings.c \ main.c \ - sasl-server.c \ - ssl-proxy.c \ - ssl-proxy-gnutls.c \ - ssl-proxy-openssl.c - -if BUILD_OPENSSL -openssl_obj = ../lib-ssl-iostream/iostream-openssl-common.lo -endif - -liblogin_la_LIBADD = \ - $(openssl_obj) \ - $(SSL_LIBS) + sasl-server.c headers = \ access-lookup.h \ @@ -39,8 +28,7 @@ headers = \ login-proxy.h \ login-proxy-state.h \ login-settings.h \ - sasl-server.h \ - ssl-proxy.h + sasl-server.h pkginc_libdir=$(pkgincludedir) pkginc_lib_HEADERS = $(headers) diff --git a/src/login-common/ssl-proxy-gnutls.c b/src/login-common/ssl-proxy-gnutls.c deleted file mode 100644 index 1d85ed2de1..0000000000 --- a/src/login-common/ssl-proxy-gnutls.c +++ /dev/null @@ -1,543 +0,0 @@ -/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ - -#include "login-common.h" -#include "ioloop.h" -#include "net.h" -#include "hash.h" -#include "ssl-proxy.h" - -#ifdef HAVE_GNUTLS - -#error broken currently - -#include -#include -#include -#include -#include - -struct ssl_proxy { - int refcount; - - gnutls_session session; - struct ip_addr ip; - - int fd_ssl, fd_plain; - struct io *io_ssl, *io_plain; - int io_ssl_dir; - - unsigned char outbuf_plain[1024]; - unsigned int outbuf_pos_plain; - - size_t send_left_ssl, send_left_plain; -}; - -const int protocol_priority[] = - { GNUTLS_TLS1, GNUTLS_SSL3, 0 }; -const int kx_priority[] = - { GNUTLS_KX_DHE_DSS, GNUTLS_KX_RSA, GNUTLS_KX_DHE_RSA, 0 }; -const int cipher_priority[] = - { GNUTLS_CIPHER_RIJNDAEL_CBC, GNUTLS_CIPHER_3DES_CBC, - GNUTLS_CIPHER_ARCFOUR_128, GNUTLS_CIPHER_ARCFOUR_40, 0 }; -const int comp_priority[] = - { GNUTLS_COMP_LZO, GNUTLS_COMP_ZLIB, GNUTLS_COMP_NULL, 0 }; -const int mac_priority[] = - { GNUTLS_MAC_SHA, GNUTLS_MAC_MD5, 0 }; -const int cert_type_priority[] = - { GNUTLS_CRT_X509, 0 }; - -static struct hash_table *ssl_proxies; -static gnutls_certificate_credentials x509_cred; -static gnutls_dh_params dh_params; -static gnutls_rsa_params rsa_params; - -static void ssl_input(struct ssl_proxy *proxy); -static void plain_input(struct ssl_proxy *proxy); -static bool ssl_proxy_destroy(struct ssl_proxy *proxy); - -static const char *get_alert_text(struct ssl_proxy *proxy) -{ - return gnutls_alert_get_name(gnutls_alert_get(proxy->session)); -} - -static int handle_ssl_error(struct ssl_proxy *proxy, int error) -{ - if (!gnutls_error_is_fatal(error)) { - if (!verbose_ssl) - return 0; - - if (error == GNUTLS_E_WARNING_ALERT_RECEIVED) { - i_warning("Received SSL warning alert: %s [%s]", - get_alert_text(proxy), - net_ip2addr(&proxy->ip)); - } else { - i_warning("Non-fatal SSL error: %s: %s", - get_alert_text(proxy), - net_ip2addr(&proxy->ip)); - } - return 0; - } - - if (verbose_ssl) { - /* fatal error occurred */ - if (error == GNUTLS_E_FATAL_ALERT_RECEIVED) { - i_warning("Received SSL fatal alert: %s [%s]", - get_alert_text(proxy), - net_ip2addr(&proxy->ip)); - } else { - i_warning("Error reading from SSL client: %s [%s]", - gnutls_strerror(error), - net_ip2addr(&proxy->ip)); - } - } - - gnutls_alert_send_appropriate(proxy->session, error); - ssl_proxy_destroy(proxy); - return -1; -} - -static int proxy_recv_ssl(struct ssl_proxy *proxy, void *data, size_t size) -{ - int rcvd; - - rcvd = gnutls_record_recv(proxy->session, data, size); - if (rcvd > 0) - return rcvd; - - if (rcvd == 0 || rcvd == GNUTLS_E_UNEXPECTED_PACKET_LENGTH) { - /* disconnected, either by nicely telling us that we'll - close the connection, or by simply killing the - connection which gives us the packet length error. */ - ssl_proxy_destroy(proxy); - return -1; - } - - return handle_ssl_error(proxy, rcvd); -} - -static int proxy_send_ssl(struct ssl_proxy *proxy, - const void *data, size_t size) -{ - int sent; - - sent = gnutls_record_send(proxy->session, data, size); - if (sent >= 0) - return sent; - - if (sent == GNUTLS_E_PUSH_ERROR || sent == GNUTLS_E_INVALID_SESSION) { - /* don't warn about errors related to unexpected - disconnection */ - ssl_proxy_destroy(proxy); - return -1; - } - - return handle_ssl_error(proxy, sent); -} - -static int ssl_proxy_destroy(struct ssl_proxy *proxy) -{ - if (--proxy->refcount > 0) - return TRUE; - - hash_table_remove(ssl_proxies, proxy); - - gnutls_deinit(proxy->session); - - if (proxy->io_ssl != NULL) - io_remove(proxy->io_ssl); - if (proxy->io_plain != NULL) - io_remove(proxy->io_plain); - - net_disconnect(proxy->fd_ssl); - net_disconnect(proxy->fd_plain); - - i_free(proxy); - - main_unref(); - return FALSE; -} - -static void ssl_output(struct ssl_proxy *proxy) -{ - int sent; - - sent = net_transmit(proxy->fd_plain, - proxy->outbuf_plain + proxy->outbuf_pos_plain, - proxy->send_left_plain); - if (sent < 0) { - /* disconnected */ - ssl_proxy_destroy(proxy); - return; - } - - proxy->send_left_plain -= sent; - proxy->outbuf_pos_plain += sent; - - if (proxy->send_left_plain > 0) - return; - - /* everything is sent, start reading again */ - io_remove(proxy->io_ssl); - proxy->io_ssl = io_add(proxy->fd_ssl, IO_READ, ssl_input, proxy); -} - -static void ssl_input(struct ssl_proxy *proxy) -{ - int rcvd, sent; - - rcvd = proxy_recv_ssl(proxy, proxy->outbuf_plain, - sizeof(proxy->outbuf_plain)); - if (rcvd <= 0) - return; - - sent = net_transmit(proxy->fd_plain, proxy->outbuf_plain, (size_t)rcvd); - if (sent == rcvd) - return; - - if (sent < 0) { - /* disconnected */ - ssl_proxy_destroy(proxy); - return; - } - - /* everything wasn't sent - don't read anything until we've - sent it all */ - proxy->outbuf_pos_plain = 0; - proxy->send_left_plain = rcvd - sent; - - io_remove(proxy->io_ssl); - proxy->io_ssl = io_add(proxy->fd_ssl, IO_WRITE, ssl_output, proxy); -} - -static void plain_output(struct ssl_proxy *proxy) -{ - int sent; - - sent = proxy_send_ssl(proxy, NULL, proxy->send_left_ssl); - if (sent <= 0) - return; - - proxy->send_left_ssl -= sent; - if (proxy->send_left_ssl > 0) - return; - - /* everything is sent, start reading again */ - io_remove(proxy->io_plain); - proxy->io_plain = io_add(proxy->fd_plain, IO_READ, plain_input, proxy); -} - -static void plain_input(struct ssl_proxy *proxy) -{ - char buf[1024]; - ssize_t rcvd, sent; - - rcvd = net_receive(proxy->fd_plain, buf, sizeof(buf)); - if (rcvd < 0) { - /* disconnected */ - gnutls_bye(proxy->session, 1); - ssl_proxy_destroy(proxy); - return; - } - - sent = proxy_send_ssl(proxy, buf, (size_t)rcvd); - if (sent < 0 || sent == rcvd) - return; - - /* everything wasn't sent - don't read anything until we've - sent it all */ - proxy->send_left_ssl = rcvd - sent; - - io_remove(proxy->io_plain); - proxy->io_plain = io_add(proxy->fd_ssl, IO_WRITE, plain_output, proxy); -} - -static void ssl_handshake(struct ssl_proxy *proxy) -{ - int ret, dir; - - ret = gnutls_handshake(proxy->session); - if (ret >= 0) { - /* handshake done, now we can start reading */ - if (proxy->io_ssl != NULL) - io_remove(proxy->io_ssl); - - proxy->io_plain = io_add(proxy->fd_plain, IO_READ, - plain_input, proxy); - proxy->io_ssl = io_add(proxy->fd_ssl, IO_READ, - ssl_input, proxy); - return; - } - - if (handle_ssl_error(proxy, ret) < 0) - return; - - /* i/o interrupted */ - dir = gnutls_record_get_direction(proxy->session) == 0 ? - IO_READ : IO_WRITE; - if (proxy->io_ssl_dir != dir) { - if (proxy->io_ssl != NULL) - io_remove(proxy->io_ssl); - proxy->io_ssl = io_add(proxy->fd_ssl, dir, - ssl_handshake, proxy); - proxy->io_ssl_dir = dir; - } -} - -static gnutls_session initialize_state(void) -{ - gnutls_session session; - - gnutls_init(&session, GNUTLS_SERVER); - - gnutls_protocol_set_priority(session, protocol_priority); - gnutls_cipher_set_priority(session, cipher_priority); - gnutls_compression_set_priority(session, comp_priority); - gnutls_kx_set_priority(session, kx_priority); - gnutls_mac_set_priority(session, mac_priority); - gnutls_certificate_type_set_priority(session, cert_type_priority); - - gnutls_cred_set(session, GNUTLS_CRD_CERTIFICATE, x509_cred); - return session; -} - -int ssl_proxy_new(int fd, struct ip_addr *ip) -{ - struct ssl_proxy *proxy; - gnutls_session session; - int sfd[2]; - - if (!ssl_initialized) { - i_error("SSL support not enabled in configuration"); - return -1; - } - - session = initialize_state(); - gnutls_transport_set_ptr(session, fd); - - if (socketpair(AF_UNIX, SOCK_STREAM, 0, sfd) == -1) { - i_error("socketpair() failed: %m"); - gnutls_deinit(session); - return -1; - } - - net_set_nonblock(sfd[0], TRUE); - net_set_nonblock(sfd[1], TRUE); - net_set_nonblock(fd, TRUE); - - proxy = i_new(struct ssl_proxy, 1); - proxy->refcount = 1; - proxy->session = session; - proxy->fd_ssl = fd; - proxy->fd_plain = sfd[0]; - proxy->ip = *ip; - - hash_table_insert(ssl_proxies, proxy, proxy); - - proxy->refcount++; - ssl_handshake(proxy); - if (!ssl_proxy_destroy(proxy)) { - /* handshake failed. return the disconnected socket anyway - so the caller doesn't try to use the old closed fd */ - return sfd[1]; - } - - main_ref(); - return sfd[1]; -} - -static void read_next_field(int fd, gnutls_datum *datum, - const char *fname, const char *field_name) -{ - ssize_t ret; - - /* get size */ - ret = read(fd, &datum->size, sizeof(datum->size)); - if (ret < 0) - i_fatal("read() failed for %s: %m", fname); - - if (ret != sizeof(datum->size)) { - i_unlink(fname); - i_fatal("Corrupted SSL parameter file %s: File too small", - fname); - } - - if (datum->size > 10240) { - i_unlink(fname); - i_fatal("Corrupted SSL parameter file %s: " - "Field '%s' too large (%u)", - fname, field_name, datum->size); - } - - /* read the actual data */ - datum->data = t_malloc(datum->size); - ret = read(fd, datum->data, datum->size); - if (ret < 0) - i_fatal("read() failed for %s: %m", fname); - - if ((size_t)ret != datum->size) { - i_unlink(fname); - i_fatal("Corrupted SSL parameter file %s: " - "Field '%s' not fully in file (%u < %u)", - fname, field_name, datum->size - ret, datum->size); - } -} - -static void read_dh_parameters(int fd, const char *fname) -{ - gnutls_datum dbits, prime, generator; - int ret, bits; - - if ((ret = gnutls_dh_params_init(&dh_params)) < 0) { - i_fatal("gnutls_dh_params_init() failed: %s", - gnutls_strerror(ret)); - } - - /* read until bits field is 0 */ - for (;;) { - read_next_field(fd, &dbits, fname, "DH bits"); - - if (dbits.size != sizeof(int)) { - i_unlink(fname); - i_fatal("Corrupted SSL parameter file %s: " - "Field 'DH bits' has invalid size %u", - fname, dbits.size); - } - - bits = *((int *) dbits.data); - if (bits == 0) - break; - - read_next_field(fd, &prime, fname, "DH prime"); - read_next_field(fd, &generator, fname, "DH generator"); - - ret = gnutls_dh_params_set(dh_params, prime, generator, bits); - if (ret < 0) { - i_fatal("gnutls_dh_params_set() failed: %s", - gnutls_strerror(ret)); - } - } -} - -static void read_rsa_parameters(int fd, const char *fname) -{ - gnutls_datum m, e, d, p, q, u; - int ret; - - read_next_field(fd, &m, fname, "RSA m"); - read_next_field(fd, &e, fname, "RSA e"); - read_next_field(fd, &d, fname, "RSA d"); - read_next_field(fd, &p, fname, "RSA p"); - read_next_field(fd, &q, fname, "RSA q"); - read_next_field(fd, &u, fname, "RSA u"); - - if ((ret = gnutls_rsa_params_init(&rsa_params)) < 0) { - i_fatal("gnutls_rsa_params_init() failed: %s", - gnutls_strerror(ret)); - } - - /* only 512bit is allowed */ - ret = gnutls_rsa_params_set(rsa_params, m, e, d, p, q, u, 512); - if (ret < 0) { - i_fatal("gnutls_rsa_params_set() failed: %s", - gnutls_strerror(ret)); - } -} - -static void read_parameters(const char *fname) -{ - int fd; - - /* we'll wait until parameter file exists */ - for (;;) { - fd = open(fname, O_RDONLY); - if (fd != -1) - break; - - if (errno != ENOENT) - i_fatal("Can't open SSL parameter file %s: %m", fname); - - sleep(1); - } - - read_dh_parameters(fd, fname); - read_rsa_parameters(fd, fname); - - i_close_fd(&fd); -} - -static void gcrypt_log_handler(void *context ATTR_UNUSED, int level, - const char *fmt, va_list args) -{ - if (level != GCRY_LOG_FATAL) - return; - - T_BEGIN { - i_error("gcrypt fatal: %s", t_strdup_vprintf(fmt, args)); - } T_END; -} - -void ssl_proxy_init(void) -{ - const char *certfile, *keyfile, *paramfile; - unsigned char buf[4]; - int ret; - - certfile = getenv("SSL_CERT_FILE"); - keyfile = getenv("SSL_KEY_FILE"); - paramfile = getenv("SSL_PARAM_FILE"); - - if (certfile == NULL || keyfile == NULL || paramfile == NULL) { - /* SSL support is disabled */ - return; - } - - if ((ret = gnutls_global_init() < 0)) { - i_fatal("gnu_tls_global_init() failed: %s", - gnutls_strerror(ret)); - } - - /* gcrypt initialization - set log handler and make sure randomizer - opens /dev/urandom now instead of after we've chrooted */ - gcry_set_log_handler(gcrypt_log_handler, NULL); - gcry_randomize(buf, sizeof(buf), GCRY_STRONG_RANDOM); - - read_parameters(paramfile); - - if ((ret = gnutls_certificate_allocate_credentials(&x509_cred)) < 0) { - i_fatal("gnutls_certificate_allocate_credentials() failed: %s", - gnutls_strerror(ret)); - } - - ret = gnutls_certificate_set_x509_key_file(x509_cred, certfile, keyfile, - GNUTLS_X509_FMT_PEM); - if (ret < 0) { - i_fatal("Can't load certificate files %s and %s: %s", - certfile, keyfile, gnutls_strerror(ret)); - } - - gnutls_certificate_set_dh_params(x509_cred, dh_params); - gnutls_certificate_set_rsa_export_params(x509_cred, rsa_params); - - ssl_proxies = hash_table_create(default_pool, 0, NULL, NULL); - ssl_initialized = TRUE; -} - -void ssl_proxy_deinit(void) -{ - struct hash_iterate_context *iter; - void *key, *value; - - if (!ssl_initialized) - return; - - iter = hash_table_iterate_init(ssl_proxies); - while (hash_table_iterate(iter, &key, &value)) - ssl_proxy_destroy(value); - hash_table_iterate_deinit(iter); - hash_table_destroy(ssl_proxies); - - gnutls_certificate_free_credentials(x509_cred); - gnutls_global_deinit(); -} - -#endif diff --git a/src/login-common/ssl-proxy-openssl.c b/src/login-common/ssl-proxy-openssl.c deleted file mode 100644 index b2432d9c63..0000000000 --- a/src/login-common/ssl-proxy-openssl.c +++ /dev/null @@ -1,1391 +0,0 @@ -/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ - -#include "login-common.h" -#include "array.h" -#include "ioloop.h" -#include "net.h" -#include "ostream.h" -#include "read-full.h" -#include "safe-memset.h" -#include "hash.h" -#include "llist.h" -#include "master-interface.h" -#include "master-service-ssl-settings.h" -#include "client-common.h" -#include "ssl-proxy.h" - -#include -#include -#include - -#ifdef HAVE_OPENSSL - -#include "iostream-openssl.h" -#include -#include -#include -#include -#include -#include -#include - -#if !defined(OPENSSL_NO_ECDH) && OPENSSL_VERSION_NUMBER >= 0x10000000L -# define HAVE_ECDH -#endif - -#ifndef SSL_CTRL_SET_TLSEXT_HOSTNAME /* FIXME: this may be unnecessary.. */ -# undef HAVE_SSL_GET_SERVERNAME -#endif - -enum ssl_io_action { - SSL_ADD_INPUT, - SSL_REMOVE_INPUT, - SSL_ADD_OUTPUT, - SSL_REMOVE_OUTPUT -}; - -struct ssl_proxy { - int refcount; - struct ssl_proxy *prev, *next; - - SSL *ssl; - struct client *client; - struct ip_addr ip; - const struct login_settings *login_set; - const struct master_service_ssl_settings *ssl_set; - pool_t set_pool; - - int fd_ssl, fd_plain; - struct io *io_ssl_read, *io_ssl_write, *io_plain_read, *io_plain_write; - - unsigned char plainout_buf[1024]; - unsigned int plainout_size; - - unsigned char sslout_buf[1024]; - unsigned int sslout_size; - - ssl_handshake_callback_t *handshake_callback; - void *handshake_context; - - const char *cert_error; - char *last_error; - bool handshaked:1; - bool destroyed:1; - bool cert_received:1; - bool cert_broken:1; - bool client_proxy:1; - bool flushing:1; - bool failed:1; -}; - -struct ssl_parameters { - DH *dh_default; -}; - -struct ssl_server_cert { - const char *cert; - const char *key; -}; - -struct ssl_server_context { - SSL_CTX *ctx; - pool_t pool; - - struct ssl_server_cert pri, alt; - - const char *ca; - const char *dh; - const char *cipher_list; - const char *curve_list; - const char *protocols; - bool verify_client_cert; - bool prefer_server_ciphers; - bool compression; - bool tickets; -}; - -static int extdata_index; -static HASH_TABLE(struct ssl_server_context *, - struct ssl_server_context *) ssl_servers; -static SSL_CTX *ssl_client_ctx; -static unsigned int ssl_proxy_count; -static struct ssl_proxy *ssl_proxies; -static struct ssl_parameters ssl_params; -static int ssl_username_nid; -static ENGINE *ssl_engine; - -static void plain_read(struct ssl_proxy *proxy); -static void ssl_read(struct ssl_proxy *proxy); -static void ssl_write(struct ssl_proxy *proxy); -static void ssl_step(struct ssl_proxy *proxy); -static void ssl_proxy_unref(struct ssl_proxy *proxy); - -static struct ssl_server_context * -ssl_server_context_init(const struct login_settings *login_set, - const struct master_service_ssl_settings *ssl_set); -static void ssl_server_context_deinit(struct ssl_server_context **_ctx); - -static void ssl_proxy_ctx_set_crypto_params(SSL_CTX *ssl_ctx, - const struct master_service_ssl_settings *set); -#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); -#endif - -static void ssl_proxy_destroy_failed(struct ssl_proxy *proxy) -{ - proxy->failed = TRUE; - ssl_proxy_destroy(proxy); -} - -static unsigned int ssl_server_context_hash(const struct ssl_server_context *ctx) -{ - unsigned int n, i, g, h = 0; - const char *cert[] = { ctx->pri.cert, ctx->alt.cert }; - - /* checking for different certs is typically good enough, - and it should be enough to check only the first few bytes. */ - for(n=0;n> 24); - h = h ^ g; - } - } - } - return h; -} - -static int ssl_server_context_cmp(const struct ssl_server_context *ctx1, - const struct ssl_server_context *ctx2) -{ - if (((ctx1->pri.cert != ctx2->pri.cert) && - (ctx1->pri.cert == NULL || ctx2->pri.cert == NULL)) || - ((ctx1->alt.cert != ctx2->alt.cert) && - (ctx1->alt.cert == NULL || ctx2->alt.cert == NULL))) - return 1; - if (ctx1->pri.cert != NULL && strcmp(ctx1->pri.cert, ctx2->pri.cert) != 0) - return 1; - if (ctx1->pri.key != NULL && strcmp(ctx1->pri.key, ctx2->pri.key) != 0) - return 1; - if (ctx1->alt.cert != NULL && strcmp(ctx1->alt.cert, ctx2->alt.cert) != 0) - return 1; - if (ctx1->alt.key != NULL && strcmp(ctx1->alt.key, ctx2->alt.key) != 0) - return 1; - if (null_strcmp(ctx1->ca, ctx2->ca) != 0) - return 1; - if (null_strcmp(ctx1->cipher_list, ctx2->cipher_list) != 0) - return 1; - if (null_strcmp(ctx1->curve_list, ctx2->curve_list) != 0) - return 1; - if (null_strcmp(ctx1->protocols, ctx2->protocols) != 0) - return 1; - - return ctx1->verify_client_cert == ctx2->verify_client_cert ? 0 : 1; -} - -static void ssl_free_parameters(struct ssl_parameters *params) -{ - if (params->dh_default != NULL) { - DH_free(params->dh_default); - params->dh_default = NULL; - } -} - -static void ssl_set_io(struct ssl_proxy *proxy, enum ssl_io_action action) -{ - switch (action) { - case SSL_ADD_INPUT: - if (proxy->io_ssl_read != NULL) - break; - proxy->io_ssl_read = io_add(proxy->fd_ssl, IO_READ, - ssl_step, proxy); - break; - case SSL_REMOVE_INPUT: - io_remove(&proxy->io_ssl_read); - break; - case SSL_ADD_OUTPUT: - if (proxy->io_ssl_write != NULL) - break; - proxy->io_ssl_write = io_add(proxy->fd_ssl, IO_WRITE, - ssl_step, proxy); - break; - case SSL_REMOVE_OUTPUT: - io_remove(&proxy->io_ssl_write); - break; - } -} - -static void plain_block_input(struct ssl_proxy *proxy, bool block) -{ - if (block) { - io_remove(&proxy->io_plain_read); - } else { - if (proxy->io_plain_read == NULL) { - proxy->io_plain_read = io_add(proxy->fd_plain, IO_READ, - plain_read, proxy); - } - } -} - -static void plain_read(struct ssl_proxy *proxy) -{ - ssize_t ret; - bool corked = FALSE; - - if (proxy->sslout_size == sizeof(proxy->sslout_buf)) { - /* buffer full, block input until it's written */ - plain_block_input(proxy, TRUE); - return; - } - - proxy->refcount++; - - while (proxy->sslout_size < sizeof(proxy->sslout_buf) && - !proxy->destroyed) { - ret = net_receive(proxy->fd_plain, - proxy->sslout_buf + proxy->sslout_size, - sizeof(proxy->sslout_buf) - - proxy->sslout_size); - if (ret <= 0) { - if (ret < 0) - ssl_proxy_destroy(proxy); - break; - } else { - proxy->sslout_size += ret; - if (!corked) { - if (net_set_cork(proxy->fd_ssl, TRUE) == 0) - corked = TRUE; - } - ssl_write(proxy); - } - } - - if (corked) - (void)net_set_cork(proxy->fd_ssl, FALSE); - - ssl_proxy_unref(proxy); -} - -static void plain_write(struct ssl_proxy *proxy) -{ - ssize_t ret; - - proxy->refcount++; - - ret = net_transmit(proxy->fd_plain, proxy->plainout_buf, - proxy->plainout_size); - if (ret < 0) - ssl_proxy_destroy(proxy); - else { - proxy->plainout_size -= ret; - memmove(proxy->plainout_buf, proxy->plainout_buf + ret, - proxy->plainout_size); - - if (proxy->plainout_size > 0) { - if (proxy->io_plain_write == NULL) { - proxy->io_plain_write = - io_add(proxy->fd_plain, IO_WRITE, - plain_write, proxy); - } - } else { - io_remove(&proxy->io_plain_write); - } - - ssl_set_io(proxy, SSL_ADD_INPUT); - if (SSL_pending(proxy->ssl) > 0) - ssl_read(proxy); - } - - ssl_proxy_unref(proxy); -} - -static void ssl_handle_error(struct ssl_proxy *proxy, int ret, - const char *func_name) -{ - const char *errstr = NULL; - int err; - - proxy->refcount++; - - i_free_and_null(proxy->last_error); - err = SSL_get_error(proxy->ssl, ret); - - switch (err) { - case SSL_ERROR_WANT_READ: - ssl_set_io(proxy, SSL_ADD_INPUT); - break; - case SSL_ERROR_WANT_WRITE: - ssl_set_io(proxy, SSL_ADD_OUTPUT); - break; - case SSL_ERROR_SYSCALL: - /* eat up the error queue */ - if (ERR_peek_error() != 0) - errstr = openssl_iostream_error(); - else if (ret != 0) - errstr = strerror(errno); - else { - /* EOF. */ - errstr = "Disconnected"; - break; - } - errstr = t_strdup_printf("%s syscall failed: %s", - func_name, errstr); - break; - case SSL_ERROR_ZERO_RETURN: - /* clean connection closing */ - ssl_proxy_destroy(proxy); - break; - case SSL_ERROR_SSL: - if (ERR_GET_REASON(ERR_peek_error()) == ERR_R_MALLOC_FAILURE) { - i_error("OpenSSL malloc() failed. " - "You may need to increase service %s { vsz_limit }", - login_binary->process_name); - } - errstr = t_strdup_printf("%s failed: %s", - func_name, openssl_iostream_error()); - break; - default: - errstr = t_strdup_printf("%s failed: unknown failure %d (%s)", - func_name, err, openssl_iostream_error()); - break; - } - - if (errstr != NULL) { - if (proxy->ssl_set->verbose_ssl) - i_debug("SSL error: %s", errstr); - proxy->last_error = i_strdup(errstr); - ssl_proxy_destroy_failed(proxy); - } - ssl_proxy_unref(proxy); -} - -static void ssl_handshake(struct ssl_proxy *proxy) -{ - int ret; - - if (proxy->client_proxy) { - ret = SSL_connect(proxy->ssl); - if (ret != 1) { - ssl_handle_error(proxy, ret, "SSL_connect()"); - return; - } - } else { - ret = SSL_accept(proxy->ssl); - if (ret != 1) { - ssl_handle_error(proxy, ret, "SSL_accept()"); - return; - } - } - i_free_and_null(proxy->last_error); - proxy->handshaked = TRUE; - - ssl_set_io(proxy, SSL_ADD_INPUT); - plain_block_input(proxy, FALSE); - - if (proxy->handshake_callback != NULL) { - if (proxy->handshake_callback(proxy->handshake_context) < 0) - ssl_proxy_destroy_failed(proxy); - } -} - -static void ssl_read(struct ssl_proxy *proxy) -{ - int ret; - - while (proxy->plainout_size < sizeof(proxy->plainout_buf) && - !proxy->destroyed) { - ret = SSL_read(proxy->ssl, - proxy->plainout_buf + proxy->plainout_size, - sizeof(proxy->plainout_buf) - - proxy->plainout_size); - if (ret <= 0) { - ssl_handle_error(proxy, ret, "SSL_read()"); - break; - } else { - i_free_and_null(proxy->last_error); - proxy->plainout_size += ret; - plain_write(proxy); - } - } -} - -static void ssl_write(struct ssl_proxy *proxy) -{ - int ret; - - ret = SSL_write(proxy->ssl, proxy->sslout_buf, proxy->sslout_size); - if (ret <= 0) - ssl_handle_error(proxy, ret, "SSL_write()"); - else { - i_free_and_null(proxy->last_error); - proxy->sslout_size -= ret; - memmove(proxy->sslout_buf, proxy->sslout_buf + ret, - proxy->sslout_size); - - ssl_set_io(proxy, proxy->sslout_size > 0 ? - SSL_ADD_OUTPUT : SSL_REMOVE_OUTPUT); - plain_block_input(proxy, FALSE); - } -} - -static void ssl_step(struct ssl_proxy *proxy) -{ - proxy->refcount++; - - if (!proxy->handshaked) { - ssl_set_io(proxy, SSL_REMOVE_OUTPUT); - ssl_handshake(proxy); - } - - if (proxy->handshaked) { - if (proxy->plainout_size == sizeof(proxy->plainout_buf)) - ssl_set_io(proxy, SSL_REMOVE_INPUT); - else - ssl_read(proxy); - - if (proxy->sslout_size == 0) - ssl_set_io(proxy, SSL_REMOVE_OUTPUT); - else { - (void)net_set_cork(proxy->fd_ssl, TRUE); - ssl_write(proxy); - (void)net_set_cork(proxy->fd_ssl, FALSE); - } - } - - ssl_proxy_unref(proxy); -} - -static int -ssl_proxy_alloc_common(SSL_CTX *ssl_ctx, int fd, const struct ip_addr *ip, - pool_t set_pool, const struct login_settings *login_set, - const struct master_service_ssl_settings *ssl_set, - struct ssl_proxy **proxy_r) -{ - struct ssl_proxy *proxy; - SSL *ssl; - int sfd[2]; - - i_assert(fd != -1); - - *proxy_r = NULL; - - if (!ssl_initialized) { - i_error("SSL support not enabled in configuration"); - return -1; - } - - - - ssl = SSL_new(ssl_ctx); - if (ssl == NULL) { - i_error("SSL_new() failed: %s", openssl_iostream_error()); - return -1; - } - - if (SSL_set_fd(ssl, fd) != 1) { - i_error("SSL_set_fd() failed: %s", openssl_iostream_error()); - SSL_free(ssl); - return -1; - } - - if (socketpair(AF_UNIX, SOCK_STREAM, 0, sfd) < 0) { - i_error("socketpair() failed: %m"); - SSL_free(ssl); - return -1; - } - - net_set_nonblock(sfd[0], TRUE); - net_set_nonblock(sfd[1], TRUE); - net_set_nonblock(fd, TRUE); - - proxy = i_new(struct ssl_proxy, 1); - proxy->refcount = 2; - proxy->ssl = ssl; - proxy->login_set = login_set; - proxy->ssl_set = ssl_set; - proxy->fd_ssl = fd; - proxy->fd_plain = sfd[0]; - proxy->ip = *ip; - proxy->set_pool = set_pool; - pool_ref(set_pool); - SSL_set_ex_data(ssl, extdata_index, proxy); - - ssl_proxy_count++; - DLLIST_PREPEND(&ssl_proxies, proxy); - - *proxy_r = proxy; - return sfd[1]; -} - -static struct ssl_server_context * -ssl_server_context_get(const struct login_settings *login_set, - const struct master_service_ssl_settings *set) -{ - struct ssl_server_context *ctx, lookup_ctx; - - i_zero(&lookup_ctx); - lookup_ctx.pri.cert = set->ssl_cert; - lookup_ctx.pri.key = set->ssl_key; - lookup_ctx.alt.cert = set->ssl_alt_cert; - lookup_ctx.alt.key = set->ssl_alt_key; - lookup_ctx.ca = set->ssl_ca; - lookup_ctx.cipher_list = set->ssl_cipher_list; - lookup_ctx.curve_list = set->ssl_curve_list; - lookup_ctx.protocols = set->ssl_protocols; - lookup_ctx.verify_client_cert = set->ssl_verify_client_cert || - login_set->auth_ssl_require_client_cert || - login_set->auth_ssl_username_from_cert; - lookup_ctx.prefer_server_ciphers = set->ssl_prefer_server_ciphers; - lookup_ctx.compression = set->parsed_opts.compression; - lookup_ctx.tickets = set->parsed_opts.tickets; - - ctx = hash_table_lookup(ssl_servers, &lookup_ctx); - if (ctx == NULL) - ctx = ssl_server_context_init(login_set, set); - return ctx; -} - -int ssl_proxy_alloc(int fd, const struct ip_addr *ip, pool_t set_pool, - const struct login_settings *login_set, - const struct master_service_ssl_settings *ssl_set, - struct ssl_proxy **proxy_r) -{ - struct ssl_server_context *ctx; - - ctx = ssl_server_context_get(login_set, ssl_set); - return ssl_proxy_alloc_common(ctx->ctx, fd, ip, - set_pool, login_set, ssl_set, proxy_r); -} - -int ssl_proxy_client_alloc(int fd, struct ip_addr *ip, pool_t set_pool, - const struct login_settings *login_set, - const struct master_service_ssl_settings *ssl_set, - ssl_handshake_callback_t *callback, void *context, - struct ssl_proxy **proxy_r) -{ - int ret; - - ret = ssl_proxy_alloc_common(ssl_client_ctx, fd, ip, - set_pool, login_set, ssl_set, proxy_r); - if (ret < 0) - return -1; - - (*proxy_r)->handshake_callback = callback; - (*proxy_r)->handshake_context = context; - (*proxy_r)->client_proxy = TRUE; - return ret; -} - -void ssl_proxy_start(struct ssl_proxy *proxy) -{ - ssl_step(proxy); -} - -void ssl_proxy_set_client(struct ssl_proxy *proxy, struct client *client) -{ - i_assert(proxy->client == NULL); - - client_ref(client); - proxy->client = client; -} - -bool ssl_proxy_has_valid_client_cert(const struct ssl_proxy *proxy) -{ - return proxy->cert_received && !proxy->cert_broken; -} - -bool ssl_proxy_has_broken_client_cert(struct ssl_proxy *proxy) -{ - return proxy->cert_received && proxy->cert_broken; -} - -int ssl_proxy_cert_match_name(struct ssl_proxy *proxy, const char *verify_name) -{ - const char *reason; - - return openssl_cert_match_name(proxy->ssl, verify_name, &reason) ? 0 : -1; -} - -const char *ssl_proxy_get_peer_name(struct ssl_proxy *proxy) -{ - X509 *x509; - char *name; - int len; - - if (!ssl_proxy_has_valid_client_cert(proxy)) - return NULL; - - x509 = SSL_get_peer_certificate(proxy->ssl); - if (x509 == NULL) - 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_malloc0(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) -{ -#if defined(HAVE_SSL_COMPRESSION) && !defined(OPENSSL_NO_COMP) - 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_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); - - io_remove(&proxy->io_ssl_read); - io_remove(&proxy->io_ssl_write); - io_remove(&proxy->io_plain_read); - io_remove(&proxy->io_plain_write); - - if (SSL_shutdown(proxy->ssl) != 1) { - /* if bidirectional shutdown fails we need to clear - the error queue. */ - openssl_iostream_clear_errors(); - } - - 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) -{ - RSA *rsa = RSA_new(); - BIGNUM *e = BN_new(); - BN_set_word(e, RSA_F4); - RSA_generate_key_ex(rsa, keylength, e, NULL); - BN_free(e); - return rsa; -} - -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; - int ctxerr; - 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; - ctxerr = X509_STORE_CTX_get_error(ctx); - - if (proxy->client_proxy && !proxy->login_set->ssl_require_crl && - (ctxerr == X509_V_ERR_UNABLE_TO_GET_CRL || - ctxerr == X509_V_ERR_CRL_HAS_EXPIRED)) { - /* no CRL given with the CA list. don't worry about it. */ - preverify_ok = 1; - } - if (preverify_ok == 0) - proxy->cert_broken = TRUE; - - subject = X509_get_subject_name(X509_STORE_CTX_get_current_cert(ctx)); - (void)X509_NAME_oneline(subject, buf, sizeof(buf)); - buf[sizeof(buf)-1] = '\0'; /* just in case.. */ - - ctxerr = X509_STORE_CTX_get_error(ctx); - - if (proxy->cert_error == NULL) { - proxy->cert_error = p_strdup_printf(proxy->client->pool, "%s: %s", - X509_verify_cert_error_string(ctxerr), buf); - } - - if (proxy->ssl_set->verbose_ssl || - (proxy->login_set->auth_verbose && preverify_ok == 0)) { - if (preverify_ok != 0) { - client_log(proxy->client, t_strdup_printf( - "Valid certificate: %s", buf)); - } else { - client_log(proxy->client, t_strdup_printf( - "Invalid certificate: %s: %s", - X509_verify_cert_error_string(ctxerr), 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 != NULL) { - 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 != NULL) - 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) != 0) - SSL_CTX_set_tmp_rsa_callback(ssl_ctx, ssl_gen_rsa_key); -#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 DH * -ssl_proxy_load_dh(const char *dhparam) -{ - DH *dh; - BIO *bio; - - bio = BIO_new_mem_buf(t_strdup_noconst(dhparam), strlen(dhparam)); - if (bio == NULL) - i_fatal("BIO_new_mem_buf() failed"); - dh = NULL; - dh = PEM_read_bio_DHparams(bio, &dh, NULL, NULL); - if (dh == NULL) - i_fatal("Couldn't parse DH parameters: %s", - openssl_iostream_key_load_error()); - BIO_free(bio); - return dh; -} - -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); - if (*set->ssl_key != '\0') { - 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 (*set->ssl_alt_key != '\0') { - pkey = ssl_proxy_load_key(set->ssl_alt_key, password); - if (SSL_CTX_use_PrivateKey(ctx, pkey) != 1) - i_fatal("Can't load private ssl_alt_key: %s", openssl_iostream_key_load_error()); - EVP_PKEY_free(pkey); - } -} - -static void -ssl_proxy_ctx_use_dh(SSL_CTX *ctx, - const struct master_service_ssl_settings *set) -{ - DH *dh; - - dh = ssl_proxy_load_dh(set->ssl_dh); - if (SSL_CTX_set_tmp_dh(ctx, dh) != 1) - i_fatal("Can't load DH parameters: %s", openssl_iostream_key_load_error()); - DH_free(dh); -} - -#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 == 0) { - 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); - } - client->local_name = p_strdup(client->pool, host); - 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->pri.cert = p_strdup(pool, ssl_set->ssl_cert); - ctx->pri.key = p_strdup(pool, ssl_set->ssl_key); - ctx->alt.cert = p_strdup(pool, ssl_set->ssl_alt_cert); - ctx->alt.key = p_strdup(pool, ssl_set->ssl_alt_key); - ctx->ca = p_strdup(pool, ssl_set->ssl_ca); - ctx->dh = p_strdup(pool, ssl_set->ssl_dh); - ctx->cipher_list = p_strdup(pool, ssl_set->ssl_cipher_list); - ctx->curve_list = p_strdup(pool, ssl_set->ssl_curve_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)); - -#ifdef HAVE_SSL_CTX_SET1_CURVES_LIST - if (ctx->curve_list != NULL && strlen(ctx->curve_list) > 0 && - SSL_CTX_set1_curves_list(ssl_ctx, ctx->curve_list) != 1) { - i_fatal("Failed to set curve list to '%s'", ctx->curve_list); - } -#endif - - if (ctx->pri.cert != NULL && *ctx->pri.cert != '\0' && - ssl_proxy_ctx_use_certificate_chain(ctx->ctx, ctx->pri.cert) != 1) { - i_fatal("Can't load ssl_cert: %s", - openssl_iostream_use_certificate_error(ctx->pri.cert, "ssl_cert")); - } - if (ctx->alt.cert != NULL && *ctx->alt.cert != '\0' && - ssl_proxy_ctx_use_certificate_chain(ctx->ctx, ctx->alt.cert) != 1) { - i_fatal("Can't load ssl_alt_cert: %s", - openssl_iostream_use_certificate_error(ctx->alt.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); - ssl_proxy_ctx_use_dh(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); - - i_zero(&ssl_params); - - 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 diff --git a/src/login-common/ssl-proxy.c b/src/login-common/ssl-proxy.c deleted file mode 100644 index c55c74bed6..0000000000 --- a/src/login-common/ssl-proxy.c +++ /dev/null @@ -1,101 +0,0 @@ -/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ - -#include "lib.h" -#include "ssl-proxy.h" - -bool ssl_initialized = FALSE; - -#ifndef HAVE_SSL - -/* no SSL support */ - -int ssl_proxy_alloc(int fd ATTR_UNUSED, const struct ip_addr *ip ATTR_UNUSED, - pool_t set_pool ATTR_UNUSED, - const struct login_settings *login_set ATTR_UNUSED, - const struct master_service_ssl_settings *ssl_set ATTR_UNUSED, - struct ssl_proxy **proxy_r ATTR_UNUSED) -{ - i_error("Dovecot wasn't built with SSL support"); - return -1; -} - -int ssl_proxy_client_alloc(int fd ATTR_UNUSED, struct ip_addr *ip ATTR_UNUSED, - pool_t set_pool ATTR_UNUSED, - const struct login_settings *login_set ATTR_UNUSED, - const struct master_service_ssl_settings *ssl_set ATTR_UNUSED, - ssl_handshake_callback_t *callback ATTR_UNUSED, - void *context ATTR_UNUSED, - struct ssl_proxy **proxy_r ATTR_UNUSED) -{ - i_error("Dovecot wasn't built with SSL support"); - return -1; -} - -void ssl_proxy_start(struct ssl_proxy *proxy ATTR_UNUSED) -{ -} - -void ssl_proxy_set_client(struct ssl_proxy *proxy ATTR_UNUSED, - struct client *client ATTR_UNUSED) -{ -} - -bool ssl_proxy_has_valid_client_cert(const struct ssl_proxy *proxy ATTR_UNUSED) -{ - return FALSE; -} - -bool ssl_proxy_has_broken_client_cert(struct ssl_proxy *proxy ATTR_UNUSED) -{ - return FALSE; -} - -int ssl_proxy_cert_match_name(struct ssl_proxy *proxy ATTR_UNUSED, - const char *verify_name ATTR_UNUSED) -{ - return -1; -} - -const char *ssl_proxy_get_peer_name(struct ssl_proxy *proxy ATTR_UNUSED) -{ - return NULL; -} - -bool ssl_proxy_is_handshaked(const struct ssl_proxy *proxy ATTR_UNUSED) -{ - return FALSE; -} - -const char *ssl_proxy_get_last_error(const struct ssl_proxy *proxy ATTR_UNUSED) -{ - return NULL; -} - -const char *ssl_proxy_get_security_string(struct ssl_proxy *proxy ATTR_UNUSED) -{ - return ""; -} - -const char *ssl_proxy_get_compression(struct ssl_proxy *proxy ATTR_UNUSED) -{ - return NULL; -} - -const char *ssl_proxy_get_cert_error(struct ssl_proxy *proxy ATTR_UNUSED) -{ - return ""; -} - -void ssl_proxy_destroy(struct ssl_proxy *proxy ATTR_UNUSED) {} - -void ssl_proxy_free(struct ssl_proxy **proxy ATTR_UNUSED) {} - -unsigned int ssl_proxy_get_count(void) -{ - return 0; -} - -void ssl_proxy_init(void) {} -void ssl_proxy_deinit(void) {} - -#endif diff --git a/src/login-common/ssl-proxy.h b/src/login-common/ssl-proxy.h deleted file mode 100644 index 2cb79cb198..0000000000 --- a/src/login-common/ssl-proxy.h +++ /dev/null @@ -1,46 +0,0 @@ -#ifndef SSL_PROXY_H -#define SSL_PROXY_H - -struct ip_addr; -struct ssl_proxy; -struct master_service_ssl_settings; -struct login_settings; -struct client; - -extern bool ssl_initialized; - -typedef int ssl_handshake_callback_t(void *context); - -/* establish SSL connection with the given fd, returns a new fd which you - must use from now on, or -1 if error occurred. Unless -1 is returned, - the given fd must be simply forgotten. */ -int ssl_proxy_alloc(int fd, const struct ip_addr *ip, pool_t set_pool, - const struct login_settings *login_set, - const struct master_service_ssl_settings *ssl_set, - struct ssl_proxy **proxy_r); -int ssl_proxy_client_alloc(int fd, struct ip_addr *ip, pool_t set_pool, - const struct login_settings *login_set, - const struct master_service_ssl_settings *ssl_set, - ssl_handshake_callback_t *callback, void *context, - struct ssl_proxy **proxy_r); -void ssl_proxy_start(struct ssl_proxy *proxy); -void ssl_proxy_set_client(struct ssl_proxy *proxy, struct client *client); -bool ssl_proxy_has_valid_client_cert(const struct ssl_proxy *proxy) ATTR_PURE; -bool ssl_proxy_has_broken_client_cert(struct ssl_proxy *proxy); -int ssl_proxy_cert_match_name(struct ssl_proxy *proxy, const char *verify_name); -const char *ssl_proxy_get_peer_name(struct ssl_proxy *proxy); -bool ssl_proxy_is_handshaked(const struct ssl_proxy *proxy) ATTR_PURE; -const char *ssl_proxy_get_last_error(const struct ssl_proxy *proxy) ATTR_PURE; -const char *ssl_proxy_get_security_string(struct ssl_proxy *proxy); -const char *ssl_proxy_get_compression(struct ssl_proxy *proxy); -const char *ssl_proxy_get_cert_error(struct ssl_proxy *proxy); -void ssl_proxy_destroy(struct ssl_proxy *proxy); -void ssl_proxy_free(struct ssl_proxy **proxy); - -/* Return number of active SSL proxies */ -unsigned int ssl_proxy_get_count(void) ATTR_PURE; - -void ssl_proxy_init(void); -void ssl_proxy_deinit(void); - -#endif