Blame SOURCES/elinks-0.12pre6-ssl-hostname.patch

d12e1a
From 30d96f81dbefffd3f1523256cc5a5328ea1c7ecb Mon Sep 17 00:00:00 2001
d12e1a
From: Kalle Olavi Niemitalo <kon@iki.fi>
d12e1a
Date: Mon, 2 May 2011 14:41:40 +0300
d12e1a
Subject: [PATCH 1/4] 1024: Use RFC 3546 server_name TLS extension
d12e1a
d12e1a
For both GnuTLS and OpenSSL.  Not tested with nss-compat-openssl.
d12e1a
d12e1a
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
d12e1a
---
d12e1a
 src/network/ssl/socket.c | 19 ++++++++++++++++++-
d12e1a
 src/network/ssl/ssl.c    | 29 ++++++++++++++++++++++++-----
d12e1a
 src/network/ssl/ssl.h    | 14 ++++++++++++--
d12e1a
 3 files changed, 54 insertions(+), 8 deletions(-)
d12e1a
d12e1a
diff --git a/src/network/ssl/socket.c b/src/network/ssl/socket.c
d12e1a
index 45b4b4a..dc682d0 100644
d12e1a
--- a/src/network/ssl/socket.c
d12e1a
+++ b/src/network/ssl/socket.c
d12e1a
@@ -22,6 +22,7 @@
d12e1a
 #include "network/socket.h"
d12e1a
 #include "network/ssl/socket.h"
d12e1a
 #include "network/ssl/ssl.h"
d12e1a
+#include "protocol/uri.h"
d12e1a
 #include "util/memory.h"
d12e1a
 
d12e1a
 
d12e1a
@@ -117,12 +118,28 @@ int
d12e1a
 ssl_connect(struct socket *socket)
d12e1a
 {
d12e1a
 	int ret;
d12e1a
+	unsigned char *server_name;
d12e1a
+	struct connection *conn = socket->conn;
d12e1a
 
d12e1a
-	if (init_ssl_connection(socket) == S_SSL_ERROR) {
d12e1a
+	/* TODO: Recode server_name to UTF-8.  */
d12e1a
+	server_name = get_uri_string(conn->proxied_uri, URI_HOST);
d12e1a
+	if (!server_name) {
d12e1a
+		socket->ops->done(socket, connection_state(S_OUT_OF_MEM));
d12e1a
+		return -1;
d12e1a
+	}
d12e1a
+
d12e1a
+	/* RFC 3546 says literal IPv4 and IPv6 addresses are not allowed.  */
d12e1a
+	if (is_ip_address(server_name, strlen(server_name)))
d12e1a
+		mem_free_set(&server_name, NULL);
d12e1a
+
d12e1a
+	if (init_ssl_connection(socket, server_name) == S_SSL_ERROR) {
d12e1a
+		mem_free_if(server_name);
d12e1a
 		socket->ops->done(socket, connection_state(S_SSL_ERROR));
d12e1a
 		return -1;
d12e1a
 	}
d12e1a
 
d12e1a
+	mem_free_if(server_name);
d12e1a
+
d12e1a
 	if (socket->no_tls)
d12e1a
 		ssl_set_no_tls(socket);
d12e1a
 
d12e1a
diff --git a/src/network/ssl/ssl.c b/src/network/ssl/ssl.c
d12e1a
index 685c31e..7767a71 100644
d12e1a
--- a/src/network/ssl/ssl.c
d12e1a
+++ b/src/network/ssl/ssl.c
d12e1a
@@ -212,13 +212,26 @@ struct module ssl_module = struct_module(
d12e1a
 );
d12e1a
 
d12e1a
 int
d12e1a
-init_ssl_connection(struct socket *socket)
d12e1a
+init_ssl_connection(struct socket *socket,
d12e1a
+		    const unsigned char *server_name)
d12e1a
 {
d12e1a
 #ifdef CONFIG_OPENSSL
d12e1a
 	socket->ssl = SSL_new(context);
d12e1a
 	if (!socket->ssl) return S_SSL_ERROR;
d12e1a
+
d12e1a
+	/* If the server name is known, pass it to OpenSSL.
d12e1a
+	 *
d12e1a
+	 * The return value of SSL_set_tlsext_host_name is not
d12e1a
+	 * documented.  The source shows that it returns 1 if
d12e1a
+	 * successful; on error, it calls SSLerr and returns 0.  */
d12e1a
+	if (server_name
d12e1a
+	    && !SSL_set_tlsext_host_name(socket->ssl, server_name)) {
d12e1a
+		SSL_free(socket->ssl);
d12e1a
+		socket->ssl = NULL;
d12e1a
+		return S_SSL_ERROR;
d12e1a
+	}
d12e1a
+
d12e1a
 #elif defined(CONFIG_GNUTLS)
d12e1a
-	/* const unsigned char server_name[] = "localhost"; */
d12e1a
 	ssl_t *state = mem_alloc(sizeof(ssl_t));
d12e1a
 
d12e1a
 	if (!state) return S_SSL_ERROR;
d12e1a
@@ -255,9 +268,15 @@ init_ssl_connection(struct socket *socket)
d12e1a
 	/* gnutls_handshake_set_private_extensions(*state, 1); */
d12e1a
 	gnutls_cipher_set_priority(*state, cipher_priority);
d12e1a
 	gnutls_kx_set_priority(*state, kx_priority);
d12e1a
-	/* gnutls_certificate_type_set_priority(*state, cert_type_priority);
d12e1a
-	gnutls_server_name_set(*state, GNUTLS_NAME_DNS, server_name,
d12e1a
-			       sizeof(server_name) - 1); */
d12e1a
+	/* gnutls_certificate_type_set_priority(*state, cert_type_priority); */
d12e1a
+
d12e1a
+	if (server_name
d12e1a
+	    && gnutls_server_name_set(*state, GNUTLS_NAME_DNS, server_name,
d12e1a
+				      strlen(server_name))) {
d12e1a
+		gnutls_deinit(*state);
d12e1a
+		mem_free(state);
d12e1a
+		return S_SSL_ERROR;
d12e1a
+	}
d12e1a
 
d12e1a
 	socket->ssl = state;
d12e1a
 #endif
d12e1a
diff --git a/src/network/ssl/ssl.h b/src/network/ssl/ssl.h
d12e1a
index 7c54a7a..bfd94e1 100644
d12e1a
--- a/src/network/ssl/ssl.h
d12e1a
+++ b/src/network/ssl/ssl.h
d12e1a
@@ -11,8 +11,18 @@ struct socket;
d12e1a
 extern struct module ssl_module;
d12e1a
 
d12e1a
 /* Initializes the SSL connection data. Returns S_OK on success and S_SSL_ERROR
d12e1a
- * on failure. */
d12e1a
-int init_ssl_connection(struct socket *socket);
d12e1a
+ * on failure.
d12e1a
+ *
d12e1a
+ * server_name is the DNS name of the server (in UTF-8), or NULL if
d12e1a
+ * ELinks knows only the IP address.  ELinks reports that name to the
d12e1a
+ * server so that the server can choose the correct certificate if it
d12e1a
+ * has multiple virtual hosts on the same IP address.  See RFC 3546
d12e1a
+ * section 3.1.
d12e1a
+ *
d12e1a
+ * server_name does not affect how ELinks verifies the certificate
d12e1a
+ * after the server has returned it.  */
d12e1a
+int init_ssl_connection(struct socket *socket,
d12e1a
+			const unsigned char *server_name);
d12e1a
 
d12e1a
 /* Releases the SSL connection data */
d12e1a
 void done_ssl_connection(struct socket *socket);
d12e1a
-- 
d12e1a
2.1.0
d12e1a
d12e1a
d12e1a
From e7484a980572b665747c28aa1376e29a12fb4b19 Mon Sep 17 00:00:00 2001
d12e1a
From: Kalle Olavi Niemitalo <kon@iki.fi>
d12e1a
Date: Tue, 3 May 2011 03:52:21 +0300
d12e1a
Subject: [PATCH 2/4] 1024: Verify server certificate hostname with OpenSSL
d12e1a
d12e1a
Not tested with nss-compat-ossl.
d12e1a
d12e1a
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
d12e1a
---
d12e1a
 src/network/ssl/Makefile                   |   7 +-
d12e1a
 src/network/ssl/match-hostname.c           | 125 +++++++++++++++++
d12e1a
 src/network/ssl/match-hostname.h           |  10 ++
d12e1a
 src/network/ssl/socket.c                   | 211 ++++++++++++++++++++++++++++-
d12e1a
 src/network/ssl/ssl.c                      |  41 +++++-
d12e1a
 src/network/ssl/ssl.h                      |   3 +
d12e1a
 src/network/ssl/test/Makefile              |   9 ++
d12e1a
 src/network/ssl/test/match-hostname-test.c | 123 +++++++++++++++++
d12e1a
 src/network/ssl/test/test-match-hostname   |   3 +
d12e1a
 9 files changed, 529 insertions(+), 3 deletions(-)
d12e1a
 create mode 100644 src/network/ssl/match-hostname.c
d12e1a
 create mode 100644 src/network/ssl/match-hostname.h
d12e1a
 create mode 100644 src/network/ssl/test/Makefile
d12e1a
 create mode 100644 src/network/ssl/test/match-hostname-test.c
d12e1a
 create mode 100755 src/network/ssl/test/test-match-hostname
d12e1a
d12e1a
diff --git a/src/network/ssl/Makefile b/src/network/ssl/Makefile
d12e1a
index 26f02c2..6f265da 100644
d12e1a
--- a/src/network/ssl/Makefile
d12e1a
+++ b/src/network/ssl/Makefile
d12e1a
@@ -3,6 +3,11 @@ include $(top_builddir)/Makefile.config
d12e1a
 
d12e1a
 INCLUDES += $(GNUTLS_CFLAGS) $(OPENSSL_CFLAGS)
d12e1a
 
d12e1a
-OBJS = ssl.o socket.o
d12e1a
+SUBDIRS = test
d12e1a
+
d12e1a
+# ELinks uses match-hostname.o only if CONFIG_OPENSSL.
d12e1a
+# However, match-hostname.o has test cases that always need it.
d12e1a
+# The test framework doesn't seem to support conditional tests.
d12e1a
+OBJS = match-hostname.o ssl.o socket.o
d12e1a
 
d12e1a
 include $(top_srcdir)/Makefile.lib
d12e1a
diff --git a/src/network/ssl/match-hostname.c b/src/network/ssl/match-hostname.c
d12e1a
new file mode 100644
d12e1a
index 0000000..9a64bb4
d12e1a
--- /dev/null
d12e1a
+++ b/src/network/ssl/match-hostname.c
d12e1a
@@ -0,0 +1,125 @@
d12e1a
+/* Matching a host name to wildcards in SSL certificates */
d12e1a
+
d12e1a
+#ifdef HAVE_CONFIG_H
d12e1a
+#include "config.h"
d12e1a
+#endif
d12e1a
+
d12e1a
+#include "elinks.h"
d12e1a
+
d12e1a
+#include "intl/charsets.h"
d12e1a
+#include "network/ssl/match-hostname.h"
d12e1a
+#include "util/conv.h"
d12e1a
+#include "util/error.h"
d12e1a
+#include "util/string.h"
d12e1a
+
d12e1a
+/** Checks whether a host name matches a pattern that may contain
d12e1a
+ * wildcards.
d12e1a
+ *
d12e1a
+ * @param[in] hostname
d12e1a
+ *   The host name to which the user wanted to connect.
d12e1a
+ *   Should be in UTF-8 and need not be null-terminated.
d12e1a
+ * @param[in] hostname_length
d12e1a
+ *   The length of @a hostname, in bytes.
d12e1a
+ * @param[in] pattern
d12e1a
+ *   A pattern that the host name might match.
d12e1a
+ *   Should be in UTF-8 and need not be null-terminated.
d12e1a
+ *   The pattern may contain wildcards, as specified in
d12e1a
+ *   RFC 2818 section 3.1.
d12e1a
+ * @param[in] pattern_length
d12e1a
+ *   The length of @a pattern, in bytes.
d12e1a
+ *
d12e1a
+ * @return
d12e1a
+ *   Nonzero if the host name matches.  Zero if it doesn't.
d12e1a
+ *
d12e1a
+ * According to RFC 2818 section 3.1, '*' matches any number of
d12e1a
+ * characters except '.'.  For example, "*r*.example.org" matches
d12e1a
+ * "random.example.org" or "history.example.org" but not
d12e1a
+ * "frozen.fruit.example.org".
d12e1a
+ *
d12e1a
+ * This function does not allocate memory, and consumes at most
d12e1a
+ * O(@a hostname_length * @a pattern_length) time.  */
d12e1a
+int
d12e1a
+match_hostname_pattern(const unsigned char *hostname,
d12e1a
+		       size_t hostname_length,
d12e1a
+		       const unsigned char *pattern,
d12e1a
+		       size_t pattern_length)
d12e1a
+{
d12e1a
+	const unsigned char *const hostname_end = hostname + hostname_length;
d12e1a
+	const unsigned char *const pattern_end = pattern + pattern_length;
d12e1a
+
d12e1a
+	assert(hostname <= hostname_end);
d12e1a
+	assert(pattern <= pattern_end);
d12e1a
+	if_assert_failed return 0;
d12e1a
+
d12e1a
+	while (pattern < pattern_end) {
d12e1a
+		if (*pattern == '*') {
d12e1a
+			const unsigned char *next_wildcard;
d12e1a
+			size_t literal_length;
d12e1a
+
d12e1a
+			++pattern;
d12e1a
+			next_wildcard = memchr(pattern, '*',
d12e1a
+					       pattern_end - pattern);
d12e1a
+			if (next_wildcard == NULL)
d12e1a
+				literal_length = pattern_end - pattern;
d12e1a
+			else
d12e1a
+				literal_length = next_wildcard - pattern;
d12e1a
+
d12e1a
+			for (;;) {
d12e1a
+				size_t hostname_left = hostname_end - hostname;
d12e1a
+				unicode_val_T uni;
d12e1a
+
d12e1a
+				if (hostname_left < literal_length)
d12e1a
+					return 0;
d12e1a
+
d12e1a
+				/* If next_wildcard == NULL, then the
d12e1a
+				 * literal string is at the end of the
d12e1a
+				 * pattern, so anchor the match to the
d12e1a
+				 * end of the hostname.  The end of
d12e1a
+				 * this function can then easily
d12e1a
+				 * verify that the whole hostname was
d12e1a
+				 * matched.
d12e1a
+				 *
d12e1a
+				 * But do not jump directly there;
d12e1a
+				 * first verify that there are no '.'
d12e1a
+				 * characters in between.  */
d12e1a
+				if ((next_wildcard != NULL
d12e1a
+				     || hostname_left == literal_length)
d12e1a
+				    && !c_strlcasecmp(pattern, literal_length,
d12e1a
+						      hostname, literal_length))
d12e1a
+					break;
d12e1a
+
d12e1a
+				/* The literal string doesn't match here.
d12e1a
+				 * Skip one character of the hostname and
d12e1a
+				 * retry.  If the skipped character is '.'
d12e1a
+				 * or one of the equivalent characters
d12e1a
+				 * listed in RFC 3490 section 3.1
d12e1a
+				 * requirement 1, then return 0, because
d12e1a
+				 * '*' must not match such characters.
d12e1a
+				 * Do the same if invalid UTF-8 is found.
d12e1a
+				 * Cast away const.  */
d12e1a
+				uni = utf8_to_unicode((unsigned char **) hostname,
d12e1a
+						      hostname_end);
d12e1a
+				if (uni == 0x002E
d12e1a
+				    || uni == 0x3002
d12e1a
+				    || uni == 0xFF0E
d12e1a
+				    || uni == 0xFF61
d12e1a
+				    || uni == UCS_NO_CHAR)
d12e1a
+					return 0;
d12e1a
+			}
d12e1a
+
d12e1a
+			pattern += literal_length;
d12e1a
+			hostname += literal_length;
d12e1a
+		} else {
d12e1a
+			if (hostname == hostname_end)
d12e1a
+				return 0;
d12e1a
+
d12e1a
+			if (c_toupper(*pattern) != c_toupper(*hostname))
d12e1a
+				return 0;
d12e1a
+
d12e1a
+			++pattern;
d12e1a
+			++hostname;
d12e1a
+		}
d12e1a
+	}
d12e1a
+
d12e1a
+	return hostname == hostname_end;
d12e1a
+}
d12e1a
diff --git a/src/network/ssl/match-hostname.h b/src/network/ssl/match-hostname.h
d12e1a
new file mode 100644
d12e1a
index 0000000..60d32b2
d12e1a
--- /dev/null
d12e1a
+++ b/src/network/ssl/match-hostname.h
d12e1a
@@ -0,0 +1,10 @@
d12e1a
+
d12e1a
+#ifndef EL__NETWORK_SSL_MATCH_HOSTNAME_H
d12e1a
+#define EL__NETWORK_SSL_MATCH_HOSTNAME_H
d12e1a
+
d12e1a
+int match_hostname_pattern(const unsigned char *hostname,
d12e1a
+			   size_t hostname_length,
d12e1a
+			   const unsigned char *pattern,
d12e1a
+			   size_t pattern_length);
d12e1a
+
d12e1a
+#endif
d12e1a
diff --git a/src/network/ssl/socket.c b/src/network/ssl/socket.c
d12e1a
index dc682d0..a67bbde 100644
d12e1a
--- a/src/network/ssl/socket.c
d12e1a
+++ b/src/network/ssl/socket.c
d12e1a
@@ -6,13 +6,24 @@
d12e1a
 
d12e1a
 #ifdef CONFIG_OPENSSL
d12e1a
 #include <openssl/ssl.h>
d12e1a
+#include <openssl/x509v3.h>
d12e1a
+#define USE_OPENSSL
d12e1a
+#elif defined(CONFIG_NSS_COMPAT_OSSL)
d12e1a
+#include <nss_compat_ossl/nss_compat_ossl.h>
d12e1a
+#define USE_OPENSSL
d12e1a
 #elif defined(CONFIG_GNUTLS)
d12e1a
 #include <gnutls/gnutls.h>
d12e1a
 #else
d12e1a
 #error "Huh?! You have SSL enabled, but not OPENSSL nor GNUTLS!! And then you want exactly *what* from me?"
d12e1a
 #endif
d12e1a
 
d12e1a
+#ifdef HAVE_ARPA_INET_H
d12e1a
+#include <arpa/inet.h>
d12e1a
+#endif
d12e1a
 #include <errno.h>
d12e1a
+#ifdef HAVE_NETINET_IN_H
d12e1a
+#include <netinet/in.h>
d12e1a
+#endif
d12e1a
 
d12e1a
 #include "elinks.h"
d12e1a
 
d12e1a
@@ -20,6 +31,7 @@
d12e1a
 #include "main/select.h"
d12e1a
 #include "network/connection.h"
d12e1a
 #include "network/socket.h"
d12e1a
+#include "network/ssl/match-hostname.h"
d12e1a
 #include "network/ssl/socket.h"
d12e1a
 #include "network/ssl/ssl.h"
d12e1a
 #include "protocol/uri.h"
d12e1a
@@ -83,6 +95,203 @@ ssl_set_no_tls(struct socket *socket)
d12e1a
 #endif
d12e1a
 }
d12e1a
 
d12e1a
+#ifdef USE_OPENSSL
d12e1a
+
d12e1a
+/** Checks whether the host component of a URI matches a host name in
d12e1a
+ * the server certificate.
d12e1a
+ *
d12e1a
+ * @param[in] uri_host
d12e1a
+ *   The host name (or IP address) to which the user wanted to connect.
d12e1a
+ *   Should be in UTF-8.
d12e1a
+ * @param[in] cert_host_asn1
d12e1a
+ *   A host name found in the server certificate: either as commonName
d12e1a
+ *   in the subject field, or as a dNSName in the subjectAltName
d12e1a
+ *   extension.  This may contain wildcards, as specified in RFC 2818
d12e1a
+ *   section 3.1.
d12e1a
+ *
d12e1a
+ * @return
d12e1a
+ *   Nonzero if the host matches.  Zero if it doesn't, or on error.
d12e1a
+ *
d12e1a
+ * If @a uri_host is an IP address literal rather than a host name,
d12e1a
+ * then this function returns 0, meaning that the host name does not match.
d12e1a
+ * According to RFC 2818, if the certificate is intended to match an
d12e1a
+ * IP address, then it must have that IP address as an iPAddress
d12e1a
+ * SubjectAltName, rather than in commonName.  For comparing those,
d12e1a
+ * match_uri_host_ip() must be used instead of this function.  */
d12e1a
+static int
d12e1a
+match_uri_host_name(const unsigned char *uri_host,
d12e1a
+		    ASN1_STRING *cert_host_asn1)
d12e1a
+{
d12e1a
+	const size_t uri_host_len = strlen(uri_host);
d12e1a
+	unsigned char *cert_host = NULL;
d12e1a
+	int cert_host_len;
d12e1a
+	int matched = 0;
d12e1a
+
d12e1a
+	if (is_ip_address(uri_host, uri_host_len))
d12e1a
+		goto mismatch;
d12e1a
+
d12e1a
+	/* This function is used for both dNSName and commonName.
d12e1a
+	 * Although dNSName is always an IA5 string, commonName allows
d12e1a
+	 * many other encodings too.  Normalize all to UTF-8.  */
d12e1a
+	cert_host_len = ASN1_STRING_to_UTF8(&cert_host,
d12e1a
+					    cert_host_asn1);
d12e1a
+	if (cert_host_len < 0)
d12e1a
+		goto mismatch;
d12e1a
+
d12e1a
+	matched = match_hostname_pattern(uri_host, uri_host_len,
d12e1a
+					 cert_host, cert_host_len);
d12e1a
+
d12e1a
+mismatch:
d12e1a
+	if (cert_host)
d12e1a
+		OPENSSL_free(cert_host);
d12e1a
+	return matched;
d12e1a
+}
d12e1a
+
d12e1a
+/** Checks whether the host component of a URI matches an IP address
d12e1a
+ * in the server certificate.
d12e1a
+ *
d12e1a
+ * @param[in] uri_host
d12e1a
+ *   The IP address (or host name) to which the user wanted to connect.
d12e1a
+ *   Should be in UTF-8.
d12e1a
+ * @param[in] cert_host_asn1
d12e1a
+ *   An IP address found as iPAddress in the subjectAltName extension
d12e1a
+ *   of the server certificate.  According to RFC 5280 section 4.2.1.6,
d12e1a
+ *   that is an octet string in network byte order.  According to
d12e1a
+ *   RFC 2818 section 3.1, wildcards are not allowed.
d12e1a
+ *
d12e1a
+ * @return
d12e1a
+ *   Nonzero if the host matches.  Zero if it doesn't, or on error.
d12e1a
+ *
d12e1a
+ * If @a uri_host is a host name rather than an IP address literal,
d12e1a
+ * then this function returns 0, meaning that the address does not match.
d12e1a
+ * This function does not try to resolve the host name to an IP address
d12e1a
+ * and compare that to @a cert_host_asn1, because such an approach would
d12e1a
+ * be vulnerable to DNS spoofing.
d12e1a
+ *
d12e1a
+ * This function does not support the address-and-netmask format used
d12e1a
+ * in the name constraints extension of a CA certificate (RFC 5280
d12e1a
+ * section 4.2.1.10).  */
d12e1a
+static int
d12e1a
+match_uri_host_ip(const unsigned char *uri_host,
d12e1a
+		  ASN1_OCTET_STRING *cert_host_asn1)
d12e1a
+{
d12e1a
+	const unsigned char *cert_host_addr = ASN1_STRING_data(cert_host_asn1);
d12e1a
+	struct in_addr uri_host_in;
d12e1a
+#ifdef CONFIG_IPV6
d12e1a
+	struct in6_addr uri_host_in6;
d12e1a
+#endif
d12e1a
+
d12e1a
+	/* RFC 5280 defines the iPAddress alternative of GeneralName
d12e1a
+	 * as an OCTET STRING.  Verify that the type is indeed that.
d12e1a
+	 * This is an assertion because, if someone puts the wrong
d12e1a
+	 * type of data there, then it will not even be recognized as
d12e1a
+	 * an iPAddress, and this function will not be called.
d12e1a
+	 *
d12e1a
+	 * (Because GeneralName is defined in an implicitly tagged
d12e1a
+	 * ASN.1 module, the OCTET STRING tag is not part of the DER
d12e1a
+	 * encoding.  BER also allows a constructed encoding where
d12e1a
+	 * each substring begins with the OCTET STRING tag; but ITU-T
d12e1a
+	 * Rec. X.690 (07/2002) subclause 8.21 says those would be
d12e1a
+	 * OCTET STRING even if the outer string were of some other
d12e1a
+	 * type.  "A Layman's Guide to a Subset of ASN.1, BER, and
d12e1a
+	 * DER" (Kaliski, 1993) claims otherwise, though.)  */
d12e1a
+	assert(ASN1_STRING_type(cert_host_asn1) == V_ASN1_OCTET_STRING);
d12e1a
+	if_assert_failed return 0;
d12e1a
+
d12e1a
+	/* cert_host_addr, url_host_in, and url_host_in6 are all in
d12e1a
+	 * network byte order.  */
d12e1a
+	switch (ASN1_STRING_length(cert_host_asn1)) {
d12e1a
+	case 4:
d12e1a
+		return inet_aton(uri_host, &uri_host_in) != 0
d12e1a
+		    && memcmp(cert_host_addr, &uri_host_in.s_addr, 4) == 0;
d12e1a
+
d12e1a
+#ifdef CONFIG_IPV6
d12e1a
+	case 16:
d12e1a
+		return inet_pton(AF_INET6, uri_host, &uri_host_in6) == 1
d12e1a
+		    && memcmp(cert_host_addr, &uri_host_in6.s6_addr, 16) == 0;
d12e1a
+#endif
d12e1a
+
d12e1a
+	default:
d12e1a
+		return 0;
d12e1a
+	}
d12e1a
+}
d12e1a
+
d12e1a
+/** Verify one certificate in the server certificate chain.
d12e1a
+ * This callback is documented in SSL_set_verify(3).  */
d12e1a
+static int
d12e1a
+verify_callback(int preverify_ok, X509_STORE_CTX *ctx)
d12e1a
+{
d12e1a
+	X509 *cert;
d12e1a
+	SSL *ssl;
d12e1a
+	struct socket *socket;
d12e1a
+	struct connection *conn;
d12e1a
+	unsigned char *host_in_uri;
d12e1a
+	GENERAL_NAMES *alts;
d12e1a
+	int saw_dns_name = 0;
d12e1a
+	int matched = 0;
d12e1a
+
d12e1a
+	/* If OpenSSL already found a problem, keep that.  */
d12e1a
+	if (!preverify_ok)
d12e1a
+		return 0;
d12e1a
+
d12e1a
+	/* Examine only the server certificate, not CA certificates.  */
d12e1a
+	if (X509_STORE_CTX_get_error_depth(ctx) != 0)
d12e1a
+		return preverify_ok;
d12e1a
+
d12e1a
+	cert = X509_STORE_CTX_get_current_cert(ctx);
d12e1a
+	ssl = X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
d12e1a
+	socket = SSL_get_ex_data(ssl, socket_SSL_ex_data_idx);
d12e1a
+	conn = socket->conn;
d12e1a
+	host_in_uri = get_uri_string(conn->uri, URI_HOST | URI_IDN);
d12e1a
+	if (!host_in_uri)
d12e1a
+		return 0;
d12e1a
+
d12e1a
+	/* RFC 5280 section 4.2.1.6 describes the subjectAltName extension.
d12e1a
+	 * RFC 2818 section 3.1 says Common Name must not be used
d12e1a
+	 * if dNSName is present.  */
d12e1a
+	alts = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL);
d12e1a
+	if (alts != NULL) {
d12e1a
+		int alt_count;
d12e1a
+		int alt_pos;
d12e1a
+		GENERAL_NAME *alt;
d12e1a
+
d12e1a
+		alt_count = sk_GENERAL_NAME_num(alts);
d12e1a
+		for (alt_pos = 0; !matched && alt_pos < alt_count; ++alt_pos) {
d12e1a
+			alt = sk_GENERAL_NAME_value(alts, alt_pos);
d12e1a
+			if (alt->type == GEN_DNS) {
d12e1a
+				saw_dns_name = 1;
d12e1a
+				matched = match_uri_host_name(host_in_uri,
d12e1a
+							      alt->d.dNSName);
d12e1a
+			} else if (alt->type == GEN_IPADD) {
d12e1a
+				matched = match_uri_host_ip(host_in_uri,
d12e1a
+							    alt->d.iPAddress);
d12e1a
+			}
d12e1a
+		}
d12e1a
+
d12e1a
+		/* Free the GENERAL_NAMES list and each element.  */
d12e1a
+		sk_GENERAL_NAME_pop_free(alts, GENERAL_NAME_free);
d12e1a
+	}
d12e1a
+
d12e1a
+	if (!matched && !saw_dns_name) {
d12e1a
+		X509_NAME *name;
d12e1a
+		int cn_index;
d12e1a
+		X509_NAME_ENTRY *entry = NULL;
d12e1a
+
d12e1a
+		name = X509_get_subject_name(cert);
d12e1a
+		cn_index = X509_NAME_get_index_by_NID(name, NID_commonName, -1);
d12e1a
+		if (cn_index >= 0)
d12e1a
+			entry = X509_NAME_get_entry(name, cn_index);
d12e1a
+		if (entry != NULL)
d12e1a
+			matched = match_uri_host_name(host_in_uri,
d12e1a
+						      X509_NAME_ENTRY_get_data(entry));
d12e1a
+	}
d12e1a
+
d12e1a
+	mem_free(host_in_uri);
d12e1a
+	return matched;
d12e1a
+}
d12e1a
+
d12e1a
+#endif	/* USE_OPENSSL */
d12e1a
+
d12e1a
 static void
d12e1a
 ssl_want_read(struct socket *socket)
d12e1a
 {
d12e1a
@@ -149,7 +358,7 @@ ssl_connect(struct socket *socket)
d12e1a
 	if (get_opt_bool("connection.ssl.cert_verify"))
d12e1a
 		SSL_set_verify(socket->ssl, SSL_VERIFY_PEER
d12e1a
 					  | SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
d12e1a
-				NULL);
d12e1a
+			       verify_callback);
d12e1a
 
d12e1a
 	if (get_opt_bool("connection.ssl.client_cert.enable")) {
d12e1a
 		unsigned char *client_cert;
d12e1a
diff --git a/src/network/ssl/ssl.c b/src/network/ssl/ssl.c
d12e1a
index 7767a71..d1881c8 100644
d12e1a
--- a/src/network/ssl/ssl.c
d12e1a
+++ b/src/network/ssl/ssl.c
d12e1a
@@ -39,7 +39,35 @@
d12e1a
 #define	PATH_MAX	256 /* according to my /usr/include/bits/posix1_lim.h */
d12e1a
 #endif
d12e1a
 
d12e1a
-SSL_CTX *context = NULL;
d12e1a
+static SSL_CTX *context = NULL;
d12e1a
+int socket_SSL_ex_data_idx = -1;
d12e1a
+
d12e1a
+/** Prevent SSL_dup() if the SSL is associated with struct socket.
d12e1a
+ * We cannot copy struct socket and it doesn't have a reference count
d12e1a
+ * either.  */
d12e1a
+static int
d12e1a
+socket_SSL_ex_data_dup(CRYPTO_EX_DATA *to, CRYPTO_EX_DATA *from,
d12e1a
+		       void *from_d, int idx, long argl, void *argp)
d12e1a
+{
d12e1a
+	/* The documentation of from_d in RSA_get_ex_new_index(3)
d12e1a
+	 * is a bit unclear.  The caller does something like:
d12e1a
+	 *
d12e1a
+	 * void *data = CRYPTO_get_ex_data(from, idx);
d12e1a
+	 * socket_SSL_dup(to, from, &data, idx, argl, argp);
d12e1a
+	 * CRYPTO_set_ex_data(to, idx, data);
d12e1a
+	 *
d12e1a
+	 * i.e., from_d always points to a pointer, even though
d12e1a
+	 * it is just a void * in the prototype.  */
d12e1a
+	struct socket *socket = *(void **) from_d;
d12e1a
+
d12e1a
+	assert(idx == socket_SSL_ex_data_idx);
d12e1a
+	if_assert_failed return 0;
d12e1a
+
d12e1a
+	if (socket)
d12e1a
+		return 0;	/* prevent SSL_dup() */
d12e1a
+	else
d12e1a
+		return 1;	/* allow SSL_dup() */
d12e1a
+}
d12e1a
 
d12e1a
 static void
d12e1a
 init_openssl(struct module *module)
d12e1a
@@ -48,12 +76,17 @@ init_openssl(struct module *module)
d12e1a
 	context = SSL_CTX_new(SSLv23_client_method());
d12e1a
 	SSL_CTX_set_options(context, SSL_OP_ALL);
d12e1a
 	SSL_CTX_set_default_verify_paths(context);
d12e1a
+	socket_SSL_ex_data_idx = SSL_get_ex_new_index(0, NULL,
d12e1a
+						      NULL,
d12e1a
+						      socket_SSL_ex_data_dup,
d12e1a
+						      NULL);
d12e1a
 }
d12e1a
 
d12e1a
 static void
d12e1a
 done_openssl(struct module *module)
d12e1a
 {
d12e1a
 	if (context) SSL_CTX_free(context);
d12e1a
+	/* There is no function that undoes SSL_get_ex_new_index.  */
d12e1a
 }
d12e1a
 
d12e1a
 static union option_info openssl_options[] = {
d12e1a
@@ -219,6 +252,12 @@ init_ssl_connection(struct socket *socket,
d12e1a
 	socket->ssl = SSL_new(context);
d12e1a
 	if (!socket->ssl) return S_SSL_ERROR;
d12e1a
 
d12e1a
+	if (!SSL_set_ex_data(socket->ssl, socket_SSL_ex_data_idx, socket)) {
d12e1a
+		SSL_free(socket->ssl);
d12e1a
+		socket->ssl = NULL;
d12e1a
+		return S_SSL_ERROR;
d12e1a
+	}
d12e1a
+
d12e1a
 	/* If the server name is known, pass it to OpenSSL.
d12e1a
 	 *
d12e1a
 	 * The return value of SSL_set_tlsext_host_name is not
d12e1a
diff --git a/src/network/ssl/ssl.h b/src/network/ssl/ssl.h
d12e1a
index bfd94e1..480b4db 100644
d12e1a
--- a/src/network/ssl/ssl.h
d12e1a
+++ b/src/network/ssl/ssl.h
d12e1a
@@ -29,6 +29,9 @@ void done_ssl_connection(struct socket *socket);
d12e1a
 
d12e1a
 unsigned char *get_ssl_connection_cipher(struct socket *socket);
d12e1a
 
d12e1a
+#if defined(CONFIG_OPENSSL) || defined(CONFIG_NSS_COMPAT_OSSL)
d12e1a
+extern int socket_SSL_ex_data_idx;
d12e1a
+#endif
d12e1a
 
d12e1a
 /* Internal type used in ssl module. */
d12e1a
 
d12e1a
diff --git a/src/network/ssl/test/Makefile b/src/network/ssl/test/Makefile
d12e1a
new file mode 100644
d12e1a
index 0000000..f2196eb
d12e1a
--- /dev/null
d12e1a
+++ b/src/network/ssl/test/Makefile
d12e1a
@@ -0,0 +1,9 @@
d12e1a
+top_builddir=../../../..
d12e1a
+include $(top_builddir)/Makefile.config
d12e1a
+
d12e1a
+SUBDIRS =
d12e1a
+TEST_PROGS = match-hostname-test
d12e1a
+TESTDEPS += \
d12e1a
+ $(top_builddir)/src/network/ssl/match-hostname.o
d12e1a
+
d12e1a
+include $(top_srcdir)/Makefile.lib
d12e1a
diff --git a/src/network/ssl/test/match-hostname-test.c b/src/network/ssl/test/match-hostname-test.c
d12e1a
new file mode 100644
d12e1a
index 0000000..fbdf6fa
d12e1a
--- /dev/null
d12e1a
+++ b/src/network/ssl/test/match-hostname-test.c
d12e1a
@@ -0,0 +1,123 @@
d12e1a
+/* Test match_hostname_pattern() */
d12e1a
+
d12e1a
+#ifdef HAVE_CONFIG_H
d12e1a
+#include "config.h"
d12e1a
+#endif
d12e1a
+
d12e1a
+#include <stdio.h>
d12e1a
+#include <stdlib.h>
d12e1a
+
d12e1a
+#include "elinks.h"
d12e1a
+
d12e1a
+#include "network/ssl/match-hostname.h"
d12e1a
+#include "util/string.h"
d12e1a
+
d12e1a
+struct match_hostname_pattern_test_case
d12e1a
+{
d12e1a
+	const unsigned char *pattern;
d12e1a
+	const unsigned char *hostname;
d12e1a
+	int match;
d12e1a
+};
d12e1a
+
d12e1a
+static const struct match_hostname_pattern_test_case match_hostname_pattern_test_cases[] = {
d12e1a
+	{ "*r*.example.org", "random.example.org", 1 },
d12e1a
+	{ "*r*.example.org", "history.example.org", 1 },
d12e1a
+	{ "*r*.example.org", "frozen.fruit.example.org", 0 },
d12e1a
+	{ "*r*.example.org", "steamed.fruit.example.org", 0 },
d12e1a
+
d12e1a
+	{ "ABC.def.Ghi", "abc.DEF.gHI", 1 },
d12e1a
+
d12e1a
+	{ "*", "localhost", 1 },
d12e1a
+	{ "*", "example.org", 0 },
d12e1a
+	{ "*.*", "example.org", 1 },
d12e1a
+	{ "*.*.*", "www.example.org", 1 },
d12e1a
+	{ "*.*.*", "example.org", 0 },
d12e1a
+
d12e1a
+	{ "assign", "assignee", 0 },
d12e1a
+	{ "*peg", "arpeggiator", 0 },
d12e1a
+	{ "*peg*", "arpeggiator", 1 },
d12e1a
+	{ "*r*gi*", "arpeggiator", 1 },
d12e1a
+	{ "*r*git*", "arpeggiator", 0 },
d12e1a
+
d12e1a
+	{ NULL, NULL, 0 }
d12e1a
+};
d12e1a
+
d12e1a
+int
d12e1a
+main(void)
d12e1a
+{
d12e1a
+	const struct match_hostname_pattern_test_case *test;
d12e1a
+	int count_ok = 0;
d12e1a
+	int count_fail = 0;
d12e1a
+	struct string hostname_str = NULL_STRING;
d12e1a
+	struct string pattern_str = NULL_STRING;
d12e1a
+
d12e1a
+	if (!init_string(&hostname_str) || !init_string(&pattern_str)) {
d12e1a
+		fputs("Out of memory.\n", stderr);
d12e1a
+		done_string(&hostname_str);
d12e1a
+		done_string(&pattern_str);
d12e1a
+		return EXIT_FAILURE;
d12e1a
+	}
d12e1a
+
d12e1a
+	for (test = match_hostname_pattern_test_cases; test->pattern; test++) {
d12e1a
+		int match;
d12e1a
+
d12e1a
+		match = match_hostname_pattern(
d12e1a
+			test->hostname,
d12e1a
+			strlen(test->hostname),
d12e1a
+			test->pattern,
d12e1a
+			strlen(test->pattern));
d12e1a
+		if (!match == !test->match) {
d12e1a
+			/* Test OK */
d12e1a
+			count_ok++;
d12e1a
+		} else {
d12e1a
+			fprintf(stderr, "match_hostname_pattern() test failed\n"
d12e1a
+				"\tHostname: %s\n"
d12e1a
+				"\tPattern: %s\n"
d12e1a
+				"\tActual result: %d\n"
d12e1a
+				"\tCorrect result: %d\n",
d12e1a
+				test->hostname,
d12e1a
+				test->pattern,
d12e1a
+				match,
d12e1a
+				test->match);
d12e1a
+			count_fail++;
d12e1a
+		}
d12e1a
+
d12e1a
+		/* Try with strings that are not null-terminated.  */
d12e1a
+		hostname_str.length = 0;
d12e1a
+		add_to_string(&hostname_str, test->hostname);
d12e1a
+		add_to_string(&hostname_str, "ZZZ");
d12e1a
+		pattern_str.length = 0;
d12e1a
+		add_to_string(&pattern_str, test->pattern);
d12e1a
+		add_to_string(&hostname_str, "______");
d12e1a
+
d12e1a
+		match = match_hostname_pattern(
d12e1a
+			hostname_str.source,
d12e1a
+			strlen(test->hostname),
d12e1a
+			pattern_str.source,
d12e1a
+			strlen(test->pattern));
d12e1a
+		if (!match == !test->match) {
d12e1a
+			/* Test OK */
d12e1a
+			count_ok++;
d12e1a
+		} else {
d12e1a
+			fprintf(stderr, "match_hostname_pattern() test failed\n"
d12e1a
+				"\tVariant: Strings were not null-terminated.\n"
d12e1a
+				"\tHostname: %s\n"
d12e1a
+				"\tPattern: %s\n"
d12e1a
+				"\tActual result: %d\n"
d12e1a
+				"\tCorrect result: %d\n",
d12e1a
+				test->hostname,
d12e1a
+				test->pattern,
d12e1a
+				match,
d12e1a
+				test->match);
d12e1a
+			count_fail++;
d12e1a
+		}
d12e1a
+	}
d12e1a
+
d12e1a
+	printf("Summary of match_hostname_pattern() tests: %d OK, %d failed.\n",
d12e1a
+	       count_ok, count_fail);
d12e1a
+
d12e1a
+	done_string(&hostname_str);
d12e1a
+	done_string(&pattern_str);
d12e1a
+	return count_fail ? EXIT_FAILURE : EXIT_SUCCESS;
d12e1a
+
d12e1a
+}
d12e1a
diff --git a/src/network/ssl/test/test-match-hostname b/src/network/ssl/test/test-match-hostname
d12e1a
new file mode 100755
d12e1a
index 0000000..01d7173
d12e1a
--- /dev/null
d12e1a
+++ b/src/network/ssl/test/test-match-hostname
d12e1a
@@ -0,0 +1,3 @@
d12e1a
+#! /bin/sh -e
d12e1a
+
d12e1a
+./match-hostname-test
d12e1a
-- 
d12e1a
2.1.0
d12e1a
d12e1a
d12e1a
From 0cb6967bb9ccabc583bbdc6ee76baf4fdf0f90cc Mon Sep 17 00:00:00 2001
d12e1a
From: mancha <mancha@mac.hush.com>
d12e1a
Date: Sun, 15 Jul 2012 23:27:53 +0200
d12e1a
Subject: [PATCH 3/4] Fix hostname verification code.
d12e1a
d12e1a
[ From bug 1123 attachment 569.  --KON ]
d12e1a
d12e1a
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
d12e1a
---
d12e1a
 src/network/ssl/match-hostname.c | 2 +-
d12e1a
 1 file changed, 1 insertion(+), 1 deletion(-)
d12e1a
d12e1a
diff --git a/src/network/ssl/match-hostname.c b/src/network/ssl/match-hostname.c
d12e1a
index 9a64bb4..80d93b0 100644
d12e1a
--- a/src/network/ssl/match-hostname.c
d12e1a
+++ b/src/network/ssl/match-hostname.c
d12e1a
@@ -97,7 +97,7 @@ match_hostname_pattern(const unsigned char *hostname,
d12e1a
 				 * '*' must not match such characters.
d12e1a
 				 * Do the same if invalid UTF-8 is found.
d12e1a
 				 * Cast away const.  */
d12e1a
-				uni = utf8_to_unicode((unsigned char **) hostname,
d12e1a
+				uni = utf8_to_unicode((unsigned char **) &hostname,
d12e1a
 						      hostname_end);
d12e1a
 				if (uni == 0x002E
d12e1a
 				    || uni == 0x3002
d12e1a
-- 
d12e1a
2.1.0
d12e1a
d12e1a
d12e1a
From cf8586b0389911d944d767646d5a91c2e1bae86c Mon Sep 17 00:00:00 2001
d12e1a
From: Kamil Dudka <kdudka@redhat.com>
d12e1a
Date: Fri, 5 Jun 2015 17:08:46 +0200
d12e1a
Subject: [PATCH 4/4] ssl: use the OpenSSL-provided host name check
d12e1a
d12e1a
... if built against a new enough version of OpenSSL
d12e1a
d12e1a
Suggested-by: Christian Heimes
d12e1a
---
d12e1a
 configure.in             |  3 +++
d12e1a
 src/network/ssl/socket.c | 50 +++++++++++++++++++++++++++++++++++++++++++++++-
d12e1a
 2 files changed, 52 insertions(+), 1 deletion(-)
d12e1a
d12e1a
diff --git a/configure.in b/configure.in
d12e1a
index 91d0257..1d858bd 100644
d12e1a
--- a/configure.in
d12e1a
+++ b/configure.in
d12e1a
@@ -1044,6 +1044,9 @@ else
d12e1a
 fi
d12e1a
 
d12e1a
 AC_MSG_RESULT($cf_result)
d12e1a
+if test "$cf_result" = yes; then
d12e1a
+	AC_CHECK_FUNCS(X509_VERIFY_PARAM_set1_host)
d12e1a
+fi
d12e1a
 
d12e1a
 # ---- GNU TLS
d12e1a
 
d12e1a
diff --git a/src/network/ssl/socket.c b/src/network/ssl/socket.c
d12e1a
index a67bbde..c9e2be4 100644
d12e1a
--- a/src/network/ssl/socket.c
d12e1a
+++ b/src/network/ssl/socket.c
d12e1a
@@ -7,6 +7,9 @@
d12e1a
 #ifdef CONFIG_OPENSSL
d12e1a
 #include <openssl/ssl.h>
d12e1a
 #include <openssl/x509v3.h>
d12e1a
+#ifdef HAVE_X509_VERIFY_PARAM_SET1_HOST
d12e1a
+#include <openssl/x509_vfy.h>
d12e1a
+#endif
d12e1a
 #define USE_OPENSSL
d12e1a
 #elif defined(CONFIG_NSS_COMPAT_OSSL)
d12e1a
 #include <nss_compat_ossl/nss_compat_ossl.h>
d12e1a
@@ -97,6 +100,30 @@ ssl_set_no_tls(struct socket *socket)
d12e1a
 
d12e1a
 #ifdef USE_OPENSSL
d12e1a
 
d12e1a
+#ifdef HAVE_X509_VERIFY_PARAM_SET1_HOST
d12e1a
+/* activate the OpenSSL-provided host name check */
d12e1a
+static int
d12e1a
+ossl_set_hostname(void *ssl, unsigned char *server_name)
d12e1a
+{
d12e1a
+	int ret = -1;
d12e1a
+
d12e1a
+	X509_VERIFY_PARAM *vpm = X509_VERIFY_PARAM_new();
d12e1a
+	if (vpm) {
d12e1a
+		if (X509_VERIFY_PARAM_set1_host(vpm, (char *) server_name, 0)
d12e1a
+				&& SSL_set1_param(ssl, vpm))
d12e1a
+		{
d12e1a
+			/* successfully activated the OpenSSL host name check */
d12e1a
+			ret = 0;
d12e1a
+		}
d12e1a
+
d12e1a
+		X509_VERIFY_PARAM_free(vpm);
d12e1a
+	}
d12e1a
+
d12e1a
+	return ret;
d12e1a
+}
d12e1a
+
d12e1a
+#else /* HAVE_X509_VERIFY_PARAM_SET1_HOST */
d12e1a
+
d12e1a
 /** Checks whether the host component of a URI matches a host name in
d12e1a
  * the server certificate.
d12e1a
  *
d12e1a
@@ -289,6 +316,7 @@ verify_callback(int preverify_ok, X509_STORE_CTX *ctx)
d12e1a
 	mem_free(host_in_uri);
d12e1a
 	return matched;
d12e1a
 }
d12e1a
+#endif	/* HAVE_X509_VERIFY_PARAM_SET1_HOST */
d12e1a
 
d12e1a
 #endif	/* USE_OPENSSL */
d12e1a
 
d12e1a
@@ -329,6 +357,9 @@ ssl_connect(struct socket *socket)
d12e1a
 	int ret;
d12e1a
 	unsigned char *server_name;
d12e1a
 	struct connection *conn = socket->conn;
d12e1a
+#ifdef USE_OPENSSL
d12e1a
+	int (*verify_callback_ptr)(int, X509_STORE_CTX *);
d12e1a
+#endif	/* USE_OPENSSL */
d12e1a
 
d12e1a
 	/* TODO: Recode server_name to UTF-8.  */
d12e1a
 	server_name = get_uri_string(conn->proxied_uri, URI_HOST);
d12e1a
@@ -347,6 +378,23 @@ ssl_connect(struct socket *socket)
d12e1a
 		return -1;
d12e1a
 	}
d12e1a
 
d12e1a
+#ifdef USE_OPENSSL
d12e1a
+#ifdef HAVE_X509_VERIFY_PARAM_SET1_HOST
d12e1a
+	/* activate the OpenSSL-provided host name check */
d12e1a
+	if (ossl_set_hostname(socket->ssl, server_name)) {
d12e1a
+		mem_free_if(server_name);
d12e1a
+		socket->ops->done(socket, connection_state(S_SSL_ERROR));
d12e1a
+		return -1;
d12e1a
+	}
d12e1a
+
d12e1a
+	/* verify_callback() is not needed with X509_VERIFY_PARAM_set1_host() */
d12e1a
+	verify_callback_ptr = NULL;
d12e1a
+#else
d12e1a
+	/* use our own callback implementing the host name check */
d12e1a
+	verify_callback_ptr = verify_callback;
d12e1a
+#endif
d12e1a
+#endif	/* USE_OPENSSL */
d12e1a
+
d12e1a
 	mem_free_if(server_name);
d12e1a
 
d12e1a
 	if (socket->no_tls)
d12e1a
@@ -358,7 +406,7 @@ ssl_connect(struct socket *socket)
d12e1a
 	if (get_opt_bool("connection.ssl.cert_verify"))
d12e1a
 		SSL_set_verify(socket->ssl, SSL_VERIFY_PEER
d12e1a
 					  | SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
d12e1a
-			       verify_callback);
d12e1a
+			       verify_callback_ptr);
d12e1a
 
d12e1a
 	if (get_opt_bool("connection.ssl.client_cert.enable")) {
d12e1a
 		unsigned char *client_cert;
d12e1a
-- 
d12e1a
2.4.3
d12e1a