This is an automated email from the ASF dual-hosted git repository.
masaori pushed a commit to branch quic-latest
in repository https://gitbox.apache.org/repos/asf/trafficserver.git
The following commit(s) were added to refs/heads/quic-latest by this push:
new 5e3ca19 QUIC: Load multiple certs
5e3ca19 is described below
commit 5e3ca198bc73f493c1df3b97aae6ecb058adf06f
Author: Masaori Koshiba <[email protected]>
AuthorDate: Wed Feb 20 12:16:14 2019 +0900
QUIC: Load multiple certs
---
iocore/net/QUICNetProcessor.cc | 1 +
iocore/net/QUICNetVConnection.cc | 4 +-
iocore/net/SSLUtils.cc | 75 ------------
iocore/net/quic/QUICConfig.cc | 255 ++++++++++++++++++++++++++++++++++-----
iocore/net/quic/QUICConfig.h | 32 ++++-
iocore/net/quic/QUICGlobals.cc | 63 +++++++++-
iocore/net/quic/QUICGlobals.h | 2 +
7 files changed, 321 insertions(+), 111 deletions(-)
diff --git a/iocore/net/QUICNetProcessor.cc b/iocore/net/QUICNetProcessor.cc
index 8c63668..5a2e373 100644
--- a/iocore/net/QUICNetProcessor.cc
+++ b/iocore/net/QUICNetProcessor.cc
@@ -63,6 +63,7 @@ QUICNetProcessor::start(int, size_t stacksize)
// This initialization order matters ...
// QUICInitializeLibrary();
QUICConfig::startup();
+ QUICCertConfig::startup();
#ifdef TLS1_3_VERSION_DRAFT_TXT
// FIXME: remove this when TLS1_3_VERSION_DRAFT_TXT is removed
diff --git a/iocore/net/QUICNetVConnection.cc b/iocore/net/QUICNetVConnection.cc
index 410b235..e8c7b53 100644
--- a/iocore/net/QUICNetVConnection.cc
+++ b/iocore/net/QUICNetVConnection.cc
@@ -240,10 +240,12 @@ QUICNetVConnection::start()
this->_five_tuple.update(this->local_addr, this->remote_addr, SOCK_DGRAM);
// Version 0x00000001 uses stream 0 for cryptographic handshake with TLS
1.3, but newer version may not
if (this->direction() == NET_VCONNECTION_IN) {
+ QUICCertConfig::scoped_config server_cert;
+
this->_pp_key_info.set_context(QUICPacketProtectionKeyInfo::Context::SERVER);
this->_ack_frame_manager.set_ack_delay_exponent(params->ack_delay_exponent_in());
this->_reset_token =
QUICStatelessResetToken(this->_quic_connection_id, params->instance_id());
- this->_hs_protocol =
this->_setup_handshake_protocol(params->server_ssl_ctx());
+ this->_hs_protocol =
this->_setup_handshake_protocol(server_cert->ssl_default);
this->_handshake_handler = new QUICHandshake(this, this->_hs_protocol,
this->_reset_token, params->stateless_retry());
this->_ack_frame_manager.set_max_ack_delay(params->max_ack_delay_in());
this->_schedule_ack_manager_periodic(params->max_ack_delay_in());
diff --git a/iocore/net/SSLUtils.cc b/iocore/net/SSLUtils.cc
index 7c7c7fe..c83b314 100644
--- a/iocore/net/SSLUtils.cc
+++ b/iocore/net/SSLUtils.cc
@@ -1557,81 +1557,6 @@ ssl_extract_certificate(const matcher_line *line_info,
SSLMultiCertConfigParams
return true;
}
-// TODO: remove this function and setup SSL_CTX for QUIC somehow
-bool
-SSLParseCertificateConfiguration(const SSLConfigParams *params, SSL_CTX
*ssl_ctx)
-{
- char *tok_state = nullptr;
- char *line = nullptr;
- ats_scoped_str file_buf;
- unsigned line_num = 0;
- matcher_line line_info;
-
- const matcher_tags sslCertTags = {nullptr, nullptr, nullptr, nullptr,
nullptr, nullptr, false};
-
- Note("loading SSL certificate configuration from %s",
params->configFilePath);
-
- if (params->configFilePath) {
- file_buf = readIntoBuffer(params->configFilePath, __func__, nullptr);
- }
-
- if (!file_buf) {
- Error("failed to read SSL certificate configuration from %s",
params->configFilePath);
- return false;
- }
-
- // Optionally elevate/allow file access to read root-only
- // certificates. The destructor will drop privilege for us.
- uint32_t elevate_setting = 0;
- REC_ReadConfigInteger(elevate_setting,
"proxy.config.ssl.cert.load_elevated");
- ElevateAccess elevate_access(elevate_setting ? ElevateAccess::FILE_PRIVILEGE
: 0);
-
- line = tokLine(file_buf, &tok_state);
- while (line != nullptr) {
- line_num++;
-
- // Skip all blank spaces at beginning of line.
- while (*line && isspace(*line)) {
- line++;
- }
-
- if (*line != '\0' && *line != '#') {
- SSLMultiCertConfigParams sslMultiCertSettings;
- const char *errPtr;
-
- errPtr = parseConfigLine(line, &line_info, &sslCertTags);
-
- if (errPtr != nullptr) {
- RecSignalWarning(REC_SIGNAL_CONFIG_ERROR, "%s: discarding %s entry at
line %d: %s", __func__, params->configFilePath,
- line_num, errPtr);
- } else {
- if (ssl_extract_certificate(&line_info, sslMultiCertSettings)) {
- // There must be a certificate specified unless the tunnel action is
set
- if (sslMultiCertSettings.cert || sslMultiCertSettings.opt !=
SSLCertContext::OPT_TUNNEL) {
- if (SSL_CTX_use_PrivateKey_file(ssl_ctx,
sslMultiCertSettings.key.get(), SSL_FILETYPE_PEM) != 1) {
- Error("Couldn't load private_key: %s",
sslMultiCertSettings.key.get());
- return false;
- }
-
- if (SSL_CTX_use_certificate_chain_file(ssl_ctx,
sslMultiCertSettings.cert.get()) != 1) {
- Error("Couldn't load cert: %s", sslMultiCertSettings.cert.get());
- return false;
- }
-
- return true;
-
- } else {
- Warning("No ssl_cert_name specified and no tunnel action set");
- }
- }
- }
- }
-
- line = tokLine(nullptr, &tok_state);
- }
- return true;
-}
-
bool
SSLMultiCertConfigLoader::load(SSLCertLookup *lookup)
{
diff --git a/iocore/net/quic/QUICConfig.cc b/iocore/net/quic/QUICConfig.cc
index 8ec9e4f..139df1a 100644
--- a/iocore/net/quic/QUICConfig.cc
+++ b/iocore/net/quic/QUICConfig.cc
@@ -28,10 +28,13 @@
#include <records/I_RecHttp.h>
#include "P_SSLConfig.h"
+#include "P_OCSPStapling.h"
#include "QUICGlobals.h"
#include "QUICTransportParameters.h"
+#define QUICConfDebug(fmt, ...) Debug("quic_conf", fmt, ##__VA_ARGS__)
+
// OpenSSL protocol-lists format (vector of 8-bit length-prefixed, byte
strings)
// https://www.openssl.org/docs/manmaster/man3/SSL_CTX_set_alpn_protos.html
// Should be integrate with IP_PROTO_TAG_HTTP_QUIC in ts/ink_inet.h ?
@@ -40,6 +43,7 @@ static constexpr std::string_view
QUIC_ALPN_PROTO_LIST("\5hq-18"sv);
int QUICConfig::_config_id = 0;
int QUICConfigParams::_connection_table_size = 65521;
+int QUICCertConfig::_config_id = 0;
static SSL_CTX *
quic_new_ssl_ctx()
@@ -71,29 +75,6 @@ quic_new_ssl_ctx()
}
static SSL_CTX *
-quic_init_server_ssl_ctx(const QUICConfigParams *params)
-{
- SSL_CTX *ssl_ctx = quic_new_ssl_ctx();
-
- SSLConfig::scoped_config ssl_params;
- SSLParseCertificateConfiguration(ssl_params, ssl_ctx);
-
- if (SSL_CTX_check_private_key(ssl_ctx) != 1) {
- Error("check private key failed");
- }
-
- SSL_CTX_set_alpn_select_cb(ssl_ctx, QUIC::ssl_select_next_protocol, nullptr);
-
- if (params->server_supported_groups() != nullptr) {
- if (SSL_CTX_set1_groups_list(ssl_ctx, params->server_supported_groups())
!= 1) {
- Error("SSL_CTX_set1_groups_list failed");
- }
- }
-
- return ssl_ctx;
-}
-
-static SSL_CTX *
quic_init_client_ssl_ctx(const QUICConfigParams *params)
{
SSL_CTX *ssl_ctx = quic_new_ssl_ctx();
@@ -125,7 +106,6 @@ QUICConfigParams::~QUICConfigParams()
this->_server_supported_groups = (char
*)ats_free_null(this->_server_supported_groups);
this->_client_supported_groups = (char
*)ats_free_null(this->_client_supported_groups);
- SSL_CTX_free(this->_server_ssl_ctx);
SSL_CTX_free(this->_client_ssl_ctx);
};
@@ -194,7 +174,6 @@ QUICConfigParams::initialize()
REC_EstablishStaticConfigInt32U(this->_cc_persistent_congestion_threshold,
"proxy.config.quic.congestion_control.persistent_congestion_threshold");
- this->_server_ssl_ctx = quic_init_server_ssl_ctx(this);
this->_client_ssl_ctx = quic_init_client_ssl_ctx(this);
}
@@ -375,12 +354,6 @@ QUICConfigParams::client_supported_groups() const
}
SSL_CTX *
-QUICConfigParams::server_ssl_ctx() const
-{
- return this->_server_ssl_ctx;
-}
-
-SSL_CTX *
QUICConfigParams::client_ssl_ctx() const
{
return this->_client_ssl_ctx;
@@ -489,3 +462,223 @@ QUICConfig::release(QUICConfigParams *params)
{
configProcessor.release(_config_id, params);
}
+
+//
+// QUICCertConfig
+//
+void
+QUICCertConfig::startup()
+{
+ reconfigure();
+}
+
+void
+QUICCertConfig::reconfigure()
+{
+ SSLConfig::scoped_config params;
+ SSLCertLookup *lookup = new SSLCertLookup();
+
+ QUICMultiCertConfigLoader loader(params);
+ loader.load(lookup);
+
+ _config_id = configProcessor.set(_config_id, lookup);
+}
+
+SSLCertLookup *
+QUICCertConfig::acquire()
+{
+ return static_cast<SSLCertLookup *>(configProcessor.get(_config_id));
+}
+
+void
+QUICCertConfig::release(SSLCertLookup *lookup)
+{
+ configProcessor.release(_config_id, lookup);
+}
+
+//
+// QUICMultiCertConfigLoader
+//
+SSL_CTX *
+QUICMultiCertConfigLoader::default_server_ssl_ctx()
+{
+ return quic_new_ssl_ctx();
+}
+
+SSL_CTX *
+QUICMultiCertConfigLoader::init_server_ssl_ctx(std::vector<X509 *> &cert_list,
const SSLMultiCertConfigParams *multi_cert_params)
+{
+ const SSLConfigParams *params = this->_params;
+
+ SSL_CTX *ctx = this->default_server_ssl_ctx();
+
+ if (multi_cert_params) {
+ if (multi_cert_params->dialog) {
+ // TODO: dialog support
+ }
+
+ if (multi_cert_params->cert) {
+ if (!SSLMultiCertConfigLoader::load_certs(ctx, cert_list, params,
multi_cert_params)) {
+ goto fail;
+ }
+ }
+
+ // SSL_CTX_load_verify_locations() builds the cert chain from the
+ // serverCACertFilename if that is not nullptr. Otherwise, it uses the
hashed
+ // symlinks in serverCACertPath.
+ //
+ // if ssl_ca_name is NOT configured for this cert in ssl_multicert.config
+ // AND
+ // if proxy.config.ssl.CA.cert.filename and proxy.config.ssl.CA.cert.path
+ // are configured
+ // pass that file as the chain (include all certs in that file)
+ // else if proxy.config.ssl.CA.cert.path is configured (and
+ // proxy.config.ssl.CA.cert.filename is nullptr)
+ // use the hashed symlinks in that directory to build the chain
+ if (!multi_cert_params->ca && params->serverCACertPath != nullptr) {
+ if ((!SSL_CTX_load_verify_locations(ctx, params->serverCACertFilename,
params->serverCACertPath)) ||
+ (!SSL_CTX_set_default_verify_paths(ctx))) {
+ Error("invalid CA Certificate file or CA Certificate path");
+ goto fail;
+ }
+ }
+ }
+
+ if (params->clientCertLevel != 0) {
+ // TODO: client cert support
+ }
+
+ if (!SSLMultiCertConfigLoader::set_session_id_context(ctx, params,
multi_cert_params)) {
+ goto fail;
+ }
+
+ if (params->server_tls13_cipher_suites != nullptr) {
+ if (!SSL_CTX_set_ciphersuites(ctx, params->server_tls13_cipher_suites)) {
+ Error("invalid tls server cipher suites in records.config");
+ goto fail;
+ }
+ }
+
+ if (params->server_groups_list != nullptr) {
+ if (!SSL_CTX_set1_groups_list(ctx, params->server_groups_list)) {
+ Error("invalid groups list for server in records.config");
+ goto fail;
+ }
+ }
+
+ // SSL_CTX_set_info_callback(ctx, ssl_callback_info);
+
+ SSL_CTX_set_alpn_select_cb(ctx, QUIC::ssl_select_next_protocol, nullptr);
+
+ 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)) {
+ Warning("failed to configure SSL_CTX for OCSP Stapling info for
certificate at %s", cert_name);
+ }
+ }
+ } else {
+ QUICConfDebug("SSL OCSP Stapling is disabled");
+ }
+
+ if (SSLConfigParams::init_ssl_ctx_cb) {
+ SSLConfigParams::init_ssl_ctx_cb(ctx, true);
+ }
+
+ return ctx;
+
+fail:
+ SSLReleaseContext(ctx);
+ for (auto cert : cert_list) {
+ X509_free(cert);
+ }
+
+ return nullptr;
+}
+
+SSL_CTX *
+QUICMultiCertConfigLoader::_store_ssl_ctx(SSLCertLookup *lookup, const
SSLMultiCertConfigParams *multi_cert_params)
+{
+ std::vector<X509 *> cert_list;
+ SSL_CTX *ctx = this->init_server_ssl_ctx(cert_list,
multi_cert_params);
+ ssl_ticket_key_block *keyblock = nullptr;
+ bool inserted = false;
+
+ if (!ctx || !multi_cert_params) {
+ lookup->is_valid = false;
+ return nullptr;
+ }
+
+ const char *certname = multi_cert_params->cert.get();
+ for (auto cert : cert_list) {
+ if (0 > SSLMultiCertConfigLoader::check_server_cert_now(cert, certname)) {
+ /* 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);
+ 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->opt, keyblock)) >= 0) {
+ inserted = true;
+ lookup->ssl_default = ctx;
+ this->_set_handshake_callbacks(ctx);
+ }
+ } 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->opt,
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;
+ }
+ }
+ }
+
+ // 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->opt), cert, certname)) {
+ inserted = true;
+ }
+ }
+
+ if (inserted) {
+ if (SSLConfigParams::init_ssl_ctx_cb) {
+ SSLConfigParams::init_ssl_ctx_cb(ctx, true);
+ }
+ }
+
+ if (!inserted) {
+ SSLReleaseContext(ctx);
+ ctx = nullptr;
+ }
+
+ for (auto &i : cert_list) {
+ X509_free(i);
+ }
+
+ return ctx;
+}
+
+void
+QUICMultiCertConfigLoader::_set_handshake_callbacks(SSL_CTX *ssl_ctx)
+{
+ SSL_CTX_set_cert_cb(ssl_ctx, QUIC::ssl_cert_cb, nullptr);
+ SSL_CTX_set_tlsext_servername_callback(ssl_ctx, QUIC::ssl_sni_cb);
+
+ // Set client hello callback if needed
+ // SSL_CTX_set_client_hello_cb(ctx, QUIC::ssl_client_hello_cb, nullptr);
+}
diff --git a/iocore/net/quic/QUICConfig.h b/iocore/net/quic/QUICConfig.h
index f40e012..4ab237a 100644
--- a/iocore/net/quic/QUICConfig.h
+++ b/iocore/net/quic/QUICConfig.h
@@ -26,6 +26,8 @@
#include <openssl/ssl.h>
#include "ProxyConfig.h"
+#include "P_SSLCertLookup.h"
+#include "P_SSLUtils.h"
class QUICConfigParams : public ConfigInfo
{
@@ -45,7 +47,6 @@ public:
const char *client_supported_groups() const;
const char *session_file() const;
- SSL_CTX *server_ssl_ctx() const;
SSL_CTX *client_ssl_ctx() const;
// Transport Parameters
@@ -101,8 +102,6 @@ private:
char *_client_supported_groups = nullptr;
char *_session_file = nullptr;
- // TODO: integrate with SSLCertLookup or SNIConfigParams
- SSL_CTX *_server_ssl_ctx = nullptr;
SSL_CTX *_client_ssl_ctx = nullptr;
// Transport Parameters
@@ -156,3 +155,30 @@ public:
private:
static int _config_id;
};
+
+class QUICCertConfig
+{
+public:
+ static void startup();
+ static void reconfigure();
+ static SSLCertLookup *acquire();
+ static void release(SSLCertLookup *lookup);
+
+ using scoped_config = ConfigProcessor::scoped_config<QUICCertConfig,
SSLCertLookup>;
+
+private:
+ static int _config_id;
+};
+
+class QUICMultiCertConfigLoader : public SSLMultiCertConfigLoader
+{
+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;
+
+private:
+ virtual SSL_CTX *_store_ssl_ctx(SSLCertLookup *lookup, const
SSLMultiCertConfigParams *multi_cert_params) override;
+ virtual void _set_handshake_callbacks(SSL_CTX *ssl_ctx) override;
+};
diff --git a/iocore/net/quic/QUICGlobals.cc b/iocore/net/quic/QUICGlobals.cc
index 0520f3e..611bddb 100644
--- a/iocore/net/quic/QUICGlobals.cc
+++ b/iocore/net/quic/QUICGlobals.cc
@@ -26,10 +26,14 @@
#include <cstring>
#include "P_SSLNextProtocolSet.h"
+
#include "QUICStats.h"
#include "QUICConfig.h"
#include "QUICConnection.h"
+#define QUICGlobalDebug(fmt, ...) Debug("quic_global", fmt, ##__VA_ARGS__)
+#define QUICGlobalQCDebug(qc, fmt, ...) Debug("quic_global", "[%s] " fmt,
qc->cids().data(), ##__VA_ARGS__)
+
RecRawStatBlock *quic_rsb;
int QUIC::ssl_quic_qc_index = -1;
@@ -67,7 +71,7 @@ QUIC::ssl_client_new_session(SSL *ssl, SSL_SESSION *session)
QUICConfig::scoped_config params;
auto file = BIO_new_file(params->session_file(), "w");
if (file == nullptr) {
- Debug("quic_global", "Could not write TLS session in %s",
params->session_file());
+ QUICGlobalDebug("Could not write TLS session in %s",
params->session_file());
return 0;
}
@@ -76,6 +80,63 @@ QUIC::ssl_client_new_session(SSL *ssl, SSL_SESSION *session)
return 0;
}
+int
+QUIC::ssl_cert_cb(SSL *ssl, void * /*arg*/)
+{
+ SSL_CTX *ctx = nullptr;
+ SSLCertContext *cc = nullptr;
+ QUICCertConfig::scoped_config lookup;
+ const char *servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
+ QUICConnection *qc = static_cast<QUICConnection *>(SSL_get_ex_data(ssl,
QUIC::ssl_quic_qc_index));
+
+ if (servername == nullptr) {
+ servername = "";
+ }
+ QUICGlobalQCDebug(qc, "SNI=%s", servername);
+
+ // The incoming SSL_CTX is either the one mapped from the inbound IP address
or the default one. If we
+ // don't find a name-based match at this point, we *do not* want to mess
with the context because we've
+ // already made a best effort to find the best match.
+ if (likely(servername)) {
+ cc = lookup->find((char *)servername);
+ if (cc && cc->ctx) {
+ ctx = cc->ctx;
+ }
+ }
+
+ // If there's no match on the server name, try to match on the peer address.
+ if (ctx == nullptr) {
+ QUICFiveTuple five_tuple = qc->five_tuple();
+ IpEndpoint ip = five_tuple.destination();
+ cc = lookup->find(ip);
+
+ if (cc && cc->ctx) {
+ ctx = cc->ctx;
+ }
+ }
+
+ bool found = true;
+ if (ctx != nullptr) {
+ SSL_set_SSL_CTX(ssl, ctx);
+ } else {
+ found = false;
+ }
+
+ ctx = SSL_get_SSL_CTX(ssl);
+
+ QUICGlobalQCDebug(qc, "%s SSL_CTX %p for requested name '%s'", found ?
"found" : "using", ctx, servername);
+
+ return 1;
+}
+
+int
+QUIC::ssl_sni_cb(SSL *ssl, int * /*ad*/, void * /*arg*/)
+{
+ // XXX: add SNIConfig support ?
+ // XXX: add TRANSPORT_BLIND_TUNNEL support ?
+ return 1;
+}
+
void
QUIC::_register_stats()
{
diff --git a/iocore/net/quic/QUICGlobals.h b/iocore/net/quic/QUICGlobals.h
index 4da21d1..cd4ac54 100644
--- a/iocore/net/quic/QUICGlobals.h
+++ b/iocore/net/quic/QUICGlobals.h
@@ -34,6 +34,8 @@ public:
static int ssl_select_next_protocol(SSL *ssl, const unsigned char **out,
unsigned char *outlen, const unsigned char *in,
unsigned inlen, void *);
static int ssl_client_new_session(SSL *ssl, SSL_SESSION *session);
+ static int ssl_cert_cb(SSL *ssl, void *arg);
+ static int ssl_sni_cb(SSL *ssl, int *ad, void *arg);
static int ssl_quic_qc_index;
static int ssl_quic_tls_index;