Repository: qpid-proton Updated Branches: refs/heads/master 669b70fff -> c80e95da7
PROTON-1229: enhance virtual_host doc and related ssl examples/CI Project: http://git-wip-us.apache.org/repos/asf/qpid-proton/repo Commit: http://git-wip-us.apache.org/repos/asf/qpid-proton/commit/c80e95da Tree: http://git-wip-us.apache.org/repos/asf/qpid-proton/tree/c80e95da Diff: http://git-wip-us.apache.org/repos/asf/qpid-proton/diff/c80e95da Branch: refs/heads/master Commit: c80e95da7710f0577c67904c8ea5ffb137a2b1f6 Parents: 669b70f Author: Clifford Jansen <[email protected]> Authored: Thu Jun 9 12:16:14 2016 -0700 Committer: Clifford Jansen <[email protected]> Committed: Thu Jun 9 13:47:44 2016 -0700 ---------------------------------------------------------------------- examples/cpp/example_test.py | 21 ++++- examples/cpp/ssl.cpp | 92 ++++++++++++++++---- .../cpp/include/proton/connection_options.hpp | 7 +- proton-c/bindings/cpp/include/proton/ssl.hpp | 6 +- .../bindings/cpp/src/connection_options.cpp | 40 ++++++--- proton-c/bindings/cpp/src/ssl.cpp | 18 ---- 6 files changed, 127 insertions(+), 57 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/c80e95da/examples/cpp/example_test.py ---------------------------------------------------------------------- diff --git a/examples/cpp/example_test.py b/examples/cpp/example_test.py index 5d927ea..65dcee8 100644 --- a/examples/cpp/example_test.py +++ b/examples/cpp/example_test.py @@ -299,14 +299,31 @@ map{string(k1):int(42), symbol(k2):boolean(0)} return os.path.join(pn_root, "examples/cpp/ssl_certs") def test_ssl(self): - # SSL without SASL + # SSL without SASL, VERIFY_PEER_NAME addr = "amqps://" + pick_addr() + "/examples" # Disable valgrind when using OpenSSL - out = self.proc(["ssl", addr, self.ssl_certs_dir()], skip_valgrind=True).wait_exit() + out = self.proc(["ssl", "-a", addr, "-c", self.ssl_certs_dir()], skip_valgrind=True).wait_exit() expect = "Outgoing client connection connected via SSL. Server certificate identity CN=test_server\nHello World!" expect_found = (out.find(expect) >= 0) self.assertEqual(expect_found, True) + def test_ssl_no_name(self): + # VERIFY_PEER + addr = "amqps://" + pick_addr() + "/examples" + # Disable valgrind when using OpenSSL + out = self.proc(["ssl", "-a", addr, "-c", self.ssl_certs_dir(), "-v", "noname"], skip_valgrind=True).wait_exit() + expect = "Outgoing client connection connected via SSL. Server certificate identity CN=test_server\nHello World!" + expect_found = (out.find(expect) >= 0) + self.assertEqual(expect_found, True) + + def test_ssl_bad_name(self): + # VERIFY_PEER + addr = "amqps://" + pick_addr() + "/examples" + # Disable valgrind when using OpenSSL + out = self.proc(["ssl", "-a", addr, "-c", self.ssl_certs_dir(), "-v", "fail"], skip_valgrind=True).wait_exit() + expect = "Expected failure of connection with wrong peer name" + expect_found = (out.find(expect) >= 0) + self.assertEqual(expect_found, True) def test_ssl_client_cert(self): # SSL with SASL EXTERNAL http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/c80e95da/examples/cpp/ssl.cpp ---------------------------------------------------------------------- diff --git a/examples/cpp/ssl.cpp b/examples/cpp/ssl.cpp index 91a4230..67394b5 100644 --- a/examples/cpp/ssl.cpp +++ b/examples/cpp/ssl.cpp @@ -19,6 +19,8 @@ * */ +#include "options.hpp" + #include <proton/connection_options.hpp> #include <proton/connection.hpp> #include <proton/default_container.hpp> @@ -40,9 +42,23 @@ using proton::ssl_certificate; bool using_OpenSSL(); std::string platform_CA(const std::string &base_name); ssl_certificate platform_certificate(const std::string &base_name, const std::string &passwd); -static std::string cert_directory; std::string find_CN(const std::string &); +namespace { + std::string verify_full("full"); // Normal verification + std::string verify_noname("noname"); // Skip matching host name against the certificate + std::string verify_fail("fail"); // Force name mismatch failure + std::string verify(verify_full); // Default for example + std::string cert_directory; + + class example_cert_error : public std::runtime_error + { + public: + explicit example_cert_error(const std::string& s) : std::runtime_error(s) {} + }; + +} + struct server_handler : public proton::messaging_handler { std::string url; @@ -50,7 +66,11 @@ struct server_handler : public proton::messaging_handler { void on_connection_open(proton::connection &c) OVERRIDE { std::cout << "Inbound server connection connected via SSL. Protocol: " << c.transport().ssl().protocol() << std::endl; - c.container().stop_listening(url); + c.container().stop_listening(url); // Just expecting the one connection. + } + + void on_transport_error(proton::transport &t) OVERRIDE { + t.connection().container().stop_listening(url); } void on_message(proton::delivery &, proton::message &m) OVERRIDE { @@ -75,14 +95,28 @@ class hello_world_direct : public proton::messaging_handler { server_opts.ssl_server_options(ssl_srv).handler(s_handler); c.server_connection_options(server_opts); - // Configure client with a Certificate Authority database populated with the server's self signed certificate. - // Since the test certifcate's credentials are unlikely to match this host's name, downgrade the verification - // from VERIFY_PEER_NAME to VERIFY_PEER. + // Configure client with a Certificate Authority database + // populated with the server's self signed certificate. connection_options client_opts; - ssl_client_options ssl_cli(platform_CA("tserver"), proton::ssl::VERIFY_PEER); - client_opts.ssl_client_options(ssl_cli); - c.client_connection_options(client_opts); + if (verify == verify_full) { + ssl_client_options ssl_cli(platform_CA("tserver")); + client_opts.ssl_client_options(ssl_cli); + // The next line is optional in normal use. Since the + // example uses IP addresses in the connection string, use + // the virtual_host option to set the server host name + // used for certificate verification: + client_opts.virtual_host("test_server"); + } else if (verify == verify_noname) { + // Downgrade the verification from VERIFY_PEER_NAME to VERIFY_PEER. + ssl_client_options ssl_cli(platform_CA("tserver"), proton::ssl::VERIFY_PEER); + client_opts.ssl_client_options(ssl_cli); + } else if (verify == verify_fail) { + ssl_client_options ssl_cli(platform_CA("tserver")); + client_opts.ssl_client_options(ssl_cli); + client_opts.virtual_host("wrong_name_for_server"); // Pick any name that doesn't match. + } else throw std::logic_error("bad verify mode: " + verify); + c.client_connection_options(client_opts); s_handler.url = url; c.listen(url); c.open_sender(url); @@ -94,6 +128,12 @@ class hello_world_direct : public proton::messaging_handler { find_CN(subject) << std::endl; } + void on_transport_error(proton::transport &t) OVERRIDE { + std::string err = t.error().what(); + if (err.find("certificate")) + throw example_cert_error(err); + } + void on_sendable(proton::sender &s) OVERRIDE { proton::message m; m.body("Hello World!"); @@ -108,22 +148,36 @@ class hello_world_direct : public proton::messaging_handler { }; int main(int argc, char **argv) { + // Pick an "unusual" port since we are going to be talking to + // ourselves, not a broker. + // Note the use of "amqps" as the URL scheme to denote a TLS/SSL connection. + std::string address("amqps://127.0.0.1:8888/examples"); + example::options opts(argc, argv); + opts.add_value(address, 'a', "address", "connect and send to URL", "URL"); + opts.add_value(cert_directory, 'c', "cert_directory", + "directory containing SSL certificates and private key information", "CERTDIR"); + opts.add_value(verify, 'v', "verify", "verify type: \"minimum\", \"full\", \"fail\"", "VERIFY"); + try { - // Pick an "unusual" port since we are going to be talking to ourselves, not a broker. - // Note the use of "amqps" as the URL scheme to denote a TLS/SSL connection. - std::string url = argc > 1 ? argv[1] : "amqps://127.0.0.1:8888/examples"; - // Location of certificates and private key information: - if (argc > 2) { - cert_directory = argv[2]; - size_t sz = cert_directory.size(); - if (sz && cert_directory[sz -1] != '/') - cert_directory.append("/"); - } + opts.parse(); + + size_t sz = cert_directory.size(); + if (sz && cert_directory[sz -1] != '/') + cert_directory.append("/"); else cert_directory = "ssl_certs/"; - hello_world_direct hwd(url); + if (verify != verify_noname && verify != verify_full && verify != verify_fail) + throw std::runtime_error("bad verify argument: " + verify); + + hello_world_direct hwd(address); proton::default_container(hwd).run(); return 0; + } catch (const example_cert_error& ce) { + if (verify == verify_fail) { + std::cout << "Expected failure of connection with wrong peer name: " << ce.what() << std::endl; + return 0; + } + std::cerr << "unexpected internal certificate failure: " << ce.what() << std::endl; } catch (const std::exception& e) { std::cerr << e.what() << std::endl; } http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/c80e95da/proton-c/bindings/cpp/include/proton/connection_options.hpp ---------------------------------------------------------------------- diff --git a/proton-c/bindings/cpp/include/proton/connection_options.hpp b/proton-c/bindings/cpp/include/proton/connection_options.hpp index 3da485a..bc00bb9 100644 --- a/proton-c/bindings/cpp/include/proton/connection_options.hpp +++ b/proton-c/bindings/cpp/include/proton/connection_options.hpp @@ -100,7 +100,12 @@ class connection_options { /// Set the container ID. PN_CPP_EXTERN connection_options& container_id(const std::string &id); - /// Set the virtual host name. + /// Set the virtual host name for the connection. If making a + /// client connection by SSL/TLS, this name is also used for + /// certificate verification and Server Name Indication. For + /// client connections, it defaults to the host name used to set + /// up the connection. It is not set by default for server + /// connections. PN_CPP_EXTERN connection_options& virtual_host(const std::string &name); /// @cond INTERNAL http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/c80e95da/proton-c/bindings/cpp/include/proton/ssl.hpp ---------------------------------------------------------------------- diff --git a/proton-c/bindings/cpp/include/proton/ssl.hpp b/proton-c/bindings/cpp/include/proton/ssl.hpp index b7f2d17..82087e2 100644 --- a/proton-c/bindings/cpp/include/proton/ssl.hpp +++ b/proton-c/bindings/cpp/include/proton/ssl.hpp @@ -73,10 +73,6 @@ class ssl { /// Get the security strength factor. PN_CPP_EXTERN int ssf() const; - /// XXX remove - PN_CPP_EXTERN void peer_hostname(const std::string &); - PN_CPP_EXTERN std::string peer_hostname() const; - /// XXX discuss, what's the meaning of "remote" here? PN_CPP_EXTERN std::string remote_subject() const; @@ -102,7 +98,7 @@ class ssl_certificate { PN_CPP_EXTERN ssl_certificate(const std::string &certdb_main); // XXX Document the following constructors - + /// @copydoc ssl_certificate PN_CPP_EXTERN ssl_certificate(const std::string &certdb_main, const std::string &certdb_extra); http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/c80e95da/proton-c/bindings/cpp/src/connection_options.cpp ---------------------------------------------------------------------- diff --git a/proton-c/bindings/cpp/src/connection_options.cpp b/proton-c/bindings/cpp/src/connection_options.cpp index c7a8a27..87f0284 100644 --- a/proton-c/bindings/cpp/src/connection_options.cpp +++ b/proton-c/bindings/cpp/src/connection_options.cpp @@ -63,6 +63,13 @@ class connection_options::impl { option<std::string> sasl_config_name; option<std::string> sasl_config_path; + /* + * There are three types of connection options: the handler + * (required at creation, so too late to apply here), open frame + * options (that never change after the original open), and + * transport options (set once per transport over the life of the + * connection). + */ void apply(connection& c) { pn_connection_t *pnc = unwrap(c); pn_transport_t *pnt = pn_connection_transport(pnc); @@ -70,13 +77,31 @@ class connection_options::impl { connection_context::get(c).handler.get()); bool uninit = c.uninitialized(); - // pnt is NULL between reconnect attempts. - // Only apply transport options if uninit or outbound with - // transport not yet configured. + // Only apply connection options if uninit. + if (uninit) { + std::string vhost; + if (virtual_host.set) + vhost = virtual_host.value; + else if (outbound) + vhost = outbound->address().host(); + + if (reconnect.set && outbound) + outbound->reconnect_timer(reconnect.value); + if (container_id.set) + pn_connection_set_container(pnc, container_id.value.c_str()); + if (!vhost.empty()) + pn_connection_set_hostname(pnc, vhost.c_str()); + } + + // Transport options. pnt is NULL between reconnect attempts + // and if there is a pipelined open frame. if (pnt && (uninit || (outbound && !outbound->transport_configured()))) { // SSL if (outbound && outbound->address().scheme() == url::AMQPS) { + // A side effect of pn_ssl() is to set the ssl peer + // hostname to the connection hostname, which has + // already been adjusted for the virtual_host option. pn_ssl_t *ssl = pn_ssl(pnt); if (pn_ssl_init(ssl, ssl_client_options.value.pn_domain(), NULL)) throw error(MSG("client SSL/TLS initialization error")); @@ -114,15 +139,6 @@ class connection_options::impl { if (idle_timeout.set) pn_transport_set_idle_timeout(pnt, idle_timeout.value.milliseconds()); } - // Only apply connection options if uninit. - if (uninit) { - if (reconnect.set && outbound) - outbound->reconnect_timer(reconnect.value); - if (container_id.set) - pn_connection_set_container(pnc, container_id.value.c_str()); - if (virtual_host.set && !virtual_host.value.empty()) - pn_connection_set_hostname(pnc, virtual_host.value.c_str()); - } } void update(const impl& x) { http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/c80e95da/proton-c/bindings/cpp/src/ssl.cpp ---------------------------------------------------------------------- diff --git a/proton-c/bindings/cpp/src/ssl.cpp b/proton-c/bindings/cpp/src/ssl.cpp index 4ef1f06..9a76442 100644 --- a/proton-c/bindings/cpp/src/ssl.cpp +++ b/proton-c/bindings/cpp/src/ssl.cpp @@ -48,24 +48,6 @@ enum ssl::resume_status ssl::resume_status() const { return (enum ssl::resume_status)pn_ssl_resume_status(object_); } -void ssl::peer_hostname(const std::string &hostname) { - if (pn_ssl_set_peer_hostname(object_, hostname.c_str())) - throw error(MSG("SSL set peer hostname failure for " << hostname)); -} - -std::string ssl::peer_hostname() const { - std::string hostname; - size_t len = 0; - if (pn_ssl_get_peer_hostname(object_, NULL, &len) || len == 0) - return hostname; - hostname.reserve(len); - if (!pn_ssl_get_peer_hostname(object_, const_cast<char*>(hostname.c_str()), &len)) - hostname.resize(len - 1); - else - hostname.resize(0); - return hostname; -} - std::string ssl::remote_subject() const { const char *s = pn_ssl_get_remote_subject(object_); return s ? std::string(s) : std::string(); --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
