You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@qpid.apache.org by as...@apache.org on 2019/07/18 02:58:44 UTC

[qpid-proton] branch master updated: PROTON-2075: [C++] Allow TLS to use system default trusted certificate store - Make the amqps scheme automatically imply tls with default trusted certs - Also fix small const issues with ssl option contructors - Added negative test to json config tests to ensure that the connection fails if we're using the system trusted certs

This is an automated email from the ASF dual-hosted git repository.

astitcher pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/qpid-proton.git


The following commit(s) were added to refs/heads/master by this push:
     new e152190  PROTON-2075: [C++] Allow TLS to use system default trusted certificate store - Make the amqps scheme automatically imply tls with default trusted certs - Also fix small const issues with ssl option contructors - Added negative test to json config tests to ensure that   the connection fails if we're using the system trusted certs
e152190 is described below

commit e152190459cd75792002d2aae72d351dc22abe27
Author: Andrew Stitcher <as...@apache.org>
AuthorDate: Tue Jul 16 17:23:44 2019 -0400

    PROTON-2075: [C++] Allow TLS to use system default trusted certificate store
    - Make the amqps scheme automatically imply tls with default trusted certs
    - Also fix small const issues with ssl option contructors
    - Added negative test to json config tests to ensure that
      the connection fails if we're using the system trusted certs
---
 cpp/CMakeLists.txt                  |   2 +-
 cpp/examples/service_bus.cpp        |   1 -
 cpp/include/proton/ssl.hpp          |  61 ++++++---------
 cpp/src/connect_config.cpp          |  19 +++--
 cpp/src/connect_config_test.cpp     |  40 +++++++++-
 cpp/src/connection_options.cpp      |  12 ++-
 cpp/src/proactor_container_impl.cpp |   8 +-
 cpp/src/ssl_domain.cpp              | 142 ----------------------------------
 cpp/src/ssl_options.cpp             | 147 ++++++++++++++++++++++++++++++++++++
 cpp/src/ssl_options_impl.hpp        |  52 +++++++++++++
 10 files changed, 290 insertions(+), 194 deletions(-)

diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt
index 9614160..fc36214 100644
--- a/cpp/CMakeLists.txt
+++ b/cpp/CMakeLists.txt
@@ -149,7 +149,7 @@ set(qpid-proton-cpp-source
   src/session_options.cpp
   src/source.cpp
   src/ssl.cpp
-  src/ssl_domain.cpp
+  src/ssl_options.cpp
   src/target.cpp
   src/terminus.cpp
   src/timestamp.cpp
diff --git a/cpp/examples/service_bus.cpp b/cpp/examples/service_bus.cpp
index 7f3052a..f10bfa7 100644
--- a/cpp/examples/service_bus.cpp
+++ b/cpp/examples/service_bus.cpp
@@ -321,7 +321,6 @@ int main(int argc, char **argv) {
                      connection_options()
                      .user(sb_key_name)
                      .password(sb_key)
-                     .ssl_client_options(ssl_client_options())
                      .sasl_allowed_mechs("PLAIN"));
         proton::container(seq).run();
         return 0;
diff --git a/cpp/include/proton/ssl.hpp b/cpp/include/proton/ssl.hpp
index 62a0dc4..ea8dd17 100644
--- a/cpp/include/proton/ssl.hpp
+++ b/cpp/include/proton/ssl.hpp
@@ -25,7 +25,6 @@
 #include "./internal/export.hpp"
 #include "./internal/config.hpp"
 
-
 #include <proton/ssl.h>
 
 #include <string>
@@ -123,38 +122,18 @@ class ssl_certificate {
     /// @endcond
 };
 
-class ssl_domain_impl;
-
-namespace internal {
-
-// 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&);
-    PN_CPP_EXTERN ~ssl_domain();
-
-  protected:
-    ssl_domain(bool is_server);
-    pn_ssl_domain_t *pn_domain();
-
-  private:
-    ssl_domain_impl *impl_;
-    bool server_type_;
-};
 
-}
 
 /// **Unsettled API** - SSL configuration for inbound connections.
-class ssl_server_options : private internal::ssl_domain {
+class ssl_server_options {
   public:
     /// Server SSL options based on the supplied X.509 certificate
     /// specifier.
-    PN_CPP_EXTERN ssl_server_options(ssl_certificate &cert);
+    PN_CPP_EXTERN ssl_server_options(const ssl_certificate &cert);
 
     /// Server SSL options requiring connecting clients to provide a
     /// client certificate.
-    PN_CPP_EXTERN ssl_server_options(ssl_certificate &cert, const std::string &trust_db,
+    PN_CPP_EXTERN ssl_server_options(const ssl_certificate &cert, const std::string &trust_db,
                                      const std::string &advertise_db = std::string(),
                                      enum ssl::verify_mode mode = ssl::VERIFY_PEER);
 
@@ -162,10 +141,13 @@ class ssl_server_options : private internal::ssl_domain {
     /// suites on the platform.
     PN_CPP_EXTERN ssl_server_options();
 
+    PN_CPP_EXTERN ~ssl_server_options();
+    PN_CPP_EXTERN ssl_server_options(const ssl_server_options&);
+    PN_CPP_EXTERN ssl_server_options& operator=(const ssl_server_options&);
+
   private:
-    // Bring pn_domain into scope and allow connection_options to use
-    // it.
-    using internal::ssl_domain::pn_domain;
+    class impl;
+    impl* impl_;
 
     /// @cond INTERNAL
   friend class connection_options;
@@ -173,24 +155,29 @@ class ssl_server_options : private internal::ssl_domain {
 };
 
 /// **Unsettled API** - SSL configuration for outbound connections.
-class ssl_client_options : private internal::ssl_domain {
+class ssl_client_options {
   public:
-    /// Create SSL client options (no client certificate).
+    /// Create SSL client with defaults (use system certificate trust database and require name verification)
+    PN_CPP_EXTERN ssl_client_options();
+
+    /// Create SSL client with unusual verification policy (but default certificate trust database)
+    PN_CPP_EXTERN ssl_client_options(enum ssl::verify_mode);
+
+    /// Create SSL client specifying the certificate trust database.
     PN_CPP_EXTERN ssl_client_options(const std::string &trust_db,
                                      enum ssl::verify_mode = ssl::VERIFY_PEER_NAME);
 
-    /// Create SSL client options with a client certificate.
-    PN_CPP_EXTERN ssl_client_options(ssl_certificate&, const std::string &trust_db,
+    /// Create SSL client with a client certificate.
+    PN_CPP_EXTERN ssl_client_options(const ssl_certificate&, const std::string &trust_db,
                                      enum ssl::verify_mode = ssl::VERIFY_PEER_NAME);
 
-    /// SSL connections restricted to available anonymous cipher
-    /// suites on the platform.
-    PN_CPP_EXTERN ssl_client_options();
+    PN_CPP_EXTERN ~ssl_client_options();
+    PN_CPP_EXTERN ssl_client_options(const ssl_client_options&);
+    PN_CPP_EXTERN ssl_client_options& operator=(const ssl_client_options&);
 
   private:
-    // Bring pn_domain into scope and allow connection_options to use
-    // it.
-    using internal::ssl_domain::pn_domain;
+    class impl;
+    impl* impl_;
 
     /// @cond INTERNAL
   friend class connection_options;
diff --git a/cpp/src/connect_config.cpp b/cpp/src/connect_config.cpp
index d157d67..6bb11c5 100644
--- a/cpp/src/connect_config.cpp
+++ b/cpp/src/connect_config.cpp
@@ -124,20 +124,25 @@ void parse_sasl(Value root, connection_options& opts) {
 void parse_tls(const string& scheme, Value root, connection_options& opts) {
     Value tls = get(objectValue, root, "tls");
     if (scheme == "amqps") { // TLS is enabled
-        bool verify = get_bool(tls, "verify", true);
-        ssl::verify_mode mode = verify ? ssl::VERIFY_PEER_NAME : ssl::ANONYMOUS_PEER;
+        ssl::verify_mode mode = ssl::VERIFY_PEER_NAME;
+        Value verifyValue = get(booleanValue, tls, "verify");
+        if (!verifyValue.empty()) {
+            mode = verifyValue.asBool() ? ssl::VERIFY_PEER_NAME : ssl::ANONYMOUS_PEER;
+        }
         string ca = get_string(tls, "ca", "");
         string cert = get_string(tls, "cert", "");
         string key = get_string(tls, "key", "");
-        ssl_client_options ssl_opts;
         if (!cert.empty()) {
             ssl_certificate sc = key.empty() ? ssl_certificate(cert) : ssl_certificate(cert, key);
-            ssl_opts = ssl_client_options(sc, ca, mode);
+            opts.ssl_client_options(ssl_client_options(sc, ca, mode));
         } else if (!ca.empty()) {
-            ssl_opts = ssl_client_options(ca, mode);
+            opts.ssl_client_options(ssl_client_options(ca, mode));
+        } else if (!verifyValue.empty()) {
+            opts.ssl_client_options(ssl_client_options(mode));
+        } else {
+            opts.ssl_client_options(ssl_client_options());
         }
-        opts.ssl_client_options(ssl_opts);
-    } else if (!tls.isNull()) {
+    } else if (!tls.empty()) {
         throw err(msg() << "'tls' object not allowed unless scheme is \"amqps\"");
     }
 }
diff --git a/cpp/src/connect_config_test.cpp b/cpp/src/connect_config_test.cpp
index 77bafc5..81dec87 100644
--- a/cpp/src/connect_config_test.cpp
+++ b/cpp/src/connect_config_test.cpp
@@ -151,7 +151,7 @@ class test_handler : public messaging_handler,  public listen_handler {
         on_listener_start(l.container());
     }
 
-    connection_options on_accept(listener& ) PN_CPP_OVERRIDE {
+    connection_options on_accept(listener& l) PN_CPP_OVERRIDE {
         return connection_options_;
     }
 
@@ -191,6 +191,10 @@ class test_handler : public messaging_handler,  public listen_handler {
         c.connect(configure(opts, config_with_port(bare_config)), opts);
     }
 
+    void stop_listener() {
+        listener_.stop();
+    }
+
   public:
     test_handler(const connection_options& listen_opts = connection_options()) :
         opened_(false), connection_options_(listen_opts) {}
@@ -252,6 +256,39 @@ class test_tls : public test_handler {
     }
 };
 
+class test_tls_default_fail : public test_handler {
+    static connection_options make_opts() {
+        ssl_certificate cert("testdata/certs/server-certificate.pem",
+                             "testdata/certs/server-private-key.pem",
+                             "server-password");
+        connection_options opts;
+        opts.ssl_server_options(ssl_server_options(cert));
+        return opts;
+    }
+    bool failed_;
+
+  public:
+
+    test_tls_default_fail() : test_handler(make_opts()), failed_(false) {}
+
+    void on_listener_start(proton::container& c) PN_CPP_OVERRIDE {
+        connect(c, RAW_STRING("scheme":"amqps"));
+    }
+
+    void on_messaging_error(const proton::error_condition& c) PN_CPP_OVERRIDE {
+        if (failed_) return;
+
+        ASSERT_SUBSTRING("verify failed", c.description());
+        failed_ = true;
+        stop_listener();
+    }
+
+    void run() {
+        container(*this).run();
+        ASSERT(failed_);
+    }
+};
+
 class test_tls_external : public test_handler {
 
     static connection_options make_opts() {
@@ -332,6 +369,7 @@ int main(int argc, char** argv) {
     if (have_ssl) {
         pn_ssl_domain_free(have_ssl);
         RUN_ARGV_TEST(failed, test_tls().run());
+        RUN_ARGV_TEST(failed, test_tls_default_fail().run());
         RUN_ARGV_TEST(failed, test_tls_external().run());
         if (have_sasl) {
             RUN_ARGV_TEST(failed, test_tls_plain().run());
diff --git a/cpp/src/connection_options.cpp b/cpp/src/connection_options.cpp
index 6371573..d47d049 100644
--- a/cpp/src/connection_options.cpp
+++ b/cpp/src/connection_options.cpp
@@ -33,6 +33,7 @@
 #include "messaging_adapter.hpp"
 #include "msg.hpp"
 #include "proton_bits.hpp"
+#include "ssl_options_impl.hpp"
 
 #include <proton/connection.h>
 #include <proton/proactor.h>
@@ -141,12 +142,15 @@ class connection_options::impl {
             // 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))
+            pn_ssl_domain_t* ssl_domain = ssl_client_options.value.impl_ ? ssl_client_options.value.impl_->pn_domain() : NULL;
+            if (pn_ssl_init(ssl, ssl_domain, NULL)) {
                 throw error(MSG("client SSL/TLS initialization error"));
+            }
         } else if (!client && ssl_server_options.set) {
-                pn_ssl_t *ssl = pn_ssl(pnt);
-                if (pn_ssl_init(ssl, ssl_server_options.value.pn_domain(), NULL))
-                    throw error(MSG("server SSL/TLS initialization error"));
+            pn_ssl_t *ssl = pn_ssl(pnt);
+            if (pn_ssl_init(ssl, ssl_server_options.value.impl_->pn_domain(), NULL)) {
+                throw error(MSG("server SSL/TLS initialization error"));
+            }
         }
 
     }
diff --git a/cpp/src/proactor_container_impl.cpp b/cpp/src/proactor_container_impl.cpp
index 7e40d4b..a559f03 100644
--- a/cpp/src/proactor_container_impl.cpp
+++ b/cpp/src/proactor_container_impl.cpp
@@ -25,6 +25,7 @@
 #include "proton/listen_handler.hpp"
 #include "proton/listener.hpp"
 #include "proton/reconnect_options.hpp"
+#include "proton/ssl.hpp"
 #include "proton/transport.hpp"
 #include "proton/url.hpp"
 
@@ -181,7 +182,12 @@ pn_connection_t* container::impl::make_connection_lh(
     if (stopping_)
         throw proton::error("container is stopping");
 
-    connection_options opts = client_connection_options_; // Defaults
+    connection_options opts;
+    // If scheme is amqps then use default tls settings
+    if (url.scheme()==url.AMQPS) {
+        opts.ssl_client_options(ssl_client_options());
+    }
+    opts.update(client_connection_options_);
     opts.update(user_opts);
     messaging_handler* mh = opts.handler();
 
diff --git a/cpp/src/ssl_domain.cpp b/cpp/src/ssl_domain.cpp
deleted file mode 100644
index 143ff5a..0000000
--- a/cpp/src/ssl_domain.cpp
+++ /dev/null
@@ -1,142 +0,0 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-#include "proton/ssl.hpp"
-#include "proton/error.hpp"
-#include "msg.hpp"
-
-#include <proton/ssl.h>
-
-namespace proton {
-
-/// TODO: This whole class isn't really needed as pn_ssl_domain_t is already refcounted, with shared ownership for all pn_ssl_t objects
-/// that hold one
-/// cjansen: but note it is not a PN_CLASS object and two pn_ssl_domain_free() from the app will cause a free while still in use.
-class ssl_domain_impl {
-  public:
-    ssl_domain_impl(bool is_server) : refcount_(1), pn_domain_(pn_ssl_domain(is_server ? PN_SSL_MODE_SERVER : PN_SSL_MODE_CLIENT)) {
-            if (!pn_domain_) throw error(MSG("SSL/TLS unavailable"));
-    }
-    ~ssl_domain_impl() { pn_ssl_domain_free(pn_domain_); }
-    void incref() { refcount_++; }
-    void decref() {
-        if (--refcount_ == 0) {
-            delete this;
-        }
-    }
-    pn_ssl_domain_t *pn_domain() { return pn_domain_; }
-  private:
-    int refcount_;
-    pn_ssl_domain_t *pn_domain_;
-    ssl_domain_impl(const ssl_domain_impl&);
-    ssl_domain_impl& operator=(const ssl_domain_impl&);
-};
-
-namespace internal {
-ssl_domain::ssl_domain(bool is_server) : impl_(0), server_type_(is_server) {}
-
-ssl_domain::ssl_domain(const ssl_domain &x) : impl_(x.impl_), server_type_(x.server_type_) {
-    if (impl_) impl_->incref();
-}
-
-ssl_domain& internal::ssl_domain::operator=(const ssl_domain&x) {
-    if (&x == this) return *this;
-    if (impl_) impl_->decref();
-    impl_ = x.impl_;
-    server_type_ = x.server_type_;
-    if (impl_) impl_->incref();
-    return *this;
-}
-
-ssl_domain::~ssl_domain() { if (impl_) impl_->decref(); }
-
-pn_ssl_domain_t *ssl_domain::pn_domain() {
-    if (!impl_)
-        // Lazily create in case never actually used or configured.
-        // The pn_ssl_domain_t is a heavy object.
-        impl_ = new ssl_domain_impl(server_type_);
-    return impl_->pn_domain();
-}
-
-} // namespace internal
-
-namespace {
-
-void set_cred(pn_ssl_domain_t *dom, const std::string &main, const std::string &extra, const std::string &pass, bool pwset) {
-    const char *cred2 = extra.empty() ? NULL : extra.c_str();
-    const char *pw = pwset ? pass.c_str() : NULL;
-    if (pn_ssl_domain_set_credentials(dom, main.c_str(), cred2, pw))
-        throw error(MSG("SSL certificate initialization failure for " << main << ":" <<
-                        (cred2 ? cred2 : "NULL") << ":" << (pw ? pw : "NULL")));
-}
-}
-
-ssl_server_options::ssl_server_options(ssl_certificate &cert) : internal::ssl_domain(true) {
-    set_cred(pn_domain(), cert.certdb_main_, cert.certdb_extra_, cert.passwd_, cert.pw_set_);
-}
-
-ssl_server_options::ssl_server_options(
-    ssl_certificate &cert,
-    const std::string &trust_db,
-    const std::string &advertise_db,
-    enum ssl::verify_mode mode) : internal::ssl_domain(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));
-    const std::string &db = advertise_db.empty() ? trust_db : advertise_db;
-    if (pn_ssl_domain_set_peer_authentication(dom, pn_ssl_verify_mode_t(mode), db.c_str()))
-        throw error(MSG("SSL server configuration failure requiring client certificates using " << db));
-}
-
-ssl_server_options::ssl_server_options() : ssl_domain(true) {}
-
-namespace {
-void client_setup(pn_ssl_domain_t *dom, const std::string &trust_db, enum ssl::verify_mode mode) {
-    if (pn_ssl_domain_set_trusted_ca_db(dom, trust_db.c_str()))
-        throw error(MSG("SSL trust store initialization failure for " << trust_db));
-    if (pn_ssl_domain_set_peer_authentication(dom, pn_ssl_verify_mode_t(mode), NULL))
-        throw error(MSG("SSL client verify mode failure"));
-}
-}
-
-ssl_client_options::ssl_client_options(const std::string &trust_db, enum ssl::verify_mode mode) : ssl_domain(false) {
-    client_setup(pn_domain(), trust_db, mode);
-}
-
-ssl_client_options::ssl_client_options(ssl_certificate &cert, const std::string &trust_db, enum ssl::verify_mode 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);
-}
-
-ssl_client_options::ssl_client_options() : ssl_domain(false) {}
-
-ssl_certificate::ssl_certificate(const std::string &main)
-    : certdb_main_(main), pw_set_(false) {}
-
-ssl_certificate::ssl_certificate(const std::string &main, const std::string &extra)
-    : certdb_main_(main), certdb_extra_(extra), pw_set_(false) {}
-
-ssl_certificate::ssl_certificate(const std::string &main, const std::string &extra, const std::string &pw)
-    : certdb_main_(main), certdb_extra_(extra), passwd_(pw), pw_set_(true) {}
-
-} // namespace
diff --git a/cpp/src/ssl_options.cpp b/cpp/src/ssl_options.cpp
new file mode 100644
index 0000000..f56b0ba
--- /dev/null
+++ b/cpp/src/ssl_options.cpp
@@ -0,0 +1,147 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "ssl_options_impl.hpp"
+
+#include "proton/ssl.hpp"
+#include "proton/error.hpp"
+#include "msg.hpp"
+
+#include <proton/ssl.h>
+
+namespace {
+
+void inline set_cred(pn_ssl_domain_t *dom, const std::string &main, const std::string &extra, const std::string &pass, bool pwset) {
+    const char *cred2 = extra.empty() ? NULL : extra.c_str();
+    const char *pw = pwset ? pass.c_str() : NULL;
+    if (pn_ssl_domain_set_credentials(dom, main.c_str(), cred2, pw))
+        throw proton::error(MSG("SSL certificate initialization failure for " << main << ":" <<
+                        (cred2 ? cred2 : "NULL") << ":" << (pw ? pw : "NULL")));
+}
+
+void inline set_trusted_ca_db(pn_ssl_domain_t *dom, const std::string &trust_db) {
+    if (pn_ssl_domain_set_trusted_ca_db(dom, trust_db.c_str()))
+        throw proton::error(MSG("SSL trust store initialization failure for " << trust_db));
+}
+
+void inline set_client_verify_mode(pn_ssl_domain_t *dom, enum proton::ssl::verify_mode mode) {
+    if (pn_ssl_domain_set_peer_authentication(dom, pn_ssl_verify_mode_t(mode), NULL))
+        throw proton::error(MSG("SSL client verify mode failure"));
+}
+
+}
+
+namespace proton {
+
+ssl_options_impl::ssl_options_impl(bool is_server) :
+    pn_domain_(pn_ssl_domain(is_server ? PN_SSL_MODE_SERVER : PN_SSL_MODE_CLIENT)),
+    refcount_(1) {
+    if (!pn_domain_) throw proton::error(MSG("SSL/TLS unavailable"));
+}
+
+ssl_options_impl::~ssl_options_impl() {
+    pn_ssl_domain_free(pn_domain_);
+}
+
+ssl_server_options& ssl_server_options::operator=(const ssl_server_options& x) {
+    if (&x!=this) {
+        if (impl_) impl_->decref();
+        impl_ = x.impl_;
+        if (impl_) impl_->incref();
+    }
+    return *this;
+}
+
+ssl_server_options::ssl_server_options(const ssl_server_options& x): impl_(x.impl_) {
+    if (impl_) impl_->incref();
+}
+
+ssl_server_options::~ssl_server_options() {
+    if (impl_) impl_->decref();
+}
+
+ssl_server_options::ssl_server_options(const ssl_certificate &cert) : impl_(new impl) {
+    set_cred(impl_->pn_domain(), cert.certdb_main_, cert.certdb_extra_, cert.passwd_, cert.pw_set_);
+}
+
+ssl_server_options::ssl_server_options(
+    const ssl_certificate &cert,
+    const std::string &trust_db,
+    const std::string &advertise_db,
+    enum ssl::verify_mode mode) : impl_(new impl)
+{
+    pn_ssl_domain_t* dom = impl_->pn_domain();
+    set_cred(dom, cert.certdb_main_, cert.certdb_extra_, cert.passwd_, cert.pw_set_);
+    set_trusted_ca_db(dom, trust_db.c_str());
+    const std::string &db = advertise_db.empty() ? trust_db : advertise_db;
+    if (pn_ssl_domain_set_peer_authentication(dom, pn_ssl_verify_mode_t(mode), db.c_str()))
+        throw error(MSG("SSL server configuration failure requiring client certificates using " << db));
+}
+
+ssl_server_options::ssl_server_options() : impl_(new impl) {}
+
+ssl_client_options::ssl_client_options(const ssl_client_options& x): impl_(x.impl_) {
+    if (impl_) impl_->incref();
+}
+
+ssl_client_options& ssl_client_options::operator=(const ssl_client_options& x) {
+    if (&x!=this) {
+        if (impl_) impl_->decref();
+        impl_ = x.impl_;
+        if (impl_) impl_->incref();
+    }
+    return *this;
+}
+
+ssl_client_options::~ssl_client_options() {
+    if (impl_) impl_->decref();
+}
+
+ssl_client_options::ssl_client_options() : impl_(0) {}
+
+ssl_client_options::ssl_client_options(enum ssl::verify_mode mode) : impl_(new impl) {
+    pn_ssl_domain_t* dom = impl_->pn_domain();
+    set_client_verify_mode(dom, mode);
+}
+
+ssl_client_options::ssl_client_options(const std::string &trust_db, enum ssl::verify_mode mode) : impl_(new impl) {
+    pn_ssl_domain_t* dom = impl_->pn_domain();
+    set_trusted_ca_db(dom, trust_db);
+    set_client_verify_mode(dom, mode);
+}
+
+ssl_client_options::ssl_client_options(const ssl_certificate &cert, const std::string &trust_db, enum ssl::verify_mode mode) : impl_(new impl) {
+    pn_ssl_domain_t* dom = impl_->pn_domain();
+    set_cred(dom, cert.certdb_main_, cert.certdb_extra_, cert.passwd_, cert.pw_set_);
+    set_trusted_ca_db(dom, trust_db);
+    set_client_verify_mode(dom, mode);
+}
+
+ssl_certificate::ssl_certificate(const std::string &main)
+    : certdb_main_(main), pw_set_(false) {}
+
+ssl_certificate::ssl_certificate(const std::string &main, const std::string &extra)
+    : certdb_main_(main), certdb_extra_(extra), pw_set_(false) {}
+
+ssl_certificate::ssl_certificate(const std::string &main, const std::string &extra, const std::string &pw)
+    : certdb_main_(main), certdb_extra_(extra), passwd_(pw), pw_set_(true) {}
+
+} // namespace
diff --git a/cpp/src/ssl_options_impl.hpp b/cpp/src/ssl_options_impl.hpp
new file mode 100644
index 0000000..b111c4c
--- /dev/null
+++ b/cpp/src/ssl_options_impl.hpp
@@ -0,0 +1,52 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "proton/ssl.hpp"
+
+struct pn_ssl_domain_t;
+
+namespace proton {
+
+class ssl_options_impl {
+  public:
+    ssl_options_impl(bool is_server);
+    ~ssl_options_impl();
+
+    void incref() {++refcount_;}
+    void decref() {if (--refcount_==0) delete this;}
+    pn_ssl_domain_t* pn_domain() {return pn_domain_;}
+
+  private:
+    pn_ssl_domain_t *pn_domain_;
+    int refcount_;
+};
+
+class ssl_server_options::impl : public ssl_options_impl {
+public:
+    impl() : ssl_options_impl(true) {}
+};
+
+class ssl_client_options::impl : public ssl_options_impl {
+public:
+    impl() : ssl_options_impl(false) {}
+};
+
+}


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