nghttpx: Select certificate by client's supported signature algo
nghttpx supports multiple certificates using --subcert option. Previously, SNI hostname is used to select certificate. With this commit, signature algorithm presented by client is also taken into consideration. nghttpx now accepts certificates which share the same hostname (CN, SAN), but have different signature algorithm (e.g., ECDSA+SHA256, RSA+SHA256). Currently, this feature requires OpenSSL >= 1.0.2. BoringSSL, and LibreSSL do not work since they lack required APIs.
This commit is contained in:
parent
aad3e275d1
commit
68a724cf7b
|
@ -72,8 +72,8 @@ int main(int argc, char *argv[]) {
|
||||||
// add the tests to the suite
|
// add the tests to the suite
|
||||||
if (!CU_add_test(pSuite, "ssl_create_lookup_tree",
|
if (!CU_add_test(pSuite, "ssl_create_lookup_tree",
|
||||||
shrpx::test_shrpx_ssl_create_lookup_tree) ||
|
shrpx::test_shrpx_ssl_create_lookup_tree) ||
|
||||||
!CU_add_test(pSuite, "ssl_cert_lookup_tree_add_cert_from_x509",
|
!CU_add_test(pSuite, "ssl_cert_lookup_tree_add_ssl_ctx",
|
||||||
shrpx::test_shrpx_ssl_cert_lookup_tree_add_cert_from_x509) ||
|
shrpx::test_shrpx_ssl_cert_lookup_tree_add_ssl_ctx) ||
|
||||||
!CU_add_test(pSuite, "ssl_tls_hostname_match",
|
!CU_add_test(pSuite, "ssl_tls_hostname_match",
|
||||||
shrpx::test_shrpx_ssl_tls_hostname_match) ||
|
shrpx::test_shrpx_ssl_tls_hostname_match) ||
|
||||||
!CU_add_test(pSuite, "http2_add_header", shrpx::test_http2_add_header) ||
|
!CU_add_test(pSuite, "http2_add_header", shrpx::test_http2_add_header) ||
|
||||||
|
|
11
src/shrpx.cc
11
src/shrpx.cc
|
@ -1943,9 +1943,14 @@ SSL/TLS:
|
||||||
--subcert=<KEYPATH>:<CERTPATH>[[;<PARAM>]...]
|
--subcert=<KEYPATH>:<CERTPATH>[[;<PARAM>]...]
|
||||||
Specify additional certificate and private key file.
|
Specify additional certificate and private key file.
|
||||||
nghttpx will choose certificates based on the hostname
|
nghttpx will choose certificates based on the hostname
|
||||||
indicated by client using TLS SNI extension. This
|
indicated by client using TLS SNI extension. If nghttpx
|
||||||
option can be used multiple times. To make OCSP
|
is built with OpenSSL >= 1.0.2, signature algorithms
|
||||||
stapling work, <CERTPATH> must be absolute path.
|
(e.g., ECDSA+SHA256, RSA+SHA256) presented by client are
|
||||||
|
also taken into consideration. This allows nghttpx to
|
||||||
|
send ECDSA certificate to modern clients, while sending
|
||||||
|
RSA based certificate to older clients. This option can
|
||||||
|
be used multiple times. To make OCSP stapling work,
|
||||||
|
<CERTPATH> must be absolute path.
|
||||||
|
|
||||||
Additional parameter can be specified in <PARAM>. The
|
Additional parameter can be specified in <PARAM>. The
|
||||||
available <PARAM> is "sct-dir=<DIR>".
|
available <PARAM> is "sct-dir=<DIR>".
|
||||||
|
|
|
@ -199,12 +199,13 @@ void ConnectionHandler::worker_replace_downstream(
|
||||||
|
|
||||||
int ConnectionHandler::create_single_worker() {
|
int ConnectionHandler::create_single_worker() {
|
||||||
cert_tree_ = ssl::create_cert_lookup_tree();
|
cert_tree_ = ssl::create_cert_lookup_tree();
|
||||||
auto sv_ssl_ctx = ssl::setup_server_ssl_context(all_ssl_ctx_, cert_tree_.get()
|
auto sv_ssl_ctx = ssl::setup_server_ssl_context(
|
||||||
|
all_ssl_ctx_, indexed_ssl_ctx_, cert_tree_.get()
|
||||||
#ifdef HAVE_NEVERBLEED
|
#ifdef HAVE_NEVERBLEED
|
||||||
,
|
,
|
||||||
nb_.get()
|
nb_.get()
|
||||||
#endif // HAVE_NEVERBLEED
|
#endif // HAVE_NEVERBLEED
|
||||||
);
|
);
|
||||||
auto cl_ssl_ctx = ssl::setup_downstream_client_ssl_context(
|
auto cl_ssl_ctx = ssl::setup_downstream_client_ssl_context(
|
||||||
#ifdef HAVE_NEVERBLEED
|
#ifdef HAVE_NEVERBLEED
|
||||||
nb_.get()
|
nb_.get()
|
||||||
|
@ -247,12 +248,13 @@ int ConnectionHandler::create_worker_thread(size_t num) {
|
||||||
assert(workers_.size() == 0);
|
assert(workers_.size() == 0);
|
||||||
|
|
||||||
cert_tree_ = ssl::create_cert_lookup_tree();
|
cert_tree_ = ssl::create_cert_lookup_tree();
|
||||||
auto sv_ssl_ctx = ssl::setup_server_ssl_context(all_ssl_ctx_, cert_tree_.get()
|
auto sv_ssl_ctx = ssl::setup_server_ssl_context(
|
||||||
|
all_ssl_ctx_, indexed_ssl_ctx_, cert_tree_.get()
|
||||||
#ifdef HAVE_NEVERBLEED
|
#ifdef HAVE_NEVERBLEED
|
||||||
,
|
,
|
||||||
nb_.get()
|
nb_.get()
|
||||||
#endif // HAVE_NEVERBLEED
|
#endif // HAVE_NEVERBLEED
|
||||||
);
|
);
|
||||||
auto cl_ssl_ctx = ssl::setup_downstream_client_ssl_context(
|
auto cl_ssl_ctx = ssl::setup_downstream_client_ssl_context(
|
||||||
#ifdef HAVE_NEVERBLEED
|
#ifdef HAVE_NEVERBLEED
|
||||||
nb_.get()
|
nb_.get()
|
||||||
|
@ -841,4 +843,9 @@ SSL_CTX *ConnectionHandler::get_ssl_ctx(size_t idx) const {
|
||||||
return all_ssl_ctx_[idx];
|
return all_ssl_ctx_[idx];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const std::vector<SSL_CTX *> &
|
||||||
|
ConnectionHandler::get_indexed_ssl_ctx(size_t idx) const {
|
||||||
|
return indexed_ssl_ctx_[idx];
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace shrpx
|
} // namespace shrpx
|
||||||
|
|
|
@ -156,6 +156,8 @@ public:
|
||||||
// array bound checking.
|
// array bound checking.
|
||||||
SSL_CTX *get_ssl_ctx(size_t idx) const;
|
SSL_CTX *get_ssl_ctx(size_t idx) const;
|
||||||
|
|
||||||
|
const std::vector<SSL_CTX *> &get_indexed_ssl_ctx(size_t idx) const;
|
||||||
|
|
||||||
#ifdef HAVE_NEVERBLEED
|
#ifdef HAVE_NEVERBLEED
|
||||||
void set_neverbleed(std::unique_ptr<neverbleed_t> nb);
|
void set_neverbleed(std::unique_ptr<neverbleed_t> nb);
|
||||||
neverbleed_t *get_neverbleed() const;
|
neverbleed_t *get_neverbleed() const;
|
||||||
|
@ -175,6 +177,12 @@ public:
|
||||||
private:
|
private:
|
||||||
// Stores all SSL_CTX objects.
|
// Stores all SSL_CTX objects.
|
||||||
std::vector<SSL_CTX *> all_ssl_ctx_;
|
std::vector<SSL_CTX *> all_ssl_ctx_;
|
||||||
|
// Stores all SSL_CTX objects in a way that its index is stored in
|
||||||
|
// cert_tree. The SSL_CTXs stored in the same index share the same
|
||||||
|
// hostname, but could have different signature algorithm. The
|
||||||
|
// selection among them are performed by hostname presented by SNI,
|
||||||
|
// and signature algorithm presented by client.
|
||||||
|
std::vector<std::vector<SSL_CTX *>> indexed_ssl_ctx_;
|
||||||
OCSPUpdateContext ocsp_;
|
OCSPUpdateContext ocsp_;
|
||||||
std::mt19937 gen_;
|
std::mt19937 gen_;
|
||||||
// ev_loop for each worker
|
// ev_loop for each worker
|
||||||
|
|
|
@ -69,7 +69,7 @@ void Router::add_node(RNode *node, const char *pattern, size_t patlen,
|
||||||
add_next_node(node, std::move(new_node));
|
add_next_node(node, std::move(new_node));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Router::add_route(const StringRef &pattern, size_t index) {
|
size_t Router::add_route(const StringRef &pattern, size_t index) {
|
||||||
auto node = &root_;
|
auto node = &root_;
|
||||||
size_t i = 0;
|
size_t i = 0;
|
||||||
|
|
||||||
|
@ -77,7 +77,7 @@ bool Router::add_route(const StringRef &pattern, size_t index) {
|
||||||
auto next_node = find_next_node(node, pattern[i]);
|
auto next_node = find_next_node(node, pattern[i]);
|
||||||
if (next_node == nullptr) {
|
if (next_node == nullptr) {
|
||||||
add_node(node, pattern.c_str() + i, pattern.size() - i, index);
|
add_node(node, pattern.c_str() + i, pattern.size() - i, index);
|
||||||
return true;
|
return index;
|
||||||
}
|
}
|
||||||
|
|
||||||
node = next_node;
|
node = next_node;
|
||||||
|
@ -93,11 +93,11 @@ bool Router::add_route(const StringRef &pattern, size_t index) {
|
||||||
if (slen == node->len) {
|
if (slen == node->len) {
|
||||||
// Complete match
|
// Complete match
|
||||||
if (node->index != -1) {
|
if (node->index != -1) {
|
||||||
// Don't allow duplicate
|
// Return the existing index for duplicates.
|
||||||
return false;
|
return node->index;
|
||||||
}
|
}
|
||||||
node->index = index;
|
node->index = index;
|
||||||
return true;
|
return index;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (slen > node->len) {
|
if (slen > node->len) {
|
||||||
|
@ -122,7 +122,7 @@ bool Router::add_route(const StringRef &pattern, size_t index) {
|
||||||
|
|
||||||
if (slen == j) {
|
if (slen == j) {
|
||||||
node->index = index;
|
node->index = index;
|
||||||
return true;
|
return index;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -131,7 +131,7 @@ bool Router::add_route(const StringRef &pattern, size_t index) {
|
||||||
assert(pattern.size() > i);
|
assert(pattern.size() > i);
|
||||||
add_node(node, pattern.c_str() + i, pattern.size() - i, index);
|
add_node(node, pattern.c_str() + i, pattern.size() - i, index);
|
||||||
|
|
||||||
return true;
|
return index;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -63,8 +63,9 @@ public:
|
||||||
Router &operator=(Router &&) = default;
|
Router &operator=(Router &&) = default;
|
||||||
Router &operator=(const Router &) = delete;
|
Router &operator=(const Router &) = delete;
|
||||||
|
|
||||||
// Adds route |pattern| with its |index|.
|
// Adds route |pattern| with its |index|. If same pattern has
|
||||||
bool add_route(const StringRef &pattern, size_t index);
|
// already been added, the existing index is returned.
|
||||||
|
size_t add_route(const StringRef &pattern, size_t index);
|
||||||
// Returns the matched index of pattern. -1 if there is no match.
|
// Returns the matched index of pattern. -1 if there is no match.
|
||||||
ssize_t match(const StringRef &host, const StringRef &path) const;
|
ssize_t match(const StringRef &host, const StringRef &path) const;
|
||||||
// Returns the matched index of pattern |s|. -1 if there is no
|
// Returns the matched index of pattern |s|. -1 if there is no
|
||||||
|
|
127
src/shrpx_ssl.cc
127
src/shrpx_ssl.cc
|
@ -183,9 +183,34 @@ int servername_callback(SSL *ssl, int *al, void *arg) {
|
||||||
}
|
}
|
||||||
|
|
||||||
auto conn_handler = worker->get_connection_handler();
|
auto conn_handler = worker->get_connection_handler();
|
||||||
auto ssl_ctx = conn_handler->get_ssl_ctx(idx);
|
|
||||||
|
|
||||||
SSL_set_SSL_CTX(ssl, ssl_ctx);
|
const auto &ssl_ctx_list = conn_handler->get_indexed_ssl_ctx(idx);
|
||||||
|
assert(!ssl_ctx_list.empty());
|
||||||
|
|
||||||
|
#if !defined(OPENSSL_IS_BORINGSSL) && !defined(LIBRESSL_VERSION_NUMBER) && \
|
||||||
|
OPENSSL_VERSION_NUMBER >= 0x10002000L
|
||||||
|
// boringssl removed SSL_get_sigalgs.
|
||||||
|
auto num_sigalg =
|
||||||
|
SSL_get_sigalgs(ssl, 0, nullptr, nullptr, nullptr, nullptr, nullptr);
|
||||||
|
|
||||||
|
for (auto i = 0; i < num_sigalg; ++i) {
|
||||||
|
int sigalg;
|
||||||
|
SSL_get_sigalgs(ssl, i, nullptr, nullptr, &sigalg, nullptr, nullptr);
|
||||||
|
for (auto ssl_ctx : ssl_ctx_list) {
|
||||||
|
auto cert = SSL_CTX_get0_certificate(ssl_ctx);
|
||||||
|
// X509_get_signature_nid is available since OpenSSL 1.0.2.
|
||||||
|
auto cert_sigalg = X509_get_signature_nid(cert);
|
||||||
|
|
||||||
|
if (sigalg == cert_sigalg) {
|
||||||
|
SSL_set_SSL_CTX(ssl, ssl_ctx);
|
||||||
|
return SSL_TLSEXT_ERR_OK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif // !defined(OPENSSL_IS_BORINGSSL) && !defined(LIBRESSL_VERSION_NUMBER) &&
|
||||||
|
// OPENSSL_VERSION_NUMBER >= 0x10002000L
|
||||||
|
|
||||||
|
SSL_set_SSL_CTX(ssl, ssl_ctx_list[0]);
|
||||||
|
|
||||||
return SSL_TLSEXT_ERR_OK;
|
return SSL_TLSEXT_ERR_OK;
|
||||||
}
|
}
|
||||||
|
@ -1239,12 +1264,12 @@ int check_cert(SSL *ssl, const DownstreamAddr *addr, const Address *raddr) {
|
||||||
|
|
||||||
CertLookupTree::CertLookupTree() {}
|
CertLookupTree::CertLookupTree() {}
|
||||||
|
|
||||||
void CertLookupTree::add_cert(const StringRef &hostname, size_t idx) {
|
ssize_t CertLookupTree::add_cert(const StringRef &hostname, size_t idx) {
|
||||||
std::array<uint8_t, NI_MAXHOST> buf;
|
std::array<uint8_t, NI_MAXHOST> buf;
|
||||||
|
|
||||||
// NI_MAXHOST includes terminal NULL byte
|
// NI_MAXHOST includes terminal NULL byte
|
||||||
if (hostname.empty() || hostname.size() + 1 > buf.size()) {
|
if (hostname.empty() || hostname.size() + 1 > buf.size()) {
|
||||||
return;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto wildcard_it = std::find(std::begin(hostname), std::end(hostname), '*');
|
auto wildcard_it = std::find(std::begin(hostname), std::end(hostname), '*');
|
||||||
|
@ -1260,8 +1285,8 @@ void CertLookupTree::add_cert(const StringRef &hostname, size_t idx) {
|
||||||
|
|
||||||
WildcardPattern *wpat;
|
WildcardPattern *wpat;
|
||||||
|
|
||||||
if (!rev_wildcard_router_.add_route(rev_suffix,
|
if (wildcard_patterns_.size() !=
|
||||||
wildcard_patterns_.size())) {
|
rev_wildcard_router_.add_route(rev_suffix, wildcard_patterns_.size())) {
|
||||||
auto wcidx = rev_wildcard_router_.match(rev_suffix);
|
auto wcidx = rev_wildcard_router_.match(rev_suffix);
|
||||||
|
|
||||||
assert(wcidx != -1);
|
assert(wcidx != -1);
|
||||||
|
@ -1277,12 +1302,18 @@ void CertLookupTree::add_cert(const StringRef &hostname, size_t idx) {
|
||||||
std::end(wildcard_prefix),
|
std::end(wildcard_prefix),
|
||||||
std::begin(buf))};
|
std::begin(buf))};
|
||||||
|
|
||||||
|
for (auto &p : wpat->rev_prefix) {
|
||||||
|
if (p.prefix == rev_prefix) {
|
||||||
|
return p.idx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
wpat->rev_prefix.emplace_back(rev_prefix, idx);
|
wpat->rev_prefix.emplace_back(rev_prefix, idx);
|
||||||
|
|
||||||
return;
|
return idx;
|
||||||
}
|
}
|
||||||
|
|
||||||
router_.add_route(hostname, idx);
|
return router_.add_route(hostname, idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
ssize_t CertLookupTree::lookup(const StringRef &hostname) {
|
ssize_t CertLookupTree::lookup(const StringRef &hostname) {
|
||||||
|
@ -1357,10 +1388,24 @@ void CertLookupTree::dump() const {
|
||||||
rev_wildcard_router_.dump();
|
rev_wildcard_router_.dump();
|
||||||
}
|
}
|
||||||
|
|
||||||
int cert_lookup_tree_add_cert_from_x509(CertLookupTree *lt, size_t idx,
|
int cert_lookup_tree_add_ssl_ctx(
|
||||||
X509 *cert) {
|
CertLookupTree *lt, std::vector<std::vector<SSL_CTX *>> &indexed_ssl_ctx,
|
||||||
|
SSL_CTX *ssl_ctx) {
|
||||||
std::array<uint8_t, NI_MAXHOST> buf;
|
std::array<uint8_t, NI_MAXHOST> buf;
|
||||||
|
|
||||||
|
#if !defined(LIBRESSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER >= 0x10002000L
|
||||||
|
auto cert = SSL_CTX_get0_certificate(ssl_ctx);
|
||||||
|
#else // defined(LIBRESSL_VERSION_NUMBER) || OPENSSL_VERSION_NUMBER <
|
||||||
|
// 0x10002000L
|
||||||
|
auto tls_ctx_data =
|
||||||
|
static_cast<TLSContextData *>(SSL_CTX_get_app_data(ssl_ctx));
|
||||||
|
auto cert = load_certificate(tls_ctx_data->cert_file);
|
||||||
|
auto cert_deleter = defer(X509_free, cert);
|
||||||
|
#endif // defined(LIBRESSL_VERSION_NUMBER) || OPENSSL_VERSION_NUMBER <
|
||||||
|
// 0x10002000L
|
||||||
|
|
||||||
|
auto idx = indexed_ssl_ctx.size();
|
||||||
|
|
||||||
auto altnames = static_cast<GENERAL_NAMES *>(
|
auto altnames = static_cast<GENERAL_NAMES *>(
|
||||||
X509_get_ext_d2i(cert, NID_subject_alt_name, nullptr, nullptr));
|
X509_get_ext_d2i(cert, NID_subject_alt_name, nullptr, nullptr));
|
||||||
if (altnames) {
|
if (altnames) {
|
||||||
|
@ -1403,7 +1448,17 @@ int cert_lookup_tree_add_cert_from_x509(CertLookupTree *lt, size_t idx,
|
||||||
auto end_buf = std::copy_n(name, len, std::begin(buf));
|
auto end_buf = std::copy_n(name, len, std::begin(buf));
|
||||||
util::inp_strlower(std::begin(buf), end_buf);
|
util::inp_strlower(std::begin(buf), end_buf);
|
||||||
|
|
||||||
lt->add_cert(StringRef{std::begin(buf), end_buf}, idx);
|
auto nidx = lt->add_cert(StringRef{std::begin(buf), end_buf}, idx);
|
||||||
|
if (nidx == -1) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
idx = nidx;
|
||||||
|
if (idx < indexed_ssl_ctx.size()) {
|
||||||
|
indexed_ssl_ctx[idx].push_back(ssl_ctx);
|
||||||
|
} else {
|
||||||
|
assert(idx == indexed_ssl_ctx.size());
|
||||||
|
indexed_ssl_ctx.emplace_back(std::vector<SSL_CTX *>{ssl_ctx});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't bother CN if we have dNSName.
|
// Don't bother CN if we have dNSName.
|
||||||
|
@ -1433,7 +1488,17 @@ int cert_lookup_tree_add_cert_from_x509(CertLookupTree *lt, size_t idx,
|
||||||
|
|
||||||
util::inp_strlower(std::begin(buf), end_buf);
|
util::inp_strlower(std::begin(buf), end_buf);
|
||||||
|
|
||||||
lt->add_cert(StringRef{std::begin(buf), end_buf}, idx);
|
auto nidx = lt->add_cert(StringRef{std::begin(buf), end_buf}, idx);
|
||||||
|
if (nidx == -1) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
idx = nidx;
|
||||||
|
if (idx < indexed_ssl_ctx.size()) {
|
||||||
|
indexed_ssl_ctx[idx].push_back(ssl_ctx);
|
||||||
|
} else {
|
||||||
|
assert(idx == indexed_ssl_ctx.size());
|
||||||
|
indexed_ssl_ctx.emplace_back(std::vector<SSL_CTX *>{ssl_ctx});
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -1474,13 +1539,15 @@ X509 *load_certificate(const char *filename) {
|
||||||
return cert;
|
return cert;
|
||||||
}
|
}
|
||||||
|
|
||||||
SSL_CTX *setup_server_ssl_context(std::vector<SSL_CTX *> &all_ssl_ctx,
|
SSL_CTX *
|
||||||
CertLookupTree *cert_tree
|
setup_server_ssl_context(std::vector<SSL_CTX *> &all_ssl_ctx,
|
||||||
|
std::vector<std::vector<SSL_CTX *>> &indexed_ssl_ctx,
|
||||||
|
CertLookupTree *cert_tree
|
||||||
#ifdef HAVE_NEVERBLEED
|
#ifdef HAVE_NEVERBLEED
|
||||||
,
|
,
|
||||||
neverbleed_t *nb
|
neverbleed_t *nb
|
||||||
#endif // HAVE_NEVERBLEED
|
#endif // HAVE_NEVERBLEED
|
||||||
) {
|
) {
|
||||||
if (!upstream_tls_enabled()) {
|
if (!upstream_tls_enabled()) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
@ -1508,17 +1575,8 @@ SSL_CTX *setup_server_ssl_context(std::vector<SSL_CTX *> &all_ssl_ctx,
|
||||||
return ssl_ctx;
|
return ssl_ctx;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if !defined(LIBRESSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER >= 0x10002000L
|
if (ssl::cert_lookup_tree_add_ssl_ctx(cert_tree, indexed_ssl_ctx, ssl_ctx) ==
|
||||||
auto cert = SSL_CTX_get0_certificate(ssl_ctx);
|
-1) {
|
||||||
#else // defined(LIBRESSL_VERSION_NUMBER) || OPENSSL_VERSION_NUMBER <
|
|
||||||
// 0x10002000L
|
|
||||||
auto cert = load_certificate(tlsconf.cert_file.c_str());
|
|
||||||
auto cert_deleter = defer(X509_free, cert);
|
|
||||||
#endif // defined(LIBRESSL_VERSION_NUMBER) || OPENSSL_VERSION_NUMBER <
|
|
||||||
// 0x10002000L
|
|
||||||
|
|
||||||
if (ssl::cert_lookup_tree_add_cert_from_x509(
|
|
||||||
cert_tree, all_ssl_ctx.size() - 1, cert) == -1) {
|
|
||||||
LOG(FATAL) << "Failed to add default certificate.";
|
LOG(FATAL) << "Failed to add default certificate.";
|
||||||
DIE();
|
DIE();
|
||||||
}
|
}
|
||||||
|
@ -1533,17 +1591,8 @@ SSL_CTX *setup_server_ssl_context(std::vector<SSL_CTX *> &all_ssl_ctx,
|
||||||
);
|
);
|
||||||
all_ssl_ctx.push_back(ssl_ctx);
|
all_ssl_ctx.push_back(ssl_ctx);
|
||||||
|
|
||||||
#if !defined(LIBRESSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER >= 0x10002000L
|
if (ssl::cert_lookup_tree_add_ssl_ctx(cert_tree, indexed_ssl_ctx,
|
||||||
auto cert = SSL_CTX_get0_certificate(ssl_ctx);
|
ssl_ctx) == -1) {
|
||||||
#else // defined(LIBRESSL_VERSION_NUMBER) || OPENSSL_VERSION_NUMBER <
|
|
||||||
// 0x10002000L
|
|
||||||
auto cert = load_certificate(c.cert_file.c_str());
|
|
||||||
auto cert_deleter = defer(X509_free, cert);
|
|
||||||
#endif // defined(LIBRESSL_VERSION_NUMBER) || OPENSSL_VERSION_NUMBER <
|
|
||||||
// 0x10002000L
|
|
||||||
|
|
||||||
if (ssl::cert_lookup_tree_add_cert_from_x509(
|
|
||||||
cert_tree, all_ssl_ctx.size() - 1, cert) == -1) {
|
|
||||||
LOG(FATAL) << "Failed to add sub certificate.";
|
LOG(FATAL) << "Failed to add sub certificate.";
|
||||||
DIE();
|
DIE();
|
||||||
}
|
}
|
||||||
|
|
|
@ -136,13 +136,20 @@ public:
|
||||||
// |index| is returned. We support wildcard pattern. The left most
|
// |index| is returned. We support wildcard pattern. The left most
|
||||||
// '*' is considered as wildcard character, and it must match at
|
// '*' is considered as wildcard character, and it must match at
|
||||||
// least one character. If the same pattern has been already added,
|
// least one character. If the same pattern has been already added,
|
||||||
// this function is noop.
|
// this function does not alter the tree, and returns the existing
|
||||||
|
// matching index.
|
||||||
//
|
//
|
||||||
// The caller should lower-case |hostname| since this function does
|
// The caller should lower-case |hostname| since this function does
|
||||||
// do that, and lookup function performs case-sensitive match.
|
// do that, and lookup function performs case-sensitive match.
|
||||||
//
|
//
|
||||||
// TODO Treat wildcard pattern described as RFC 6125.
|
// TODO Treat wildcard pattern described as RFC 6125.
|
||||||
void add_cert(const StringRef &hostname, size_t index);
|
//
|
||||||
|
// This function returns the index. It returns -1 if it fails
|
||||||
|
// (e.g., hostname is too long). If the returned index equals to
|
||||||
|
// |index|, then hostname is added to the tree with the value
|
||||||
|
// |index|. If it is not -1, and does not equal to |index|, same
|
||||||
|
// hostname has already been added to the tree.
|
||||||
|
ssize_t add_cert(const StringRef &hostname, size_t index);
|
||||||
|
|
||||||
// Looks up index using the given |hostname|. The exact match takes
|
// Looks up index using the given |hostname|. The exact match takes
|
||||||
// precedence over wildcard match. For wildcard match, longest
|
// precedence over wildcard match. For wildcard match, longest
|
||||||
|
@ -166,12 +173,14 @@ private:
|
||||||
std::vector<WildcardPattern> wildcard_patterns_;
|
std::vector<WildcardPattern> wildcard_patterns_;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Adds hostnames in |cert| to lookup tree |lt|. The subjectAltNames
|
// Adds hostnames in certificate in |ssl_ctx| to lookup tree |lt|.
|
||||||
// and commonName are considered as eligible hostname. If there is at
|
// The subjectAltNames and commonName are considered as eligible
|
||||||
// least one dNSName in subjectAltNames, commonName is not considered.
|
// hostname. If there is at least one dNSName in subjectAltNames,
|
||||||
// This function returns 0 if it succeeds, or -1.
|
// commonName is not considered. |ssl_ctx| is also added to
|
||||||
int cert_lookup_tree_add_cert_from_x509(CertLookupTree *lt, size_t idx,
|
// |indexed_ssl_ctx|. This function returns 0 if it succeeds, or -1.
|
||||||
X509 *cert);
|
int cert_lookup_tree_add_ssl_ctx(
|
||||||
|
CertLookupTree *lt, std::vector<std::vector<SSL_CTX *>> &indexed_ssl_ctx,
|
||||||
|
SSL_CTX *ssl_ctx);
|
||||||
|
|
||||||
// Returns true if |proto| is included in the
|
// Returns true if |proto| is included in the
|
||||||
// protocol list |protos|.
|
// protocol list |protos|.
|
||||||
|
@ -194,14 +203,18 @@ int set_alpn_prefs(std::vector<unsigned char> &out,
|
||||||
// construct default SSL_CTX. If subcerts are available
|
// construct default SSL_CTX. If subcerts are available
|
||||||
// (get_config()->subcerts), caller should provide CertLookupTree
|
// (get_config()->subcerts), caller should provide CertLookupTree
|
||||||
// object as |cert_tree| parameter, otherwise SNI does not work. All
|
// object as |cert_tree| parameter, otherwise SNI does not work. All
|
||||||
// the created SSL_CTX is stored into |all_ssl_ctx|.
|
// the created SSL_CTX is stored into |all_ssl_ctx|. They are also
|
||||||
SSL_CTX *setup_server_ssl_context(std::vector<SSL_CTX *> &all_ssl_ctx,
|
// added to |indexed_ssl_ctx|. |cert_tree| uses its index to
|
||||||
CertLookupTree *cert_tree
|
// associate hostname to the SSL_CTX.
|
||||||
|
SSL_CTX *
|
||||||
|
setup_server_ssl_context(std::vector<SSL_CTX *> &all_ssl_ctx,
|
||||||
|
std::vector<std::vector<SSL_CTX *>> &indexed_ssl_ctx,
|
||||||
|
CertLookupTree *cert_tree
|
||||||
#ifdef HAVE_NEVERBLEED
|
#ifdef HAVE_NEVERBLEED
|
||||||
,
|
,
|
||||||
neverbleed_t *nb
|
neverbleed_t *nb
|
||||||
#endif // HAVE_NEVERBLEED
|
#endif // HAVE_NEVERBLEED
|
||||||
);
|
);
|
||||||
|
|
||||||
// Setups client side SSL_CTX.
|
// Setups client side SSL_CTX.
|
||||||
SSL_CTX *setup_downstream_client_ssl_context(
|
SSL_CTX *setup_downstream_client_ssl_context(
|
||||||
|
|
|
@ -115,24 +115,39 @@ void test_shrpx_ssl_create_lookup_tree(void) {
|
||||||
// -config=ca-config.json -profile=server test.example.com.csr |
|
// -config=ca-config.json -profile=server test.example.com.csr |
|
||||||
// cfssljson -bare test.example.com
|
// cfssljson -bare test.example.com
|
||||||
//
|
//
|
||||||
void test_shrpx_ssl_cert_lookup_tree_add_cert_from_x509(void) {
|
void test_shrpx_ssl_cert_lookup_tree_add_ssl_ctx(void) {
|
||||||
int rv;
|
int rv;
|
||||||
|
|
||||||
constexpr char nghttp2_certfile[] = NGHTTP2_SRC_DIR "/test.nghttp2.org.pem";
|
constexpr char nghttp2_certfile[] = NGHTTP2_SRC_DIR "/test.nghttp2.org.pem";
|
||||||
auto nghttp2_cert = ssl::load_certificate(nghttp2_certfile);
|
auto nghttp2_ssl_ctx = SSL_CTX_new(SSLv23_server_method());
|
||||||
auto nghttp2_cert_deleter = defer(X509_free, nghttp2_cert);
|
auto nghttp2_ssl_ctx_del = defer(SSL_CTX_free, nghttp2_ssl_ctx);
|
||||||
|
auto nghttp2_tls_ctx_data = make_unique<ssl::TLSContextData>();
|
||||||
|
nghttp2_tls_ctx_data->cert_file = nghttp2_certfile;
|
||||||
|
SSL_CTX_set_app_data(nghttp2_ssl_ctx, nghttp2_tls_ctx_data.get());
|
||||||
|
rv = SSL_CTX_use_certificate_chain_file(nghttp2_ssl_ctx, nghttp2_certfile);
|
||||||
|
|
||||||
|
CU_ASSERT(1 == rv);
|
||||||
|
|
||||||
constexpr char examples_certfile[] = NGHTTP2_SRC_DIR "/test.example.com.pem";
|
constexpr char examples_certfile[] = NGHTTP2_SRC_DIR "/test.example.com.pem";
|
||||||
auto examples_cert = ssl::load_certificate(examples_certfile);
|
auto examples_ssl_ctx = SSL_CTX_new(SSLv23_server_method());
|
||||||
auto examples_cert_deleter = defer(X509_free, examples_cert);
|
auto examples_ssl_ctx_del = defer(SSL_CTX_free, examples_ssl_ctx);
|
||||||
|
auto examples_tls_ctx_data = make_unique<ssl::TLSContextData>();
|
||||||
|
examples_tls_ctx_data->cert_file = examples_certfile;
|
||||||
|
SSL_CTX_set_app_data(examples_ssl_ctx, examples_tls_ctx_data.get());
|
||||||
|
rv = SSL_CTX_use_certificate_chain_file(examples_ssl_ctx, examples_certfile);
|
||||||
|
|
||||||
|
CU_ASSERT(1 == rv);
|
||||||
|
|
||||||
ssl::CertLookupTree tree;
|
ssl::CertLookupTree tree;
|
||||||
|
std::vector<std::vector<SSL_CTX *>> indexed_ssl_ctx;
|
||||||
|
|
||||||
rv = ssl::cert_lookup_tree_add_cert_from_x509(&tree, 0, nghttp2_cert);
|
rv = ssl::cert_lookup_tree_add_ssl_ctx(&tree, indexed_ssl_ctx,
|
||||||
|
nghttp2_ssl_ctx);
|
||||||
|
|
||||||
CU_ASSERT(0 == rv);
|
CU_ASSERT(0 == rv);
|
||||||
|
|
||||||
rv = ssl::cert_lookup_tree_add_cert_from_x509(&tree, 1, examples_cert);
|
rv = ssl::cert_lookup_tree_add_ssl_ctx(&tree, indexed_ssl_ctx,
|
||||||
|
examples_ssl_ctx);
|
||||||
|
|
||||||
CU_ASSERT(0 == rv);
|
CU_ASSERT(0 == rv);
|
||||||
|
|
||||||
|
|
|
@ -32,7 +32,7 @@
|
||||||
namespace shrpx {
|
namespace shrpx {
|
||||||
|
|
||||||
void test_shrpx_ssl_create_lookup_tree(void);
|
void test_shrpx_ssl_create_lookup_tree(void);
|
||||||
void test_shrpx_ssl_cert_lookup_tree_add_cert_from_x509(void);
|
void test_shrpx_ssl_cert_lookup_tree_add_ssl_ctx(void);
|
||||||
void test_shrpx_ssl_tls_hostname_match(void);
|
void test_shrpx_ssl_tls_hostname_match(void);
|
||||||
|
|
||||||
} // namespace shrpx
|
} // namespace shrpx
|
||||||
|
|
Loading…
Reference in New Issue