login proxy: Verify SSL certificate hostname when connecting to remote server.
authorTimo Sirainen <tss@iki.fi>
Wed Nov 16 23:31:46 2011 +0200 (2011-11-16)
changeset 129775e9eaf63a6b1
parent 12976 e4aa32586f17
child 12978 de8715e4d793
login proxy: Verify SSL certificate hostname when connecting to remote server.
src/login-common/login-proxy.c
src/login-common/ssl-proxy-openssl.c
src/login-common/ssl-proxy.c
src/login-common/ssl-proxy.h
     1.1 --- a/src/login-common/login-proxy.c	Wed Nov 16 22:59:36 2011 +0200
     1.2 +++ b/src/login-common/login-proxy.c	Wed Nov 16 23:31:46 2011 +0200
     1.3 @@ -505,18 +505,24 @@
     1.4  {
     1.5  	struct login_proxy *proxy = context;
     1.6  
     1.7 -	if ((proxy->ssl_flags & PROXY_SSL_FLAG_ANY_CERT) != 0 ||
     1.8 -	    ssl_proxy_has_valid_client_cert(proxy->ssl_server_proxy))
     1.9 +	if ((proxy->ssl_flags & PROXY_SSL_FLAG_ANY_CERT) != 0)
    1.10  		return 0;
    1.11  
    1.12 -	if (!ssl_proxy_has_broken_client_cert(proxy->ssl_server_proxy)) {
    1.13 +	if (ssl_proxy_has_broken_client_cert(proxy->ssl_server_proxy)) {
    1.14 +		client_log_err(proxy->client, t_strdup_printf(
    1.15 +			"proxy: Received invalid SSL certificate from %s:%u",
    1.16 +			proxy->host, proxy->port));
    1.17 +	} else if (!ssl_proxy_has_valid_client_cert(proxy->ssl_server_proxy)) {
    1.18  		client_log_err(proxy->client, t_strdup_printf(
    1.19  			"proxy: SSL certificate not received from %s:%u",
    1.20  			proxy->host, proxy->port));
    1.21 +	} else if (ssl_proxy_cert_match_name(proxy->ssl_server_proxy,
    1.22 +					     proxy->host) < 0) {
    1.23 +		client_log_err(proxy->client, t_strdup_printf(
    1.24 +			"proxy: hostname doesn't match SSL certificate at %s:%u",
    1.25 +			proxy->host, proxy->port));
    1.26  	} else {
    1.27 -		client_log_err(proxy->client, t_strdup_printf(
    1.28 -			"proxy: Received invalid SSL certificate from %s:%u",
    1.29 -			proxy->host, proxy->port));
    1.30 +		return 0;
    1.31  	}
    1.32  	proxy->disconnecting = TRUE;
    1.33  	return -1;
     2.1 --- a/src/login-common/ssl-proxy-openssl.c	Wed Nov 16 22:59:36 2011 +0200
     2.2 +++ b/src/login-common/ssl-proxy-openssl.c	Wed Nov 16 23:31:46 2011 +0200
     2.3 @@ -21,6 +21,7 @@
     2.4  
     2.5  #include <openssl/crypto.h>
     2.6  #include <openssl/x509.h>
     2.7 +#include <openssl/x509v3.h>
     2.8  #include <openssl/pem.h>
     2.9  #include <openssl/ssl.h>
    2.10  #include <openssl/err.h>
    2.11 @@ -661,6 +662,87 @@
    2.12  	return proxy->cert_received && proxy->cert_broken;
    2.13  }
    2.14  
    2.15 +static const char *asn1_string_to_c(ASN1_STRING *asn_str)
    2.16 +{
    2.17 +	const char *cstr;
    2.18 +	unsigned int len;
    2.19 +
    2.20 +	len = ASN1_STRING_length(asn_str);
    2.21 +	cstr = t_strndup(ASN1_STRING_data(asn_str), len);
    2.22 +	if (strlen(cstr) != len) {
    2.23 +		/* NULs in the name - could be some MITM attack.
    2.24 +		   never allow. */
    2.25 +		return "";
    2.26 +	}
    2.27 +	return cstr;
    2.28 +}
    2.29 +
    2.30 +static const char *get_general_dns_name(const GENERAL_NAME *name)
    2.31 +{
    2.32 +	if (ASN1_STRING_type(name->d.ia5) != V_ASN1_IA5STRING)
    2.33 +		return "";
    2.34 +
    2.35 +	return asn1_string_to_c(name->d.ia5);
    2.36 +}
    2.37 +
    2.38 +static const char *get_cname(X509 *cert)
    2.39 +{
    2.40 +	X509_NAME *name;
    2.41 +	X509_NAME_ENTRY *entry;
    2.42 +	ASN1_STRING *str;
    2.43 +	int cn_idx;
    2.44 +
    2.45 +	name = X509_get_subject_name(cert);
    2.46 +	if (name == NULL)
    2.47 +		return "";
    2.48 +	cn_idx = X509_NAME_get_index_by_NID(name, NID_commonName, -1);
    2.49 +	if (cn_idx == -1)
    2.50 +		return "";
    2.51 +	entry = X509_NAME_get_entry(name, cn_idx);
    2.52 +	i_assert(entry != NULL);
    2.53 +	str = X509_NAME_ENTRY_get_data(entry);
    2.54 +	i_assert(str != NULL);
    2.55 +	return asn1_string_to_c(str);
    2.56 +}
    2.57 +
    2.58 +static int openssl_cert_match_name(SSL *ssl, const char *verify_name)
    2.59 +{
    2.60 +	X509 *cert;
    2.61 +	STACK_OF(GENERAL_NAME) *gnames;
    2.62 +	const GENERAL_NAME *gn;
    2.63 +	const char *dnsname;
    2.64 +	bool dns_names = FALSE;
    2.65 +	unsigned int i, count;
    2.66 +
    2.67 +	cert = SSL_get_peer_certificate(ssl);
    2.68 +	i_assert(cert != NULL);
    2.69 +
    2.70 +	/* verify against SubjectAltNames */
    2.71 +	gnames = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL);
    2.72 +	count = gnames == NULL ? 0 : sk_GENERAL_NAME_num(gnames);
    2.73 +	for (i = 0; i < count; i++) {
    2.74 +		gn = sk_GENERAL_NAME_value(gnames, i);
    2.75 +		if (gn->type == GEN_DNS) {
    2.76 +			dns_names = TRUE;
    2.77 +			dnsname = get_general_dns_name(gn);
    2.78 +			if (strcmp(dnsname, verify_name) == 0)
    2.79 +				break;
    2.80 +		}
    2.81 +	}
    2.82 +	sk_GENERAL_NAME_pop_free(gnames, GENERAL_NAME_free);
    2.83 +	/* verify against CommonName only when there wasn't any DNS
    2.84 +	   SubjectAltNames */
    2.85 +	if (dns_names)
    2.86 +		return i < count ? 0 : -1;
    2.87 +
    2.88 +	return strcmp(get_cname(cert), verify_name) == 0 ? 0 : -1;
    2.89 +}
    2.90 +
    2.91 +int ssl_proxy_cert_match_name(struct ssl_proxy *proxy, const char *verify_name)
    2.92 +{
    2.93 +	return openssl_cert_match_name(proxy->ssl, verify_name);
    2.94 +}
    2.95 +
    2.96  const char *ssl_proxy_get_peer_name(struct ssl_proxy *proxy)
    2.97  {
    2.98  	X509 *x509;
     3.1 --- a/src/login-common/ssl-proxy.c	Wed Nov 16 22:59:36 2011 +0200
     3.2 +++ b/src/login-common/ssl-proxy.c	Wed Nov 16 23:31:46 2011 +0200
     3.3 @@ -46,6 +46,12 @@
     3.4  	return FALSE;
     3.5  }
     3.6  
     3.7 +int ssl_proxy_cert_match_name(struct ssl_proxy *proxy ATTR_UNUSED,
     3.8 +			      const char *verify_name ATTR_UNUSED)
     3.9 +{
    3.10 +	return -1;
    3.11 +}
    3.12 +
    3.13  const char *ssl_proxy_get_peer_name(struct ssl_proxy *proxy ATTR_UNUSED)
    3.14  {
    3.15  	return NULL;
     4.1 --- a/src/login-common/ssl-proxy.h	Wed Nov 16 22:59:36 2011 +0200
     4.2 +++ b/src/login-common/ssl-proxy.h	Wed Nov 16 23:31:46 2011 +0200
     4.3 @@ -24,6 +24,7 @@
     4.4  void ssl_proxy_set_client(struct ssl_proxy *proxy, struct client *client);
     4.5  bool ssl_proxy_has_valid_client_cert(const struct ssl_proxy *proxy) ATTR_PURE;
     4.6  bool ssl_proxy_has_broken_client_cert(struct ssl_proxy *proxy);
     4.7 +int ssl_proxy_cert_match_name(struct ssl_proxy *proxy, const char *verify_name);
     4.8  const char *ssl_proxy_get_peer_name(struct ssl_proxy *proxy);
     4.9  bool ssl_proxy_is_handshaked(const struct ssl_proxy *proxy) ATTR_PURE;
    4.10  const char *ssl_proxy_get_last_error(const struct ssl_proxy *proxy) ATTR_PURE;