From 831f3bcdde51fa388462eda1daa555e90651ca2e Mon Sep 17 00:00:00 2001 From: Timo Sirainen Date: Tue, 8 Nov 2011 23:40:54 +0200 Subject: [PATCH] login proxy: Verify that remote hostname matches SSL cert, unless ssl=any-cert --- configure.in | 2 +- src/lib-ssl-iostream/iostream-openssl.c | 17 +++++++++++------ src/lib-ssl-iostream/iostream-openssl.h | 1 + src/login-common/Makefile.am | 3 ++- src/login-common/login-proxy.c | 16 +++++++++++----- src/login-common/ssl-proxy-openssl.c | 6 ++++++ src/login-common/ssl-proxy.c | 6 ++++++ src/login-common/ssl-proxy.h | 1 + 8 files changed, 39 insertions(+), 13 deletions(-) diff --git a/configure.in b/configure.in index 32f154c460..fde242cb62 100644 --- a/configure.in +++ b/configure.in @@ -2487,7 +2487,7 @@ else 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' LIBDOVECOT_STORAGE_FIRST='$(top_builddir)/src/lib-storage/libstorage_service.la $(top_builddir)/src/lib-storage/register/libstorage_register.la' LIBDOVECOT_STORAGE="$LIBDOVECOT_STORAGE_FIRST $LINKED_STORAGE_LIBS $LIBDOVECOT_STORAGE_LAST" - LIBDOVECOT_LOGIN='$(top_builddir)/src/login-common/liblogin.la' + LIBDOVECOT_LOGIN='$(top_builddir)/src/login-common/liblogin.la $(top_builddir)/src/lib-ssl-iostream/libssl_iostream.la' LIBDOVECOT_LDA='$(top_builddir)/src/lib-lda/liblda.la' fi LIBDOVECOT_SQL='$(top_builddir)/src/lib-sql/libsql.la' diff --git a/src/lib-ssl-iostream/iostream-openssl.c b/src/lib-ssl-iostream/iostream-openssl.c index daf8149e57..63ca55a2bc 100644 --- a/src/lib-ssl-iostream/iostream-openssl.c +++ b/src/lib-ssl-iostream/iostream-openssl.c @@ -492,8 +492,7 @@ static const char *get_cname(X509 *cert) return asn1_string_to_c(str); } -int ssl_iostream_cert_match_name(struct ssl_iostream *ssl_io, - const char *verify_name) +int openssl_cert_match_name(SSL *ssl, const char *verify_name) { X509 *cert; STACK_OF(GENERAL_NAME) *gnames; @@ -502,10 +501,7 @@ int ssl_iostream_cert_match_name(struct ssl_iostream *ssl_io, bool dns_names = FALSE; unsigned int i, count; - if (!ssl_iostream_has_valid_client_cert(ssl_io)) - return -1; - - cert = SSL_get_peer_certificate(ssl_io->ssl); + cert = SSL_get_peer_certificate(ssl); i_assert(cert != NULL); /* verify against SubjectAltNames */ @@ -529,6 +525,15 @@ int ssl_iostream_cert_match_name(struct ssl_iostream *ssl_io, return strcmp(get_cname(cert), verify_name) == 0 ? 0 : -1; } +int ssl_iostream_cert_match_name(struct ssl_iostream *ssl_io, + const char *verify_name) +{ + if (!ssl_iostream_has_valid_client_cert(ssl_io)) + return -1; + + return openssl_cert_match_name(ssl_io->ssl, verify_name); +} + int ssl_iostream_handshake(struct ssl_iostream *ssl_io) { int ret; diff --git a/src/lib-ssl-iostream/iostream-openssl.h b/src/lib-ssl-iostream/iostream-openssl.h index 0549a2d83e..fecd859dc0 100644 --- a/src/lib-ssl-iostream/iostream-openssl.h +++ b/src/lib-ssl-iostream/iostream-openssl.h @@ -60,6 +60,7 @@ void ssl_iostream_unref(struct ssl_iostream **ssl_io); int ssl_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); /* Sync plain_input/plain_output streams with BIOs. Returns TRUE if at least one byte was read/written. */ diff --git a/src/login-common/Makefile.am b/src/login-common/Makefile.am index 06b6eff734..efe59c7098 100644 --- a/src/login-common/Makefile.am +++ b/src/login-common/Makefile.am @@ -6,6 +6,7 @@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib-auth \ -I$(top_srcdir)/src/lib-dns \ -I$(top_srcdir)/src/lib-master \ + -I$(top_srcdir)/src/lib-ssl-iostream \ -DPKG_STATEDIR=\""$(statedir)"\" liblogin_la_SOURCES = \ @@ -39,6 +40,6 @@ pkginc_lib_HEADERS = $(headers) pkglib_LTLIBRARIES = libdovecot-login.la libdovecot_login_la_SOURCES = -libdovecot_login_la_LIBADD = liblogin.la ../lib-dovecot/libdovecot.la +libdovecot_login_la_LIBADD = liblogin.la ../lib-ssl-iostream/libssl_iostream.la ../lib-dovecot/libdovecot.la libdovecot_login_la_DEPENDENCIES = liblogin.la libdovecot_login_la_LDFLAGS = -export-dynamic diff --git a/src/login-common/login-proxy.c b/src/login-common/login-proxy.c index 4b18292644..0b42c8ba29 100644 --- a/src/login-common/login-proxy.c +++ b/src/login-common/login-proxy.c @@ -507,18 +507,24 @@ static int login_proxy_ssl_handshaked(void *context) { struct login_proxy *proxy = context; - if ((proxy->ssl_flags & PROXY_SSL_FLAG_ANY_CERT) != 0 || - ssl_proxy_has_valid_client_cert(proxy->ssl_server_proxy)) + if ((proxy->ssl_flags & PROXY_SSL_FLAG_ANY_CERT) != 0) return 0; - if (!ssl_proxy_has_broken_client_cert(proxy->ssl_server_proxy)) { + if (ssl_proxy_has_broken_client_cert(proxy->ssl_server_proxy)) { + client_log_err(proxy->client, t_strdup_printf( + "proxy: Received invalid SSL certificate from %s:%u", + proxy->host, proxy->port)); + } else if (!ssl_proxy_has_valid_client_cert(proxy->ssl_server_proxy)) { client_log_err(proxy->client, t_strdup_printf( "proxy: SSL certificate not received from %s:%u", proxy->host, proxy->port)); - } else { + } else if (ssl_proxy_cert_match_name(proxy->ssl_server_proxy, + proxy->host) < 0) { client_log_err(proxy->client, t_strdup_printf( - "proxy: Received invalid SSL certificate from %s:%u", + "proxy: hostname doesn't match SSL certificate at %s:%u", proxy->host, proxy->port)); + } else { + return 0; } proxy->disconnecting = TRUE; return -1; diff --git a/src/login-common/ssl-proxy-openssl.c b/src/login-common/ssl-proxy-openssl.c index 76ee4246d5..64a0d45038 100644 --- a/src/login-common/ssl-proxy-openssl.c +++ b/src/login-common/ssl-proxy-openssl.c @@ -19,6 +19,7 @@ #ifdef HAVE_OPENSSL +#include "iostream-openssl.h" #include #include #include @@ -665,6 +666,11 @@ 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) +{ + return openssl_cert_match_name(proxy->ssl, verify_name); +} + const char *ssl_proxy_get_peer_name(struct ssl_proxy *proxy) { X509 *x509; diff --git a/src/login-common/ssl-proxy.c b/src/login-common/ssl-proxy.c index 8e7e3a94f9..466f79ee68 100644 --- a/src/login-common/ssl-proxy.c +++ b/src/login-common/ssl-proxy.c @@ -46,6 +46,12 @@ 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; diff --git a/src/login-common/ssl-proxy.h b/src/login-common/ssl-proxy.h index 7be84b76b5..0fb456bacb 100644 --- a/src/login-common/ssl-proxy.h +++ b/src/login-common/ssl-proxy.h @@ -24,6 +24,7 @@ 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; -- 2.47.3