PROTON-2397: make client TLS connection verification defaults consistent: verify peer certificate and name
diff --git a/c/include/proton/ssl.h b/c/include/proton/ssl.h
index 03acbf5..a84a9b5 100644
--- a/c/include/proton/ssl.h
+++ b/c/include/proton/ssl.h
@@ -195,7 +195,7 @@
* identity as contained in the certificate to be valid (see
* ::pn_ssl_set_peer_hostname).
*
- * ANONYMOUS_PEER is configured by default.
+ * VERIFY_PEER_NAME is configured by default.
*/
typedef enum {
PN_SSL_VERIFY_NULL = 0, /**< internal use only */
@@ -208,7 +208,8 @@
* Configure the level of verification used on the peer certificate.
*
* This method controls how the peer's certificate is validated, if at all. By default,
- * neither servers nor clients attempt to verify their peers (PN_SSL_ANONYMOUS_PEER).
+ * servers do not attempt to verify their peers (PN_SSL_ANONYMOUS_PEER) but
+ * clients attempt to verify both the certificate and peer name (PN_SSL_VERIFY_PEER_NAME).
* Once certificates and trusted CAs are configured, peer verification can be enabled.
*
* @note In order to verify a peer, a trusted CA must be configured. See
diff --git a/c/src/ssl/openssl.c b/c/src/ssl/openssl.c
index 3a98200..b4b7564 100644
--- a/c/src/ssl/openssl.c
+++ b/c/src/ssl/openssl.c
@@ -562,12 +562,6 @@
return NULL;
}
- // Insecure, but backward compatible client default
- if (mode==PN_SSL_MODE_CLIENT &&
- pn_ssl_domain_set_peer_authentication(domain, PN_SSL_ANONYMOUS_PEER, NULL)) {
- free(domain);
- return NULL;
- }
return domain;
}
diff --git a/c/src/ssl/schannel.cpp b/c/src/ssl/schannel.cpp
index 3056890..d167678 100644
--- a/c/src/ssl/schannel.cpp
+++ b/c/src/ssl/schannel.cpp
@@ -486,6 +486,8 @@
pn_ssl_domain_free(domain);
return NULL;
}
+ if (mode == PN_SSL_MODE_CLIENT)
+ domain->verify_mode == PN_SSL_VERIFY_PEER_NAME;
return domain;
}
diff --git a/c/tests/proactor_test.cpp b/c/tests/proactor_test.cpp
index e13fcf4..73e285e 100644
--- a/c/tests/proactor_test.cpp
+++ b/c/tests/proactor_test.cpp
@@ -533,9 +533,21 @@
pn_listener_t *l = p.listen(":0", &listener);
REQUIRE_RUN(p, PN_LISTENER_OPEN);
- /* Basic SSL connection */
+ /* Not Anonymous by default */
p.connect(l, &client);
- /* Open ok at both ends */
+ REQUIRE_RUN(p, PN_TRANSPORT_ERROR);
+ CHECK_THAT(*client.last_condition,
+ cond_matches("amqp:connection:framing-error", "SSL"));
+ REQUIRE_RUN(p, PN_TRANSPORT_CLOSED);
+ REQUIRE_RUN(p, PN_TRANSPORT_ERROR);
+ REQUIRE_RUN(p, PN_TRANSPORT_CLOSED);
+
+ /* Deliberate use of Anonymous */
+ pn_ssl_domain_t *cd = client.ssl_domain;
+ REQUIRE(0 == pn_ssl_domain_set_peer_authentication(
+ cd, PN_SSL_ANONYMOUS_PEER, NULL));
+ pn_connection_t *c = pn_connection();
+ p.connect(l, &client, c);
REQUIRE_RUN(p, PN_CONNECTION_REMOTE_OPEN);
REQUIRE_RUN(p, PN_CONNECTION_REMOTE_OPEN);
CHECK_THAT(*server.last_condition, cond_empty());
@@ -544,11 +556,10 @@
REQUIRE_RUN(p, PN_TRANSPORT_CLOSED);
/* Verify peer with good hostname */
- pn_ssl_domain_t *cd = client.ssl_domain;
REQUIRE(0 == pn_ssl_domain_set_trusted_ca_db(cd, CERTIFICATE("tserver")));
REQUIRE(0 == pn_ssl_domain_set_peer_authentication(
cd, PN_SSL_VERIFY_PEER_NAME, NULL));
- pn_connection_t *c = pn_connection();
+ c = pn_connection();
pn_connection_set_hostname(c, "test_server");
p.connect(l, &client, c);
REQUIRE_RUN(p, PN_CONNECTION_REMOTE_OPEN);
@@ -565,6 +576,22 @@
REQUIRE_RUN(p, PN_TRANSPORT_ERROR);
CHECK_THAT(*client.last_condition,
cond_matches("amqp:connection:framing-error", "SSL"));
+ REQUIRE_RUN(p, PN_TRANSPORT_CLOSED);
+ REQUIRE_RUN(p, PN_TRANSPORT_ERROR);
+ REQUIRE_RUN(p, PN_TRANSPORT_CLOSED);
+
+ /* Can ignore bad hostname */
+ REQUIRE(0 == pn_ssl_domain_set_peer_authentication(
+ cd, PN_SSL_VERIFY_PEER, NULL));
+ c = pn_connection();
+ pn_connection_set_hostname(c, "wrongname");
+ p.connect(l, &client, c);
+ REQUIRE_RUN(p, PN_CONNECTION_REMOTE_OPEN);
+ REQUIRE_RUN(p, PN_CONNECTION_REMOTE_OPEN);
+ CHECK_THAT(*server.last_condition, cond_empty());
+ CHECK_THAT(*client.last_condition, cond_empty());
+ REQUIRE_RUN(p, PN_TRANSPORT_CLOSED);
+ REQUIRE_RUN(p, PN_TRANSPORT_CLOSED);
}
TEST_CASE("proactor_addr") {
diff --git a/python/proton/_reactor.py b/python/proton/_reactor.py
index a56c57e..28d02c9 100644
--- a/python/proton/_reactor.py
+++ b/python/proton/_reactor.py
@@ -1199,9 +1199,10 @@
* ``ca``: Path to a database of trusted CAs that the server will advertise.
* ``cert``: Path to a file/database containing the identifying certificate.
* ``key``: An optional key to access the identifying certificate.
- * ``verify``: If ``True``, verify the peer name
- (:const:`proton.SSLDomain.VERIFY_PEER_NAME`) and certificate using the
- ``ca`` above.
+ * ``verify``: If ``False``, do not verify the peer name
+ (:const:`proton.SSLDomain.ANONYMOUS_PEER`) or certificate. By default
+ (or if ``True``) verify the peer name and certificate using the
+ ``ca`` above (:const:`proton.SSLDomain.VERIFY_PEER_NAME`).
:param url: URL string of process to connect to
:type url: ``str``
@@ -1302,10 +1303,11 @@
ca = tls_config.get('ca')
cert = tls_config.get('cert')
key = tls_config.get('key')
+ verify = tls_config.get('verify', True)
if ca:
_ssl_domain.set_trusted_ca_db(str(ca))
- if tls_config.get('verify', True):
- _ssl_domain.set_peer_authentication(SSLDomain.VERIFY_PEER_NAME, str(ca))
+ if not verify:
+ _ssl_domain.set_peer_authentication(SSLDomain.ANONYMOUS_PEER, None)
if cert and key:
_ssl_domain.set_credentials(str(cert), str(key), None)
diff --git a/python/proton/_transport.py b/python/proton/_transport.py
index 57f75e8..5764384 100644
--- a/python/proton/_transport.py
+++ b/python/proton/_transport.py
@@ -832,7 +832,8 @@
def set_peer_authentication(self, verify_mode, trusted_CAs=None):
"""
This method controls how the peer's certificate is validated, if at all. By default,
- neither servers nor clients attempt to verify their peers (PN_SSL_ANONYMOUS_PEER).
+ servers do not attempt to verify their peers (PN_SSL_ANONYMOUS_PEER) but
+ clients attempt to verify both the certificate and peer name (PN_SSL_VERIFY_PEER_NAME).
Once certificates and trusted CAs are configured, peer verification can be enabled.
.. note:: In order to verify a peer, a trusted CA must be configured. See
diff --git a/python/tests/proton_tests/connect.py b/python/tests/proton_tests/connect.py
index 024f69a..bc18885 100644
--- a/python/tests/proton_tests/connect.py
+++ b/python/tests/proton_tests/connect.py
@@ -68,10 +68,10 @@
class ConnectConfigTest(Test):
def test_port(self):
ensureCanTestExtendedSASL()
- server = Server()
+ server = Server(scheme='amqp')
container = Container(server)
client = Client()
- write_connect_conf({'port': server.port})
+ write_connect_conf({'port': server.port, 'scheme': 'amqp'})
container.connect(handler=client, reconnect=False)
container.run()
assert client.opened
@@ -82,8 +82,20 @@
password = 'password'
server = Server(user)
container = Container(server)
+ container.ssl.server.set_credentials(_testpath('server-certificate.pem'),
+ _testpath('server-private-key.pem'),
+ 'server-password')
client = Client()
- write_connect_conf({'port': server.port, 'user': user, 'password': password})
+ config = {
+ 'user': user,
+ 'password': password,
+ 'port': server.port,
+ 'tls': {
+ 'verify': False,
+ 'ca': _testpath('ca-certificate.pem')
+ }
+ }
+ write_connect_conf(config)
container.connect(handler=client, reconnect=False)
container.run()
assert client.opened
@@ -93,15 +105,16 @@
ensureCanTestExtendedSASL()
server = Server(scheme='amqps')
container = Container(server)
- container.ssl.server.set_credentials(_testpath('server-certificate.pem'),
- _testpath('server-private-key.pem'),
+ container.ssl.server.set_credentials(_testpath('server-certificate-lh.pem'),
+ _testpath('server-private-key-lh.pem'),
'server-password')
client = Client()
config = {
'scheme': 'amqps',
'port': server.port,
'tls': {
- 'verify': False
+ 'verify': True,
+ 'ca': _testpath('ca-certificate.pem')
}
}
write_connect_conf(config)
diff --git a/python/tests/proton_tests/sasl.py b/python/tests/proton_tests/sasl.py
index 55f83f5..c5a6e72 100644
--- a/python/tests/proton_tests/sasl.py
+++ b/python/tests/proton_tests/sasl.py
@@ -412,6 +412,8 @@
self.c1.password = 'password'
self.c1.hostname = 'localhost'
+ self.client_domain.set_peer_authentication(SSLDomain.ANONYMOUS_PEER)
+
ssl1 = _sslConnection(self.client_domain, self.t1, self.c1)
ssl2 = _sslConnection(self.server_domain, self.t2, self.c2)
@@ -431,6 +433,8 @@
self.c1.password = 'password'
self.c1.hostname = 'localhost'
+ self.client_domain.set_peer_authentication(SSLDomain.ANONYMOUS_PEER)
+
ssl1 = _sslConnection(self.client_domain, self.t1, self.c1)
ssl2 = _sslConnection(self.server_domain, self.t2, self.c2)
@@ -448,6 +452,8 @@
self.c1.password = 'password'
self.c1.hostname = 'localhost'
+ self.client_domain.set_peer_authentication(SSLDomain.ANONYMOUS_PEER)
+
ssl1 = _sslConnection(self.client_domain, self.t1, self.c1)
ssl2 = _sslConnection(self.server_domain, self.t2, self.c2)
diff --git a/python/tests/proton_tests/ssl.py b/python/tests/proton_tests/ssl.py
index 04dc4df..a2efeb2 100644
--- a/python/tests/proton_tests/ssl.py
+++ b/python/tests/proton_tests/ssl.py
@@ -97,12 +97,13 @@
server.connection.close()
self._pump(client, server)
- def test_defaults(self):
+ def test_anonymous_cipher(self):
if os.name == "nt":
raise Skipped("Windows SChannel lacks anonymous cipher support.")
""" By default, both the server and the client support anonymous
ciphers - they should connect without need for a certificate.
"""
+ self.client_domain.set_peer_authentication(SSLDomain.ANONYMOUS_PEER)
server = SslTest.SslTestConnection(self.server_domain, mode=Transport.SERVER)
client = SslTest.SslTestConnection(self.client_domain)
diff --git a/ruby/examples/ssl_send.rb b/ruby/examples/ssl_send.rb
index 4ff5da6..9e437bf 100644
--- a/ruby/examples/ssl_send.rb
+++ b/ruby/examples/ssl_send.rb
@@ -32,8 +32,9 @@
end
def on_container_start(container)
- # Use a default client SSL domain
+ # Use anonymous client SSL domain
ssl_domain = Qpid::Proton::SSLDomain.new(Qpid::Proton::SSLDomain::MODE_CLIENT)
+ ssl_domain.peer_authentication(Qpid::Proton::SSLDomain::ANONYMOUS_PEER)
c = container.connect(@url, { :ssl_domain => ssl_domain })
c.open_sender(@address)
end
diff --git a/ruby/lib/core/ssl_domain.rb b/ruby/lib/core/ssl_domain.rb
index 813fc1a..8a45b64 100644
--- a/ruby/lib/core/ssl_domain.rb
+++ b/ruby/lib/core/ssl_domain.rb
@@ -105,9 +105,10 @@
# Configures the level of verification used on the peer certificate.
#
# This method congtrols how the peer's certificate is validated, if at all.
- # By default, neither servers nor clients attempt to verify their peers
- # (*ANONYMOUS_PEER*). Once certficates and trusted CAs are configured, peer
- # verification can be enabled.
+ # By default, servers do not attempt to verify their peers
+ # (*ANONYMOUS_PEER*) but clients attempt to verify both the certificate and
+ # peer name (*VERIFY_PEER_NAME*). Once certficates and trusted CAs are
+ # configured, peer verification can be enabled.
#
# *NOTE:* In order to verify a peer, a trusted CA must be configured.
#