You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@qpid.apache.org by ac...@apache.org on 2015/12/30 22:13:08 UTC

[20/50] [abbrv] qpid-proton git commit: PROTON-1074: C++ ssl_domain: restore removed ref counting; add test

PROTON-1074: C++ ssl_domain: restore removed ref counting; add test


Project: http://git-wip-us.apache.org/repos/asf/qpid-proton/repo
Commit: http://git-wip-us.apache.org/repos/asf/qpid-proton/commit/d9c0ed5a
Tree: http://git-wip-us.apache.org/repos/asf/qpid-proton/tree/d9c0ed5a
Diff: http://git-wip-us.apache.org/repos/asf/qpid-proton/diff/d9c0ed5a

Branch: refs/heads/go1
Commit: d9c0ed5a4087b621af13c2f395afba91f2bd9063
Parents: 595a854
Author: Clifford Jansen <cl...@apache.org>
Authored: Fri Dec 11 09:25:46 2015 -0800
Committer: Clifford Jansen <cl...@apache.org>
Committed: Fri Dec 11 09:57:04 2015 -0800

----------------------------------------------------------------------
 examples/cpp/example_test.py                 | 25 +++++++
 examples/cpp/ssl.cpp                         | 17 ++++-
 examples/cpp/ssl_client_cert.cpp             | 86 +++++++++++++----------
 proton-c/bindings/cpp/include/proton/ssl.hpp | 13 ++--
 proton-c/bindings/cpp/src/ssl_domain.cpp     | 72 +++++++++++++------
 5 files changed, 149 insertions(+), 64 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/d9c0ed5a/examples/cpp/example_test.py
----------------------------------------------------------------------
diff --git a/examples/cpp/example_test.py b/examples/cpp/example_test.py
index 2f8b08c..3ea0c28 100644
--- a/examples/cpp/example_test.py
+++ b/examples/cpp/example_test.py
@@ -25,6 +25,7 @@ from  random import randrange
 from subprocess import Popen, PIPE, STDOUT
 from copy import copy
 import platform
+from os.path import dirname as dirname
 
 def cmdline(*args):
     """Adjust executable name args[0] for windows and/or valgrind"""
@@ -82,6 +83,11 @@ def pick_addr():
     p =  randrange(10000, 20000)
     return "127.0.0.1:%s" % p
 
+def ssl_certs_dir():
+    """Absolute path to the test SSL certificates"""
+    pn_root = dirname(dirname(dirname(sys.argv[0])))
+    return os.path.join(pn_root, "examples/cpp/ssl_certs")
+
 class Broker(object):
     """Run the test broker"""
 
@@ -237,5 +243,24 @@ Tock...
         finally:
             os.environ = env    # Restore environment
 
+    def test_ssl(self):
+        # SSL without SASL
+        expect="""Outgoing client connection connected via SSL.  Server certificate identity CN=test_server
+"Hello World!"
+"""
+        addr = "amqps://" + pick_addr() + "/examples"
+        ignore_first_line, ignore_nl, ssl_hw = execute("ssl", addr, ssl_certs_dir()).partition('\n')
+        self.assertEqual(expect, ssl_hw)
+
+    def test_ssl_client_cert(self):
+        # SSL with SASL EXTERNAL
+        expect="""Inbound client certificate identity CN=test_client
+Outgoing client connection connected via SSL.  Server certificate identity CN=test_server
+"Hello World!"
+"""
+        addr = "amqps://" + pick_addr() + "/examples"
+        ignore_first_line, ignore_nl, ssl_hw = execute("ssl_client_cert", addr, ssl_certs_dir()).partition('\n')
+        self.assertEqual(expect, ssl_hw)
+
 if __name__ == "__main__":
     unittest.main()

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/d9c0ed5a/examples/cpp/ssl.cpp
----------------------------------------------------------------------
diff --git a/examples/cpp/ssl.cpp b/examples/cpp/ssl.cpp
index 13ad76f..5e670c2 100644
--- a/examples/cpp/ssl.cpp
+++ b/examples/cpp/ssl.cpp
@@ -38,6 +38,7 @@ 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);
 std::string cert_directory;
+std::string find_CN(const std::string &);
 
 
 struct server_handler : public proton::messaging_handler {
@@ -83,8 +84,9 @@ class hello_world_direct : public proton::messaging_handler {
     }
 
     void on_connection_opened(proton::event &e) {
-        std::cout << "Outgoing client connection connected via SSL.  Server certificate has subject " <<
-            e.connection().transport().ssl().remote_subject() << std::endl;
+        std::string subject = e.connection().transport().ssl().remote_subject();
+        std::cout << "Outgoing client connection connected via SSL.  Server certificate identity " <<
+            find_CN(subject) << std::endl;
     }
 
     void on_sendable(proton::event &e) {
@@ -159,3 +161,14 @@ std::string platform_CA(const std::string &base_name) {
         return cert_directory + base_name + "-certificate.p12";
     }
 }
+
+std::string find_CN(const std::string &subject) {
+    // The subject string is returned with different whitespace and component ordering between platforms.
+    // Here we just return the common name by searching for "CN=...." in the subject, knowing that
+    // the test certificates do not contain any escaped characters.
+    size_t pos = subject.find("CN=");
+    if (pos == std::string::npos) throw std::runtime_error("No common name in certificate subject");
+    std::string cn = subject.substr(pos);
+    pos = cn.find(',');
+    return pos == std::string::npos ? cn : cn.substr(0, pos);
+}

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/d9c0ed5a/examples/cpp/ssl_client_cert.cpp
----------------------------------------------------------------------
diff --git a/examples/cpp/ssl_client_cert.cpp b/examples/cpp/ssl_client_cert.cpp
index 412162a..0be0c32 100644
--- a/examples/cpp/ssl_client_cert.cpp
+++ b/examples/cpp/ssl_client_cert.cpp
@@ -40,22 +40,24 @@ 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);
 std::string cert_directory;
+std::string find_CN(const std::string &);
 
 
 struct server_handler : public proton::messaging_handler {
     proton::acceptor inbound_listener;
 
     void on_connection_opened(proton::event &e) {
-        std::cout << "Inbound server connection connected via SSL.  Protocol: " << 
+        std::cout << "Inbound server connection connected via SSL.  Protocol: " <<
             e.connection().transport().ssl().protocol() << std::endl;
-        if (e.connection().transport().sasl().outcome() == sasl::OK)
-            std::cout << "Inbound client certificate subject is " << 
-                e.connection().transport().ssl().remote_subject() << std::endl;
+        if (e.connection().transport().sasl().outcome() == sasl::OK) {
+            std::string subject = e.connection().transport().ssl().remote_subject();
+            std::cout << "Inbound client certificate identity " << find_CN(subject) << std::endl;
+        }
         else {
             std::cout << "Inbound client authentication failed" <<std::endl;
             e.connection().close();
         }
-	inbound_listener.close();
+        inbound_listener.close();
     }
 
     void on_message(proton::event &e) {
@@ -75,18 +77,18 @@ class hello_world_direct : public proton::messaging_handler {
     void on_start(proton::event &e) {
         // Configure listener.  Details vary by platform.
         ssl_certificate server_cert = platform_certificate("tserver", "tserverpw");
-	std::string client_CA = platform_CA("tclient");
+        std::string client_CA = platform_CA("tclient");
         // Specify an SSL domain with CA's for client certificate verification.
         server_domain sdomain(server_cert, client_CA);
         connection_options server_opts;
         server_opts.server_domain(sdomain).handler(&s_handler);
-	server_opts.allowed_mechs("EXTERNAL");
+        server_opts.allowed_mechs("EXTERNAL");
         e.container().server_connection_options(server_opts);
 
         // Configure client.
-	ssl_certificate client_cert = platform_certificate("tclient", "tclientpw");
-	std::string server_CA = platform_CA("tserver");
-	client_domain cdomain(client_cert, server_CA);
+        ssl_certificate client_cert = platform_certificate("tclient", "tclientpw");
+        std::string server_CA = platform_CA("tserver");
+        client_domain cdomain(client_cert, server_CA);
         connection_options client_opts;
         client_opts.client_domain(cdomain).allowed_mechs("EXTERNAL");
         // Validate the server certificate against this name:
@@ -98,8 +100,9 @@ class hello_world_direct : public proton::messaging_handler {
     }
 
     void on_connection_opened(proton::event &e) {
-        std::cout << "Outgoing client connection connected via SSL.  Server certificate has subject " << 
-            e.connection().transport().ssl().remote_subject() << std::endl;
+        std::string subject = e.connection().transport().ssl().remote_subject();
+        std::cout << "Outgoing client connection connected via SSL.  Server certificate identity " <<
+            find_CN(subject) << std::endl;
     }
 
     void on_sendable(proton::event &e) {
@@ -110,7 +113,7 @@ class hello_world_direct : public proton::messaging_handler {
     }
 
     void on_accepted(proton::event &e) {
-	// All done.
+        // All done.
         e.connection().close();
     }
 };
@@ -118,16 +121,16 @@ class hello_world_direct : public proton::messaging_handler {
 int main(int argc, char **argv) {
     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.
+        // 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("/");
-	}
-	else cert_directory = "ssl_certs/";
+        // 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("/");
+        }
+        else cert_directory = "ssl_certs/";
 
         hello_world_direct hwd(url);
         proton::container(hwd).run();
@@ -139,7 +142,7 @@ int main(int argc, char **argv) {
 }
 
 
-bool using_OpenSSL() { 
+bool using_OpenSSL() {
     // Current defaults.
 #if defined(WIN32)
     return false;
@@ -150,27 +153,38 @@ bool using_OpenSSL() {
 
 ssl_certificate platform_certificate(const std::string &base_name, const std::string &passwd) {
     if (using_OpenSSL()) {
-	// The first argument will be the name of the file containing the public certificate, the
-	// second argument will be the name of the file containing the private key.
-	return ssl_certificate(cert_directory + base_name + "-certificate.pem",
-			       cert_directory + base_name + "-private-key.pem", passwd);
+        // The first argument will be the name of the file containing the public certificate, the
+        // second argument will be the name of the file containing the private key.
+        return ssl_certificate(cert_directory + base_name + "-certificate.pem",
+                               cert_directory + base_name + "-private-key.pem", passwd);
     }
     else {
-	// Windows SChannel
-	// The first argument will be the database or store that contains one or more complete certificates
-	// (public and private data).  The second will be an optional name of the certificate in the store
-	// (not used in this example with one certificate per store).
-	return ssl_certificate(cert_directory + base_name + "-full.p12", "", passwd);
+        // Windows SChannel
+        // The first argument will be the database or store that contains one or more complete certificates
+        // (public and private data).  The second will be an optional name of the certificate in the store
+        // (not used in this example with one certificate per store).
+        return ssl_certificate(cert_directory + base_name + "-full.p12", "", passwd);
     }
 }
 
 std::string platform_CA(const std::string &base_name) {
     if (using_OpenSSL()) {
-	// In this simple example with self-signed certificates, the peer's certificate is the CA database.
-	return cert_directory + base_name + "-certificate.pem";
+        // In this simple example with self-signed certificates, the peer's certificate is the CA database.
+        return cert_directory + base_name + "-certificate.pem";
     }
     else {
-	// Windows SChannel.  Use a pkcs#12 file with just the peer's public certificate information.
-	return cert_directory + base_name + "-certificate.p12";
+        // Windows SChannel.  Use a pkcs#12 file with just the peer's public certificate information.
+        return cert_directory + base_name + "-certificate.p12";
     }
 }
+
+std::string find_CN(const std::string &subject) {
+    // The subject string is returned with different whitespace and component ordering between platforms.
+    // Here we just return the common name by searching for "CN=...." in the subject, knowing that
+    // the test certificates do not contain any escaped characters.
+    size_t pos = subject.find("CN=");
+    if (pos == std::string::npos) throw std::runtime_error("No common name in certificate subject");
+    std::string cn = subject.substr(pos);
+    pos = cn.find(',');
+    return pos == std::string::npos ? cn : cn.substr(0, pos);
+}

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/d9c0ed5a/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 f1b5974..bb78ae3 100644
--- a/proton-c/bindings/cpp/include/proton/ssl.hpp
+++ b/proton-c/bindings/cpp/include/proton/ssl.hpp
@@ -71,18 +71,22 @@ class ssl_certificate {
     friend class server_domain;
 };
 
+
+class ssl_domain_impl;
+
 // Base class for SSL configuration
 class ssl_domain {
   public:
+    PN_CPP_EXTERN ssl_domain(const ssl_domain&);
+    PN_CPP_EXTERN ssl_domain& operator=(const ssl_domain&);
     ~ssl_domain();
 
   protected:
-    ssl_domain();
-    pn_ssl_domain_t *init(bool is_server);
+    ssl_domain(bool is_server);
     pn_ssl_domain_t *pn_domain();
 
   private:
-    pn_ssl_domain_t *impl_;
+    ssl_domain_impl *impl_;
 };
 
 /** SSL/TLS configuration for inbound connections created from a listener */
@@ -122,9 +126,6 @@ class client_domain : private ssl_domain {
     friend class connection_options;
 };
 
-
-
-
 }
 
 #endif  /*!PROTON_CPP_SSL_H*/

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/d9c0ed5a/proton-c/bindings/cpp/src/ssl_domain.cpp
----------------------------------------------------------------------
diff --git a/proton-c/bindings/cpp/src/ssl_domain.cpp b/proton-c/bindings/cpp/src/ssl_domain.cpp
index 2631880..9062713 100644
--- a/proton-c/bindings/cpp/src/ssl_domain.cpp
+++ b/proton-c/bindings/cpp/src/ssl_domain.cpp
@@ -26,19 +26,50 @@
 
 namespace proton {
 
-ssl_domain::ssl_domain() : impl_(0) {}
-
-// Create on demand
-pn_ssl_domain_t *ssl_domain::init(bool server_type) {
-    if (impl_) return impl_;
-    impl_ = pn_ssl_domain(server_type ? PN_SSL_MODE_SERVER : PN_SSL_MODE_CLIENT);
-    if (!impl_) throw error(MSG("SSL/TLS unavailable"));
-    return impl_;
+class ssl_domain_impl {
+  public:
+    ssl_domain_impl(bool is_server) : refcount_(1), server_type_(is_server), pn_domain_(0) {}
+    void incref() { refcount_++; }
+    void decref() {
+        if (--refcount_ == 0) {
+            if (pn_domain_) pn_ssl_domain_free(pn_domain_);
+            delete this;
+        }
+    }
+    pn_ssl_domain_t *pn_domain();
+  private:
+    int refcount_;
+    bool server_type_;
+    pn_ssl_domain_t *pn_domain_;
+    ssl_domain_impl(const ssl_domain_impl&);
+    ssl_domain_impl& operator=(const ssl_domain_impl&);
+};
+
+pn_ssl_domain_t *ssl_domain_impl::pn_domain() {
+    if (pn_domain_) return pn_domain_;
+    // Lazily create in case never actually used or configured.
+    pn_domain_ = pn_ssl_domain(server_type_ ? PN_SSL_MODE_SERVER : PN_SSL_MODE_CLIENT);
+    if (!pn_domain_) throw error(MSG("SSL/TLS unavailable"));
+    return pn_domain_;
 }
 
-pn_ssl_domain_t *ssl_domain::pn_domain() { return impl_; }
+ssl_domain::ssl_domain(bool is_server) : impl_(new ssl_domain_impl(is_server)) {}
+
+ssl_domain::ssl_domain(const ssl_domain &x) {
+    impl_ = x.impl_;
+    impl_->incref();
+}
+ssl_domain& ssl_domain::operator=(const ssl_domain&x) {
+    if (this != &x) {
+        impl_ = x.impl_;
+        impl_->incref();
+    }
+    return *this;
+}
+ssl_domain::~ssl_domain() { impl_->decref(); }
+
+pn_ssl_domain_t *ssl_domain::pn_domain() { return impl_->pn_domain(); }
 
-ssl_domain::~ssl_domain() { if (impl_) pn_ssl_domain_free(impl_); }
 
 namespace {
 
@@ -51,17 +82,17 @@ void set_cred(pn_ssl_domain_t *dom, const std::string &main, const std::string &
 }
 }
 
-server_domain::server_domain(ssl_certificate &cert) {
-    set_cred(init(true), cert.certdb_main_, cert.certdb_extra_, cert.passwd_, cert.pw_set_);
+server_domain::server_domain(ssl_certificate &cert) : ssl_domain(true) {
+    set_cred(pn_domain(), cert.certdb_main_, cert.certdb_extra_, cert.passwd_, cert.pw_set_);
 }
 
 server_domain::server_domain(
     ssl_certificate &cert,
     const std::string &trust_db,
     const std::string &advertise_db,
-    ssl::verify_mode_t mode)
+    ssl::verify_mode_t mode) : ssl_domain(true)
 {
-    pn_ssl_domain_t* dom = init(true);
+    pn_ssl_domain_t* dom = pn_domain();
     set_cred(dom, cert.certdb_main_, cert.certdb_extra_, cert.passwd_, cert.pw_set_);
     if (pn_ssl_domain_set_trusted_ca_db(dom, trust_db.c_str()))
         throw error(MSG("SSL trust store initialization failure for " << trust_db));
@@ -70,9 +101,10 @@ server_domain::server_domain(
         throw error(MSG("SSL server configuration failure requiring client certificates using " << db));
 }
 
-server_domain::server_domain() {}
+server_domain::server_domain() : ssl_domain(true) {}
 server_domain::~server_domain() {}
 
+
 namespace {
 void client_setup(pn_ssl_domain_t *dom, const std::string &trust_db, ssl::verify_mode_t mode) {
     if (pn_ssl_domain_set_trusted_ca_db(dom, trust_db.c_str()))
@@ -82,17 +114,17 @@ void client_setup(pn_ssl_domain_t *dom, const std::string &trust_db, ssl::verify
 }
 }
 
-client_domain::client_domain(const std::string &trust_db, ssl::verify_mode_t mode) {
-    client_setup(init(false), trust_db, mode);
+client_domain::client_domain(const std::string &trust_db, ssl::verify_mode_t mode) : ssl_domain(false) {
+    client_setup(pn_domain(), trust_db, mode);
 }
 
-client_domain::client_domain(ssl_certificate &cert, const std::string &trust_db, ssl::verify_mode_t mode) {
-    pn_ssl_domain_t *dom = init(false);
+client_domain::client_domain(ssl_certificate &cert, const std::string &trust_db, ssl::verify_mode_t mode) : ssl_domain(false) {
+    pn_ssl_domain_t *dom = pn_domain();
     set_cred(dom, cert.certdb_main_, cert.certdb_extra_, cert.passwd_, cert.pw_set_);
     client_setup(dom, trust_db, mode);
 }
 
-client_domain::client_domain() {}
+client_domain::client_domain() : ssl_domain(false) {}
 client_domain::~client_domain() {}
 
 ssl_certificate::ssl_certificate(const std::string &main, const std::string &extra)


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org