dovecot-2.0-sslstream: *-login: Create SSL context for each diff...

dovecot at dovecot.org dovecot at dovecot.org
Sat Feb 13 02:55:51 EET 2010


details:   http://hg.dovecot.org/dovecot-2.0-sslstream/rev/168f2e6ef6d6
changeset: 10223:168f2e6ef6d6
user:      Timo Sirainen <tss at iki.fi>
date:      Wed Oct 28 21:05:19 2009 -0400
description:
*-login: Create SSL context for each different SSL config, instead of changing each connection.

diffstat:

1 file changed, 155 insertions(+), 106 deletions(-)
src/login-common/ssl-proxy-openssl.c |  261 ++++++++++++++++++++--------------

diffs (truncated from 381 to 300 lines):

diff -r 09fe6d98cf98 -r 168f2e6ef6d6 src/login-common/ssl-proxy-openssl.c
--- a/src/login-common/ssl-proxy-openssl.c	Wed Oct 28 20:53:21 2009 -0400
+++ b/src/login-common/ssl-proxy-openssl.c	Wed Oct 28 21:05:19 2009 -0400
@@ -7,6 +7,7 @@
 #include "ostream.h"
 #include "read-full.h"
 #include "safe-memset.h"
+#include "hash.h"
 #include "llist.h"
 #include "master-service.h"
 #include "master-interface.h"
@@ -75,8 +76,19 @@ struct ssl_parameters {
 	DH *dh_512, *dh_1024;
 };
 
+struct ssl_server_context {
+	SSL_CTX *ctx;
+	pool_t pool;
+
+	const char *cert;
+	const char *key;
+	const char *ca_file;
+	const char *cipher_list;
+	bool verify_client_cert;
+};
+
 static int extdata_index;
-static SSL_CTX *ssl_server_ctx;
+static struct hash_table *ssl_servers;
 static SSL_CTX *ssl_client_ctx;
 static unsigned int ssl_proxy_count;
 static struct ssl_proxy *ssl_proxies;
@@ -90,8 +102,40 @@ static void ssl_proxy_destroy(struct ssl
 static void ssl_proxy_destroy(struct ssl_proxy *proxy);
 static void ssl_proxy_unref(struct ssl_proxy *proxy);
 
-static int ssl_proxy_use_certificate(SSL *ssl, const char *cert);
-static int ssl_proxy_use_key(SSL *ssl, const struct login_settings *set);
+static struct ssl_server_context *
+ssl_server_context_init(const struct login_settings *set);
+static void ssl_server_context_deinit(struct ssl_server_context **_ctx);
+
+static unsigned int ssl_server_context_hash(const void *p)
+{
+	const struct ssl_server_context *ctx = p;
+	unsigned int i, g, h = 0;
+
+	/* checking for different certs is typically good enough,
+	   and it should be enough to check only the first few bytes. */
+	for (i = 0; i < 16 && ctx->cert[i] != '\0'; i++) {
+		h = (h << 4) + ctx->cert[i];
+		if ((g = h & 0xf0000000UL)) {
+			h = h ^ (g >> 24);
+			h = h ^ g;
+		}
+	}
+	return h;
+}
+
+static int ssl_server_context_cmp(const void *p1, const void *p2)
+{
+	const struct ssl_server_context *ctx1 = p1, *ctx2 = p2;
+
+	if (strcmp(ctx1->cert, ctx2->cert) != 0)
+		return 1;
+	if (strcmp(ctx1->key, ctx2->key) != 0)
+		return 1;
+	if (null_strcmp(ctx1->cipher_list, ctx2->cipher_list) != 0)
+		return 1;
+
+	return ctx1->verify_client_cert == ctx2->verify_client_cert ? 0 : 1;
+}
 
 static void ssl_params_corrupted(void)
 {
@@ -514,15 +558,6 @@ ssl_proxy_new_common(SSL_CTX *ssl_ctx, i
 		return -1;
 	}
 
-	if (strcmp(global_login_settings->ssl_cert, set->ssl_cert) != 0 ||
-	    strcmp(global_login_settings->ssl_key, set->ssl_key) != 0) {
-		if (ssl_proxy_use_certificate(ssl, set->ssl_cert) < 0 ||
-		    ssl_proxy_use_key(ssl, set) < 0) {
-			SSL_free(ssl);
-			return -1;
-		}
-	}
-
 	if (SSL_set_fd(ssl, fd) != 1) {
 		i_error("SSL_set_fd() failed: %s", ssl_last_error());
 		SSL_free(ssl);
@@ -558,9 +593,21 @@ int ssl_proxy_new(int fd, const struct i
 int ssl_proxy_new(int fd, const struct ip_addr *ip,
 		  const struct login_settings *set, struct ssl_proxy **proxy_r)
 {
+	struct ssl_server_context *ctx, lookup_ctx;
 	int ret;
 
-	ret = ssl_proxy_new_common(ssl_server_ctx, fd, ip, set, proxy_r);
+	memset(&lookup_ctx, 0, sizeof(lookup_ctx));
+	lookup_ctx.cert = set->ssl_cert;
+	lookup_ctx.key = set->ssl_key;
+	lookup_ctx.ca_file = set->ssl_ca_file;
+	lookup_ctx.cipher_list = set->ssl_cipher_list;
+	lookup_ctx.verify_client_cert = set->ssl_verify_client_cert;
+
+	ctx = hash_table_lookup(ssl_servers, &lookup_ctx);
+	if (ctx == NULL)
+		ctx = ssl_server_context_init(set);
+
+	ret = ssl_proxy_new_common(ctx->ctx, fd, ip, set, proxy_r);
 	if (ret < 0)
 		return -1;
 
@@ -744,6 +791,9 @@ static void ssl_info_callback(const SSL 
 
 	proxy = SSL_get_ex_data(ssl, extdata_index);
 
+	if (!proxy->set->verbose_ssl)
+		return;
+
 	if ((where & SSL_CB_ALERT) != 0) {
 		i_warning("SSL alert: where=0x%x, ret=%d: %s %s [%s]",
 			  where, ret, SSL_alert_type_string_long(ret),
@@ -824,15 +874,14 @@ ssl_proxy_ctx_init(SSL_CTX *ssl_ctx, con
 				set->ssl_ca_file, ssl_last_error());
 		}
 	}
-	if (set->verbose_ssl)
-		SSL_CTX_set_info_callback(ssl_ctx, ssl_info_callback);
+	SSL_CTX_set_info_callback(ssl_ctx, ssl_info_callback);
 	if (SSL_CTX_need_tmp_RSA(ssl_ctx))
 		SSL_CTX_set_tmp_rsa_callback(ssl_ctx, ssl_gen_rsa_key);
 	SSL_CTX_set_tmp_dh_callback(ssl_ctx, ssl_tmp_dh_callback);
 }
 
 static void
-ssl_proxy_ctx_verify_client(SSL_CTX *ssl_ctx, const struct login_settings *set)
+ssl_proxy_ctx_verify_client(SSL_CTX *ssl_ctx, const char *ca_file)
 {
 #if OPENSSL_VERSION_NUMBER >= 0x00907000L
 	X509_STORE *store;
@@ -843,8 +892,7 @@ ssl_proxy_ctx_verify_client(SSL_CTX *ssl
 #endif
 	SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE,
 			   ssl_verify_client_cert);
-	SSL_CTX_set_client_CA_list(ssl_ctx,
-				   SSL_load_client_CA_file(set->ssl_ca_file));
+	SSL_CTX_set_client_CA_list(ssl_ctx, SSL_load_client_CA_file(ca_file));
 }
 
 static const char *ssl_proxy_get_use_certificate_error(const char *cert)
@@ -863,34 +911,6 @@ static const char *ssl_proxy_get_use_cer
 	}
 }
 
-static int ssl_proxy_use_certificate(SSL *ssl, const char *cert)
-{
-	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) {
-		ret = SSL_use_certificate(ssl, x);
-		if (ERR_peek_error() != 0)
-			ret = 0;
-	}
-
-	if (x != NULL) X509_free(x);
-	BIO_free(in);
-
-	if (ret == 0) {
-		i_error("Can't load ssl_cert: %s",
-			ssl_proxy_get_use_certificate_error(cert));
-		return -1;
-	}
-	return 0;
-}
-
 static EVP_PKEY *ssl_proxy_load_key(const struct login_settings *set)
 {
 	EVP_PKEY *pkey;
@@ -935,19 +955,6 @@ static void ssl_proxy_ctx_use_key(SSL_CT
 	EVP_PKEY_free(pkey);
 }
 
-static int ssl_proxy_use_key(SSL *ssl, const struct login_settings *set)
-{
-	EVP_PKEY *pkey;
-
-	pkey = ssl_proxy_load_key(set);
-	if (SSL_use_PrivateKey(ssl, pkey) != 1) {
-		i_error("Can't load private ssl_key: %s", ssl_key_load_error());
-		return -1;
-	}
-	EVP_PKEY_free(pkey);
-	return 0;
-}
-
 static int
 ssl_proxy_ctx_use_certificate_chain(SSL_CTX *ctx, const char *cert)
 {
@@ -965,8 +972,11 @@ ssl_proxy_ctx_use_certificate_chain(SSL_
 		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
@@ -998,60 +1008,88 @@ end:
 	return ret;
 }
 
-static void ssl_proxy_init_server(const struct login_settings *set)
-{
-	if ((ssl_server_ctx = SSL_CTX_new(SSLv23_server_method())) == NULL)
+static struct ssl_server_context *
+ssl_server_context_init(const struct login_settings *set)
+{
+	struct ssl_server_context *ctx;
+	SSL_CTX *ssl_ctx;
+	pool_t pool;
+
+	pool = pool_alloconly_create("ssl server context", 2048);
+	ctx = i_new(struct ssl_server_context, 1);
+	ctx->pool = pool;
+	ctx->cert = p_strdup(pool, set->ssl_cert);
+	ctx->key = p_strdup(pool, set->ssl_key);
+	ctx->ca_file = p_strdup(pool, set->ssl_ca_file);
+	ctx->cipher_list = p_strdup(pool, set->ssl_cipher_list);
+	ctx->verify_client_cert = set->ssl_verify_client_cert;
+
+	ctx->ctx = ssl_ctx = SSL_CTX_new(SSLv23_server_method());
+	if (ssl_ctx == NULL)
 		i_fatal("SSL_CTX_new() failed");
-	ssl_proxy_ctx_init(ssl_server_ctx, set);
-
-	if (SSL_CTX_set_cipher_list(ssl_server_ctx, set->ssl_cipher_list) != 1) {
+	ssl_proxy_ctx_init(ssl_ctx, set);
+
+	if (SSL_CTX_set_cipher_list(ssl_ctx, ctx->cipher_list) != 1) {
 		i_fatal("Can't set cipher list to '%s': %s",
-			set->ssl_cipher_list, ssl_last_error());
-	}
-
-	if (ssl_proxy_ctx_use_certificate_chain(ssl_server_ctx,
-						set->ssl_cert) != 1) {
+			ctx->cipher_list, ssl_last_error());
+	}
+
+	if (ssl_proxy_ctx_use_certificate_chain(ctx->ctx, ctx->cert) != 1) {
 		i_fatal("Can't load ssl_cert: %s",
-			ssl_proxy_get_use_certificate_error(set->ssl_cert));
-	}
-
-	ssl_proxy_ctx_use_key(ssl_server_ctx, set);
-	if (set->verbose_ssl)
-		SSL_CTX_set_info_callback(ssl_server_ctx, ssl_info_callback);
-
-	if (set->ssl_verify_client_cert)
-		ssl_proxy_ctx_verify_client(ssl_server_ctx, set);
-
+			ssl_proxy_get_use_certificate_error(ctx->cert));
+	}
+
+	ssl_proxy_ctx_use_key(ctx->ctx, set);
+	SSL_CTX_set_info_callback(ctx->ctx, ssl_info_callback);
+
+	if (ctx->verify_client_cert)
+		ssl_proxy_ctx_verify_client(ctx->ctx, ctx->ca_file);
+
+	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_init_client(const struct login_settings *set)
+{
+	if ((ssl_client_ctx = SSL_CTX_new(SSLv23_client_method())) == NULL)
+		i_fatal("SSL_CTX_new() failed");
+	ssl_proxy_ctx_init(ssl_client_ctx, set);
+	ssl_proxy_ctx_verify_client(ssl_client_ctx, set->ssl_ca_file);
+}


More information about the dovecot-cvs mailing list