nghttpx: Refactor CertLookupTree
This commit is contained in:
parent
90746cdd0e
commit
b165775811
|
@ -48,7 +48,7 @@ struct LogFragment;
|
|||
|
||||
namespace ssl {
|
||||
|
||||
struct CertLookupTree;
|
||||
class CertLookupTree;
|
||||
|
||||
} // namespace ssl
|
||||
|
||||
|
|
127
src/shrpx_ssl.cc
127
src/shrpx_ssl.cc
|
@ -51,6 +51,7 @@
|
|||
#include "shrpx_downstream_connection_pool.h"
|
||||
#include "util.h"
|
||||
#include "ssl.h"
|
||||
#include "template.h"
|
||||
|
||||
using namespace nghttp2;
|
||||
|
||||
|
@ -133,8 +134,7 @@ int servername_callback(SSL *ssl, int *al, void *arg) {
|
|||
if (cert_tree) {
|
||||
const char *hostname = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
|
||||
if (hostname) {
|
||||
auto ssl_ctx =
|
||||
cert_lookup_tree_lookup(cert_tree, hostname, strlen(hostname));
|
||||
auto ssl_ctx = cert_tree->lookup(hostname, strlen(hostname));
|
||||
if (ssl_ctx) {
|
||||
SSL_set_SSL_CTX(ssl, ssl_ctx);
|
||||
}
|
||||
|
@ -720,44 +720,22 @@ int check_cert(SSL *ssl) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
CertLookupTree *cert_lookup_tree_new() {
|
||||
auto tree = new CertLookupTree();
|
||||
auto root = new CertNode();
|
||||
root->ssl_ctx = 0;
|
||||
root->str = 0;
|
||||
root->first = root->last = 0;
|
||||
tree->root = root;
|
||||
return tree;
|
||||
}
|
||||
|
||||
namespace {
|
||||
void cert_node_del(CertNode *node) {
|
||||
for (auto &a : node->next) {
|
||||
cert_node_del(a);
|
||||
}
|
||||
delete node;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void cert_lookup_tree_del(CertLookupTree *lt) {
|
||||
cert_node_del(lt->root);
|
||||
for (auto &s : lt->hosts) {
|
||||
delete[] s;
|
||||
}
|
||||
delete lt;
|
||||
CertLookupTree::CertLookupTree() {
|
||||
root_.ssl_ctx = nullptr;
|
||||
root_.str = nullptr;
|
||||
root_.first = root_.last = 0;
|
||||
}
|
||||
|
||||
namespace {
|
||||
// The |offset| is the index in the hostname we are examining. We are
|
||||
// going to scan from |offset| in backwards.
|
||||
void cert_lookup_tree_add_cert(CertLookupTree *lt, CertNode *node,
|
||||
SSL_CTX *ssl_ctx, char *hostname, size_t len,
|
||||
int offset) {
|
||||
void cert_lookup_tree_add_cert(CertNode *node, SSL_CTX *ssl_ctx, char *hostname,
|
||||
size_t len, int offset) {
|
||||
int i, next_len = node->next.size();
|
||||
char c = hostname[offset];
|
||||
CertNode *cn = nullptr;
|
||||
for (i = 0; i < next_len; ++i) {
|
||||
cn = node->next[i];
|
||||
cn = node->next[i].get();
|
||||
if (cn->str[cn->first] == c) {
|
||||
break;
|
||||
}
|
||||
|
@ -769,10 +747,12 @@ void cert_lookup_tree_add_cert(CertLookupTree *lt, CertNode *node,
|
|||
// some restrictions for wildcard hostname. We just ignore
|
||||
// these rules here but do the proper check when we do the
|
||||
// match.
|
||||
node->wildcard_certs.push_back(std::make_pair(hostname, ssl_ctx));
|
||||
} else {
|
||||
node->wildcard_certs.emplace_back(hostname, ssl_ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
int j;
|
||||
auto new_node = new CertNode();
|
||||
auto new_node = make_unique<CertNode>();
|
||||
new_node->str = hostname;
|
||||
new_node->first = offset;
|
||||
// If wildcard is found, set the region before it because we
|
||||
|
@ -784,29 +764,34 @@ void cert_lookup_tree_add_cert(CertLookupTree *lt, CertNode *node,
|
|||
new_node->ssl_ctx = ssl_ctx;
|
||||
} else {
|
||||
new_node->ssl_ctx = nullptr;
|
||||
new_node->wildcard_certs.push_back(std::make_pair(hostname, ssl_ctx));
|
||||
new_node->wildcard_certs.emplace_back(hostname, ssl_ctx);
|
||||
}
|
||||
node->next.push_back(new_node);
|
||||
node->next.push_back(std::move(new_node));
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
|
||||
int j;
|
||||
for (i = cn->first, j = offset;
|
||||
i > cn->last && j >= 0 && cn->str[i] == hostname[j]; --i, --j)
|
||||
;
|
||||
if (i == cn->last) {
|
||||
if (j == -1) {
|
||||
if (cn->ssl_ctx) {
|
||||
// same hostname, we don't overwrite exiting ssl_ctx
|
||||
} else {
|
||||
// If the same hostname already exists, we don't overwrite
|
||||
// exiting ssl_ctx
|
||||
if (!cn->ssl_ctx) {
|
||||
cn->ssl_ctx = ssl_ctx;
|
||||
}
|
||||
} else {
|
||||
// The existing hostname is a suffix of this hostname.
|
||||
// Continue matching at potion j.
|
||||
cert_lookup_tree_add_cert(lt, cn, ssl_ctx, hostname, len, j);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
auto new_node = new CertNode();
|
||||
|
||||
// The existing hostname is a suffix of this hostname. Continue
|
||||
// matching at potion j.
|
||||
cert_lookup_tree_add_cert(cn, ssl_ctx, hostname, len, j);
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
auto new_node = make_unique<CertNode>();
|
||||
new_node->ssl_ctx = cn->ssl_ctx;
|
||||
new_node->str = cn->str;
|
||||
new_node->first = i;
|
||||
|
@ -814,40 +799,40 @@ void cert_lookup_tree_add_cert(CertLookupTree *lt, CertNode *node,
|
|||
new_node->wildcard_certs.swap(cn->wildcard_certs);
|
||||
new_node->next.swap(cn->next);
|
||||
|
||||
cn->next.push_back(new_node);
|
||||
cn->next.push_back(std::move(new_node));
|
||||
}
|
||||
|
||||
cn->last = i;
|
||||
if (j == -1) {
|
||||
// This hostname is a suffix of the existing hostname.
|
||||
cn->ssl_ctx = ssl_ctx;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
// This hostname and existing one share suffix.
|
||||
cn->ssl_ctx = nullptr;
|
||||
cert_lookup_tree_add_cert(lt, cn, ssl_ctx, hostname, len, j);
|
||||
}
|
||||
}
|
||||
}
|
||||
cert_lookup_tree_add_cert(cn, ssl_ctx, hostname, len, j);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void cert_lookup_tree_add_cert(CertLookupTree *lt, SSL_CTX *ssl_ctx,
|
||||
const char *hostname, size_t len) {
|
||||
void CertLookupTree::add_cert(SSL_CTX *ssl_ctx, const char *hostname,
|
||||
size_t len) {
|
||||
if (len == 0) {
|
||||
return;
|
||||
}
|
||||
// Copy hostname including terminal NULL
|
||||
char *host_copy = new char[len + 1];
|
||||
hosts_.push_back(make_unique<char[]>(len + 1));
|
||||
const auto &host_copy = hosts_.back();
|
||||
for (size_t i = 0; i < len; ++i) {
|
||||
host_copy[i] = util::lowcase(hostname[i]);
|
||||
}
|
||||
host_copy[len] = '\0';
|
||||
lt->hosts.push_back(host_copy);
|
||||
cert_lookup_tree_add_cert(lt, lt->root, ssl_ctx, host_copy, len, len - 1);
|
||||
cert_lookup_tree_add_cert(&root_, ssl_ctx, host_copy.get(), len, len - 1);
|
||||
}
|
||||
|
||||
namespace {
|
||||
SSL_CTX *cert_lookup_tree_lookup(CertLookupTree *lt, CertNode *node,
|
||||
const char *hostname, size_t len, int offset) {
|
||||
SSL_CTX *cert_lookup_tree_lookup(CertNode *node, const char *hostname,
|
||||
size_t len, int offset) {
|
||||
int i, j;
|
||||
for (i = node->first, j = offset;
|
||||
i > node->last && j >= 0 && node->str[i] == util::lowcase(hostname[j]);
|
||||
|
@ -860,30 +845,29 @@ SSL_CTX *cert_lookup_tree_lookup(CertLookupTree *lt, CertNode *node,
|
|||
if (node->ssl_ctx) {
|
||||
// exact match
|
||||
return node->ssl_ctx;
|
||||
} else {
|
||||
}
|
||||
|
||||
// Do not perform wildcard-match because '*' must match at least
|
||||
// one character.
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
for (auto &wildcert : node->wildcard_certs) {
|
||||
for (const auto &wildcert : node->wildcard_certs) {
|
||||
if (tls_hostname_match(wildcert.first, hostname)) {
|
||||
return wildcert.second;
|
||||
}
|
||||
}
|
||||
char c = util::lowcase(hostname[j]);
|
||||
for (auto &next_node : node->next) {
|
||||
auto c = util::lowcase(hostname[j]);
|
||||
for (const auto &next_node : node->next) {
|
||||
if (next_node->str[next_node->first] == c) {
|
||||
return cert_lookup_tree_lookup(lt, next_node, hostname, len, j);
|
||||
return cert_lookup_tree_lookup(next_node.get(), hostname, len, j);
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
SSL_CTX *cert_lookup_tree_lookup(CertLookupTree *lt, const char *hostname,
|
||||
size_t len) {
|
||||
return cert_lookup_tree_lookup(lt, lt->root, hostname, len, len - 1);
|
||||
SSL_CTX *CertLookupTree::lookup(const char *hostname, size_t len) {
|
||||
return cert_lookup_tree_lookup(&root_, hostname, len, len - 1);
|
||||
}
|
||||
|
||||
int cert_lookup_tree_add_cert_from_file(CertLookupTree *lt, SSL_CTX *ssl_ctx,
|
||||
|
@ -910,10 +894,9 @@ int cert_lookup_tree_add_cert_from_file(CertLookupTree *lt, SSL_CTX *ssl_ctx,
|
|||
std::vector<std::string> ip_addrs;
|
||||
get_altnames(cert, dns_names, ip_addrs, common_name);
|
||||
for (auto &dns_name : dns_names) {
|
||||
cert_lookup_tree_add_cert(lt, ssl_ctx, dns_name.c_str(), dns_name.size());
|
||||
lt->add_cert(ssl_ctx, dns_name.c_str(), dns_name.size());
|
||||
}
|
||||
cert_lookup_tree_add_cert(lt, ssl_ctx, common_name.c_str(),
|
||||
common_name.size());
|
||||
lt->add_cert(ssl_ctx, common_name.c_str(), common_name.size());
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -956,7 +939,7 @@ SSL_CTX *setup_server_ssl_context() {
|
|||
return ssl_ctx;
|
||||
}
|
||||
|
||||
auto cert_tree = cert_lookup_tree_new();
|
||||
auto cert_tree = new CertLookupTree();
|
||||
|
||||
worker_config->cert_tree = cert_tree;
|
||||
|
||||
|
|
|
@ -85,40 +85,40 @@ void get_altnames(X509 *cert, std::vector<std::string> &dns_names,
|
|||
// matches, query is continued to the next character.
|
||||
|
||||
struct CertNode {
|
||||
// SSL_CTX for exact match
|
||||
SSL_CTX *ssl_ctx;
|
||||
// list of wildcard domain name and its SSL_CTX pair, the wildcard
|
||||
// '*' appears in this position.
|
||||
std::vector<std::pair<char *, SSL_CTX *>> wildcard_certs;
|
||||
// Next CertNode index of CertLookupTree::nodes
|
||||
std::vector<CertNode *> next;
|
||||
std::vector<std::unique_ptr<CertNode>> next;
|
||||
// SSL_CTX for exact match
|
||||
SSL_CTX *ssl_ctx;
|
||||
char *str;
|
||||
// [first, last) in the reverse direction in str, first >=
|
||||
// last. This indices only work for str member.
|
||||
int first, last;
|
||||
};
|
||||
|
||||
struct CertLookupTree {
|
||||
std::vector<SSL_CTX *> certs;
|
||||
std::vector<char *> hosts;
|
||||
CertNode *root;
|
||||
class CertLookupTree {
|
||||
public:
|
||||
CertLookupTree();
|
||||
|
||||
// Adds |ssl_ctx| with hostname pattern |hostname| with length |len|
|
||||
// to the lookup tree. The |hostname| must be NULL-terminated.
|
||||
void add_cert(SSL_CTX *ssl_ctx, const char *hostname, size_t len);
|
||||
|
||||
// Looks up SSL_CTX using the given |hostname| with length |len|.
|
||||
// If more than one SSL_CTX which matches the query, it is undefined
|
||||
// which one is returned. The |hostname| must be NULL-terminated.
|
||||
// If no matching SSL_CTX found, returns NULL.
|
||||
SSL_CTX *lookup(const char *hostname, size_t len);
|
||||
|
||||
private:
|
||||
CertNode root_;
|
||||
// Stores pointers to copied hostname when adding hostname and
|
||||
// ssl_ctx pair.
|
||||
std::vector<std::unique_ptr<char[]>> hosts_;
|
||||
};
|
||||
|
||||
CertLookupTree *cert_lookup_tree_new();
|
||||
void cert_lookup_tree_del(CertLookupTree *lt);
|
||||
|
||||
// Adds |ssl_ctx| with hostname pattern |hostname| with length |len|
|
||||
// to the lookup tree |lt|. The |hostname| must be NULL-terminated.
|
||||
void cert_lookup_tree_add_cert(CertLookupTree *lt, SSL_CTX *ssl_ctx,
|
||||
const char *hostname, size_t len);
|
||||
|
||||
// Looks up SSL_CTX using the given |hostname| with length |len|. If
|
||||
// more than one SSL_CTX which matches the query, it is undefined
|
||||
// which one is returned. The |hostname| must be NULL-terminated. If
|
||||
// no matching SSL_CTX found, returns NULL.
|
||||
SSL_CTX *cert_lookup_tree_lookup(CertLookupTree *lt, const char *hostname,
|
||||
size_t len);
|
||||
|
||||
// Adds |ssl_ctx| to lookup tree |lt| using hostnames read from
|
||||
// |certfile|. The subjectAltNames and commonName are considered as
|
||||
// eligible hostname. This function returns 0 if it succeeds, or -1.
|
||||
|
|
|
@ -28,13 +28,14 @@
|
|||
|
||||
#include "shrpx_ssl.h"
|
||||
#include "util.h"
|
||||
#include "template.h"
|
||||
|
||||
using namespace nghttp2;
|
||||
|
||||
namespace shrpx {
|
||||
|
||||
void test_shrpx_ssl_create_lookup_tree(void) {
|
||||
ssl::CertLookupTree *tree = ssl::cert_lookup_tree_new();
|
||||
auto tree = make_unique<ssl::CertLookupTree>();
|
||||
SSL_CTX *ctxs[] = {
|
||||
SSL_CTX_new(SSLv23_method()), SSL_CTX_new(SSLv23_method()),
|
||||
SSL_CTX_new(SSLv23_method()), SSL_CTX_new(SSLv23_method()),
|
||||
|
@ -51,40 +52,34 @@ void test_shrpx_ssl_create_lookup_tree(void) {
|
|||
"oo.bar"};
|
||||
int num = util::array_size(ctxs);
|
||||
for (int i = 0; i < num; ++i) {
|
||||
ssl::cert_lookup_tree_add_cert(tree, ctxs[i], hostnames[i],
|
||||
strlen(hostnames[i]));
|
||||
tree->add_cert(ctxs[i], hostnames[i], strlen(hostnames[i]));
|
||||
}
|
||||
|
||||
CU_ASSERT(ctxs[0] == ssl::cert_lookup_tree_lookup(tree, hostnames[0],
|
||||
strlen(hostnames[0])));
|
||||
CU_ASSERT(ctxs[1] == ssl::cert_lookup_tree_lookup(tree, hostnames[1],
|
||||
strlen(hostnames[1])));
|
||||
CU_ASSERT(ctxs[0] == tree->lookup(hostnames[0], strlen(hostnames[0])));
|
||||
CU_ASSERT(ctxs[1] == tree->lookup(hostnames[1], strlen(hostnames[1])));
|
||||
const char h1[] = "2www.example.org";
|
||||
CU_ASSERT(ctxs[2] == ssl::cert_lookup_tree_lookup(tree, h1, strlen(h1)));
|
||||
CU_ASSERT(ctxs[2] == tree->lookup(h1, strlen(h1)));
|
||||
const char h2[] = "www2.example.org";
|
||||
CU_ASSERT(0 == ssl::cert_lookup_tree_lookup(tree, h2, strlen(h2)));
|
||||
CU_ASSERT(0 == tree->lookup(h2, strlen(h2)));
|
||||
const char h3[] = "x1.host.domain";
|
||||
CU_ASSERT(ctxs[3] == ssl::cert_lookup_tree_lookup(tree, h3, strlen(h3)));
|
||||
CU_ASSERT(ctxs[3] == tree->lookup(h3, strlen(h3)));
|
||||
// Does not match *yy.host.domain, because * must match at least 1
|
||||
// character.
|
||||
const char h4[] = "yy.Host.domain";
|
||||
CU_ASSERT(0 == ssl::cert_lookup_tree_lookup(tree, h4, strlen(h4)));
|
||||
CU_ASSERT(0 == tree->lookup(h4, strlen(h4)));
|
||||
const char h5[] = "zyy.host.domain";
|
||||
CU_ASSERT(ctxs[4] == ssl::cert_lookup_tree_lookup(tree, h5, strlen(h5)));
|
||||
CU_ASSERT(0 == ssl::cert_lookup_tree_lookup(tree, "", 0));
|
||||
CU_ASSERT(ctxs[5] == ssl::cert_lookup_tree_lookup(tree, hostnames[5],
|
||||
strlen(hostnames[5])));
|
||||
CU_ASSERT(ctxs[6] == ssl::cert_lookup_tree_lookup(tree, hostnames[6],
|
||||
strlen(hostnames[6])));
|
||||
CU_ASSERT(ctxs[4] == tree->lookup(h5, strlen(h5)));
|
||||
CU_ASSERT(0 == tree->lookup("", 0));
|
||||
CU_ASSERT(ctxs[5] == tree->lookup(hostnames[5], strlen(hostnames[5])));
|
||||
CU_ASSERT(ctxs[6] == tree->lookup(hostnames[6], strlen(hostnames[6])));
|
||||
const char h6[] = "pdylay.sourceforge.net";
|
||||
for (int i = 0; i < 7; ++i) {
|
||||
CU_ASSERT(0 == ssl::cert_lookup_tree_lookup(tree, h6 + i, strlen(h6) - i));
|
||||
CU_ASSERT(0 == tree->lookup(h6 + i, strlen(h6) - i));
|
||||
}
|
||||
const char h7[] = "x.foo.bar";
|
||||
CU_ASSERT(ctxs[8] == ssl::cert_lookup_tree_lookup(tree, h7, strlen(h7)));
|
||||
CU_ASSERT(ctxs[9] == ssl::cert_lookup_tree_lookup(tree, hostnames[9],
|
||||
strlen(hostnames[9])));
|
||||
ssl::cert_lookup_tree_del(tree);
|
||||
CU_ASSERT(ctxs[8] == tree->lookup(h7, strlen(h7)));
|
||||
CU_ASSERT(ctxs[9] == tree->lookup(hostnames[9], strlen(hostnames[9])));
|
||||
|
||||
for (int i = 0; i < num; ++i) {
|
||||
SSL_CTX_free(ctxs[i]);
|
||||
}
|
||||
|
@ -94,15 +89,15 @@ void test_shrpx_ssl_create_lookup_tree(void) {
|
|||
SSL_CTX_new(SSLv23_method()), SSL_CTX_new(SSLv23_method())};
|
||||
const char *names[] = {"rab", "zab", "zzub", "ab"};
|
||||
num = util::array_size(ctxs2);
|
||||
tree = ssl::cert_lookup_tree_new();
|
||||
|
||||
tree = make_unique<ssl::CertLookupTree>();
|
||||
for (int i = 0; i < num; ++i) {
|
||||
ssl::cert_lookup_tree_add_cert(tree, ctxs2[i], names[i], strlen(names[i]));
|
||||
tree->add_cert(ctxs2[i], names[i], strlen(names[i]));
|
||||
}
|
||||
for (int i = 0; i < num; ++i) {
|
||||
CU_ASSERT(ctxs2[i] ==
|
||||
ssl::cert_lookup_tree_lookup(tree, names[i], strlen(names[i])));
|
||||
CU_ASSERT(ctxs2[i] == tree->lookup(names[i], strlen(names[i])));
|
||||
}
|
||||
ssl::cert_lookup_tree_del(tree);
|
||||
|
||||
for (int i = 0; i < num; ++i) {
|
||||
SSL_CTX_free(ctxs2[i]);
|
||||
}
|
||||
|
@ -110,15 +105,14 @@ void test_shrpx_ssl_create_lookup_tree(void) {
|
|||
|
||||
void test_shrpx_ssl_cert_lookup_tree_add_cert_from_file(void) {
|
||||
int rv;
|
||||
ssl::CertLookupTree *tree = ssl::cert_lookup_tree_new();
|
||||
SSL_CTX *ssl_ctx = SSL_CTX_new(SSLv23_method());
|
||||
ssl::CertLookupTree tree;
|
||||
auto ssl_ctx = SSL_CTX_new(SSLv23_method());
|
||||
const char certfile[] = NGHTTP2_TESTS_DIR "/testdata/cacert.pem";
|
||||
rv = ssl::cert_lookup_tree_add_cert_from_file(tree, ssl_ctx, certfile);
|
||||
rv = ssl::cert_lookup_tree_add_cert_from_file(&tree, ssl_ctx, certfile);
|
||||
CU_ASSERT(0 == rv);
|
||||
const char localhost[] = "localhost";
|
||||
CU_ASSERT(ssl_ctx == ssl::cert_lookup_tree_lookup(tree, localhost,
|
||||
sizeof(localhost) - 1));
|
||||
ssl::cert_lookup_tree_del(tree);
|
||||
CU_ASSERT(ssl_ctx == tree.lookup(localhost, sizeof(localhost) - 1));
|
||||
|
||||
SSL_CTX_free(ssl_ctx);
|
||||
}
|
||||
|
||||
|
|
|
@ -48,7 +48,7 @@ class Http2Session;
|
|||
class ConnectBlocker;
|
||||
|
||||
namespace ssl {
|
||||
struct CertLookupTree;
|
||||
class CertLookupTree;
|
||||
} // namespace ssl
|
||||
|
||||
struct WorkerStat {
|
||||
|
|
|
@ -32,7 +32,7 @@
|
|||
namespace shrpx {
|
||||
|
||||
namespace ssl {
|
||||
struct CertLookupTree;
|
||||
class CertLookupTree;
|
||||
} // namespace ssl
|
||||
|
||||
struct TicketKeys;
|
||||
|
|
Loading…
Reference in New Issue