dovecot-2.0: login proxy: Verify SSL certificate hostname when c...
dovecot at dovecot.org
dovecot at dovecot.org
Wed Nov 16 23:31:57 EET 2011
details: http://hg.dovecot.org/dovecot-2.0/rev/5e9eaf63a6b1
changeset: 12977:5e9eaf63a6b1
user: Timo Sirainen <tss at iki.fi>
date: Wed Nov 16 23:31:46 2011 +0200
description:
login proxy: Verify SSL certificate hostname when connecting to remote server.
diffstat:
src/login-common/login-proxy.c | 18 +++++--
src/login-common/ssl-proxy-openssl.c | 82 ++++++++++++++++++++++++++++++++++++
src/login-common/ssl-proxy.c | 6 ++
src/login-common/ssl-proxy.h | 1 +
4 files changed, 101 insertions(+), 6 deletions(-)
diffs (160 lines):
diff -r e4aa32586f17 -r 5e9eaf63a6b1 src/login-common/login-proxy.c
--- a/src/login-common/login-proxy.c Wed Nov 16 22:59:36 2011 +0200
+++ b/src/login-common/login-proxy.c Wed Nov 16 23:31:46 2011 +0200
@@ -505,18 +505,24 @@
{
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 if (ssl_proxy_cert_match_name(proxy->ssl_server_proxy,
+ proxy->host) < 0) {
+ client_log_err(proxy->client, t_strdup_printf(
+ "proxy: hostname doesn't match SSL certificate at %s:%u",
+ proxy->host, proxy->port));
} else {
- client_log_err(proxy->client, t_strdup_printf(
- "proxy: Received invalid SSL certificate from %s:%u",
- proxy->host, proxy->port));
+ return 0;
}
proxy->disconnecting = TRUE;
return -1;
diff -r e4aa32586f17 -r 5e9eaf63a6b1 src/login-common/ssl-proxy-openssl.c
--- a/src/login-common/ssl-proxy-openssl.c Wed Nov 16 22:59:36 2011 +0200
+++ b/src/login-common/ssl-proxy-openssl.c Wed Nov 16 23:31:46 2011 +0200
@@ -21,6 +21,7 @@
#include <openssl/crypto.h>
#include <openssl/x509.h>
+#include <openssl/x509v3.h>
#include <openssl/pem.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
@@ -661,6 +662,87 @@
return proxy->cert_received && proxy->cert_broken;
}
+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);
+}
+
+static 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;
+
+ 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)
+ return i < count ? 0 : -1;
+
+ return strcmp(get_cname(cert), verify_name) == 0 ? 0 : -1;
+}
+
+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 -r e4aa32586f17 -r 5e9eaf63a6b1 src/login-common/ssl-proxy.c
--- a/src/login-common/ssl-proxy.c Wed Nov 16 22:59:36 2011 +0200
+++ b/src/login-common/ssl-proxy.c Wed Nov 16 23:31:46 2011 +0200
@@ -46,6 +46,12 @@
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 -r e4aa32586f17 -r 5e9eaf63a6b1 src/login-common/ssl-proxy.h
--- a/src/login-common/ssl-proxy.h Wed Nov 16 22:59:36 2011 +0200
+++ b/src/login-common/ssl-proxy.h Wed Nov 16 23:31:46 2011 +0200
@@ -24,6 +24,7 @@
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;
More information about the dovecot-cvs
mailing list