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 <shinr...@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"); +