changeset 15379:68d21f872fd7

lib-ssl-iostream now dynamically loads openssl library instead of linking to it. This allowed removing the separate libdovecot-ssl library. In future if GnuTLS/NSS support is added it would also allow switching between them dynamically.
author Timo Sirainen <tss@iki.fi>
date Tue, 06 Nov 2012 01:04:24 +0200
parents 94778985bb6a
children 7c75559cd8f6
files configure.ac src/auth/Makefile.am src/lib-dovecot/Makefile.am src/lib-master/Makefile.am src/lib-ssl-iostream/Makefile.am src/lib-ssl-iostream/iostream-openssl-common.c src/lib-ssl-iostream/iostream-openssl-context.c src/lib-ssl-iostream/iostream-openssl-params.c src/lib-ssl-iostream/iostream-openssl.c src/lib-ssl-iostream/iostream-openssl.h src/lib-ssl-iostream/iostream-ssl-private.h src/lib-ssl-iostream/iostream-ssl.c src/lib-ssl-iostream/istream-openssl.c src/lib-ssl-iostream/ostream-openssl.c src/login-common/Makefile.am
diffstat 15 files changed, 520 insertions(+), 303 deletions(-) [+]
line wrap: on
line diff
--- a/configure.ac	Mon Nov 05 18:16:56 2012 +0200
+++ b/configure.ac	Tue Nov 06 01:04:24 2012 +0200
@@ -2499,17 +2499,15 @@
   LIBDOVECOT="$LIBDOVECOT_DEPS"
   LIBDOVECOT_STORAGE_DEPS='$(top_builddir)/src/lib-storage/libdovecot-storage.la $(top_builddir)/src/lib-imap-storage/libimap-storage.la'
   LIBDOVECOT_LOGIN='$(top_builddir)/src/login-common/libdovecot-login.la'
-  LIBDOVECOT_SSL='$(top_builddir)/src/lib-master/libmaster_ssl.la $(top_builddir)/src/lib-ssl-iostream/libssl_iostream.la'
   LIBDOVECOT_COMPRESS='$(top_builddir)/src/lib-compression/libcompression.la'
   LIBDOVECOT_LDA='$(top_builddir)/src/lib-lda/libdovecot-lda.la'
 else
-  LIBDOVECOT_DEPS='$(top_builddir)/src/lib-master/libmaster.la $(top_builddir)/src/lib-settings/libsettings.la $(top_builddir)/src/lib-dict/libdict.la $(top_builddir)/src/lib-dns/libdns.la $(top_builddir)/src/lib-fs/libfs.la $(top_builddir)/src/lib-imap/libimap.la $(top_builddir)/src/lib-mail/libmail.la $(top_builddir)/src/lib-auth/libauth.la $(top_builddir)/src/lib-charset/libcharset.la $(top_builddir)/src/lib-test/libtest.la $(top_builddir)/src/lib/liblib.la'
+  LIBDOVECOT_DEPS='$(top_builddir)/src/lib-master/libmaster.la $(top_builddir)/src/lib-settings/libsettings.la $(top_builddir)/src/lib-dict/libdict.la $(top_builddir)/src/lib-dns/libdns.la $(top_builddir)/src/lib-fs/libfs.la $(top_builddir)/src/lib-imap/libimap.la $(top_builddir)/src/lib-mail/libmail.la $(top_builddir)/src/lib-auth/libauth.la $(top_builddir)/src/lib-charset/libcharset.la $(top_builddir)/src/lib-ssl-iostream/libssl_iostream.la $(top_builddir)/src/lib-test/libtest.la $(top_builddir)/src/lib/liblib.la'
   LIBDOVECOT="$LIBDOVECOT_DEPS \$(LIBICONV)"
   LIBDOVECOT_STORAGE_LAST='$(top_builddir)/src/lib-storage/list/libstorage_list.la $(top_builddir)/src/lib-storage/index/libstorage_index.la $(top_builddir)/src/lib-storage/libstorage.la $(top_builddir)/src/lib-index/libindex.la $(top_builddir)/src/lib-imap-storage/libimap-storage.la'
   LIBDOVECOT_STORAGE_FIRST='$(top_builddir)/src/lib-storage/libstorage_service.la $(top_builddir)/src/lib-storage/register/libstorage_register.la'
   LIBDOVECOT_STORAGE_DEPS="$LIBDOVECOT_STORAGE_FIRST $LINKED_STORAGE_LIBS $LIBDOVECOT_STORAGE_LAST"
-  LIBDOVECOT_LOGIN='$(top_builddir)/src/login-common/liblogin.la $(top_builddir)/src/lib-ssl-iostream/libssl_iostream.la'
-  LIBDOVECOT_SSL='$(top_builddir)/src/lib-master/libmaster_ssl.la $(top_builddir)/src/lib-ssl-iostream/libssl_iostream.la'
+  LIBDOVECOT_LOGIN='$(top_builddir)/src/login-common/liblogin.la'
   LIBDOVECOT_COMPRESS='$(top_builddir)/src/lib-compression/libcompression.la'
   LIBDOVECOT_LDA='$(top_builddir)/src/lib-lda/liblda.la'
 fi
--- a/src/auth/Makefile.am	Mon Nov 05 18:16:56 2012 +0200
+++ b/src/auth/Makefile.am	Tue Nov 06 01:04:24 2012 +0200
@@ -176,8 +176,7 @@
 libauthdb_imap_la_LDFLAGS = -module -avoid-version
 libauthdb_imap_la_LIBADD = \
 	../lib-imap-client/libimap_client.la \
-	../lib-ssl-iostream/libssl_iostream.la \
-	$(LIBDOVECOT) $(SSL_LIBS)
+	$(LIBDOVECOT)
 libauthdb_imap_la_CPPFLAGS = \
 	$(AM_CPPFLAGS) \
 	-I$(top_srcdir)/src/lib-imap \
--- a/src/lib-dovecot/Makefile.am	Mon Nov 05 18:16:56 2012 +0200
+++ b/src/lib-dovecot/Makefile.am	Tue Nov 06 01:04:24 2012 +0200
@@ -8,32 +8,18 @@
 	../lib-fs/libfs.la \
 	../lib-charset/libcharset.la \
 	../lib-master/libmaster.la \
+	../lib-ssl-iostream/libssl_iostream.la
 	../lib-test/libtest.la \
 	../lib/liblib.la
 
-ssl_libs = \
-	../lib-master/libmaster_ssl.la \
-	../lib-ssl-iostream/libssl_iostream.la
-
-pkglib_LTLIBRARIES = libdovecot.la libdovecot-ssl.la
+pkglib_LTLIBRARIES = libdovecot.la
 
 libdovecot_la_SOURCES = 
-libdovecot_ssl_la_SOURCES = 
 
 libdovecot_la_LIBADD = \
 	$(libs) \
 	$(MODULE_LIBS) \
 	$(LTLIBICONV)
 
-libdovecot_ssl_la_LIBADD = \
-	libdovecot.la \
-	../lib/liblib.la \
-	$(MODULE_LIBS) \
-	$(ssl_libs) \
-	$(SSL_LIBS)
-
 libdovecot_la_DEPENDENCIES = $(libs)
-libdovecot_ssl_la_DEPENDENCIES = $(ssl_libs) libdovecot.la
-
 libdovecot_la_LDFLAGS = -export-dynamic
-libdovecot_ssl_la_LDFLAGS = -export-dynamic
--- a/src/lib-master/Makefile.am	Mon Nov 05 18:16:56 2012 +0200
+++ b/src/lib-master/Makefile.am	Tue Nov 06 01:04:24 2012 +0200
@@ -1,6 +1,6 @@
 pkgsysconfdir = $(sysconfdir)/dovecot
 
-noinst_LTLIBRARIES = libmaster.la libmaster_ssl.la
+noinst_LTLIBRARIES = libmaster.la
 
 AM_CPPFLAGS = \
 	-I$(top_srcdir)/src/lib \
@@ -22,13 +22,11 @@
 	master-service.c \
 	master-service-settings.c \
 	master-service-settings-cache.c \
+	master-service-ssl.c \
 	master-service-ssl-settings.c \
 	mountpoint-list.c \
 	syslog-util.c
 
-libmaster_ssl_la_SOURCES = \
-	master-service-ssl.c
-
 headers = \
 	anvil-client.h \
 	ipc-client.h \
--- a/src/lib-ssl-iostream/Makefile.am	Mon Nov 05 18:16:56 2012 +0200
+++ b/src/lib-ssl-iostream/Makefile.am	Tue Nov 06 01:04:24 2012 +0200
@@ -1,29 +1,34 @@
 noinst_LTLIBRARIES = libssl_iostream.la
 
+NOPLUGIN_LDFLAGS =
+
 AM_CPPFLAGS = \
 	-I$(top_srcdir)/src/lib \
-	-I$(top_srcdir)/src/lib-test
+	-I$(top_srcdir)/src/lib-test \
+	-DMODULE_DIR=\""$(moduledir)"\"
 
 if BUILD_OPENSSL
-ssl_sources = \
+module_LTLIBRARIES = libssl_iostream_openssl.la
+
+libssl_iostream_openssl_la_LDFLAGS = -module -avoid-version
+libssl_iostream_openssl_la_LIBADD = $(SSL_LIBS)
+libssl_iostream_openssl_la_SOURCES = \
 	iostream-openssl.c \
+	iostream-openssl-common.c \
 	iostream-openssl-context.c \
 	iostream-openssl-params.c \
 	istream-openssl.c \
 	ostream-openssl.c
-else
-ssl_sources = \
-	iostream-ssl-none.c
 endif
 
 libssl_iostream_la_SOURCES = \
+	iostream-ssl.c \
 	$(ssl_sources)
-libssl_iostream_la_LIBADD = \
-	$(SSL_LIBS)
 
 headers = \
 	iostream-openssl.h \
-	iostream-ssl.h
+	iostream-ssl.h \
+	iostream-ssl-private.h
 
 pkginc_libdir=$(pkgincludedir)
 pkginc_lib_HEADERS = $(headers)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-ssl-iostream/iostream-openssl-common.c	Tue Nov 06 01:04:24 2012 +0200
@@ -0,0 +1,135 @@
+/* Copyright (c) 2009-2012 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "iostream-openssl.h"
+
+#include <openssl/x509v3.h>
+
+enum {
+	DOVECOT_SSL_PROTO_SSLv2	= 0x01,
+	DOVECOT_SSL_PROTO_SSLv3	= 0x02,
+	DOVECOT_SSL_PROTO_TLSv1	= 0x04,
+	DOVECOT_SSL_PROTO_ALL	= 0x07
+};
+
+int openssl_get_protocol_options(const char *protocols)
+{
+	const char *const *tmp;
+	int proto, op = 0, include = 0, exclude = 0;
+	bool neg;
+
+	tmp = t_strsplit_spaces(protocols, " ");
+	for (; *tmp != NULL; tmp++) {
+		const char *name = *tmp;
+
+		if (*name != '!')
+			neg = FALSE;
+		else {
+			name++;
+			neg = TRUE;
+		}
+		if (strcasecmp(name, SSL_TXT_SSLV2) == 0)
+			proto = DOVECOT_SSL_PROTO_SSLv2;
+		else if (strcasecmp(name, SSL_TXT_SSLV3) == 0)
+			proto = DOVECOT_SSL_PROTO_SSLv3;
+		else if (strcasecmp(name, SSL_TXT_TLSV1) == 0)
+			proto = DOVECOT_SSL_PROTO_TLSv1;
+		else {
+			i_fatal("Invalid ssl_protocols setting: "
+				"Unknown protocol '%s'", name);
+		}
+		if (neg)
+			exclude |= proto;
+		else
+			include |= proto;
+	}
+	if (include != 0) {
+		/* exclude everything, except those that are included
+		   (and let excludes still override those) */
+		exclude |= DOVECOT_SSL_PROTO_ALL & ~include;
+	}
+	if ((exclude & DOVECOT_SSL_PROTO_SSLv2) != 0) op |= SSL_OP_NO_SSLv2;
+	if ((exclude & DOVECOT_SSL_PROTO_SSLv3) != 0) op |= SSL_OP_NO_SSLv3;
+	if ((exclude & DOVECOT_SSL_PROTO_TLSv1) != 0) op |= SSL_OP_NO_TLSv1;
+	return op;
+}
+
+static const char *asn1_string_to_c(ASN1_STRING *asn_str)
+{
+	const char *cstr;
+	unsigned int len;
+
+	len = ASN1_STRING_length(asn_str);
+	cstr = t_strndup(ASN1_STRING_data(asn_str), len);
+	if (strlen(cstr) != len) {
+		/* NULs in the name - could be some MITM attack.
+		   never allow. */
+		return "";
+	}
+	return cstr;
+}
+
+static const char *get_general_dns_name(const GENERAL_NAME *name)
+{
+	if (ASN1_STRING_type(name->d.ia5) != V_ASN1_IA5STRING)
+		return "";
+
+	return asn1_string_to_c(name->d.ia5);
+}
+
+static const char *get_cname(X509 *cert)
+{
+	X509_NAME *name;
+	X509_NAME_ENTRY *entry;
+	ASN1_STRING *str;
+	int cn_idx;
+
+	name = X509_get_subject_name(cert);
+	if (name == NULL)
+		return "";
+	cn_idx = X509_NAME_get_index_by_NID(name, NID_commonName, -1);
+	if (cn_idx == -1)
+		return "";
+	entry = X509_NAME_get_entry(name, cn_idx);
+	i_assert(entry != NULL);
+	str = X509_NAME_ENTRY_get_data(entry);
+	i_assert(str != NULL);
+	return asn1_string_to_c(str);
+}
+
+int openssl_cert_match_name(SSL *ssl, const char *verify_name)
+{
+	X509 *cert;
+	STACK_OF(GENERAL_NAME) *gnames;
+	const GENERAL_NAME *gn;
+	const char *dnsname;
+	bool dns_names = FALSE;
+	unsigned int i, count;
+	int ret;
+
+	cert = SSL_get_peer_certificate(ssl);
+	i_assert(cert != NULL);
+
+	/* verify against SubjectAltNames */
+	gnames = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL);
+	count = gnames == NULL ? 0 : sk_GENERAL_NAME_num(gnames);
+	for (i = 0; i < count; i++) {
+		gn = sk_GENERAL_NAME_value(gnames, i);
+		if (gn->type == GEN_DNS) {
+			dns_names = TRUE;
+			dnsname = get_general_dns_name(gn);
+			if (strcmp(dnsname, verify_name) == 0)
+				break;
+		}
+	}
+	sk_GENERAL_NAME_pop_free(gnames, GENERAL_NAME_free);
+
+	/* verify against CommonName only when there wasn't any DNS
+	   SubjectAltNames */
+	if (dns_names)
+		ret = i < count ? 0 : -1;
+	else
+		ret = strcmp(get_cname(cert), verify_name) == 0 ? 0 : -1;
+	X509_free(cert);
+	return ret;
+}
--- a/src/lib-ssl-iostream/iostream-openssl-context.c	Mon Nov 05 18:16:56 2012 +0200
+++ b/src/lib-ssl-iostream/iostream-openssl-context.c	Tue Nov 06 01:04:24 2012 +0200
@@ -23,7 +23,7 @@
 
 static void ssl_iostream_init_global(const struct ssl_iostream_settings *set);
 
-const char *ssl_iostream_error(void)
+const char *openssl_iostream_error(void)
 {
 	unsigned long err;
 	char *buf;
@@ -44,7 +44,7 @@
 	return buf;
 }
 
-const char *ssl_iostream_key_load_error(void)
+const char *openssl_iostream_key_load_error(void)
 {
        unsigned long err = ERR_peek_error();
 
@@ -52,7 +52,7 @@
            ERR_GET_REASON(err) == X509_R_KEY_VALUES_MISMATCH)
                return "Key is for a different cert than ssl_cert";
        else
-               return ssl_iostream_error();
+               return openssl_iostream_error();
 }
 
 static RSA *ssl_gen_rsa_key(SSL *ssl ATTR_UNUSED,
@@ -95,8 +95,8 @@
 	return strlen(buf);
 }
 
-int ssl_iostream_load_key(const struct ssl_iostream_settings *set,
-			  const char *key_source, EVP_PKEY **pkey_r)
+int openssl_iostream_load_key(const struct ssl_iostream_settings *set,
+			      const char *key_source, EVP_PKEY **pkey_r)
 {
 	struct ssl_iostream_password_context ctx;
 	EVP_PKEY *pkey;
@@ -106,7 +106,7 @@
 	key = t_strdup_noconst(set->key);
 	bio = BIO_new_mem_buf(key, strlen(key));
 	if (bio == NULL) {
-		i_error("BIO_new_mem_buf() failed: %s", ssl_iostream_error());
+		i_error("BIO_new_mem_buf() failed: %s", openssl_iostream_error());
 		safe_memset(key, 0, strlen(key));
 		return -1;
 	}
@@ -117,7 +117,7 @@
 	pkey = PEM_read_bio_PrivateKey(bio, NULL, pem_password_callback, &ctx);
 	if (pkey == NULL) {
 		i_error("%s: Couldn't parse private SSL key: %s",
-			key_source, ssl_iostream_error());
+			key_source, openssl_iostream_error());
 	}
 	BIO_free(bio);
 
@@ -133,11 +133,11 @@
 	EVP_PKEY *pkey;
 	int ret = 0;
 
-	if (ssl_iostream_load_key(set, ctx->source, &pkey) < 0)
+	if (openssl_iostream_load_key(set, ctx->source, &pkey) < 0)
 		return -1;
 	if (!SSL_CTX_use_PrivateKey(ctx->ssl_ctx, pkey)) {
 		i_error("%s: Can't load SSL private key: %s",
-			ctx->source, ssl_iostream_key_load_error());
+			ctx->source, openssl_iostream_key_load_error());
 		ret = -1;
 	}
 	EVP_PKEY_free(pkey);
@@ -156,7 +156,7 @@
 	err = ERR_peek_error();
 	if (ERR_GET_LIB(err) != ERR_LIB_PEM ||
 	    ERR_GET_REASON(err) != PEM_R_NO_START_LINE)
-		return ssl_iostream_error();
+		return openssl_iostream_error();
 	else if (is_pem_key(cert)) {
 		return "The file contains a private key "
 			"(you've mixed ssl_cert and ssl_key settings)";
@@ -288,55 +288,6 @@
 	return new_set;
 }
 
-enum {
-	DOVECOT_SSL_PROTO_SSLv2	= 0x01,
-	DOVECOT_SSL_PROTO_SSLv3	= 0x02,
-	DOVECOT_SSL_PROTO_TLSv1	= 0x04,
-	DOVECOT_SSL_PROTO_ALL	= 0x07
-};
-
-int openssl_get_protocol_options(const char *protocols)
-{
-	const char *const *tmp;
-	int proto, op = 0, include = 0, exclude = 0;
-	bool neg;
-
-	tmp = t_strsplit_spaces(protocols, " ");
-	for (; *tmp != NULL; tmp++) {
-		const char *name = *tmp;
-
-		if (*name != '!')
-			neg = FALSE;
-		else {
-			name++;
-			neg = TRUE;
-		}
-		if (strcasecmp(name, SSL_TXT_SSLV2) == 0)
-			proto = DOVECOT_SSL_PROTO_SSLv2;
-		else if (strcasecmp(name, SSL_TXT_SSLV3) == 0)
-			proto = DOVECOT_SSL_PROTO_SSLv3;
-		else if (strcasecmp(name, SSL_TXT_TLSV1) == 0)
-			proto = DOVECOT_SSL_PROTO_TLSv1;
-		else {
-			i_fatal("Invalid ssl_protocols setting: "
-				"Unknown protocol '%s'", name);
-		}
-		if (neg)
-			exclude |= proto;
-		else
-			include |= proto;
-	}
-	if (include != 0) {
-		/* exclude everything, except those that are included
-		   (and let excludes still override those) */
-		exclude |= DOVECOT_SSL_PROTO_ALL & ~include;
-	}
-	if ((exclude & DOVECOT_SSL_PROTO_SSLv2) != 0) op |= SSL_OP_NO_SSLv2;
-	if ((exclude & DOVECOT_SSL_PROTO_SSLv3) != 0) op |= SSL_OP_NO_SSLv3;
-	if ((exclude & DOVECOT_SSL_PROTO_TLSv1) != 0) op |= SSL_OP_NO_TLSv1;
-	return op;
-}
-
 static int
 ssl_iostream_context_set(struct ssl_iostream_context *ctx,
 			 const struct ssl_iostream_settings *set)
@@ -349,7 +300,7 @@
 	    !SSL_CTX_set_cipher_list(ctx->ssl_ctx, set->cipher_list)) {
 		i_error("%s: Can't set cipher list to '%s': %s",
 			ctx->source, set->cipher_list,
-			ssl_iostream_error());
+			openssl_iostream_error());
 		return -1;
 	}
 	if (ctx->set->protocols != NULL) {
@@ -374,7 +325,7 @@
 		store = SSL_CTX_get_cert_store(ctx->ssl_ctx);
 		if (load_ca(store, set->ca, &xnames) < 0) {
 			i_error("%s: Couldn't parse ssl_ca: %s", ctx->source,
-				ssl_iostream_error());
+				openssl_iostream_error());
 			return -1;
 		}
 		if (ssl_iostream_ctx_verify_remote_cert(ctx, xnames) < 0)
@@ -383,7 +334,7 @@
 		if (!SSL_CTX_load_verify_locations(ctx->ssl_ctx, NULL,
 						   set->ca_dir)) {
 			i_error("%s: Can't load CA certs from directory %s: %s",
-				ctx->source, set->ca_dir, ssl_iostream_error());
+				ctx->source, set->ca_dir, openssl_iostream_error());
 			return -1;
 		}
 	} else {
@@ -421,16 +372,16 @@
 	return ssl_iostream_context_set(ctx, set);
 }
 
-int ssl_iostream_context_init_client(const char *source,
-				     const struct ssl_iostream_settings *set,
-				     struct ssl_iostream_context **ctx_r)
+int openssl_iostream_context_init_client(const char *source,
+					 const struct ssl_iostream_settings *set,
+					 struct ssl_iostream_context **ctx_r)
 {
 	struct ssl_iostream_context *ctx;
 	SSL_CTX *ssl_ctx;
 
 	ssl_iostream_init_global(set);
 	if ((ssl_ctx = SSL_CTX_new(SSLv23_client_method())) == NULL) {
-		i_error("SSL_CTX_new() failed: %s", ssl_iostream_error());
+		i_error("SSL_CTX_new() failed: %s", openssl_iostream_error());
 		return -1;
 	}
 
@@ -445,16 +396,16 @@
 	return 0;
 }
 
-int ssl_iostream_context_init_server(const char *source,
-				     const struct ssl_iostream_settings *set,
-				     struct ssl_iostream_context **ctx_r)
+int openssl_iostream_context_init_server(const char *source,
+					 const struct ssl_iostream_settings *set,
+					 struct ssl_iostream_context **ctx_r)
 {
 	struct ssl_iostream_context *ctx;
 	SSL_CTX *ssl_ctx;
 
 	ssl_iostream_init_global(set);
 	if ((ssl_ctx = SSL_CTX_new(SSLv23_server_method())) == NULL) {
-		i_error("SSL_CTX_new() failed: %s", ssl_iostream_error());
+		i_error("SSL_CTX_new() failed: %s", openssl_iostream_error());
 		return -1;
 	}
 
@@ -468,13 +419,10 @@
 	return 0;
 }
 
-void ssl_iostream_context_deinit(struct ssl_iostream_context **_ctx)
+void openssl_iostream_context_deinit(struct ssl_iostream_context *ctx)
 {
-	struct ssl_iostream_context *ctx = *_ctx;
-
-	*_ctx = NULL;
 	SSL_CTX_free(ctx->ssl_ctx);
-	ssl_iostream_context_free_params(ctx);
+	openssl_iostream_context_free_params(ctx);
 	pool_unref(&ctx->pool);
 	i_free(ctx);
 }
--- a/src/lib-ssl-iostream/iostream-openssl-params.c	Mon Nov 05 18:16:56 2012 +0200
+++ b/src/lib-ssl-iostream/iostream-openssl-params.c	Tue Nov 06 01:04:24 2012 +0200
@@ -19,13 +19,13 @@
 	dh = DH_generate_parameters(bitsize, DH_GENERATOR, NULL, NULL);
 	if (dh == NULL) {
 		i_error("DH_generate_parameters(bits=%d, gen=%d) failed: %s",
-			bitsize, DH_GENERATOR, ssl_iostream_error());
+			bitsize, DH_GENERATOR, openssl_iostream_error());
 		return -1;
 	}
 
 	len = i2d_DHparams(dh, NULL);
 	if (len < 0) {
-		i_error("i2d_DHparams() failed: %s", ssl_iostream_error());
+		i_error("i2d_DHparams() failed: %s", openssl_iostream_error());
 		DH_free(dh);
 		return -1;
 	}
@@ -40,7 +40,7 @@
 	return 0;
 }
 
-int ssl_iostream_generate_params(buffer_t *output)
+int openssl_iostream_generate_params(buffer_t *output)
 {
 	unsigned int i;
 
@@ -101,13 +101,13 @@
 	return ret;
 }
 
-int ssl_iostream_context_import_params(struct ssl_iostream_context *ctx,
-				       const buffer_t *input)
+int openssl_iostream_context_import_params(struct ssl_iostream_context *ctx,
+					   const buffer_t *input)
 {
 	const unsigned char *data, *end;
 	int ret;
 
-	ssl_iostream_context_free_params(ctx);
+	openssl_iostream_context_free_params(ctx);
 
 	data = input->data;
 	end = data + input->used;
@@ -116,7 +116,7 @@
 	return ret < 0 || data != end ? -1 : 0;
 }
 
-void ssl_iostream_context_free_params(struct ssl_iostream_context *ctx)
+void openssl_iostream_context_free_params(struct ssl_iostream_context *ctx)
 {
 	if (ctx->dh_512 != NULL) {
 		DH_free(ctx->dh_512);
--- a/src/lib-ssl-iostream/iostream-openssl.c	Mon Nov 05 18:16:56 2012 +0200
+++ b/src/lib-ssl-iostream/iostream-openssl.c	Tue Nov 06 01:04:24 2012 +0200
@@ -6,11 +6,10 @@
 #include "iostream-openssl.h"
 
 #include <openssl/err.h>
-#include <openssl/x509v3.h>
 
-static void ssl_iostream_free(struct ssl_iostream *ssl_io);
+static void openssl_iostream_free(struct ssl_iostream *ssl_io);
 
-static void ssl_info_callback(const SSL *ssl, int where, int ret)
+static void openssl_info_callback(const SSL *ssl, int where, int ret)
 {
 	struct ssl_iostream *ssl_io;
 
@@ -31,7 +30,7 @@
 }
 
 static int
-ssl_iostream_use_certificate(struct ssl_iostream *ssl_io, const char *cert)
+openssl_iostream_use_certificate(struct ssl_iostream *ssl_io, const char *cert)
 {
 	BIO *in;
 	X509 *x;
@@ -39,7 +38,7 @@
 
 	in = BIO_new_mem_buf(t_strdup_noconst(cert), strlen(cert));
 	if (in == NULL) {
-		i_error("BIO_new_mem_buf() failed: %s", ssl_iostream_error());
+		i_error("BIO_new_mem_buf() failed: %s", openssl_iostream_error());
 		return -1;
 	}
 
@@ -61,17 +60,17 @@
 }
 
 static int
-ssl_iostream_use_key(struct ssl_iostream *ssl_io,
-		     const struct ssl_iostream_settings *set)
+openssl_iostream_use_key(struct ssl_iostream *ssl_io,
+			 const struct ssl_iostream_settings *set)
 {
 	EVP_PKEY *pkey;
 	int ret = 0;
 
-	if (ssl_iostream_load_key(set, ssl_io->source, &pkey) < 0)
+	if (openssl_iostream_load_key(set, ssl_io->source, &pkey) < 0)
 		return -1;
 	if (SSL_use_PrivateKey(ssl_io->ssl, pkey) != 1) {
 		i_error("%s: Can't load SSL private key: %s",
-			ssl_io->source, ssl_iostream_key_load_error());
+			ssl_io->source, openssl_iostream_key_load_error());
 		ret = -1;
 	}
 	EVP_PKEY_free(pkey);
@@ -79,7 +78,7 @@
 }
 
 static int
-ssl_iostream_verify_client_cert(int preverify_ok, X509_STORE_CTX *ctx)
+openssl_iostream_verify_client_cert(int preverify_ok, X509_STORE_CTX *ctx)
 {
 	int ssl_extidx = SSL_get_ex_data_X509_STORE_CTX_idx();
 	SSL *ssl;
@@ -116,21 +115,21 @@
 }
 
 static int
-ssl_iostream_set(struct ssl_iostream *ssl_io,
-		 const struct ssl_iostream_settings *set)
+openssl_iostream_set(struct ssl_iostream *ssl_io,
+		     const struct ssl_iostream_settings *set)
 {
 	const struct ssl_iostream_settings *ctx_set = ssl_io->ctx->set;
 	int verify_flags;
 
 	if (set->verbose)
-		SSL_set_info_callback(ssl_io->ssl, ssl_info_callback);
+		SSL_set_info_callback(ssl_io->ssl, openssl_info_callback);
 
 	if (set->cipher_list != NULL &&
 	    strcmp(ctx_set->cipher_list, set->cipher_list) != 0) {
 		if (!SSL_set_cipher_list(ssl_io->ssl, set->cipher_list)) {
 			i_error("%s: Can't set cipher list to '%s': %s",
 				ssl_io->source, set->cipher_list,
-				ssl_iostream_error());
+				openssl_iostream_error());
 		}
 		return -1;
 	}
@@ -141,11 +140,11 @@
 	}
 
 	if (set->cert != NULL && strcmp(ctx_set->cert, set->cert) != 0) {
-		if (ssl_iostream_use_certificate(ssl_io, set->cert) < 0)
+		if (openssl_iostream_use_certificate(ssl_io, set->cert) < 0)
 			return -1;
 	}
 	if (set->key != NULL && strcmp(ctx_set->key, set->key) != 0) {
-		if (ssl_iostream_use_key(ssl_io, set) < 0)
+		if (openssl_iostream_use_key(ssl_io, set) < 0)
 			return -1;
 	}
 	if (set->verify_remote_cert) {
@@ -154,7 +153,7 @@
 		else
 			verify_flags = SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE;
 		SSL_set_verify(ssl_io->ssl, verify_flags,
-			       ssl_iostream_verify_client_cert);
+			       openssl_iostream_verify_client_cert);
 	}
 
 	if (set->cert_username_field != NULL) {
@@ -173,10 +172,11 @@
 	return 0;
 }
 
-int io_stream_create_ssl(struct ssl_iostream_context *ctx, const char *source,
-			 const struct ssl_iostream_settings *set,
-			 struct istream **input, struct ostream **output,
-			 struct ssl_iostream **iostream_r)
+static int
+openssl_iostream_create(struct ssl_iostream_context *ctx, const char *source,
+			const struct ssl_iostream_settings *set,
+			struct istream **input, struct ostream **output,
+			struct ssl_iostream **iostream_r)
 {
 	struct ssl_iostream *ssl_io;
 	SSL *ssl;
@@ -185,7 +185,7 @@
 
 	ssl = SSL_new(ctx->ssl_ctx);
 	if (ssl == NULL) {
-		i_error("SSL_new() failed: %s", ssl_iostream_error());
+		i_error("SSL_new() failed: %s", openssl_iostream_error());
 		return -1;
 	}
 
@@ -195,7 +195,8 @@
 	   into the given buffer. The bio_int is used by OpenSSL and bio_ext
 	   is used by this library. */
 	if (BIO_new_bio_pair(&bio_int, 0, &bio_ext, 0) != 1) {
-		i_error("BIO_new_bio_pair() failed: %s", ssl_iostream_error());
+		i_error("BIO_new_bio_pair() failed: %s",
+			openssl_iostream_error());
 		SSL_free(ssl);
 		return -1;
 	}
@@ -213,17 +214,17 @@
         SSL_set_ex_data(ssl_io->ssl, dovecot_ssl_extdata_index, ssl_io);
 
 	T_BEGIN {
-		ret = ssl_iostream_set(ssl_io, set);
+		ret = openssl_iostream_set(ssl_io, set);
 	} T_END;
 	if (ret < 0) {
-		ssl_iostream_free(ssl_io);
+		openssl_iostream_free(ssl_io);
 		return -1;
 	}
 
 	o_stream_uncork(ssl_io->plain_output);
 
-	*input = i_stream_create_ssl(ssl_io);
-	*output = o_stream_create_ssl(ssl_io);
+	*input = openssl_i_stream_create_ssl(ssl_io);
+	*output = openssl_o_stream_create_ssl(ssl_io);
 	i_stream_set_name(*input, t_strconcat("SSL ",
 		i_stream_get_name(ssl_io->plain_input), NULL));
 	o_stream_set_name(*output, t_strconcat("SSL ",
@@ -237,7 +238,7 @@
 	return 0;
 }
 
-static void ssl_iostream_free(struct ssl_iostream *ssl_io)
+static void openssl_iostream_free(struct ssl_iostream *ssl_io)
 {
 	i_stream_unref(&ssl_io->plain_input);
 	o_stream_unref(&ssl_io->plain_output);
@@ -248,33 +249,25 @@
 	i_free(ssl_io);
 }
 
-void ssl_iostream_unref(struct ssl_iostream **_ssl_io)
+static void openssl_iostream_unref(struct ssl_iostream *ssl_io)
 {
-	struct ssl_iostream *ssl_io = *_ssl_io;
-
-	*_ssl_io = NULL;
-
 	i_assert(ssl_io->refcount > 0);
 	if (--ssl_io->refcount > 0)
 		return;
 
-	ssl_iostream_free(ssl_io);
+	openssl_iostream_free(ssl_io);
 }
 
-void ssl_iostream_destroy(struct ssl_iostream **_ssl_io)
+static void openssl_iostream_destroy(struct ssl_iostream *ssl_io)
 {
-	struct ssl_iostream *ssl_io = *_ssl_io;
-
-	*_ssl_io = NULL;
-
 	(void)SSL_shutdown(ssl_io->ssl);
-	(void)ssl_iostream_more(ssl_io);
+	(void)openssl_iostream_more(ssl_io);
 	(void)o_stream_flush(ssl_io->plain_output);
 
 	ssl_iostream_unref(&ssl_io);
 }
 
-static bool ssl_iostream_bio_output(struct ssl_iostream *ssl_io)
+static bool openssl_iostream_bio_output(struct ssl_iostream *ssl_io)
 {
 	size_t bytes, max_bytes;
 	ssize_t sent;
@@ -324,8 +317,8 @@
 }
 
 static ssize_t
-ssl_iostream_read_more(struct ssl_iostream *ssl_io,
-		       const unsigned char **data_r, size_t *size_r)
+openssl_iostream_read_more(struct ssl_iostream *ssl_io,
+			   const unsigned char **data_r, size_t *size_r)
 {
 	*data_r = i_stream_get_data(ssl_io->plain_input, size_r);
 	if (*size_r > 0)
@@ -343,7 +336,7 @@
 	return 0;
 }
 
-static bool ssl_iostream_bio_input(struct ssl_iostream *ssl_io)
+static bool openssl_iostream_bio_input(struct ssl_iostream *ssl_io)
 {
 	const unsigned char *data;
 	size_t bytes, size;
@@ -353,7 +346,7 @@
 	while ((bytes = BIO_ctrl_get_write_guarantee(ssl_io->bio_ext)) > 0) {
 		/* bytes contains how many bytes we can write to bio_ext */
 		ssl_io->plain_input->real_stream->try_alloc_limit = bytes;
-		ret = ssl_iostream_read_more(ssl_io, &data, &size);
+		ret = openssl_iostream_read_more(ssl_io, &data, &size);
 		ssl_io->plain_input->real_stream->try_alloc_limit = 0;
 		if (ret == -1 && size == 0 && !bytes_read) {
 			ssl_io->plain_stream_errno =
@@ -397,17 +390,17 @@
 	return bytes_read;
 }
 
-bool ssl_iostream_bio_sync(struct ssl_iostream *ssl_io)
+bool openssl_iostream_bio_sync(struct ssl_iostream *ssl_io)
 {
 	bool ret;
 
-	ret = ssl_iostream_bio_output(ssl_io);
-	if (ssl_iostream_bio_input(ssl_io))
+	ret = openssl_iostream_bio_output(ssl_io);
+	if (openssl_iostream_bio_input(ssl_io))
 		ret = TRUE;
 	return ret;
 }
 
-int ssl_iostream_more(struct ssl_iostream *ssl_io)
+int openssl_iostream_more(struct ssl_iostream *ssl_io)
 {
 	int ret;
 
@@ -415,19 +408,20 @@
 		if ((ret = ssl_iostream_handshake(ssl_io)) <= 0)
 			return ret;
 	}
-	(void)ssl_iostream_bio_sync(ssl_io);
+	(void)openssl_iostream_bio_sync(ssl_io);
 	return 1;
 }
 
-static void ssl_iostream_set_error(struct ssl_iostream *ssl_io, const char *str)
+static void
+openssl_iostream_set_error(struct ssl_iostream *ssl_io, const char *str)
 {
 	i_free(ssl_io->last_error);
 	ssl_io->last_error = i_strdup(str);
 }
 
 static int
-ssl_iostream_handle_error_full(struct ssl_iostream *ssl_io, int ret,
-			       const char *func_name, bool write_error)
+openssl_iostream_handle_error_full(struct ssl_iostream *ssl_io, int ret,
+				   const char *func_name, bool write_error)
 {
 	const char *errstr = NULL;
 	int err;
@@ -435,7 +429,7 @@
 	err = SSL_get_error(ssl_io->ssl, ret);
 	switch (err) {
 	case SSL_ERROR_WANT_WRITE:
-		if (!ssl_iostream_bio_sync(ssl_io)) {
+		if (!openssl_iostream_bio_sync(ssl_io)) {
 			if (!write_error)
 				i_panic("SSL ostream buffer size not unlimited");
 			return 0;
@@ -448,7 +442,7 @@
 		return 1;
 	case SSL_ERROR_WANT_READ:
 		ssl_io->want_read = TRUE;
-		(void)ssl_iostream_bio_sync(ssl_io);
+		(void)openssl_iostream_bio_sync(ssl_io);
 		if (ssl_io->closed) {
 			errno = ssl_io->plain_stream_errno != 0 ?
 				ssl_io->plain_stream_errno : EPIPE;
@@ -458,7 +452,7 @@
 	case SSL_ERROR_SYSCALL:
 		/* eat up the error queue */
 		if (ERR_peek_error() != 0) {
-			errstr = ssl_iostream_error();
+			errstr = openssl_iostream_error();
 			errno = EINVAL;
 		} else if (ret != 0) {
 			i_assert(errno != 0);
@@ -478,114 +472,36 @@
 		break;
 	case SSL_ERROR_SSL:
 		errstr = t_strdup_printf("%s failed: %s",
-					 func_name, ssl_iostream_error());
+					 func_name, openssl_iostream_error());
 		errno = EINVAL;
 		break;
 	default:
 		errstr = t_strdup_printf("%s failed: unknown failure %d (%s)",
-					 func_name, err, ssl_iostream_error());
+					 func_name, err,
+					 openssl_iostream_error());
 		errno = EINVAL;
 		break;
 	}
 
 	if (errstr != NULL)
-		ssl_iostream_set_error(ssl_io, errstr);
+		openssl_iostream_set_error(ssl_io, errstr);
 	return -1;
 }
 
-int ssl_iostream_handle_error(struct ssl_iostream *ssl_io, int ret,
-			      const char *func_name)
-{
-	return ssl_iostream_handle_error_full(ssl_io, ret, func_name, FALSE);
-}
-
-int ssl_iostream_handle_write_error(struct ssl_iostream *ssl_io, int ret,
-				    const char *func_name)
-{
-	return ssl_iostream_handle_error_full(ssl_io, ret, func_name, TRUE);
-}
-
-static const char *asn1_string_to_c(ASN1_STRING *asn_str)
+int openssl_iostream_handle_error(struct ssl_iostream *ssl_io, int ret,
+				  const char *func_name)
 {
-	const char *cstr;
-	unsigned int len;
-
-	len = ASN1_STRING_length(asn_str);
-	cstr = t_strndup(ASN1_STRING_data(asn_str), len);
-	if (strlen(cstr) != len) {
-		/* NULs in the name - could be some MITM attack.
-		   never allow. */
-		return "";
-	}
-	return cstr;
-}
-
-static const char *get_general_dns_name(const GENERAL_NAME *name)
-{
-	if (ASN1_STRING_type(name->d.ia5) != V_ASN1_IA5STRING)
-		return "";
-
-	return asn1_string_to_c(name->d.ia5);
+	return openssl_iostream_handle_error_full(ssl_io, ret, func_name, FALSE);
 }
 
-static const char *get_cname(X509 *cert)
+int openssl_iostream_handle_write_error(struct ssl_iostream *ssl_io, int ret,
+					const char *func_name)
 {
-	X509_NAME *name;
-	X509_NAME_ENTRY *entry;
-	ASN1_STRING *str;
-	int cn_idx;
-
-	name = X509_get_subject_name(cert);
-	if (name == NULL)
-		return "";
-	cn_idx = X509_NAME_get_index_by_NID(name, NID_commonName, -1);
-	if (cn_idx == -1)
-		return "";
-	entry = X509_NAME_get_entry(name, cn_idx);
-	i_assert(entry != NULL);
-	str = X509_NAME_ENTRY_get_data(entry);
-	i_assert(str != NULL);
-	return asn1_string_to_c(str);
+	return openssl_iostream_handle_error_full(ssl_io, ret, func_name, TRUE);
 }
 
-int openssl_cert_match_name(SSL *ssl, const char *verify_name)
-{
-	X509 *cert;
-	STACK_OF(GENERAL_NAME) *gnames;
-	const GENERAL_NAME *gn;
-	const char *dnsname;
-	bool dns_names = FALSE;
-	unsigned int i, count;
-	int ret;
-
-	cert = SSL_get_peer_certificate(ssl);
-	i_assert(cert != NULL);
-
-	/* verify against SubjectAltNames */
-	gnames = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL);
-	count = gnames == NULL ? 0 : sk_GENERAL_NAME_num(gnames);
-	for (i = 0; i < count; i++) {
-		gn = sk_GENERAL_NAME_value(gnames, i);
-		if (gn->type == GEN_DNS) {
-			dns_names = TRUE;
-			dnsname = get_general_dns_name(gn);
-			if (strcmp(dnsname, verify_name) == 0)
-				break;
-		}
-	}
-	sk_GENERAL_NAME_pop_free(gnames, GENERAL_NAME_free);
-
-	/* verify against CommonName only when there wasn't any DNS
-	   SubjectAltNames */
-	if (dns_names)
-		ret = i < count ? 0 : -1;
-	else
-		ret = strcmp(get_cname(cert), verify_name) == 0 ? 0 : -1;
-	X509_free(cert);
-	return ret;
-}
-
-int ssl_iostream_cert_match_name(struct ssl_iostream *ssl_io,
+static int
+openssl_iostream_cert_match_name(struct ssl_iostream *ssl_io,
 				 const char *verify_name)
 {
 	if (!ssl_iostream_has_valid_client_cert(ssl_io))
@@ -594,7 +510,7 @@
 	return openssl_cert_match_name(ssl_io->ssl, verify_name);
 }
 
-int ssl_iostream_handshake(struct ssl_iostream *ssl_io)
+static int openssl_iostream_handshake(struct ssl_iostream *ssl_io)
 {
 	int ret;
 
@@ -602,21 +518,21 @@
 
 	if (ssl_io->ctx->client_ctx) {
 		while ((ret = SSL_connect(ssl_io->ssl)) <= 0) {
-			ret = ssl_iostream_handle_error(ssl_io, ret,
-							"SSL_connect()");
+			ret = openssl_iostream_handle_error(ssl_io, ret,
+							    "SSL_connect()");
 			if (ret <= 0)
 				return ret;
 		}
 	} else {
 		while ((ret = SSL_accept(ssl_io->ssl)) <= 0) {
-			ret = ssl_iostream_handle_error(ssl_io, ret,
-							"SSL_accept()");
+			ret = openssl_iostream_handle_error(ssl_io, ret,
+							    "SSL_accept()");
 			if (ret <= 0)
 				return ret;
 		}
 	}
 	/* handshake finished */
-	(void)ssl_iostream_bio_sync(ssl_io);
+	(void)openssl_iostream_bio_sync(ssl_io);
 
 	i_free_and_null(ssl_io->last_error);
 	ssl_io->handshaked = TRUE;
@@ -632,30 +548,34 @@
 	return 1;
 }
 
-void ssl_iostream_set_handshake_callback(struct ssl_iostream *ssl_io,
-					 int (*callback)(void *context),
-					 void *context)
+static void
+openssl_iostream_set_handshake_callback(struct ssl_iostream *ssl_io,
+					int (*callback)(void *context),
+					void *context)
 {
 	ssl_io->handshake_callback = callback;
 	ssl_io->handshake_context = context;
 }
 
-bool ssl_iostream_is_handshaked(const struct ssl_iostream *ssl_io)
+static bool openssl_iostream_is_handshaked(const struct ssl_iostream *ssl_io)
 {
 	return ssl_io->handshaked;
 }
 
-bool ssl_iostream_has_valid_client_cert(const struct ssl_iostream *ssl_io)
+static bool
+openssl_iostream_has_valid_client_cert(const struct ssl_iostream *ssl_io)
 {
 	return ssl_io->cert_received && !ssl_io->cert_broken;
 }
 
-bool ssl_iostream_has_broken_client_cert(struct ssl_iostream *ssl_io)
+static bool
+openssl_iostream_has_broken_client_cert(struct ssl_iostream *ssl_io)
 {
 	return ssl_io->cert_received && ssl_io->cert_broken;
 }
 
-const char *ssl_iostream_get_peer_name(struct ssl_iostream *ssl_io)
+static const char *
+openssl_iostream_get_peer_name(struct ssl_iostream *ssl_io)
 {
 	X509 *x509;
 	char *name;
@@ -688,7 +608,8 @@
 	return *name == '\0' ? NULL : name;
 }
 
-const char *ssl_iostream_get_security_string(struct ssl_iostream *ssl_io)
+static const char *
+openssl_iostream_get_security_string(struct ssl_iostream *ssl_io)
 {
 	const SSL_CIPHER *cipher;
 #ifdef HAVE_SSL_COMPRESSION
@@ -715,7 +636,32 @@
 			       bits, alg_bits, comp_str);
 }
 
-const char *ssl_iostream_get_last_error(struct ssl_iostream *ssl_io)
+static const char *
+openssl_iostream_get_last_error(struct ssl_iostream *ssl_io)
 {
 	return ssl_io->last_error;
 }
+
+const struct iostream_ssl_vfuncs ssl_vfuncs = {
+	openssl_iostream_context_init_client,
+	openssl_iostream_context_init_server,
+	openssl_iostream_context_deinit,
+
+	openssl_iostream_generate_params,
+	openssl_iostream_context_import_params,
+
+	openssl_iostream_create,
+	openssl_iostream_unref,
+	openssl_iostream_destroy,
+
+	openssl_iostream_handshake,
+	openssl_iostream_set_handshake_callback,
+
+	openssl_iostream_is_handshaked,
+	openssl_iostream_has_valid_client_cert,
+	openssl_iostream_has_broken_client_cert,
+	openssl_iostream_cert_match_name,
+	openssl_iostream_get_peer_name,
+	openssl_iostream_get_security_string,
+	openssl_iostream_get_last_error
+};
--- a/src/lib-ssl-iostream/iostream-openssl.h	Mon Nov 05 18:16:56 2012 +0200
+++ b/src/lib-ssl-iostream/iostream-openssl.h	Tue Nov 06 01:04:24 2012 +0200
@@ -1,7 +1,7 @@
 #ifndef IOSTREAM_OPENSSL_H
 #define IOSTREAM_OPENSSL_H
 
-#include "iostream-ssl.h"
+#include "iostream-ssl-private.h"
 
 #include <openssl/ssl.h>
 
@@ -54,12 +54,19 @@
 
 extern int dovecot_ssl_extdata_index;
 
-struct istream *i_stream_create_ssl(struct ssl_iostream *ssl_io);
-struct ostream *o_stream_create_ssl(struct ssl_iostream *ssl_io);
-void ssl_iostream_unref(struct ssl_iostream **ssl_io);
+struct istream *openssl_i_stream_create_ssl(struct ssl_iostream *ssl_io);
+struct ostream *openssl_o_stream_create_ssl(struct ssl_iostream *ssl_io);
 
-int ssl_iostream_load_key(const struct ssl_iostream_settings *set,
-			  const char *key_source, EVP_PKEY **pkey_r);
+int openssl_iostream_context_init_client(const char *source,
+					 const struct ssl_iostream_settings *set,
+					 struct ssl_iostream_context **ctx_r);
+int openssl_iostream_context_init_server(const char *source,
+					 const struct ssl_iostream_settings *set,
+					 struct ssl_iostream_context **ctx_r);
+void openssl_iostream_context_deinit(struct ssl_iostream_context *ctx);
+
+int openssl_iostream_load_key(const struct ssl_iostream_settings *set,
+			      const char *key_source, EVP_PKEY **pkey_r);
 const char *ssl_iostream_get_use_certificate_error(const char *cert);
 int openssl_cert_match_name(SSL *ssl, const char *verify_name);
 int openssl_get_protocol_options(const char *protocols);
@@ -68,23 +75,26 @@
 
 /* Sync plain_input/plain_output streams with BIOs. Returns TRUE if at least
    one byte was read/written. */
-bool ssl_iostream_bio_sync(struct ssl_iostream *ssl_io);
+bool openssl_iostream_bio_sync(struct ssl_iostream *ssl_io);
 /* Call when there's more data available in plain_input/plain_output.
    Returns 1 if it's ok to continue with SSL_read/SSL_write, 0 if not
    (still handshaking), -1 if error occurred. */
-int ssl_iostream_more(struct ssl_iostream *ssl_io);
+int openssl_iostream_more(struct ssl_iostream *ssl_io);
 
 /* Returns 1 if the operation should be retried (we read/wrote more data),
    0 if the operation should retried later once more data has been
    read/written, -1 if a fatal error occurred (errno is set). */
-int ssl_iostream_handle_error(struct ssl_iostream *ssl_io, int ret,
-			      const char *func_name);
-int ssl_iostream_handle_write_error(struct ssl_iostream *ssl_io, int ret,
-				    const char *func_name);
+int openssl_iostream_handle_error(struct ssl_iostream *ssl_io, int ret,
+				  const char *func_name);
+int openssl_iostream_handle_write_error(struct ssl_iostream *ssl_io, int ret,
+					const char *func_name);
 
-const char *ssl_iostream_error(void);
-const char *ssl_iostream_key_load_error(void);
+const char *openssl_iostream_error(void);
+const char *openssl_iostream_key_load_error(void);
 
-void ssl_iostream_context_free_params(struct ssl_iostream_context *ctx);
+int openssl_iostream_generate_params(buffer_t *output);
+int openssl_iostream_context_import_params(struct ssl_iostream_context *ctx,
+					   const buffer_t *input);
+void openssl_iostream_context_free_params(struct ssl_iostream_context *ctx);
 
 #endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-ssl-iostream/iostream-ssl-private.h	Tue Nov 06 01:04:24 2012 +0200
@@ -0,0 +1,40 @@
+#ifndef IOSTREAM_SSL_PRIVATE_H
+#define IOSTREAM_SSL_PRIVATE_H
+
+#include "iostream-ssl.h"
+
+struct iostream_ssl_vfuncs {
+	int (*context_init_client)(const char *source,
+				   const struct ssl_iostream_settings *set,
+				   struct ssl_iostream_context **ctx_r);
+	int (*context_init_server)(const char *source,
+				   const struct ssl_iostream_settings *set,
+				   struct ssl_iostream_context **ctx_r);
+	void (*context_deinit)(struct ssl_iostream_context *ctx);
+
+	int (*generate_params)(buffer_t *output);
+	int (*context_import_params)(struct ssl_iostream_context *ctx,
+				     const buffer_t *input);
+
+	int (*create)(struct ssl_iostream_context *ctx, const char *source,
+		      const struct ssl_iostream_settings *set,
+		      struct istream **input, struct ostream **output,
+		      struct ssl_iostream **iostream_r);
+	void (*unref)(struct ssl_iostream *ssl_io);
+	void (*destroy)(struct ssl_iostream *ssl_io);
+
+	int (*handshake)(struct ssl_iostream *ssl_io);
+	void (*set_handshake_callback)(struct ssl_iostream *ssl_io,
+				       int (*callback)(void *context),
+				       void *context);
+
+	bool (*is_handshaked)(const struct ssl_iostream *ssl_io);
+	bool (*has_valid_client_cert)(const struct ssl_iostream *ssl_io);
+	bool (*has_broken_client_cert)(struct ssl_iostream *ssl_io);
+	int (*cert_match_name)(struct ssl_iostream *ssl_io, const char *name);
+	const char *(*get_peer_name)(struct ssl_iostream *ssl_io);
+	const char *(*get_security_string)(struct ssl_iostream *ssl_io);
+	const char *(*get_last_error)(struct ssl_iostream *ssl_io);
+};
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-ssl-iostream/iostream-ssl.c	Tue Nov 06 01:04:24 2012 +0200
@@ -0,0 +1,147 @@
+/* Copyright (c) 2009-2012 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "module-dir.h"
+#include "iostream-ssl-private.h"
+
+static bool ssl_module_loaded = FALSE;
+static struct module *ssl_module = NULL;
+static const struct iostream_ssl_vfuncs *ssl_vfuncs = NULL;
+
+static int ssl_module_load(void)
+{
+#ifdef HAVE_SSL
+	const char *plugin_name = "ssl_iostream_openssl";
+	struct module_dir_load_settings mod_set;
+
+	memset(&mod_set, 0, sizeof(mod_set));
+	mod_set.abi_version = DOVECOT_ABI_VERSION;
+	mod_set.setting_name = "<built-in lib-ssl-iostream lookup>";
+	ssl_module = module_dir_load(MODULE_DIR, plugin_name, &mod_set);
+
+	ssl_vfuncs = module_get_symbol(ssl_module, "ssl_vfuncs");
+	if (ssl_vfuncs == NULL)
+		i_fatal("%s: ssl_vfuncs symbol not found", plugin_name);
+	ssl_module_loaded = TRUE;
+	return 0;
+#else
+	/* SSL not enabled */
+	return -1;
+#endif
+}
+
+int ssl_iostream_context_init_client(const char *source,
+				     const struct ssl_iostream_settings *set,
+				     struct ssl_iostream_context **ctx_r)
+{
+	if (!ssl_module_loaded) {
+		if (ssl_module_load() < 0)
+			return -1;
+	}
+	return ssl_vfuncs->context_init_client(source, set, ctx_r);
+}
+
+int ssl_iostream_context_init_server(const char *source,
+				     const struct ssl_iostream_settings *set,
+				     struct ssl_iostream_context **ctx_r)
+{
+	if (!ssl_module_loaded) {
+		if (ssl_module_load() < 0)
+			return -1;
+	}
+	return ssl_vfuncs->context_init_server(source, set, ctx_r);
+}
+
+void ssl_iostream_context_deinit(struct ssl_iostream_context **_ctx)
+{
+	struct ssl_iostream_context *ctx = *_ctx;
+
+	*_ctx = NULL;
+	ssl_vfuncs->context_deinit(ctx);
+}
+
+int ssl_iostream_generate_params(buffer_t *output)
+{
+	if (!ssl_module_loaded) {
+		if (ssl_module_load() < 0)
+			return -1;
+	}
+	return ssl_vfuncs->generate_params(output);
+}
+
+int ssl_iostream_context_import_params(struct ssl_iostream_context *ctx,
+				       const buffer_t *input)
+{
+	return ssl_vfuncs->context_import_params(ctx, input);
+}
+
+int io_stream_create_ssl(struct ssl_iostream_context *ctx, const char *source,
+			 const struct ssl_iostream_settings *set,
+			 struct istream **input, struct ostream **output,
+			 struct ssl_iostream **iostream_r)
+{
+	return ssl_vfuncs->create(ctx, source, set, input, output, iostream_r);
+}
+
+void ssl_iostream_unref(struct ssl_iostream **_ssl_io)
+{
+	struct ssl_iostream *ssl_io = *_ssl_io;
+
+	*_ssl_io = NULL;
+	ssl_vfuncs->unref(ssl_io);
+}
+
+void ssl_iostream_destroy(struct ssl_iostream **_ssl_io)
+{
+	struct ssl_iostream *ssl_io = *_ssl_io;
+
+	*_ssl_io = NULL;
+	ssl_vfuncs->destroy(ssl_io);
+}
+
+int ssl_iostream_handshake(struct ssl_iostream *ssl_io)
+{
+	return ssl_vfuncs->handshake(ssl_io);
+}
+
+void ssl_iostream_set_handshake_callback(struct ssl_iostream *ssl_io,
+					 int (*callback)(void *context),
+					 void *context)
+{
+	ssl_vfuncs->set_handshake_callback(ssl_io, callback, context);
+}
+
+bool ssl_iostream_is_handshaked(const struct ssl_iostream *ssl_io)
+{
+	return ssl_vfuncs->is_handshaked(ssl_io);
+}
+
+bool ssl_iostream_has_valid_client_cert(const struct ssl_iostream *ssl_io)
+{
+	return ssl_vfuncs->has_valid_client_cert(ssl_io);
+}
+
+bool ssl_iostream_has_broken_client_cert(struct ssl_iostream *ssl_io)
+{
+	return ssl_vfuncs->has_broken_client_cert(ssl_io);
+}
+
+int ssl_iostream_cert_match_name(struct ssl_iostream *ssl_io, const char *name)
+{
+	return ssl_vfuncs->cert_match_name(ssl_io, name);
+}
+
+const char *ssl_iostream_get_peer_name(struct ssl_iostream *ssl_io)
+{
+	return ssl_vfuncs->get_peer_name(ssl_io);
+}
+
+const char *ssl_iostream_get_security_string(struct ssl_iostream *ssl_io)
+{
+	return ssl_vfuncs->get_security_string(ssl_io);
+}
+
+const char *ssl_iostream_get_last_error(struct ssl_iostream *ssl_io)
+{
+	return ssl_vfuncs->get_last_error(ssl_io);
+}
--- a/src/lib-ssl-iostream/istream-openssl.c	Mon Nov 05 18:16:56 2012 +0200
+++ b/src/lib-ssl-iostream/istream-openssl.c	Tue Nov 06 01:04:24 2012 +0200
@@ -45,7 +45,7 @@
 			return -2;
 	}
 
-	ret = ssl_iostream_more(ssl_io);
+	ret = openssl_iostream_more(ssl_io);
 	if (ret <= 0) {
 		if (ret < 0) {
 			/* handshake failed */
@@ -65,7 +65,7 @@
 	while ((ret = SSL_read(ssl_io->ssl,
 			       stream->w_buffer + stream->pos, size)) <= 0) {
 		/* failed to read anything */
-		ret = ssl_iostream_handle_error(ssl_io, ret, "SSL_read");
+		ret = openssl_iostream_handle_error(ssl_io, ret, "SSL_read");
 		if (ret <= 0) {
 			if (ret < 0) {
 				stream->istream.stream_errno = errno;
@@ -108,7 +108,7 @@
 	return ret;
 }
 
-struct istream *i_stream_create_ssl(struct ssl_iostream *ssl_io)
+struct istream *openssl_i_stream_create_ssl(struct ssl_iostream *ssl_io)
 {
 	struct ssl_istream *sstream;
 
--- a/src/lib-ssl-iostream/ostream-openssl.c	Mon Nov 05 18:16:56 2012 +0200
+++ b/src/lib-ssl-iostream/ostream-openssl.c	Tue Nov 06 01:04:24 2012 +0200
@@ -87,8 +87,8 @@
 				CONST_PTR_OFFSET(sstream->buffer->data, pos),
 				sstream->buffer->used - pos);
 		if (ret <= 0) {
-			ret = ssl_iostream_handle_write_error(sstream->ssl_io,
-							      ret, "SSL_write");
+			ret = openssl_iostream_handle_write_error(sstream->ssl_io,
+								  ret, "SSL_write");
 			if (ret < 0) {
 				sstream->ostream.ostream.stream_errno = errno;
 				break;
@@ -97,7 +97,7 @@
 				break;
 		} else {
 			pos += ret;
-			(void)ssl_iostream_bio_sync(sstream->ssl_io);
+			(void)openssl_iostream_bio_sync(sstream->ssl_io);
 		}
 	}
 	buffer_delete(sstream->buffer, 0, pos);
@@ -109,7 +109,7 @@
 	struct ssl_ostream *sstream = (struct ssl_ostream *)stream;
 	int ret;
 
-	if ((ret = ssl_iostream_more(sstream->ssl_io)) < 0) {
+	if ((ret = openssl_iostream_more(sstream->ssl_io)) < 0) {
 		/* handshake failed */
 		stream->ostream.stream_errno = errno;
 	} else if (ret > 0 && sstream->buffer != NULL &&
@@ -143,8 +143,8 @@
 				CONST_PTR_OFFSET(iov[i].iov_base, pos),
 				iov[i].iov_len - pos);
 		if (ret <= 0) {
-			ret = ssl_iostream_handle_write_error(sstream->ssl_io,
-							      ret, "SSL_write");
+			ret = openssl_iostream_handle_write_error(sstream->ssl_io,
+								  ret, "SSL_write");
 			if (ret < 0) {
 				sstream->ostream.ostream.stream_errno = errno;
 				break;
@@ -159,7 +159,7 @@
 				i++;
 				pos = 0;
 			}
-			(void)ssl_iostream_bio_sync(sstream->ssl_io);
+			(void)openssl_iostream_bio_sync(sstream->ssl_io);
 		}
 	}
 	return ret < 0 ? -1 : 0;
@@ -231,7 +231,7 @@
 	o_stream_set_max_buffer_size(sstream->ssl_io->plain_output, max_size);
 }
 
-struct ostream *o_stream_create_ssl(struct ssl_iostream *ssl_io)
+struct ostream *openssl_o_stream_create_ssl(struct ssl_iostream *ssl_io)
 {
 	struct ssl_ostream *sstream;
 
--- a/src/login-common/Makefile.am	Mon Nov 05 18:16:56 2012 +0200
+++ b/src/login-common/Makefile.am	Tue Nov 06 01:04:24 2012 +0200
@@ -22,7 +22,12 @@
 	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)
 
 headers = \