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]

Reply via email to