You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@trafficserver.apache.org by zw...@apache.org on 2020/05/20 16:45:35 UTC

[trafficserver] branch 9.0.x updated (f0ba454 -> 1264746)

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

zwoop pushed a change to branch 9.0.x
in repository https://gitbox.apache.org/repos/asf/trafficserver.git.


    from f0ba454  Remove unused index for SSL application specific data
     new c986f64  Rework server side SSL_CTX creation to better handle dual_cert mismatches (#6483)
     new 7c08b05  Fixes memory leak loading certs
     new d18538b  Fix SDK_API_TSSslServerContextCreate
     new 31d4958  Do not fail multicert load if line does not create entry (#6760)
     new 5be94b6  Fixes crash loading combined(cert+key) certs
     new 1264746  Add TXN_CLOSE hook to CPPAPI TransactionPlugin (#6800)

The 6 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 include/tscpp/api/Plugin.h                         |  12 +-
 include/tscpp/api/TransactionPlugin.h              |   4 +
 iocore/net/P_SSLCertLookup.h                       |   2 +-
 iocore/net/P_SSLUtils.h                            |  26 +-
 iocore/net/QUICMultiCertConfigLoader.cc            | 114 ++-----
 iocore/net/QUICMultiCertConfigLoader.h             |   6 +-
 iocore/net/SSLUtils.cc                             | 371 ++++++++++++++-------
 src/tscpp/api/GlobalPlugin.cc                      |   1 +
 src/tscpp/api/utils_internal.cc                    |  39 ++-
 tests/gold_tests/tls/ssl/signed-foo-ec.key         |   8 +
 tests/gold_tests/tls/ssl/signed-foo-ec.pem         |  14 +
 tests/gold_tests/tls/ssl/signed-san-ec.key         |   5 +
 tests/gold_tests/tls/ssl/signed-san-ec.pem         |  15 +
 tests/gold_tests/tls/ssl/signed-san.key            |  28 ++
 tests/gold_tests/tls/ssl/signed-san.pem            |  19 ++
 tests/gold_tests/tls/ssl/signer.pem                |  15 -
 .../tls/tls_check_dual_cert_selection.test.py      | 127 +++++++
 17 files changed, 576 insertions(+), 230 deletions(-)
 create mode 100644 tests/gold_tests/tls/ssl/signed-foo-ec.key
 create mode 100644 tests/gold_tests/tls/ssl/signed-foo-ec.pem
 create mode 100644 tests/gold_tests/tls/ssl/signed-san-ec.key
 create mode 100644 tests/gold_tests/tls/ssl/signed-san-ec.pem
 create mode 100644 tests/gold_tests/tls/ssl/signed-san.key
 create mode 100644 tests/gold_tests/tls/ssl/signed-san.pem
 create mode 100644 tests/gold_tests/tls/tls_check_dual_cert_selection.test.py


[trafficserver] 05/06: Fixes crash loading combined(cert+key) certs

Posted by zw...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

zwoop pushed a commit to branch 9.0.x
in repository https://gitbox.apache.org/repos/asf/trafficserver.git

commit 5be94b6f7e672abf4b52eb9818a15d598e565f12
Author: Randall Meyer <rr...@apache.org>
AuthorDate: Fri Apr 10 09:58:58 2020 -0700

    Fixes crash loading combined(cert+key) certs
    
    This crash was introduced by f729c9dc41ff1635132f4bdc6331ce826f3bc2fe
    
    (cherry picked from commit 96e1f4613316bda260debe0578cb626b0443f6a8)
---
 iocore/net/SSLUtils.cc | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/iocore/net/SSLUtils.cc b/iocore/net/SSLUtils.cc
index 5d297a3..f204aed 100644
--- a/iocore/net/SSLUtils.cc
+++ b/iocore/net/SSLUtils.cc
@@ -1400,7 +1400,9 @@ SSLMultiCertConfigLoader::_store_ssl_ctx(SSLCertLookup *lookup, const shared_SSL
   std::set<std::string> common_names;
   std::unordered_map<int, std::set<std::string>> unique_names;
   SSLMultiCertConfigLoader::CertLoadData data;
+
   const SSLConfigParams *params = this->_params;
+
   this->load_certs_and_cross_reference_names(cert_list, data, params, sslMultCertSettings.get(), common_names, unique_names);
 
   int i = 0;
@@ -1923,8 +1925,15 @@ SSLMultiCertConfigLoader::load_certs_and_cross_reference_names(std::vector<X509
 {
   SimpleTokenizer cert_tok(sslMultCertSettings && sslMultCertSettings->cert ? (const char *)sslMultCertSettings->cert : "",
                            SSL_CERT_SEPARATE_DELIM);
-  SimpleTokenizer key_tok((sslMultCertSettings && sslMultCertSettings->key ? (const char *)sslMultCertSettings->key : ""),
-                          SSL_CERT_SEPARATE_DELIM);
+
+  SimpleTokenizer key_tok(SSL_CERT_SEPARATE_DELIM);
+  if (sslMultCertSettings && sslMultCertSettings->key) {
+    key_tok.setString((const char *)sslMultCertSettings->key);
+  } else if (sslMultCertSettings && sslMultCertSettings->cert) {
+    key_tok.setString((const char *)sslMultCertSettings->cert);
+  } else {
+    key_tok.setString("");
+  }
 
   if (sslMultCertSettings && sslMultCertSettings->key && cert_tok.getNumTokensRemaining() != key_tok.getNumTokensRemaining()) {
     Error("the number of certificates in ssl_cert_name and ssl_key_name doesn't match");


[trafficserver] 06/06: Add TXN_CLOSE hook to CPPAPI TransactionPlugin (#6800)

Posted by zw...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

zwoop pushed a commit to branch 9.0.x
in repository https://gitbox.apache.org/repos/asf/trafficserver.git

commit 1264746ba577158217a452a4295a22e2610a34d6
Author: Sudheer Vinukonda <su...@apache.org>
AuthorDate: Wed May 20 09:13:58 2020 -0700

    Add TXN_CLOSE hook to CPPAPI TransactionPlugin (#6800)
    
    * Add TXN_CLOSE hook to CPPAPI TransactionPlugin
    
    * Clean up TransactionPlugin object and associated Continuation in txn_close
    
    * Address review comments
    
    * More review comments
    
    (cherry picked from commit 34b57fccb40ef711ce2e6b31042c96efc74c0ecc)
---
 include/tscpp/api/Plugin.h            | 12 ++++++++++-
 include/tscpp/api/TransactionPlugin.h |  4 ++++
 src/tscpp/api/GlobalPlugin.cc         |  1 +
 src/tscpp/api/utils_internal.cc       | 39 ++++++++++++++++++++++++++++-------
 4 files changed, 48 insertions(+), 8 deletions(-)

diff --git a/include/tscpp/api/Plugin.h b/include/tscpp/api/Plugin.h
index 2f57352..f37b805 100644
--- a/include/tscpp/api/Plugin.h
+++ b/include/tscpp/api/Plugin.h
@@ -58,7 +58,8 @@ public:
     HOOK_READ_REQUEST_HEADERS,  /**< This hook will be fired after the request is read. */
     HOOK_READ_CACHE_HEADERS,    /**< This hook will be fired after the CACHE hdrs. */
     HOOK_CACHE_LOOKUP_COMPLETE, /**< This hook will be fired after cache lookup complete. */
-    HOOK_SELECT_ALT             /**< This hook will be fired after select alt. */
+    HOOK_TXN_CLOSE, /**< This hook will be fired after send response headers, only for TransactionPlugins::registerHook()!. */
+    HOOK_SELECT_ALT /**< This hook will be fired after select alt. */
   };
 
   /**
@@ -143,6 +144,15 @@ public:
   };
 
   /**
+   * This method must be implemented when you hook HOOK_TXN_CLOSE
+   */
+  virtual void
+  handleTxnClose(Transaction &transaction)
+  {
+    transaction.resume();
+  };
+
+  /**
    * This method must be implemented when you hook HOOK_SELECT_ALT
    */
   virtual void handleSelectAlt(const Request &clientReq, const Request &cachedReq, const Response &cachedResp){};
diff --git a/include/tscpp/api/TransactionPlugin.h b/include/tscpp/api/TransactionPlugin.h
index b34fba0..ce3f1ca 100644
--- a/include/tscpp/api/TransactionPlugin.h
+++ b/include/tscpp/api/TransactionPlugin.h
@@ -93,6 +93,10 @@ public:
    *  see HookType and Plugin for the correspond HookTypes and callback methods. If you fail to implement the
    *  callback, a default implementation will be used that will only resume the Transaction.
    *
+   * \note For automatic destruction, you must either register dynamically allocated instances of
+   *  classes derived from this class with the the corresponding Transaction object (using
+   *  Transaction::addPlugin()), or register HOOK_TXN_CLOSE (but not both).
+   *
    * @param HookType the type of hook you wish to register
    * @see HookType
    * @see Plugin
diff --git a/src/tscpp/api/GlobalPlugin.cc b/src/tscpp/api/GlobalPlugin.cc
index b1be230..8e5f05c 100644
--- a/src/tscpp/api/GlobalPlugin.cc
+++ b/src/tscpp/api/GlobalPlugin.cc
@@ -87,6 +87,7 @@ GlobalPlugin::~GlobalPlugin()
 void
 GlobalPlugin::registerHook(Plugin::HookType hook_type)
 {
+  assert(hook_type != Plugin::HOOK_TXN_CLOSE);
   TSHttpHookID hook_id = utils::internal::convertInternalHookToTsHook(hook_type);
   TSHttpHookAdd(hook_id, state_->cont_);
   LOG_DEBUG("Registered global plugin %p for hook %s", this, HOOK_TYPE_STRINGS[hook_type].c_str());
diff --git a/src/tscpp/api/utils_internal.cc b/src/tscpp/api/utils_internal.cc
index 7cb86e0..61f9044 100644
--- a/src/tscpp/api/utils_internal.cc
+++ b/src/tscpp/api/utils_internal.cc
@@ -49,6 +49,25 @@ resetTransactionHandles(Transaction &transaction, TSEvent event)
   return;
 }
 
+void
+cleanupTransaction(Transaction &transaction, TSHttpTxn ats_txn_handle)
+{
+  delete &transaction;
+  // reset the txn arg to prevent use-after-free
+  TSUserArgSet(ats_txn_handle, TRANSACTION_STORAGE_INDEX, nullptr);
+}
+
+void
+cleanupTransactionPlugin(Plugin *plugin)
+{
+  TransactionPlugin *transaction_plugin = static_cast<TransactionPlugin *>(plugin);
+  std::shared_ptr<Mutex> trans_mutex    = utils::internal::getTransactionPluginMutex(*transaction_plugin);
+  LOG_DEBUG("Locking TransactionPlugin mutex to delete transaction plugin at %p", transaction_plugin);
+  trans_mutex->lock();
+  delete transaction_plugin;
+  trans_mutex->unlock();
+}
+
 int
 handleTransactionEvents(TSCont cont, TSEvent event, void *edata)
 {
@@ -77,14 +96,9 @@ handleTransactionEvents(TSCont cont, TSEvent event, void *edata)
     resetTransactionHandles(transaction, event);
     const std::list<TransactionPlugin *> &plugins = utils::internal::getTransactionPlugins(transaction);
     for (auto plugin : plugins) {
-      std::shared_ptr<Mutex> trans_mutex = utils::internal::getTransactionPluginMutex(*plugin);
-      LOG_DEBUG("Locking TransactionPlugin mutex to delete transaction plugin at %p", plugin);
-      trans_mutex->lock();
-      LOG_DEBUG("Locked Mutex...Deleting transaction plugin at %p", plugin);
-      delete plugin;
-      trans_mutex->unlock();
+      cleanupTransactionPlugin(plugin);
     }
-    delete &transaction;
+    cleanupTransaction(transaction, ats_txn_handle);
   } break;
   default:
     assert(false); /* we should never get here */
@@ -141,6 +155,15 @@ void inline invokePluginForEvent(Plugin *plugin, TSHttpTxn ats_txn_handle, TSEve
   case TS_EVENT_HTTP_CACHE_LOOKUP_COMPLETE:
     plugin->handleReadCacheLookupComplete(transaction);
     break;
+  case TS_EVENT_HTTP_TXN_CLOSE:
+    if (plugin) {
+      plugin->handleTxnClose(transaction);
+      cleanupTransactionPlugin(plugin);
+    } else {
+      LOG_ERROR("stray event TS_EVENT_HTTP_TXN_CLOSE, no transaction plugin to handle it!");
+    }
+    cleanupTransaction(transaction, ats_txn_handle);
+    break;
   default:
     assert(false); /* we should never get here */
     break;
@@ -191,6 +214,8 @@ utils::internal::convertInternalHookToTsHook(Plugin::HookType hooktype)
     return TS_HTTP_CACHE_LOOKUP_COMPLETE_HOOK;
   case Plugin::HOOK_SELECT_ALT:
     return TS_HTTP_SELECT_ALT_HOOK;
+  case Plugin::HOOK_TXN_CLOSE:
+    return TS_HTTP_TXN_CLOSE_HOOK;
   default:
     assert(false); // shouldn't happen, let's catch it early
     break;


[trafficserver] 01/06: Rework server side SSL_CTX creation to better handle dual_cert mismatches (#6483)

Posted by zw...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

zwoop pushed a commit to branch 9.0.x
in repository https://gitbox.apache.org/repos/asf/trafficserver.git

commit c986f64b8c67f38993d208b11666b4bae070cc8d
Author: Susan Hinrichs <sh...@yahoo-inc.com>
AuthorDate: Thu Mar 12 08:41:38 2020 -0500

    Rework server side SSL_CTX creation to better handle dual_cert mismatches (#6483)
    
    Rework server side SSL_CTX creation to better handle dual_cert name mismatches
    
    (cherry picked from commit f729c9dc41ff1635132f4bdc6331ce826f3bc2fe)
---
 iocore/net/P_SSLCertLookup.h                       |   2 +-
 iocore/net/P_SSLUtils.h                            |  26 +-
 iocore/net/QUICMultiCertConfigLoader.cc            | 112 ++-----
 iocore/net/QUICMultiCertConfigLoader.h             |   6 +-
 iocore/net/SSLUtils.cc                             | 348 ++++++++++++++-------
 tests/gold_tests/tls/ssl/signed-foo-ec.key         |   8 +
 tests/gold_tests/tls/ssl/signed-foo-ec.pem         |  14 +
 tests/gold_tests/tls/ssl/signed-san-ec.key         |   5 +
 tests/gold_tests/tls/ssl/signed-san-ec.pem         |  15 +
 tests/gold_tests/tls/ssl/signed-san.key            |  28 ++
 tests/gold_tests/tls/ssl/signed-san.pem            |  19 ++
 tests/gold_tests/tls/ssl/signer.pem                |  15 -
 .../tls/tls_check_dual_cert_selection.test.py      | 127 ++++++++
 13 files changed, 505 insertions(+), 220 deletions(-)

diff --git a/iocore/net/P_SSLCertLookup.h b/iocore/net/P_SSLCertLookup.h
index 1e89361..7ee0f2a 100644
--- a/iocore/net/P_SSLCertLookup.h
+++ b/iocore/net/P_SSLCertLookup.h
@@ -100,7 +100,7 @@ public:
   {
   }
   SSLCertContext(shared_SSL_CTX sc, shared_SSLMultiCertConfigParams u)
-    : ctx_mutex(), ctx(sc), opt(u->opt), userconfig(nullptr), keyblock(nullptr)
+    : ctx_mutex(), ctx(sc), opt(u->opt), userconfig(u), keyblock(nullptr)
   {
   }
   SSLCertContext(shared_SSL_CTX sc, shared_SSLMultiCertConfigParams u, shared_ssl_ticket_key_block kb)
diff --git a/iocore/net/P_SSLUtils.h b/iocore/net/P_SSLUtils.h
index da85e1a..ac43cf2 100644
--- a/iocore/net/P_SSLUtils.h
+++ b/iocore/net/P_SSLUtils.h
@@ -34,6 +34,9 @@
 #include "records/I_RecCore.h"
 #include "P_SSLCertLookup.h"
 
+#include <set>
+#include <map>
+
 struct SSLConfigParams;
 class SSLNetVConnection;
 
@@ -54,28 +57,39 @@ ssl_curve_id SSLGetCurveNID(SSL *ssl);
 class SSLMultiCertConfigLoader
 {
 public:
+  struct CertLoadData {
+    std::vector<std::string> cert_names_list, key_list, ca_list, ocsp_list;
+  };
   SSLMultiCertConfigLoader(const SSLConfigParams *p) : _params(p) {}
   virtual ~SSLMultiCertConfigLoader(){};
 
   bool load(SSLCertLookup *lookup);
 
   virtual SSL_CTX *default_server_ssl_ctx();
-  virtual SSL_CTX *init_server_ssl_ctx(std::vector<X509 *> &certList, const SSLMultiCertConfigParams *sslMultCertSettings);
-
-  static bool load_certs(SSL_CTX *ctx, std::vector<X509 *> &certList, const SSLConfigParams *params,
-                         const SSLMultiCertConfigParams *ssl_multi_cert_params);
+  virtual SSL_CTX *init_server_ssl_ctx(CertLoadData const &data, const SSLMultiCertConfigParams *sslMultCertSettings,
+                                       std::set<std::string> &names);
+
+  static bool load_certs(SSL_CTX *ctx, CertLoadData const &data, const SSLConfigParams *params,
+                         const SSLMultiCertConfigParams *sslMultCertSettings);
+  bool load_certs_and_cross_reference_names(std::vector<X509 *> &cert_list, CertLoadData &data, const SSLConfigParams *params,
+                                            const SSLMultiCertConfigParams *sslMultCertSettings,
+                                            std::set<std::string> &common_names,
+                                            std::unordered_map<int, std::set<std::string>> &unique_names);
   static bool set_session_id_context(SSL_CTX *ctx, const SSLConfigParams *params,
                                      const SSLMultiCertConfigParams *sslMultCertSettings);
 
-  static bool index_certificate(SSLCertLookup *lookup, SSLCertContext const &cc, X509 *cert, const char *certname);
+  static bool index_certificate(SSLCertLookup *lookup, SSLCertContext const &cc, const char *sni_name);
   static int check_server_cert_now(X509 *cert, const char *certname);
   static void clear_pw_references(SSL_CTX *ssl_ctx);
 
 protected:
   const SSLConfigParams *_params;
 
+  bool _store_single_ssl_ctx(SSLCertLookup *lookup, shared_SSLMultiCertConfigParams sslMultCertSettings, shared_SSL_CTX ctx,
+                             std::set<std::string> &names);
+
 private:
-  virtual SSL_CTX *_store_ssl_ctx(SSLCertLookup *lookup, const shared_SSLMultiCertConfigParams ssl_multi_cert_params);
+  virtual bool _store_ssl_ctx(SSLCertLookup *lookup, shared_SSLMultiCertConfigParams ssl_multi_cert_params);
   virtual void _set_handshake_callbacks(SSL_CTX *ctx);
 };
 
diff --git a/iocore/net/QUICMultiCertConfigLoader.cc b/iocore/net/QUICMultiCertConfigLoader.cc
index 2b1aecf..288c0f0 100644
--- a/iocore/net/QUICMultiCertConfigLoader.cc
+++ b/iocore/net/QUICMultiCertConfigLoader.cc
@@ -80,7 +80,8 @@ QUICMultiCertConfigLoader::default_server_ssl_ctx()
 }
 
 SSL_CTX *
-QUICMultiCertConfigLoader::init_server_ssl_ctx(std::vector<X509 *> &cert_list, const SSLMultiCertConfigParams *multi_cert_params)
+QUICMultiCertConfigLoader::init_server_ssl_ctx(SSLMultiCertConfigLoader::CertLoadData const &data,
+                                               const SSLMultiCertConfigParams *multi_cert_params, std::set<std::string> &names)
 {
   const SSLConfigParams *params = this->_params;
 
@@ -92,7 +93,7 @@ QUICMultiCertConfigLoader::init_server_ssl_ctx(std::vector<X509 *> &cert_list, c
     }
 
     if (multi_cert_params->cert) {
-      if (!SSLMultiCertConfigLoader::load_certs(ctx, cert_list, params, multi_cert_params)) {
+      if (!SSLMultiCertConfigLoader::load_certs(ctx, data, params, multi_cert_params)) {
         goto fail;
       }
     }
@@ -152,26 +153,6 @@ QUICMultiCertConfigLoader::init_server_ssl_ctx(std::vector<X509 *> &cert_list, c
 
   SSL_CTX_set_alpn_select_cb(ctx, QUICMultiCertConfigLoader::ssl_select_next_protocol, nullptr);
 
-#if TS_USE_TLS_OCSP
-  if (SSLConfigParams::ssl_ocsp_enabled) {
-    QUICConfDebug("SSL OCSP Stapling is enabled");
-    SSL_CTX_set_tlsext_status_cb(ctx, ssl_callback_ocsp_stapling);
-    const char *cert_name = multi_cert_params ? multi_cert_params->cert.get() : nullptr;
-
-    for (auto cert : cert_list) {
-      if (!ssl_stapling_init_cert(ctx, cert, cert_name, nullptr)) {
-        Warning("failed to configure SSL_CTX for OCSP Stapling info for certificate at %s", cert_name);
-      }
-    }
-  } else {
-    QUICConfDebug("SSL OCSP Stapling is disabled");
-  }
-#else
-  if (SSLConfigParams::ssl_ocsp_enabled) {
-    Warning("failed to enable SSL OCSP Stapling; this version of OpenSSL does not support it");
-  }
-#endif /* TS_USE_TLS_OCSP */
-
   if (SSLConfigParams::init_ssl_ctx_cb) {
     SSLConfigParams::init_ssl_ctx_cb(ctx, true);
   }
@@ -180,85 +161,60 @@ QUICMultiCertConfigLoader::init_server_ssl_ctx(std::vector<X509 *> &cert_list, c
 
 fail:
   SSLReleaseContext(ctx);
-  for (auto cert : cert_list) {
-    X509_free(cert);
-  }
-
   return nullptr;
 }
 
-SSL_CTX *
+bool
 QUICMultiCertConfigLoader::_store_ssl_ctx(SSLCertLookup *lookup, const shared_SSLMultiCertConfigParams multi_cert_params)
 {
+  bool retval = true;
   std::vector<X509 *> cert_list;
-  shared_SSL_CTX ctx(this->init_server_ssl_ctx(cert_list, multi_cert_params.get()), SSL_CTX_free);
-  shared_ssl_ticket_key_block keyblock = nullptr;
-  bool inserted                        = false;
-
-  if (!ctx || !multi_cert_params) {
-    lookup->is_valid = false;
-    return nullptr;
-  }
+  SSLMultiCertConfigLoader::CertLoadData data;
+  std::set<std::string> common_names;
+  std::unordered_map<int, std::set<std::string>> unique_names;
+  const SSLConfigParams *params = this->_params;
+  this->load_certs_and_cross_reference_names(cert_list, data, params, multi_cert_params.get(), common_names, unique_names);
 
-  const char *certname = multi_cert_params->cert.get();
-  for (auto cert : cert_list) {
-    if (0 > SSLMultiCertConfigLoader::check_server_cert_now(cert, certname)) {
+  for (size_t i = 0; i < cert_list.size(); i++) {
+    const char *current_cert_name = data.cert_names_list[i].c_str();
+    if (0 > SSLMultiCertConfigLoader::check_server_cert_now(cert_list[i], current_cert_name)) {
       /* At this point, we know cert is bad, and we've already printed a
          descriptive reason as to why cert is bad to the log file */
-      QUICConfDebug("Marking certificate as NOT VALID: %s", certname);
+      QUICConfDebug("Marking certificate as NOT VALID: %s", current_cert_name);
       lookup->is_valid = false;
     }
   }
 
-  // Index this certificate by the specified IP(v6) address. If the address is "*", make it the default context.
-  if (multi_cert_params->addr) {
-    if (strcmp(multi_cert_params->addr, "*") == 0) {
-      if (lookup->insert(multi_cert_params->addr, SSLCertContext(ctx, multi_cert_params, keyblock)) >= 0) {
-        inserted            = true;
-        lookup->ssl_default = ctx;
-        this->_set_handshake_callbacks(ctx.get());
-      }
-    } else {
-      IpEndpoint ep;
-
-      if (ats_ip_pton(multi_cert_params->addr, &ep) == 0) {
-        QUICConfDebug("mapping '%s' to certificate %s", (const char *)multi_cert_params->addr, (const char *)certname);
-        if (lookup->insert(ep, SSLCertContext(ctx, multi_cert_params, keyblock)) >= 0) {
-          inserted = true;
-        }
-      } else {
-        Error("'%s' is not a valid IPv4 or IPv6 address", (const char *)multi_cert_params->addr);
-        lookup->is_valid = false;
-      }
-    }
-  }
+  shared_SSL_CTX ctx(this->init_server_ssl_ctx(data, multi_cert_params.get(), common_names), SSL_CTX_free);
 
-  // Insert additional mappings. Note that this maps multiple keys to the same value, so when
-  // this code is updated to reconfigure the SSL certificates, it will need some sort of
-  // refcounting or alternate way of avoiding double frees.
-  QUICConfDebug("importing SNI names from %s", (const char *)certname);
-  for (auto cert : cert_list) {
-    if (SSLMultiCertConfigLoader::index_certificate(lookup, SSLCertContext(ctx, multi_cert_params), cert, certname)) {
-      inserted = true;
-    }
-  }
+  shared_ssl_ticket_key_block keyblock = nullptr;
 
-  if (inserted) {
-    if (SSLConfigParams::init_ssl_ctx_cb) {
-      SSLConfigParams::init_ssl_ctx_cb(ctx.get(), true);
-    }
+  if (!ctx || !multi_cert_params || !this->_store_single_ssl_ctx(lookup, multi_cert_params, ctx, common_names)) {
+    lookup->is_valid = false;
+    retval           = false;
   }
 
-  if (!inserted) {
-    SSLReleaseContext(ctx.get());
-    ctx = nullptr;
+  for (auto iter = unique_names.begin(); retval && iter != unique_names.end(); ++iter) {
+    size_t i = iter->first;
+
+    SSLMultiCertConfigLoader::CertLoadData single_data;
+    single_data.cert_names_list.push_back(data.cert_names_list[i]);
+    single_data.key_list.push_back(i < data.key_list.size() ? data.key_list[i] : "");
+    single_data.ca_list.push_back(i < data.ca_list.size() ? data.ca_list[i] : "");
+    single_data.ocsp_list.push_back(i < data.ocsp_list.size() ? data.ocsp_list[i] : "");
+
+    shared_SSL_CTX unique_ctx(this->init_server_ssl_ctx(single_data, multi_cert_params.get(), iter->second), SSL_CTX_free);
+    if (!unique_ctx || !this->_store_single_ssl_ctx(lookup, multi_cert_params, unique_ctx, iter->second)) {
+      lookup->is_valid = false;
+      retval           = false;
+    }
   }
 
   for (auto &i : cert_list) {
     X509_free(i);
   }
 
-  return ctx.get();
+  return retval;
 }
 
 void
diff --git a/iocore/net/QUICMultiCertConfigLoader.h b/iocore/net/QUICMultiCertConfigLoader.h
index f29bda6..796c083 100644
--- a/iocore/net/QUICMultiCertConfigLoader.h
+++ b/iocore/net/QUICMultiCertConfigLoader.h
@@ -46,10 +46,12 @@ public:
   QUICMultiCertConfigLoader(const SSLConfigParams *p) : SSLMultiCertConfigLoader(p) {}
 
   virtual SSL_CTX *default_server_ssl_ctx() override;
-  virtual SSL_CTX *init_server_ssl_ctx(std::vector<X509 *> &cert_list, const SSLMultiCertConfigParams *multi_cert_params) override;
+  // override;
+  SSL_CTX *init_server_ssl_ctx(SSLMultiCertConfigLoader::CertLoadData const &data,
+                               const SSLMultiCertConfigParams *sslMultCertSettings, std::set<std::string> &names) override;
 
 private:
-  virtual SSL_CTX *_store_ssl_ctx(SSLCertLookup *lookup, const shared_SSLMultiCertConfigParams multi_cert_params) override;
+  bool _store_ssl_ctx(SSLCertLookup *lookup, shared_SSLMultiCertConfigParams ssl_multi_cert_params) override;
   virtual void _set_handshake_callbacks(SSL_CTX *ssl_ctx) override;
   static int ssl_select_next_protocol(SSL *ssl, const unsigned char **out, unsigned char *outlen, const unsigned char *in,
                                       unsigned inlen, void *);
diff --git a/iocore/net/SSLUtils.cc b/iocore/net/SSLUtils.cc
index f9f8ec4..2c32232 100644
--- a/iocore/net/SSLUtils.cc
+++ b/iocore/net/SSLUtils.cc
@@ -60,6 +60,7 @@
 #include <openssl/pem.h>
 #include <openssl/rand.h>
 #include <openssl/x509.h>
+#include <openssl/x509v3.h>
 
 #if HAVE_OPENSSL_TS_H
 #include <openssl/ts.h>
@@ -1034,63 +1035,15 @@ asn1_strdup(ASN1_STRING *s)
    @static
 */
 bool
-SSLMultiCertConfigLoader::index_certificate(SSLCertLookup *lookup, SSLCertContext const &cc, X509 *cert, const char *certname)
+SSLMultiCertConfigLoader::index_certificate(SSLCertLookup *lookup, SSLCertContext const &cc, const char *sni_name)
 {
-  X509_NAME *subject = nullptr;
-  bool inserted      = false;
+  bool inserted = false;
 
-  if (nullptr == cert) {
-    Error("Failed to load certificate %s", certname);
-    lookup->is_valid = false;
-    return false;
-  }
-
-  // Insert a key for the subject CN.
-  subject = X509_get_subject_name(cert);
-  ats_scoped_str subj_name;
-  if (subject) {
-    int pos = -1;
-    for (;;) {
-      pos = X509_NAME_get_index_by_NID(subject, NID_commonName, pos);
-      if (pos == -1) {
-        break;
-      }
-
-      X509_NAME_ENTRY *e = X509_NAME_get_entry(subject, pos);
-      ASN1_STRING *cn    = X509_NAME_ENTRY_get_data(e);
-      subj_name          = asn1_strdup(cn);
-
-      Debug("ssl", "mapping '%s' to certificate %s", (const char *)subj_name, certname);
-      if (lookup->insert(subj_name, cc) >= 0) {
-        inserted = true;
-      }
-    }
+  Debug("ssl", "mapping '%s'", sni_name);
+  if (lookup->insert(sni_name, cc) >= 0) {
+    inserted = true;
   }
 
-#if HAVE_OPENSSL_TS_H
-  // Traverse the subjectAltNames (if any) and insert additional keys for the SSL context.
-  GENERAL_NAMES *names = static_cast<GENERAL_NAMES *>(X509_get_ext_d2i(cert, NID_subject_alt_name, nullptr, nullptr));
-  if (names) {
-    unsigned count = sk_GENERAL_NAME_num(names);
-    for (unsigned i = 0; i < count; ++i) {
-      GENERAL_NAME *name;
-
-      name = sk_GENERAL_NAME_value(names, i);
-      if (name->type == GEN_DNS) {
-        ats_scoped_str dns(asn1_strdup(name->d.dNSName));
-        // only try to insert if the alternate name is not the main name
-        if (subj_name == nullptr || strcmp(dns, subj_name) != 0) {
-          Debug("ssl", "mapping '%s' to certificates %s", (const char *)dns, certname);
-          if (lookup->insert(dns, cc) >= 0) {
-            inserted = true;
-          }
-        }
-      }
-    }
-
-    GENERAL_NAMES_free(names);
-  }
-#endif // HAVE_OPENSSL_TS_H
   return inserted;
 }
 
@@ -1193,7 +1146,8 @@ setClientCertLevel(SSL *ssl, uint8_t certLevel)
    This is public function because of used by SSLCreateServerContext.
  */
 SSL_CTX *
-SSLMultiCertConfigLoader::init_server_ssl_ctx(std::vector<X509 *> &cert_list, const SSLMultiCertConfigParams *sslMultCertSettings)
+SSLMultiCertConfigLoader::init_server_ssl_ctx(CertLoadData const &data, const SSLMultiCertConfigParams *sslMultCertSettings,
+                                              std::set<std::string> &names)
 {
   const SSLConfigParams *params = this->_params;
 
@@ -1277,7 +1231,7 @@ SSLMultiCertConfigLoader::init_server_ssl_ctx(std::vector<X509 *> &cert_list, co
     }
 
     if (sslMultCertSettings->cert) {
-      if (!SSLMultiCertConfigLoader::load_certs(ctx, cert_list, params, sslMultCertSettings)) {
+      if (!SSLMultiCertConfigLoader::load_certs(ctx, data, params, sslMultCertSettings)) {
         goto fail;
       }
     }
@@ -1395,10 +1349,6 @@ SSLMultiCertConfigLoader::init_server_ssl_ctx(std::vector<X509 *> &cert_list, co
 fail:
   SSLMultiCertConfigLoader::clear_pw_references(ctx);
   SSLReleaseContext(ctx);
-  for (auto cert : cert_list) {
-    X509_free(cert);
-  }
-
   return nullptr;
 }
 
@@ -1407,29 +1357,34 @@ SSLCreateServerContext(const SSLConfigParams *params, const SSLMultiCertConfigPa
                        const char *key_path)
 {
   SSLMultiCertConfigLoader loader(params);
-  std::vector<X509 *> cert_list;
   std::unique_ptr<SSL_CTX, decltype(&SSL_CTX_free)> ctx(nullptr, &SSL_CTX_free);
-  ctx.reset(loader.init_server_ssl_ctx(cert_list, sslMultiCertSettings));
-  ink_assert(cert_list.empty());
-
-  if (cert_path) {
+  std::vector<X509 *> cert_list;
+  std::set<std::string> common_names;
+  std::unordered_map<int, std::set<std::string>> unique_names;
+  SSLMultiCertConfigLoader::CertLoadData data;
+  if (loader.load_certs_and_cross_reference_names(cert_list, data, params, sslMultiCertSettings, common_names, unique_names)) {
+    ctx.reset(loader.init_server_ssl_ctx(data, sslMultiCertSettings, common_names));
+  }
+  for (auto &i : cert_list) {
+    X509_free(i);
+  }
+  if (ctx && cert_path) {
     if (!SSL_CTX_use_certificate_file(ctx.get(), cert_path, SSL_FILETYPE_PEM)) {
       SSLError("SSLCreateServerContext(): failed to load server certificate.");
-      return nullptr;
-    }
-    if (!key_path || key_path[0] == '\0') {
+      ctx = nullptr;
+    } else if (!key_path || key_path[0] == '\0') {
       key_path = cert_path;
     }
-    if (!SSL_CTX_use_PrivateKey_file(ctx.get(), key_path, SSL_FILETYPE_PEM)) {
-      SSLError("SSLCreateServerContext(): failed to load server private key.");
-      return nullptr;
-    }
-    if (!SSL_CTX_check_private_key(ctx.get())) {
-      SSLError("SSLCreateServerContext(): server private key does not match server certificate.");
-      return nullptr;
+    if (ctx) {
+      if (!SSL_CTX_use_PrivateKey_file(ctx.get(), key_path, SSL_FILETYPE_PEM)) {
+        SSLError("SSLCreateServerContext(): failed to load server private key.");
+        ctx = nullptr;
+      } else if (!SSL_CTX_check_private_key(ctx.get())) {
+        SSLError("SSLCreateServerContext(): server private key does not match server certificate.");
+        ctx = nullptr;
+      }
     }
   }
-
   return ctx.release();
 }
 
@@ -1437,29 +1392,65 @@ SSLCreateServerContext(const SSLConfigParams *params, const SSLMultiCertConfigPa
    Insert SSLCertContext (SSL_CTX ans options) into SSLCertLookup with key.
    Do NOT call SSL_CTX_set_* functions from here. SSL_CTX should be set up by SSLMultiCertConfigLoader::init_server_ssl_ctx().
  */
-SSL_CTX *
+bool
 SSLMultiCertConfigLoader::_store_ssl_ctx(SSLCertLookup *lookup, const shared_SSLMultiCertConfigParams sslMultCertSettings)
 {
+  bool retval = true;
   std::vector<X509 *> cert_list;
-  shared_ssl_ticket_key_block keyblock = nullptr;
-  bool inserted                        = false;
-  shared_SSL_CTX ctx(this->init_server_ssl_ctx(cert_list, sslMultCertSettings.get()), SSL_CTX_free);
-
-  if (!ctx || !sslMultCertSettings) {
-    lookup->is_valid = false;
-    return nullptr;
-  }
+  std::set<std::string> common_names;
+  std::unordered_map<int, std::set<std::string>> unique_names;
+  SSLMultiCertConfigLoader::CertLoadData data;
+  const SSLConfigParams *params = this->_params;
+  this->load_certs_and_cross_reference_names(cert_list, data, params, sslMultCertSettings.get(), common_names, unique_names);
 
-  const char *certname = sslMultCertSettings->cert.get();
+  int i = 0;
   for (auto cert : cert_list) {
-    if (0 > SSLMultiCertConfigLoader::check_server_cert_now(cert, certname)) {
+    const char *current_cert_name = data.cert_names_list[i].c_str();
+    if (0 > SSLMultiCertConfigLoader::check_server_cert_now(cert, current_cert_name)) {
       /* At this point, we know cert is bad, and we've already printed a
          descriptive reason as to why cert is bad to the log file */
-      Debug("ssl", "Marking certificate as NOT VALID: %s", certname);
+      Debug("ssl", "Marking certificate as NOT VALID: %s", current_cert_name);
+      lookup->is_valid = false;
+    }
+    i++;
+  }
+
+  shared_SSL_CTX ctx(this->init_server_ssl_ctx(data, sslMultCertSettings.get(), common_names), SSL_CTX_free);
+
+  if (!ctx || !sslMultCertSettings || !this->_store_single_ssl_ctx(lookup, sslMultCertSettings, ctx, common_names)) {
+    lookup->is_valid = false;
+    retval           = false;
+  }
+
+  for (auto iter = unique_names.begin(); retval && iter != unique_names.end(); ++iter) {
+    size_t i = iter->first;
+
+    SSLMultiCertConfigLoader::CertLoadData single_data;
+    single_data.cert_names_list.push_back(data.cert_names_list[i]);
+    single_data.key_list.push_back(i < data.key_list.size() ? data.key_list[i] : "");
+    single_data.ca_list.push_back(i < data.ca_list.size() ? data.ca_list[i] : "");
+    single_data.ocsp_list.push_back(i < data.ocsp_list.size() ? data.ocsp_list[i] : "");
+
+    shared_SSL_CTX unique_ctx(this->init_server_ssl_ctx(single_data, sslMultCertSettings.get(), iter->second), SSL_CTX_free);
+    if (!unique_ctx || !this->_store_single_ssl_ctx(lookup, sslMultCertSettings, unique_ctx, iter->second)) {
       lookup->is_valid = false;
+      retval           = false;
     }
   }
 
+  for (auto &i : cert_list) {
+    X509_free(i);
+  }
+
+  return retval;
+}
+
+bool
+SSLMultiCertConfigLoader::_store_single_ssl_ctx(SSLCertLookup *lookup, const shared_SSLMultiCertConfigParams sslMultCertSettings,
+                                                shared_SSL_CTX ctx, std::set<std::string> &names)
+{
+  bool inserted                        = false;
+  shared_ssl_ticket_key_block keyblock = nullptr;
   // Load the session ticket key if session tickets are not disabled
   if (sslMultCertSettings->session_ticket_enabled != 0) {
     keyblock = shared_ssl_ticket_key_block(ssl_context_enable_tickets(ctx.get(), nullptr), ticket_block_free);
@@ -1477,7 +1468,6 @@ SSLMultiCertConfigLoader::_store_ssl_ctx(SSLCertLookup *lookup, const shared_SSL
       IpEndpoint ep;
 
       if (ats_ip_pton(sslMultCertSettings->addr, &ep) == 0) {
-        Debug("ssl", "mapping '%s' to certificate %s", (const char *)sslMultCertSettings->addr, (const char *)certname);
         if (lookup->insert(ep, SSLCertContext(ctx, sslMultCertSettings, keyblock)) >= 0) {
           inserted = true;
         }
@@ -1491,9 +1481,8 @@ SSLMultiCertConfigLoader::_store_ssl_ctx(SSLCertLookup *lookup, const shared_SSL
   // Insert additional mappings. Note that this maps multiple keys to the same value, so when
   // this code is updated to reconfigure the SSL certificates, it will need some sort of
   // refcounting or alternate way of avoiding double frees.
-  Debug("ssl", "importing SNI names from %s", (const char *)certname);
-  for (auto cert : cert_list) {
-    if (SSLMultiCertConfigLoader::index_certificate(lookup, SSLCertContext(ctx, sslMultCertSettings), cert, certname)) {
+  for (auto sni_name : names) {
+    if (SSLMultiCertConfigLoader::index_certificate(lookup, SSLCertContext(ctx, sslMultCertSettings), sni_name.c_str())) {
       inserted = true;
     }
   }
@@ -1508,10 +1497,6 @@ SSLMultiCertConfigLoader::_store_ssl_ctx(SSLCertLookup *lookup, const shared_SSL
     ctx = nullptr;
   }
 
-  for (auto &i : cert_list) {
-    X509_free(i);
-  }
-
   return ctx.get();
 }
 
@@ -1658,7 +1643,7 @@ SSLMultiCertConfigLoader::load(SSLCertLookup *lookup)
   if (lookup->ssl_default == nullptr) {
     shared_SSLMultiCertConfigParams sslMultiCertSettings(new SSLMultiCertConfigParams);
     sslMultiCertSettings->addr = ats_strdup("*");
-    if (this->_store_ssl_ctx(lookup, sslMultiCertSettings) == nullptr) {
+    if (!this->_store_ssl_ctx(lookup, sslMultiCertSettings)) {
       Error("failed set default context");
       return false;
     }
@@ -1919,20 +1904,27 @@ SSLConnect(SSL *ssl)
 }
 
 /**
-   Load certificates to SSL_CTX
-   @static
+ * Process the config to pull out the list of file names, and process the certs to get the list
+ * of subject and sni names.  Thanks to dual cert configurations, there may be mulitple files of each type.
+ * If some names are not in all the listed certs they are listed in the uniqe_names map, keyed by the index
+ * of the including certificate
  */
 bool
-SSLMultiCertConfigLoader::load_certs(SSL_CTX *ctx, std::vector<X509 *> &cert_list, const SSLConfigParams *params,
-                                     const SSLMultiCertConfigParams *sslMultCertSettings)
+SSLMultiCertConfigLoader::load_certs_and_cross_reference_names(std::vector<X509 *> &cert_list,
+                                                               SSLMultiCertConfigLoader::CertLoadData &data,
+                                                               const SSLConfigParams *params,
+                                                               const SSLMultiCertConfigParams *sslMultCertSettings,
+                                                               std::set<std::string> &common_names,
+                                                               std::unordered_map<int, std::set<std::string>> &unique_names)
 {
-  SimpleTokenizer cert_tok((const char *)sslMultCertSettings->cert, SSL_CERT_SEPARATE_DELIM);
+  SimpleTokenizer cert_tok(sslMultCertSettings->cert ? (const char *)sslMultCertSettings->cert : "", SSL_CERT_SEPARATE_DELIM);
   SimpleTokenizer key_tok((sslMultCertSettings->key ? (const char *)sslMultCertSettings->key : ""), SSL_CERT_SEPARATE_DELIM);
 
   if (sslMultCertSettings->key && cert_tok.getNumTokensRemaining() != key_tok.getNumTokensRemaining()) {
     Error("the number of certificates in ssl_cert_name and ssl_key_name doesn't match");
     return false;
   }
+
   SimpleTokenizer ca_tok("", SSL_CERT_SEPARATE_DELIM);
   if (sslMultCertSettings->ca) {
     ca_tok.setString(sslMultCertSettings->ca);
@@ -1942,13 +1934,6 @@ SSLMultiCertConfigLoader::load_certs(SSL_CTX *ctx, std::vector<X509 *> &cert_lis
     }
   }
 
-#if TS_USE_TLS_OCSP
-  if (SSLConfigParams::ssl_ocsp_enabled) {
-    Debug("ssl", "SSL OCSP Stapling is enabled");
-    SSL_CTX_set_tlsext_status_cb(ctx, ssl_callback_ocsp_stapling);
-  } else {
-    Debug("ssl", "SSL OCSP Stapling is disabled");
-  }
   SimpleTokenizer ocsp_tok("", SSL_CERT_SEPARATE_DELIM);
   if (sslMultCertSettings->ocsp_response) {
     ocsp_tok.setString(sslMultCertSettings->ocsp_response);
@@ -1957,14 +1942,141 @@ SSLMultiCertConfigLoader::load_certs(SSL_CTX *ctx, std::vector<X509 *> &cert_lis
       return false;
     }
   }
+
+  for (const char *keyname = key_tok.getNext(); keyname; keyname = key_tok.getNext()) {
+    data.key_list.push_back(keyname);
+  }
+
+  for (const char *caname = ca_tok.getNext(); caname; caname = ca_tok.getNext()) {
+    data.ca_list.push_back(caname);
+  }
+
+  for (const char *ocspname = ocsp_tok.getNext(); ocspname; ocspname = ocsp_tok.getNext()) {
+    data.ocsp_list.push_back(ocspname);
+  }
+
+  bool first_pass = true;
+  int cert_index  = 0;
+  for (const char *certname = cert_tok.getNext(); certname; certname = cert_tok.getNext()) {
+    data.cert_names_list.push_back(certname);
+    std::string completeServerCertPath = Layout::relative_to(params->serverCertPathOnly, certname);
+    scoped_BIO bio(BIO_new_file(completeServerCertPath.c_str(), "r"));
+    X509 *cert = nullptr;
+    if (bio) {
+      cert = PEM_read_bio_X509(bio.get(), nullptr, nullptr, nullptr);
+    }
+    if (!bio || !cert) {
+      SSLError("failed to load certificate chain from %s", completeServerCertPath.c_str());
+      return false;
+    }
+
+    cert_list.push_back(cert);
+    if (SSLConfigParams::load_ssl_file_cb) {
+      SSLConfigParams::load_ssl_file_cb(completeServerCertPath.c_str());
+    }
+
+    std::set<std::string> name_set;
+    // Grub through the names in the certs
+    X509_NAME *subject = nullptr;
+
+    // Insert a key for the subject CN.
+    subject = X509_get_subject_name(cert);
+    ats_scoped_str subj_name;
+    if (subject) {
+      int pos = -1;
+      for (;;) {
+        pos = X509_NAME_get_index_by_NID(subject, NID_commonName, pos);
+        if (pos == -1) {
+          break;
+        }
+
+        X509_NAME_ENTRY *e = X509_NAME_get_entry(subject, pos);
+        ASN1_STRING *cn    = X509_NAME_ENTRY_get_data(e);
+        subj_name          = asn1_strdup(cn);
+
+        Debug("ssl", "subj '%s' in certificate %s %p", (const char *)subj_name, certname, cert);
+        name_set.insert(subj_name.get());
+      }
+    }
+
+    // Traverse the subjectAltNames (if any) and insert additional keys for the SSL context.
+    GENERAL_NAMES *names = static_cast<GENERAL_NAMES *>(X509_get_ext_d2i(cert, NID_subject_alt_name, nullptr, nullptr));
+    if (names) {
+      unsigned count = sk_GENERAL_NAME_num(names);
+      for (unsigned i = 0; i < count; ++i) {
+        GENERAL_NAME *name;
+
+        name = sk_GENERAL_NAME_value(names, i);
+        if (name->type == GEN_DNS) {
+          ats_scoped_str dns(asn1_strdup(name->d.dNSName));
+          name_set.insert(dns.get());
+        }
+      }
+    }
+    if (first_pass) {
+      first_pass   = false;
+      common_names = name_set;
+    } else {
+      // Check that all elements in common_names are in name_set
+      for (auto name : common_names) {
+        auto iter = name_set.find(name);
+        if (iter == name_set.end()) {
+          // Common_name not in new set, move name to unique set
+          auto iter = unique_names.find(cert_index - 1);
+          if (iter == unique_names.end()) {
+            std::set<std::string> new_set;
+            new_set.insert(name);
+            unique_names.insert({cert_index - 1, new_set});
+          } else {
+            iter->second.insert(name);
+          }
+          auto erase_iter = common_names.find(name);
+          common_names.erase(erase_iter);
+        } else {
+          // New name already in common set, go ahead and remove it from further consideration
+          name_set.erase(iter);
+        }
+      }
+      // Anything still in name_set was not in common_names
+      for (auto name : name_set) {
+        auto iter = unique_names.find(cert_index);
+        if (iter == unique_names.end()) {
+          std::set<std::string> new_set;
+          new_set.insert(name);
+          unique_names.insert({cert_index, new_set});
+        } else {
+          iter->second.insert(name);
+        }
+      }
+    }
+    cert_index++;
+  }
+  return true;
+}
+
+/**
+   Load certificates to SSL_CTX
+   @static
+ */
+bool
+SSLMultiCertConfigLoader::load_certs(SSL_CTX *ctx, SSLMultiCertConfigLoader::CertLoadData const &data,
+                                     const SSLConfigParams *params, const SSLMultiCertConfigParams *sslMultCertSettings)
+{
+#if TS_USE_TLS_OCSP
+  if (SSLConfigParams::ssl_ocsp_enabled) {
+    Debug("ssl", "SSL OCSP Stapling is enabled");
+    SSL_CTX_set_tlsext_status_cb(ctx, ssl_callback_ocsp_stapling);
+  } else {
+    Debug("ssl", "SSL OCSP Stapling is disabled");
+  }
 #else
   if (SSLConfigParams::ssl_ocsp_enabled) {
     Warning("failed to enable SSL OCSP Stapling; this version of OpenSSL does not support it");
   }
 #endif /* TS_USE_TLS_OCSP */
 
-  for (const char *certname = cert_tok.getNext(); certname; certname = cert_tok.getNext()) {
-    std::string completeServerCertPath = Layout::relative_to(params->serverCertPathOnly, certname);
+  for (size_t i = 0; i < data.cert_names_list.size(); i++) {
+    std::string completeServerCertPath = Layout::relative_to(params->serverCertPathOnly, data.cert_names_list[i]);
     scoped_BIO bio(BIO_new_file(completeServerCertPath.c_str(), "r"));
     X509 *cert = nullptr;
     if (bio) {
@@ -1983,12 +2095,11 @@ SSLMultiCertConfigLoader::load_certs(SSL_CTX *ctx, std::vector<X509 *> &cert_lis
     // Load up any additional chain certificates
     SSL_CTX_add_extra_chain_cert_bio(ctx, bio);
 
-    const char *keyPath = key_tok.getNext();
+    const char *keyPath = data.key_list[i].c_str();
     if (!SSLPrivateKeyHandler(ctx, params, completeServerCertPath, keyPath)) {
       return false;
     }
 
-    cert_list.push_back(cert);
     if (SSLConfigParams::load_ssl_file_cb) {
       SSLConfigParams::load_ssl_file_cb(completeServerCertPath.c_str());
     }
@@ -2011,7 +2122,7 @@ SSLMultiCertConfigLoader::load_certs(SSL_CTX *ctx, std::vector<X509 *> &cert_lis
 
     // Now, load any additional certificate chains specified in this entry.
     if (sslMultCertSettings->ca) {
-      const char *ca_name = ca_tok.getNext();
+      const char *ca_name = data.ca_list[i].c_str();
       if (ca_name != nullptr) {
         ats_scoped_str completeServerCertChainPath(Layout::relative_to(params->serverCertPathOnly, ca_name));
         if (!SSL_CTX_add_extra_chain_cert_file(ctx, completeServerCertChainPath)) {
@@ -2026,18 +2137,19 @@ SSLMultiCertConfigLoader::load_certs(SSL_CTX *ctx, std::vector<X509 *> &cert_lis
 #if TS_USE_TLS_OCSP
     if (SSLConfigParams::ssl_ocsp_enabled) {
       if (sslMultCertSettings->ocsp_response) {
-        const char *ocsp_response_name = ocsp_tok.getNext();
+        const char *ocsp_response_name = data.ocsp_list[i].c_str();
         ats_scoped_str completeOCSPResponsePath(Layout::relative_to(params->ssl_ocsp_response_path_only, ocsp_response_name));
-        if (!ssl_stapling_init_cert(ctx, cert, certname, (const char *)completeOCSPResponsePath)) {
-          Warning("failed to configure SSL_CTX for OCSP Stapling info for certificate at %s", certname);
+        if (!ssl_stapling_init_cert(ctx, cert, data.cert_names_list[i].c_str(), (const char *)completeOCSPResponsePath)) {
+          Warning("failed to configure SSL_CTX for OCSP Stapling info for certificate at %s", data.cert_names_list[i].c_str());
         }
       } else {
-        if (!ssl_stapling_init_cert(ctx, cert, certname, nullptr)) {
-          Warning("failed to configure SSL_CTX for OCSP Stapling info for certificate at %s", certname);
+        if (!ssl_stapling_init_cert(ctx, cert, data.cert_names_list[i].c_str(), nullptr)) {
+          Warning("failed to configure SSL_CTX for OCSP Stapling info for certificate at %s", data.cert_names_list[i].c_str());
         }
       }
     }
 #endif /* TS_USE_TLS_OCSP */
+    X509_free(cert);
   }
 
   return true;
diff --git a/tests/gold_tests/tls/ssl/signed-foo-ec.key b/tests/gold_tests/tls/ssl/signed-foo-ec.key
new file mode 100644
index 0000000..cbf1418
--- /dev/null
+++ b/tests/gold_tests/tls/ssl/signed-foo-ec.key
@@ -0,0 +1,8 @@
+-----BEGIN EC PARAMETERS-----
+BggqhkjOPQMBBw==
+-----END EC PARAMETERS-----
+-----BEGIN EC PRIVATE KEY-----
+MHcCAQEEIG2eKyPB3C9Efe1s7MQm6wdbFpAsIbpovesRCIJnYMb4oAoGCCqGSM49
+AwEHoUQDQgAEpz5axf97f4X/kx6PN8nyjkx5ree8nPmRI/TB0rq3e0ldH32hCrVQ
+chq/mZzJCD3D/uuS27xJhl6t29JyABfAlw==
+-----END EC PRIVATE KEY-----
diff --git a/tests/gold_tests/tls/ssl/signed-foo-ec.pem b/tests/gold_tests/tls/ssl/signed-foo-ec.pem
new file mode 100644
index 0000000..14289ad
--- /dev/null
+++ b/tests/gold_tests/tls/ssl/signed-foo-ec.pem
@@ -0,0 +1,14 @@
+-----BEGIN CERTIFICATE-----
+MIICHjCCAYcCCQC81MtBCwmQvDANBgkqhkiG9w0BAQsFADCBnTELMAkGA1UEBhMC
+VVMxCzAJBgNVBAgTAklMMRIwEAYDVQQHEwlDaGFtcGFpZ24xDjAMBgNVBAoTBVlh
+aG9vMQ0wCwYDVQQLEwRFZGdlMSgwJgYDVQQDEx9qdWljZXByb2R1Y2UuY29ycC5u
+ZTEueWFob28uY29tMSQwIgYJKoZIhvcNAQkBFhVwZXJzaWEuYXppekB5YWhvby5j
+b20wHhcNMjAwMjE3MjIzOTEyWhcNMzAwMjE0MjIzOTEyWjBQMQswCQYDVQQGEwJV
+UzELMAkGA1UECAwCSUwxEjAQBgNVBAcMCUNoYW1wYWlnbjEOMAwGA1UECgwFeWFo
+b28xEDAOBgNVBAMMB2Zvby5jb20wWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASn
+PlrF/3t/hf+THo83yfKOTHmt57yc+ZEj9MHSurd7SV0ffaEKtVByGr+ZnMkIPcP+
+65LbvEmGXq3b0nIAF8CXMA0GCSqGSIb3DQEBCwUAA4GBAALTvnMS/uh2BmORNzh/
+MdxfJxmgJiAPGCclJGiAdAnRAUhR0i2XlSnkFiCzxbIc8rwv84beztmeRnnLUcJK
+Qc4eSdrsHyfH3g8eFmzNW0sVDaYOiXVRReif4wQzO0mf8a3m5tBWcwBt2VucO0bL
+Qh8dytlcF7egrVhXMVGHVwzk
+-----END CERTIFICATE-----
diff --git a/tests/gold_tests/tls/ssl/signed-san-ec.key b/tests/gold_tests/tls/ssl/signed-san-ec.key
new file mode 100644
index 0000000..8dd236e
--- /dev/null
+++ b/tests/gold_tests/tls/ssl/signed-san-ec.key
@@ -0,0 +1,5 @@
+-----BEGIN EC PRIVATE KEY-----
+MHcCAQEEIBh9PX40P1qCTpRxqbrxRyHsy1dAlEMZmUJpOZjgS2eXoAoGCCqGSM49
+AwEHoUQDQgAEBwvhzslwxPu7OdDlJFHpFoCTfa2zxqD7MI9AI5263g5Dov8Kcs1t
+Kr1GTz3sunVCEOjVv5ASNokITXFu7+Bvcw==
+-----END EC PRIVATE KEY-----
diff --git a/tests/gold_tests/tls/ssl/signed-san-ec.pem b/tests/gold_tests/tls/ssl/signed-san-ec.pem
new file mode 100644
index 0000000..78cc9d4
--- /dev/null
+++ b/tests/gold_tests/tls/ssl/signed-san-ec.pem
@@ -0,0 +1,15 @@
+-----BEGIN CERTIFICATE-----
+MIICVTCCAb6gAwIBAgIJALzUy0ELCZDBMA0GCSqGSIb3DQEBCwUAMIGdMQswCQYD
+VQQGEwJVUzELMAkGA1UECBMCSUwxEjAQBgNVBAcTCUNoYW1wYWlnbjEOMAwGA1UE
+ChMFWWFob28xDTALBgNVBAsTBEVkZ2UxKDAmBgNVBAMTH2p1aWNlcHJvZHVjZS5j
+b3JwLm5lMS55YWhvby5jb20xJDAiBgkqhkiG9w0BCQEWFXBlcnNpYS5heml6QHlh
+aG9vLmNvbTAeFw0yMDAyMTgxNjM4MTZaFw0zMDAyMTUxNjM4MTZaMFkxCzAJBgNV
+BAYTAlVTMQswCQYDVQQIDAJJTDESMBAGA1UEBwwJQ2hhbXBhaWduMRUwEwYDVQQK
+DAxWZXJpem9uTWVkaWExEjAQBgNVBAMMCWdyb3VwLmNvbTBZMBMGByqGSM49AgEG
+CCqGSM49AwEHA0IABAcL4c7JcMT7uznQ5SRR6RaAk32ts8ag+zCPQCOdut4OQ6L/
+CnLNbSq9Rk897Lp1QhDo1b+QEjaJCE1xbu/gb3OjJzAlMCMGA1UdEQQcMBqCB29u
+ZS5jb22CB3R3by5jb22CBmVjLmNvbTANBgkqhkiG9w0BAQsFAAOBgQCRgoh5YGCc
+V++/kil6USZaQ0TTNhAtCeao9p5WCN1NdHNtnulacu0cYPCI0cbpy2CVZC6JMrNE
+21SFssxKaeM1yoyIDEjIkr5IaCCOnr5XdOAO6/eISapkIPE/1GEbxyEgk1yqTJkr
+KB22uwprz1abKaQNBfTR2bV+57JlmnNuzg==
+-----END CERTIFICATE-----
diff --git a/tests/gold_tests/tls/ssl/signed-san.key b/tests/gold_tests/tls/ssl/signed-san.key
new file mode 100644
index 0000000..aee6da6
--- /dev/null
+++ b/tests/gold_tests/tls/ssl/signed-san.key
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCtQayAL7EHSxEP
+uclHEa4mMh/pO5N8VULdVA6sHN9k5mUAEnL3p3nNsdVE9pLtHIxgcoXCcLAPG+eL
+bSAk+C/l+pubz8GAWHdOC/3xpdrzQjOiYzi2Megl6M+VpwaMOd0s14zdFZloS37y
+M7GnjLKIRO8dMz744SSAPZYIvnx1XAJ/zA9uLbYzvFWbwkuT8G2+Jo+IHl4FyMX2
+wTTXLeYf66RKUnTFcNG7ysNbUr7hDJR8E/lek00K3t8YGzYrqCtHIH3OLkpKNU3l
+HhWRQ0zrNRwfF+hWfYXq3SZ5+C1eo2RZe8SHPbNPbpJxGMMkxNRkCyWuMywoc2dA
+nm1HyI2nAgMBAAECggEAKkhOyvHYqEj/nvDeWEPOVnABLbBma/960/0Bn6tkMYGw
+wHXALQRoS3TM8Ymjjc5by+XnEu7haK6MsZAuOhd/yQaCF2J6fNIaO6fdj63EY32S
+kFzaqExBtY69qm4awPoWKi1oqUPuLm/OSVmoT5WctHjuShgJlD+N4uYkyXmDcjhd
+cED+Uy6LCK0TelhkN2J3TBMD1yI2bccORTVtihXWQhr7ROzXiyV5O143LFvSTNdr
+l8FlOQbJhIxThUmydecONi1PYAkP/ySYFqLjLfCmonoVJFdjdgJsiXBEtUJ/8CQY
++ar2vqRL4/4Y+9eerlZFy3e6lidofPu0YGTWdCTcUQKBgQDSvBW5+61DZ8p9f3UX
+04an8CyKgHDcH/lqKQMB8GvEhWCQG2nnrs6fQieiKj4P5bwuf01oNu4txxIg1GnS
+r5MTZfzCEe89X06vcwzcTOQsMfUlzx7qMpQaqzshwchFLpCD2pj2vGIlL+g6piN0
++X7RjRykBe0Nx5wYx3RYI78DCwKBgQDSeLuz2Q3n8Oqz+m9Zl+g1jMNgY861+zv5
+fZb43xXegef82RUata9ZGaRelODYdWmNiRGGAgfe1ddOW0xc3Ve1W9lonFLE84je
+Oc57S2q9LiVuKJbhDQ6br1eGLv8RywsUhNdWRBIH9Mn3+1nIXAtlHt7Jp3lDeVO/
+WJa3vvyBVQKBgFDTMsISdXHU7SUVLaPlzU+8HllAygijetXsxOqJe8v0HAUpfoUN
+1tHeXbUk3ojaZEKxMM83wkJsh9dvoObd0FswUrFcj5XKaDOCvPwBwcHxp0TJG+JX
+Y9aWtidMW7OtGGB6BxEbT8lTho54CkFjL/DPXpzKaRFP7d7TIRxtGWXhAoGANjTw
+KvbZNQaAfFAgw5NzM++IFlg+UfJd1Pj6nChgqokMpbuHSvTGL42CHvX7HuTGhbRq
+tffp7QNoS38KINTFFSmNyfqQ+ra6Znm+61RWLlknPMLpcRb6zzAOu7l46i1AMk2w
+ZEBt4Gy0Y9Dxo7/JE4cq3AbtHWqvHhYD41kmEW0CgYB2gL7IbeCeQG0wNOb0xHtu
+aXQzK5JUOut811QJLNPzxS+G70bbSRC9gkqFAizCSvCqzao7/3pw9unQzaR0h/3V
+OKEZF8angN9ORP5mOmlMQSvUtAZYfiuCMnZ4EeAhklA9hbAqGScevrdJUxnCN2c3
+DtR0mYMkmrdwISjW9aZnrg==
+-----END PRIVATE KEY-----
diff --git a/tests/gold_tests/tls/ssl/signed-san.pem b/tests/gold_tests/tls/ssl/signed-san.pem
new file mode 100644
index 0000000..15aeed3
--- /dev/null
+++ b/tests/gold_tests/tls/ssl/signed-san.pem
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDITCCAoqgAwIBAgIJALzUy0ELCZDAMA0GCSqGSIb3DQEBCwUAMIGdMQswCQYD
+VQQGEwJVUzELMAkGA1UECBMCSUwxEjAQBgNVBAcTCUNoYW1wYWlnbjEOMAwGA1UE
+ChMFWWFob28xDTALBgNVBAsTBEVkZ2UxKDAmBgNVBAMTH2p1aWNlcHJvZHVjZS5j
+b3JwLm5lMS55YWhvby5jb20xJDAiBgkqhkiG9w0BCQEWFXBlcnNpYS5heml6QHlh
+aG9vLmNvbTAeFw0yMDAyMTgxNjM2MTZaFw0zMDAyMTUxNjM2MTZaMFkxCzAJBgNV
+BAYTAlVTMQswCQYDVQQIDAJJTDESMBAGA1UEBwwJQ2hhbXBhaWduMRUwEwYDVQQK
+DAxWZXJpem9uTWVkaWExEjAQBgNVBAMMCWdyb3VwLmNvbTCCASIwDQYJKoZIhvcN
+AQEBBQADggEPADCCAQoCggEBAK1BrIAvsQdLEQ+5yUcRriYyH+k7k3xVQt1UDqwc
+32TmZQAScvenec2x1UT2ku0cjGByhcJwsA8b54ttICT4L+X6m5vPwYBYd04L/fGl
+2vNCM6JjOLYx6CXoz5WnBow53SzXjN0VmWhLfvIzsaeMsohE7x0zPvjhJIA9lgi+
+fHVcAn/MD24ttjO8VZvCS5Pwbb4mj4geXgXIxfbBNNct5h/rpEpSdMVw0bvKw1tS
+vuEMlHwT+V6TTQre3xgbNiuoK0cgfc4uSko1TeUeFZFDTOs1HB8X6FZ9herdJnn4
+LV6jZFl7xIc9s09uknEYwyTE1GQLJa4zLChzZ0CebUfIjacCAwEAAaMoMCYwJAYD
+VR0RBB0wG4IHb25lLmNvbYIHdHdvLmNvbYIHcnNhLmNvbTANBgkqhkiG9w0BAQsF
+AAOBgQA00nnSb9iqOa8EPJrkbEasuAqe5gw7ehDgaVHLxUrWeJUPwNJdnbYK4hLw
+qWeRKM6Qgxt8rjC/vqDjAxuNjHqFbdhL3supu2bHaBH5xFRqibY5rOY6AkL9SfMU
+r8Lj/NQvqtIzoFM81rhSTDRoHNazVv0TjbcZKTAT25ARX4HQIw==
+-----END CERTIFICATE-----
diff --git a/tests/gold_tests/tls/ssl/signer.pem b/tests/gold_tests/tls/ssl/signer.pem
index 58b9b97..111cd07 100644
--- a/tests/gold_tests/tls/ssl/signer.pem
+++ b/tests/gold_tests/tls/ssl/signer.pem
@@ -1,18 +1,3 @@
------BEGIN RSA PRIVATE KEY-----
-MIICXQIBAAKBgQDWMHOiUF+ORmZjAxI8MWE9dblb7gQSJ36WCXlPFiFx6ynF+S1E
-kXAYpIip5X0pzDUaIbLukxJUAAnOtMEO0PCgxJQUrEtRWh8wiJdbdQJF0Zs/9R+u
-SUgb61f+mdTQvhqefBGx+xrpfAcgtcWiZuSA9Q3fvpDj5WOWSPWXBUuxywIDAQAB
-AoGBAJPxRX2gjFAGWmQbU/YVmXfNH6navh8X/nx9sLeqrpE0AFeJI/ZPiqDKzMal
-B43eSfNxwVi+ZxN0L1ICUbL9KKZvHs/QBxWLA1fGVAXrz7sRplEVvakPpTfHoEnv
-sKaMWVKaK/S5WGbDhElb6zb/Lwo19DsIAPjGYqFvzFJBmobJAkEA9iSeTGkR9X26
-GywZoYrIMlRh34htOIRx1UUq88rFzdrCF21kQ4lhBIkX5OZMMy652i2gyak4OZTe
-YewIv8jw9QJBAN7EQNHG8jPwXfVp91/fqxVQEfumuP2i6uiWWYQgZCmla2+0xcLZ
-pMQ6sQEe10hhTrVnzHgAUVp50Ntn2jwBX78CQF09veGAI9d1Cxzj9cmmAvRd1r2Q
-tp8kPOLnUsALXib+6WtqewLCdcf8DtsdClyRJMIraq85tRzK8fryKNZNzkkCQEgA
-yS7FDj5JgCU15hZgFk1iPx3HCt44jZM2HaL+UUHAzRQjKxTLAl3G1rWVAWLMyQML
-lORoveLvotl4HOruSsMCQQCAx9dV9JUSFoyc1CWILp/FgUH/se4cjQCThGO0DoQQ
-vGTYmntY7j9WRJ9esQrjdD6Clw8zM/45GIBNwnXzqo7Z
------END RSA PRIVATE KEY-----
 -----BEGIN CERTIFICATE-----
 MIICszCCAhwCCQD4jSkztmlO1TANBgkqhkiG9w0BAQsFADCBnTELMAkGA1UEBhMC
 VVMxCzAJBgNVBAgTAklMMRIwEAYDVQQHEwlDaGFtcGFpZ24xDjAMBgNVBAoTBVlh
diff --git a/tests/gold_tests/tls/tls_check_dual_cert_selection.test.py b/tests/gold_tests/tls/tls_check_dual_cert_selection.test.py
new file mode 100644
index 0000000..8c1a25d
--- /dev/null
+++ b/tests/gold_tests/tls/tls_check_dual_cert_selection.test.py
@@ -0,0 +1,127 @@
+'''
+'''
+#  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.
+
+Test.Summary = '''
+Test ATS offering both RSA and EC certificates
+'''
+
+# Define default ATS
+ts = Test.MakeATSProcess("ts", select_ports=True, enable_tls=True)
+server = Test.MakeOriginServer("server", ssl=True)
+dns = Test.MakeDNServer("dns")
+
+request_header = {"headers": "GET / HTTP/1.1\r\n\r\n", "timestamp": "1469733493.993", "body": ""}
+response_header = {"headers": "HTTP/1.1 200 OK\r\nConnection: close\r\n\r\n", "timestamp": "1469733493.993", "body": ""}
+server.addResponse("sessionlog.json", request_header, response_header)
+
+# add ssl materials like key, certificates for the server
+ts.addSSLfile("ssl/signed-foo.pem")
+ts.addSSLfile("ssl/signed-foo.key")
+ts.addSSLfile("ssl/signed-foo-ec.pem")
+ts.addSSLfile("ssl/signed-foo-ec.key")
+ts.addSSLfile("ssl/signed-san.pem")
+ts.addSSLfile("ssl/signed-san.key")
+ts.addSSLfile("ssl/signed-san-ec.pem")
+ts.addSSLfile("ssl/signed-san-ec.key")
+ts.addSSLfile("ssl/signer.pem")
+ts.addSSLfile("ssl/signer.key")
+ts.addSSLfile("ssl/server.pem")
+ts.addSSLfile("ssl/server.key")
+
+ts.Disk.remap_config.AddLine(
+    'map / https://foo.com:{1}'.format(ts.Variables.ssl_port, server.Variables.SSL_Port))
+
+ts.Disk.ssl_multicert_config.AddLines([
+    'ssl_cert_name=signed-foo-ec.pem,signed-foo.pem ssl_key_name=signed-foo-ec.key,signed-foo.key',
+    'ssl_cert_name=signed-san-ec.pem,signed-san.pem ssl_key_name=signed-san-ec.key,signed-san.key',
+    'dest_ip=* ssl_cert_name=server.pem ssl_key_name=server.key'
+])
+
+# Case 1, global config policy=permissive properties=signature
+#         override for foo.com policy=enforced properties=all
+ts.Disk.records_config.update({
+    'proxy.config.ssl.server.cert.path': '{0}'.format(ts.Variables.SSLDir),
+    'proxy.config.ssl.server.private_key.path': '{0}'.format(ts.Variables.SSLDir),
+    'proxy.config.ssl.server.cipher_suite': 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA384:AES128-GCM-SHA256:AES256-GCM-SHA384:ECDHE-RSA-RC4-SHA:ECDHE-RSA-AES128-SHA:ECDHE-RSA-AES256-SHA:RC4-SHA:RC4-MD5:AES128-SHA:AES256-SHA:DES-CBC3-SHA!SRP:!DSS:!PSK:!aNULL:!eNULL:!SSLv2',
+    'proxy.config.url_remap.pristine_host_hdr': 1,
+    'proxy.config.dns.nameservers': '127.0.0.1:{0}'.format(dns.Variables.Port),
+    'proxy.config.exec_thread.autoconfig.scale': 1.0,
+    'proxy.config.dns.resolv_conf': 'NULL',
+    'proxy.config.diags.debug.tags': 'ssl',
+    'proxy.config.diags.debug.enabled': 1
+})
+
+dns.addRecords(records={"foo.com.": ["127.0.0.1"]})
+dns.addRecords(records={"bar.com.": ["127.0.0.1"]})
+
+# Should receive a EC cert
+tr = Test.AddTestRun("Default for foo should return EC cert")
+tr.Setup.Copy("ssl/signer.pem")
+tr.Processes.Default.Command = "echo foo | openssl s_client -servername foo.com -connect 127.0.0.1:{0}".format(ts.Variables.ssl_port)
+tr.ReturnCode = 0
+tr.Processes.Default.StartBefore(server)
+tr.Processes.Default.StartBefore(dns)
+tr.Processes.Default.StartBefore(Test.Processes.ts, ready=When.PortOpen(ts.Variables.ssl_port))
+tr.StillRunningAfter = server
+tr.StillRunningAfter = ts
+tr.Processes.Default.Streams.All += Testers.ContainsExpression("Peer signature type: ECDSA", "Should select EC cert")
+
+# Should receive a RSA cert
+tr = Test.AddTestRun("Only offer RSA ciphers, should receive RSA cert")
+tr.Processes.Default.Command = "echo foo | openssl s_client -servername foo.com -sigalgs 'RSA-PSS+SHA256' -connect 127.0.0.1:{0}".format(ts.Variables.ssl_port)
+tr.ReturnCode = 0
+tr.StillRunningAfter = server
+tr.StillRunningAfter = ts
+tr.Processes.Default.Streams.All += Testers.ContainsExpression("Peer signature type: RSA-PSS", "Should select RSA cert")
+
+# Should receive a EC cert
+tr = Test.AddTestRun("Default for one.com should return EC cert")
+tr.Processes.Default.Command = "echo foo | openssl s_client -servername one.com -connect 127.0.0.1:{0}".format(ts.Variables.ssl_port)
+tr.ReturnCode = 0
+tr.StillRunningAfter = server
+tr.StillRunningAfter = ts
+tr.Processes.Default.Streams.All += Testers.ContainsExpression("Peer signature type: ECDSA", "Should select EC cert")
+tr.Processes.Default.Streams.All += Testers.ContainsExpression("CN = group.com", "Should select a group SAN");
+
+# Should receive a RSA cert
+tr = Test.AddTestRun("Only offer RSA ciphers, should receive RSA cert")
+tr.Processes.Default.Command = "echo foo | openssl s_client -servername one.com -sigalgs 'RSA-PSS+SHA256' -connect 127.0.0.1:{0}".format(ts.Variables.ssl_port)
+tr.ReturnCode = 0
+tr.StillRunningAfter = server
+tr.StillRunningAfter = ts
+tr.Processes.Default.Streams.All += Testers.ContainsExpression("Peer signature type: RSA-PSS", "Should select RSA cert")
+tr.Processes.Default.Streams.All += Testers.ContainsExpression("CN = group.com", "Should select a group SAN");
+
+# Should receive a RSA cert
+tr = Test.AddTestRun("rsa.com only in rsa cert")
+tr.Processes.Default.Command = "echo foo | openssl s_client -servername rsa.com -connect 127.0.0.1:{0}".format(ts.Variables.ssl_port)
+tr.ReturnCode = 0
+tr.StillRunningAfter = server
+tr.StillRunningAfter = ts
+tr.Processes.Default.Streams.All += Testers.ContainsExpression("Peer signature type: RSA-PSS", "Should select RSA cert")
+tr.Processes.Default.Streams.All += Testers.ContainsExpression("CN = group.com", "Should select a group SAN");
+
+# Should receive a EC cert
+tr = Test.AddTestRun("ec.com only in ec cert")
+tr.Processes.Default.Command = "echo foo | openssl s_client -servername ec.com -connect 127.0.0.1:{0}".format(ts.Variables.ssl_port)
+tr.ReturnCode = 0
+tr.StillRunningAfter = server
+tr.StillRunningAfter = ts
+tr.Processes.Default.Streams.All += Testers.ContainsExpression("Peer signature type: ECDSA", "Should select EC cert")
+tr.Processes.Default.Streams.All += Testers.ContainsExpression("CN = group.com", "Should select a group SAN");
+


[trafficserver] 04/06: Do not fail multicert load if line does not create entry (#6760)

Posted by zw...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

zwoop pushed a commit to branch 9.0.x
in repository https://gitbox.apache.org/repos/asf/trafficserver.git

commit 31d49587d48e113dc1c378d3510ee76cbd19911e
Author: Susan Hinrichs <sh...@yahoo-inc.com>
AuthorDate: Wed May 13 10:03:47 2020 -0500

    Do not fail multicert load if line does not create entry (#6760)
    
    Co-authored-by: Susan Hinrichs <sh...@verizonmedia.com>
    (cherry picked from commit 0265ac7f8a9f0c4773fc6f7d00f6f212bdc08558)
---
 iocore/net/QUICMultiCertConfigLoader.cc | 12 ++++++++----
 iocore/net/SSLUtils.cc                  | 12 ++++++++----
 2 files changed, 16 insertions(+), 8 deletions(-)

diff --git a/iocore/net/QUICMultiCertConfigLoader.cc b/iocore/net/QUICMultiCertConfigLoader.cc
index 288c0f0..cf9c74f 100644
--- a/iocore/net/QUICMultiCertConfigLoader.cc
+++ b/iocore/net/QUICMultiCertConfigLoader.cc
@@ -190,8 +190,13 @@ QUICMultiCertConfigLoader::_store_ssl_ctx(SSLCertLookup *lookup, const shared_SS
   shared_ssl_ticket_key_block keyblock = nullptr;
 
   if (!ctx || !multi_cert_params || !this->_store_single_ssl_ctx(lookup, multi_cert_params, ctx, common_names)) {
-    lookup->is_valid = false;
-    retval           = false;
+    retval = false;
+    std::string names;
+    for (auto name : data.cert_names_list) {
+      names.append(name);
+      names.append(" ");
+    }
+    Warning("QUIC: Failed to insert SSL_CTX for certificate %s entries for names already made", names.c_str());
   }
 
   for (auto iter = unique_names.begin(); retval && iter != unique_names.end(); ++iter) {
@@ -205,8 +210,7 @@ QUICMultiCertConfigLoader::_store_ssl_ctx(SSLCertLookup *lookup, const shared_SS
 
     shared_SSL_CTX unique_ctx(this->init_server_ssl_ctx(single_data, multi_cert_params.get(), iter->second), SSL_CTX_free);
     if (!unique_ctx || !this->_store_single_ssl_ctx(lookup, multi_cert_params, unique_ctx, iter->second)) {
-      lookup->is_valid = false;
-      retval           = false;
+      retval = false;
     }
   }
 
diff --git a/iocore/net/SSLUtils.cc b/iocore/net/SSLUtils.cc
index ffc6e8a..5d297a3 100644
--- a/iocore/net/SSLUtils.cc
+++ b/iocore/net/SSLUtils.cc
@@ -1418,8 +1418,13 @@ SSLMultiCertConfigLoader::_store_ssl_ctx(SSLCertLookup *lookup, const shared_SSL
   shared_SSL_CTX ctx(this->init_server_ssl_ctx(data, sslMultCertSettings.get(), common_names), SSL_CTX_free);
 
   if (!ctx || !sslMultCertSettings || !this->_store_single_ssl_ctx(lookup, sslMultCertSettings, ctx, common_names)) {
-    lookup->is_valid = false;
-    retval           = false;
+    retval = false;
+    std::string names;
+    for (auto name : data.cert_names_list) {
+      names.append(name);
+      names.append(" ");
+    }
+    Warning("Failed to insert SSL_CTX for certificate %s entries for names already made", names.c_str());
   }
 
   for (auto iter = unique_names.begin(); retval && iter != unique_names.end(); ++iter) {
@@ -1433,8 +1438,7 @@ SSLMultiCertConfigLoader::_store_ssl_ctx(SSLCertLookup *lookup, const shared_SSL
 
     shared_SSL_CTX unique_ctx(this->init_server_ssl_ctx(single_data, sslMultCertSettings.get(), iter->second), SSL_CTX_free);
     if (!unique_ctx || !this->_store_single_ssl_ctx(lookup, sslMultCertSettings, unique_ctx, iter->second)) {
-      lookup->is_valid = false;
-      retval           = false;
+      retval = false;
     }
   }
 


[trafficserver] 02/06: Fixes memory leak loading certs

Posted by zw...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

zwoop pushed a commit to branch 9.0.x
in repository https://gitbox.apache.org/repos/asf/trafficserver.git

commit 7c08b051cd8a70a0e271c29b8f4d55dffa78529e
Author: Randall Meyer <rr...@apache.org>
AuthorDate: Wed Apr 15 08:44:21 2020 -0700

    Fixes memory leak loading certs
    
    This leak was introduced by f729c9dc41ff1635132f4bdc6331ce826f3bc2fe
    
    (cherry picked from commit 9fb6f961baf31383b106d734f524e89406e2fa1e)
---
 iocore/net/SSLUtils.cc | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/iocore/net/SSLUtils.cc b/iocore/net/SSLUtils.cc
index 2c32232..6b3774d 100644
--- a/iocore/net/SSLUtils.cc
+++ b/iocore/net/SSLUtils.cc
@@ -2012,7 +2012,9 @@ SSLMultiCertConfigLoader::load_certs_and_cross_reference_names(std::vector<X509
           name_set.insert(dns.get());
         }
       }
+      sk_GENERAL_NAME_pop_free(names, GENERAL_NAME_free);
     }
+
     if (first_pass) {
       first_pass   = false;
       common_names = name_set;


[trafficserver] 03/06: Fix SDK_API_TSSslServerContextCreate

Posted by zw...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

zwoop pushed a commit to branch 9.0.x
in repository https://gitbox.apache.org/repos/asf/trafficserver.git

commit d18538b232131e83066b46c830ce707ad78ce6c8
Author: Susan Hinrichs <sh...@verizonmedia.com>
AuthorDate: Fri Mar 13 18:45:32 2020 +0000

    Fix SDK_API_TSSslServerContextCreate
    
    (cherry picked from commit 3808b31d3c679d4490d144383ae6df70a75fd150)
---
 iocore/net/SSLUtils.cc | 12 +++++++-----
 1 file changed, 7 insertions(+), 5 deletions(-)

diff --git a/iocore/net/SSLUtils.cc b/iocore/net/SSLUtils.cc
index 6b3774d..ffc6e8a 100644
--- a/iocore/net/SSLUtils.cc
+++ b/iocore/net/SSLUtils.cc
@@ -1917,16 +1917,18 @@ SSLMultiCertConfigLoader::load_certs_and_cross_reference_names(std::vector<X509
                                                                std::set<std::string> &common_names,
                                                                std::unordered_map<int, std::set<std::string>> &unique_names)
 {
-  SimpleTokenizer cert_tok(sslMultCertSettings->cert ? (const char *)sslMultCertSettings->cert : "", SSL_CERT_SEPARATE_DELIM);
-  SimpleTokenizer key_tok((sslMultCertSettings->key ? (const char *)sslMultCertSettings->key : ""), SSL_CERT_SEPARATE_DELIM);
+  SimpleTokenizer cert_tok(sslMultCertSettings && sslMultCertSettings->cert ? (const char *)sslMultCertSettings->cert : "",
+                           SSL_CERT_SEPARATE_DELIM);
+  SimpleTokenizer key_tok((sslMultCertSettings && sslMultCertSettings->key ? (const char *)sslMultCertSettings->key : ""),
+                          SSL_CERT_SEPARATE_DELIM);
 
-  if (sslMultCertSettings->key && cert_tok.getNumTokensRemaining() != key_tok.getNumTokensRemaining()) {
+  if (sslMultCertSettings && sslMultCertSettings->key && cert_tok.getNumTokensRemaining() != key_tok.getNumTokensRemaining()) {
     Error("the number of certificates in ssl_cert_name and ssl_key_name doesn't match");
     return false;
   }
 
   SimpleTokenizer ca_tok("", SSL_CERT_SEPARATE_DELIM);
-  if (sslMultCertSettings->ca) {
+  if (sslMultCertSettings && sslMultCertSettings->ca) {
     ca_tok.setString(sslMultCertSettings->ca);
     if (cert_tok.getNumTokensRemaining() != ca_tok.getNumTokensRemaining()) {
       Error("the number of certificates in ssl_cert_name and ssl_ca_name doesn't match");
@@ -1935,7 +1937,7 @@ SSLMultiCertConfigLoader::load_certs_and_cross_reference_names(std::vector<X509
   }
 
   SimpleTokenizer ocsp_tok("", SSL_CERT_SEPARATE_DELIM);
-  if (sslMultCertSettings->ocsp_response) {
+  if (sslMultCertSettings && sslMultCertSettings->ocsp_response) {
     ocsp_tok.setString(sslMultCertSettings->ocsp_response);
     if (cert_tok.getNumTokensRemaining() != ocsp_tok.getNumTokensRemaining()) {
       Error("the number of certificates in ssl_cert_name and ssl_ocsp_name doesn't match");