nghttpx: Refactor CertLookupTree

This commit is contained in:
Tatsuhiro Tsujikawa 2015-02-02 22:47:12 +09:00
parent 90746cdd0e
commit b165775811
6 changed files with 140 additions and 163 deletions

View File

@ -48,7 +48,7 @@ struct LogFragment;
namespace ssl {
struct CertLookupTree;
class CertLookupTree;
} // namespace ssl

View File

@ -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,85 +747,92 @@ 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 {
int j;
auto new_node = new CertNode();
new_node->str = hostname;
new_node->first = offset;
// If wildcard is found, set the region before it because we
// don't include it in [first, last).
for (j = offset; j >= 0 && hostname[j] != '*'; --j)
;
new_node->last = j;
if (j == -1) {
new_node->ssl_ctx = ssl_ctx;
} else {
new_node->ssl_ctx = nullptr;
new_node->wildcard_certs.push_back(std::make_pair(hostname, ssl_ctx));
}
node->next.push_back(new_node);
node->wildcard_certs.emplace_back(hostname, ssl_ctx);
return;
}
} else {
int j;
for (i = cn->first, j = offset;
i > cn->last && j >= 0 && cn->str[i] == hostname[j]; --i, --j)
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
// don't include it in [first, last).
for (j = offset; j >= 0 && hostname[j] != '*'; --j)
;
if (i == cn->last) {
if (j == -1) {
if (cn->ssl_ctx) {
// same hostname, we don't overwrite exiting ssl_ctx
} else {
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);
}
new_node->last = j;
if (j == -1) {
new_node->ssl_ctx = ssl_ctx;
} else {
auto new_node = new CertNode();
new_node->ssl_ctx = cn->ssl_ctx;
new_node->str = cn->str;
new_node->first = i;
new_node->last = cn->last;
new_node->wildcard_certs.swap(cn->wildcard_certs);
new_node->next.swap(cn->next);
cn->next.push_back(new_node);
cn->last = i;
if (j == -1) {
// This hostname is a suffix of the existing hostname.
cn->ssl_ctx = ssl_ctx;
} else {
// This hostname and existing one share suffix.
cn->ssl_ctx = nullptr;
cert_lookup_tree_add_cert(lt, cn, ssl_ctx, hostname, len, j);
}
new_node->ssl_ctx = nullptr;
new_node->wildcard_certs.emplace_back(hostname, ssl_ctx);
}
node->next.push_back(std::move(new_node));
return;
}
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 the same hostname already exists, we don't overwrite
// exiting ssl_ctx
if (!cn->ssl_ctx) {
cn->ssl_ctx = ssl_ctx;
}
return;
}
// 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;
new_node->last = cn->last;
new_node->wildcard_certs.swap(cn->wildcard_certs);
new_node->next.swap(cn->next);
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;
return;
}
// This hostname and existing one share suffix.
cn->ssl_ctx = nullptr;
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;
}
// 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;

View File

@ -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.

View File

@ -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);
}

View File

@ -48,7 +48,7 @@ class Http2Session;
class ConnectBlocker;
namespace ssl {
struct CertLookupTree;
class CertLookupTree;
} // namespace ssl
struct WorkerStat {

View File

@ -32,7 +32,7 @@
namespace shrpx {
namespace ssl {
struct CertLookupTree;
class CertLookupTree;
} // namespace ssl
struct TicketKeys;