diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 8e6b7d7b..ee96cd83 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -166,7 +166,7 @@ if(ENABLE_APP) ) target_include_directories(nghttpx-unittest PRIVATE ${CUNIT_INCLUDE_DIRS}) target_compile_definitions(nghttpx-unittest - PRIVATE "-DNGHTTP2_TESTS_DIR=\"${CMAKE_SOURCE_DIR}/tests\"" + PRIVATE "-DNGHTTP2_SRC_DIR=\"${CMAKE_SOURCE_DIR}/src\"" ) target_link_libraries(nghttpx-unittest nghttpx_static ${CUNIT_LIBRARIES}) if(HAVE_MRUBY) diff --git a/src/Makefile.am b/src/Makefile.am index 587b9e4b..cee98e85 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -22,7 +22,10 @@ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. SUBDIRS = includes -EXTRA_DIST = CMakeLists.txt +EXTRA_DIST = \ + CMakeLists.txt \ + test.example.com.pem \ + test.nghttp2.org.pem bin_PROGRAMS = check_PROGRAMS = @@ -187,7 +190,7 @@ nghttpx_unittest_SOURCES = shrpx-unittest.cc \ template_test.cc template_test.h \ base64_test.cc base64_test.h nghttpx_unittest_CPPFLAGS = ${AM_CPPFLAGS} \ - -DNGHTTP2_TESTS_DIR=\"$(top_srcdir)/tests\" + -DNGHTTP2_SRC_DIR=\"$(top_srcdir)/src\" nghttpx_unittest_LDADD = libnghttpx.a ${LDADD} @CUNIT_LIBS@ @TESTLDADD@ if HAVE_MRUBY diff --git a/src/ca-config.json b/src/ca-config.json new file mode 100644 index 00000000..5788d073 --- /dev/null +++ b/src/ca-config.json @@ -0,0 +1,17 @@ +{ + "signing": { + "default": { + "expiry": "87600h" + }, + "profiles": { + "server": { + "expiry": "87600h", + "usages": [ + "signing", + "key encipherment", + "server auth" + ] + } + } + } +} diff --git a/src/ca.nghttp2.org-key.pem b/src/ca.nghttp2.org-key.pem new file mode 100644 index 00000000..6ce8707f --- /dev/null +++ b/src/ca.nghttp2.org-key.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpQIBAAKCAQEA1kVkF8QSUwW/HV9EFRPSMoiOVmYwB8vqKDtT0d6MFiKAM8/Y +JFUq2uKlUydgT4IPE7PATvVcIj3GtL9XzPhscqYO/S0Y7scyTE2VAPmtz+StPWf2 +wZ1IQR09HrnDTc44KvYGZpefBZkD9UjbmJ9a1ZmJjJiMr3hTnKE/sxZ2+dMsnMZX +N822cfaHyTN+T0+Tyw5vBBboCDsZzxmf+9FFIDJNs3NL34cR8EZRhpfaegapH8bt +OJ+D+RZ2kg7E/YYkGcS6NodvTjSUFCFHpWjHCfTFhn/owBIAooCdWorh6dc8Q72l +AodwNLXS8uuPgPqM5s4Cz57m7Zgs4OilNmIdawIDAQABAoIBAQCwqLtygLye6KD+ +RXorapEmCsJX5553/x6Klwdvg+25ni5XCWjp47IWj0DBQzi7tL5bfxrxvod8z7QR +d6SbIMLA77px8Ima7G7CzEAqcrBkM+TFOP8P+G4HCWVH/N5SOtDCUt9KHH4Grna9 +95jdx5yreRAX8/oh/bHp9GRBcicbpwYMVWOnjTE2seEUYQOpdpYdP4bOPUvAju0l +mwmy2/dDGmbibktN3sdHEhDodKu+Znv7nFZo0jzhlyoXse653WcvaQeZZYuojvSe +Sr92DvPp7UaYrb4KvT7ujXiPavSV2m/4EmGtyqevUf2dZ6sfMXZjmXsjWz9txhWp +4BgbHyHRAoGBAPqyuNj2CDD3FE7N3Hxyba8d+ZtsVUNawjq2gwOvT9NLsMstOGyH +OCc1v4W6Sq4w1wo4nIJyY8kNZwtReaTHOPZlDgBhVvk/x8eLBu+QTMRyocRt1LoD +8HyKxWSAnYTtCh/GUEQ37amIqvOJ5GNL+25WDzevLa5kMYWG743uxEupAoGBANrN +c/fVxepvP0GISlLpL3aZCFGAjMrq3xUYcf/w4wPoMq6AdpIPeRVBmJ1/Uqw1FkV8 +NRKJNPE2YcMuv8iMeQlacoPd34KT9ob80EYVlMwAkeC0NK+FfiM/UteR0wB49gmi +ugX9YlJytOP9aUgPvEGT6l+XtgGC44W1TQWe62zzAoGBAKZenNU+0UjNb6isbToZ +Jjkkh1Vhm2PLg0I7hM6ZNTxf6r+rDtrXEajTvnocmxrmRo796r+W8immv09/jl6P +53l8rsIJ1xIqBYai+MNa29cyy6/zw0x++MVtwnlj8SUZubJEhVgAVbRAglKEnBBZ +iE48xnSJyKMG0uZuGePzJEmhAoGBAIOHJcNBumum3DuklikpC+MbMyjrQbdpYRjp +TP4x7AWZO34ysxQyQPNKL1feBfCHKRA0DiNKX4zwx+vw2lDQQKIiwNwMMCPqljOn +HfxDVOMdJJQTP+iTMrQ1iLMVceXC0QQR0glvu/8b/SlgWD19WAmDxUwZgst9xw/F +YLuUQKmJAoGAREeTugd4hc0U/YV/BQQjSCLhl11EtVry/oQMHj8KZpIJhP7tj8lw +hSE0+z04oMhiTeq55PYKQkTo5l6V4PW0zfpEwlKEEm0erab1G9Ddh7us47XFcKLl +Rmk192EVZ0lQuzftsYv7dzRLiAR7yDFXwD1ELIK/uPkwBtu7wtHlq+M= +-----END RSA PRIVATE KEY----- diff --git a/src/ca.nghttp2.org.csr b/src/ca.nghttp2.org.csr new file mode 100644 index 00000000..37ee5600 --- /dev/null +++ b/src/ca.nghttp2.org.csr @@ -0,0 +1,17 @@ +-----BEGIN CERTIFICATE REQUEST----- +MIICwjCCAaoCAQAwXjELMAkGA1UEBhMCQVUxEzARBgNVBAgTClNvbWUtU3RhdGUx +ITAfBgNVBAoTGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEXMBUGA1UEAxMOY2Eu +bmdodHRwMi5vcmcwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDWRWQX +xBJTBb8dX0QVE9IyiI5WZjAHy+ooO1PR3owWIoAzz9gkVSra4qVTJ2BPgg8Ts8BO +9VwiPca0v1fM+Gxypg79LRjuxzJMTZUA+a3P5K09Z/bBnUhBHT0eucNNzjgq9gZm +l58FmQP1SNuYn1rVmYmMmIyveFOcoT+zFnb50yycxlc3zbZx9ofJM35PT5PLDm8E +FugIOxnPGZ/70UUgMk2zc0vfhxHwRlGGl9p6Bqkfxu04n4P5FnaSDsT9hiQZxLo2 +h29ONJQUIUelaMcJ9MWGf+jAEgCigJ1aiuHp1zxDvaUCh3A0tdLy64+A+ozmzgLP +nubtmCzg6KU2Yh1rAgMBAAGgHzAdBgkqhkiG9w0BCQ4xEDAOMAwGA1UdEwQFMAMB +Af8wDQYJKoZIhvcNAQELBQADggEBACI5v8GbOXKv38h9/tuGEwJ9uxpYEljgGt8h +QL5lwfEifh/7A8b39b9JEzWk5hnMRCOb8J6Jc3/6nmVgtKkQ+Mceupqpwsp1gT/v +uUoAkJE03Iuja9zLhHmy74oZ7LWOQrZ1T7Z0eGQ+5u+LBZiPKnKxmkLCQoUPTbc4 +NQ9BbKhr8OaoJ4DDvJnszcL7to6kih7SkdoNZsq4zB0/ai/cPhvoVgkYfbLH2++D +Tcs7TqU2L7gKzqXUtHeAKM2y81ewL7QTrcYzgiW86s3NmquxZG5pq0mjD+P4BYLc +MOdnCxKbBuE/1R29pa6+JKgc46jOa2yRgv5+8rXkkpu53Ke3FGc= +-----END CERTIFICATE REQUEST----- diff --git a/src/ca.nghttp2.org.csr.json b/src/ca.nghttp2.org.csr.json new file mode 100644 index 00000000..69d9c6a4 --- /dev/null +++ b/src/ca.nghttp2.org.csr.json @@ -0,0 +1,17 @@ +{ + "CN": "ca.nghttp2.org", + "key": { + "algo": "rsa", + "size": 2048 + }, + "ca": { + "expiry": "87600h" + }, + "names": [ + { + "C": "AU", + "ST": "Some-State", + "O": "Internet Widgits Pty Ltd" + } + ] +} diff --git a/src/ca.nghttp2.org.pem b/src/ca.nghttp2.org.pem new file mode 100644 index 00000000..e50acfc9 --- /dev/null +++ b/src/ca.nghttp2.org.pem @@ -0,0 +1,22 @@ +-----BEGIN CERTIFICATE----- +MIIDrTCCApWgAwIBAgIUe4dvx8haIjsT3ZpNCMrl62Xk6E0wDQYJKoZIhvcNAQEL +BQAwXjELMAkGA1UEBhMCQVUxEzARBgNVBAgTClNvbWUtU3RhdGUxITAfBgNVBAoT +GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEXMBUGA1UEAxMOY2EubmdodHRwMi5v +cmcwHhcNMTYwNjI1MDkzMzAwWhcNMjYwNjIzMDkzMzAwWjBeMQswCQYDVQQGEwJB +VTETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0 +cyBQdHkgTHRkMRcwFQYDVQQDEw5jYS5uZ2h0dHAyLm9yZzCCASIwDQYJKoZIhvcN +AQEBBQADggEPADCCAQoCggEBANZFZBfEElMFvx1fRBUT0jKIjlZmMAfL6ig7U9He +jBYigDPP2CRVKtripVMnYE+CDxOzwE71XCI9xrS/V8z4bHKmDv0tGO7HMkxNlQD5 +rc/krT1n9sGdSEEdPR65w03OOCr2BmaXnwWZA/VI25ifWtWZiYyYjK94U5yhP7MW +dvnTLJzGVzfNtnH2h8kzfk9Pk8sObwQW6Ag7Gc8Zn/vRRSAyTbNzS9+HEfBGUYaX +2noGqR/G7Tifg/kWdpIOxP2GJBnEujaHb040lBQhR6Voxwn0xYZ/6MASAKKAnVqK +4enXPEO9pQKHcDS10vLrj4D6jObOAs+e5u2YLODopTZiHWsCAwEAAaNjMGEwDgYD +VR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFNA5xVR1Zcax +RJL9VC6pzuLmvduGMB8GA1UdIwQYMBaAFNA5xVR1ZcaxRJL9VC6pzuLmvduGMA0G +CSqGSIb3DQEBCwUAA4IBAQCmdVfn/hUyEdvkKG7svg5d8o6BENOj8695KtWmzJjK +zxH8J5Vy3mn89XrHQ+BOYXCDPyhs0aDS8aq3Z+HY0n9z1oAicyGzlVwZQQNX3YId +Y2vcf7qu/2ATm/1S+mebE1/EXMUlWISKKUYXjggCwFgjDhH87Ai+A8MKScVdmqgL +Hf+fRSzH3ToW7BCXlRl5bPAq2g+v1ALYc8wU9cT1MYm4dqAXh870LGFyUpaSWmFr +TtX1DXBTgLp62syNlDthAvGigYFDtCa4cDM2vdTD9wpec2V9EKpfVqiRDDuYjUVX +UXl27MvkNWnEBKCIoNv5abWXpZVG2zQdEMmUOkVuAXUC +-----END CERTIFICATE----- diff --git a/src/shrpx-unittest.cc b/src/shrpx-unittest.cc index 12f7e84e..f9acf880 100644 --- a/src/shrpx-unittest.cc +++ b/src/shrpx-unittest.cc @@ -72,8 +72,8 @@ int main(int argc, char *argv[]) { // add the tests to the suite if (!CU_add_test(pSuite, "ssl_create_lookup_tree", shrpx::test_shrpx_ssl_create_lookup_tree) || - !CU_add_test(pSuite, "ssl_cert_lookup_tree_add_cert_from_file", - shrpx::test_shrpx_ssl_cert_lookup_tree_add_cert_from_file) || + !CU_add_test(pSuite, "ssl_cert_lookup_tree_add_cert_from_x509", + shrpx::test_shrpx_ssl_cert_lookup_tree_add_cert_from_x509) || !CU_add_test(pSuite, "ssl_tls_hostname_match", shrpx::test_shrpx_ssl_tls_hostname_match) || !CU_add_test(pSuite, "http2_add_header", shrpx::test_http2_add_header) || diff --git a/src/shrpx_connection_handler.cc b/src/shrpx_connection_handler.cc index ae41cf7e..933a6092 100644 --- a/src/shrpx_connection_handler.cc +++ b/src/shrpx_connection_handler.cc @@ -878,4 +878,8 @@ void ConnectionHandler::send_serial_event(SerialEvent ev) { ev_async_send(loop_, &serial_event_asyncev_); } +SSL_CTX *ConnectionHandler::get_ssl_ctx(size_t idx) const { + return all_ssl_ctx_[idx]; +} + } // namespace shrpx diff --git a/src/shrpx_connection_handler.h b/src/shrpx_connection_handler.h index 019a2545..2003c5cf 100644 --- a/src/shrpx_connection_handler.h +++ b/src/shrpx_connection_handler.h @@ -146,6 +146,9 @@ public: ev_timer *w); void schedule_next_tls_ticket_key_memcached_get(ev_timer *w); SSL_CTX *create_tls_ticket_key_memcached_ssl_ctx(); + // Returns the SSL_CTX at all_ssl_ctx_[idx]. This does not perform + // array bound checking. + SSL_CTX *get_ssl_ctx(size_t idx) const; #ifdef HAVE_NEVERBLEED void set_neverbleed(std::unique_ptr nb); diff --git a/src/shrpx_router.cc b/src/shrpx_router.cc index caf8383a..67516932 100644 --- a/src/shrpx_router.cc +++ b/src/shrpx_router.cc @@ -279,6 +279,22 @@ ssize_t Router::match(const StringRef &host, const StringRef &path) const { return node->index; } +ssize_t Router::match(const StringRef &s) const { + const RNode *node; + size_t offset; + + node = match_complete(&offset, &root_, std::begin(s), std::end(s)); + if (node == nullptr) { + return -1; + } + + if (node->len != offset) { + return -1; + } + + return node->index; +} + namespace { const RNode *match_prefix(size_t *nread, const RNode *node, const char *first, const char *last) { diff --git a/src/shrpx_router.h b/src/shrpx_router.h index b193e64a..a5a893d3 100644 --- a/src/shrpx_router.h +++ b/src/shrpx_router.h @@ -67,6 +67,9 @@ public: bool add_route(const StringRef &pattern, size_t index); // Returns the matched index of pattern. -1 if there is no match. ssize_t match(const StringRef &host, const StringRef &path) const; + // Returns the matched index of pattern |s|. -1 if there is no + // match. + ssize_t match(const StringRef &s) const; // Returns the matched index of pattern if a pattern is a suffix of // |s|, otherwise -1. If |*last_node| is not nullptr, it specifies // the first node to start matching. If it is nullptr, match will diff --git a/src/shrpx_ssl.cc b/src/shrpx_ssl.cc index 7ff70955..1d835ec8 100644 --- a/src/shrpx_ssl.cc +++ b/src/shrpx_ssl.cc @@ -38,6 +38,8 @@ #include #include +#include + #include #include #include @@ -58,6 +60,7 @@ #include "shrpx_http2_session.h" #include "shrpx_memcached_request.h" #include "shrpx_memcached_dispatcher.h" +#include "shrpx_connection_handler.h" #include "util.h" #include "ssl.h" #include "template.h" @@ -143,16 +146,39 @@ int servername_callback(SSL *ssl, int *al, void *arg) { auto handler = static_cast(conn->data); auto worker = handler->get_worker(); auto cert_tree = worker->get_cert_lookup_tree(); - if (cert_tree) { - const char *hostname = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name); - if (hostname) { - auto len = strlen(hostname); - auto ssl_ctx = cert_tree->lookup(StringRef{hostname, len}); - if (ssl_ctx) { - SSL_set_SSL_CTX(ssl, ssl_ctx); - } - } + if (!cert_tree) { + return SSL_TLSEXT_ERR_OK; } + + std::array buf; + + auto rawhost = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name); + if (rawhost == nullptr) { + return SSL_TLSEXT_ERR_OK; + } + + auto len = strlen(rawhost); + // NI_MAXHOST includes terminal NULL. + if (len == 0 || len + 1 > buf.size()) { + return SSL_TLSEXT_ERR_OK; + } + + auto end_buf = std::copy_n(rawhost, len, std::begin(buf)); + + util::inp_strlower(std::begin(buf), end_buf); + + auto hostname = StringRef{std::begin(buf), end_buf}; + + auto idx = cert_tree->lookup(hostname); + if (idx == -1) { + return SSL_TLSEXT_ERR_OK; + } + + auto conn_handler = worker->get_connection_handler(); + auto ssl_ctx = conn_handler->get_ssl_ctx(idx); + + SSL_set_SSL_CTX(ssl, ssl_ctx); + return SSL_TLSEXT_ERR_OK; } } // namespace @@ -1075,181 +1101,129 @@ int check_cert(SSL *ssl, const DownstreamAddr *addr) { return check_cert(ssl, &addr->addr, hostname); } -CertLookupTree::CertLookupTree() { - root_.ssl_ctx = nullptr; - root_.str = nullptr; - root_.first = root_.last = 0; -} +CertLookupTree::CertLookupTree() {} -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(CertNode *node, SSL_CTX *ssl_ctx, - const 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].get(); - if (cn->str[cn->first] == c) { - break; - } +void CertLookupTree::add_cert(const StringRef &hostname, size_t idx) { + std::array buf; + + // NI_MAXHOST includes terminal NULL byte + if (hostname.empty() || hostname.size() + 1 > buf.size()) { + return; } - if (i == next_len) { - if (c == '*') { - // We assume hostname as wildcard hostname when first '*' is - // encountered. Note that as per RFC 6125 (6.4.3), there are - // 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({ssl_ctx, hostname, len}); - return; - } - int j; - auto new_node = make_unique(); - 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; + auto wildcard_it = std::find(std::begin(hostname), std::end(hostname), '*'); + if (wildcard_it != std::end(hostname) && + wildcard_it + 1 != std::end(hostname)) { + auto wildcard_prefix = StringRef{std::begin(hostname), wildcard_it}; + auto wildcard_suffix = StringRef{wildcard_it + 1, std::end(hostname)}; + + auto rev_suffix = StringRef{std::begin(buf), + std::reverse_copy(std::begin(wildcard_suffix), + std::end(wildcard_suffix), + std::begin(buf))}; + + WildcardPattern *wpat; + + if (!rev_wildcard_router_.add_route(rev_suffix, + wildcard_patterns_.size())) { + auto wcidx = rev_wildcard_router_.match(rev_suffix); + + assert(wcidx != -1); + + wpat = &wildcard_patterns_[wcidx]; } else { - new_node->ssl_ctx = nullptr; - new_node->wildcard_certs.push_back({ssl_ctx, hostname, len}); + wildcard_patterns_.emplace_back(); + wpat = &wildcard_patterns_.back(); } - node->next.push_back(std::move(new_node)); + + auto rev_prefix = StringRef{std::begin(buf), + std::reverse_copy(std::begin(wildcard_prefix), + std::end(wildcard_prefix), + std::begin(buf))}; + + wpat->rev_prefix.emplace_back(rev_prefix, idx); + 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; + router_.add_route(hostname, idx); +} + +ssize_t CertLookupTree::lookup(const StringRef &hostname) { + std::array buf; + + // NI_MAXHOST includes terminal NULL byte + if (hostname.empty() || hostname.size() + 1 > buf.size()) { + return -1; + } + + // Always prefer exact match + auto idx = router_.match(hostname); + if (idx != -1) { + return idx; + } + + if (wildcard_patterns_.empty()) { + return -1; + } + + ssize_t best_idx = -1; + size_t best_prefixlen = 0; + const RNode *last_node = nullptr; + + auto rev_host = StringRef{ + std::begin(buf), std::reverse_copy(std::begin(hostname), + std::end(hostname), std::begin(buf))}; + + for (;;) { + size_t nread = 0; + + auto wcidx = + rev_wildcard_router_.match_prefix(&nread, &last_node, rev_host); + if (wcidx == -1) { + return best_idx; + } + + // '*' must match at least one byte + if (nread == rev_host.size()) { + return best_idx; + } + + rev_host = StringRef{std::begin(rev_host) + nread, std::end(rev_host)}; + + auto rev_prefix = StringRef{std::begin(rev_host) + 1, std::end(rev_host)}; + + auto &wpat = wildcard_patterns_[wcidx]; + for (auto &wprefix : wpat.rev_prefix) { + if (!util::ends_with(rev_prefix, wprefix.prefix)) { + continue; } - 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 prefixlen = + wprefix.prefix.size() + + (reinterpret_cast(&rev_host[0]) - &buf[0]); - { - auto new_node = make_unique(); - 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); + // Breaking a tie with longer suffix + if (prefixlen < best_prefixlen) { + continue; + } - 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 CertLookupTree::add_cert(SSL_CTX *ssl_ctx, const StringRef &hostname) { - if (hostname.empty()) { - return; - } - // Copy hostname - auto host_copy = make_unique(hostname.size() + 1); - std::copy(std::begin(hostname), std::end(hostname), host_copy.get()); - host_copy[hostname.size()] = '\0'; - util::inp_strlower(&host_copy[0], &host_copy[0] + hostname.size()); - - cert_lookup_tree_add_cert(&root_, ssl_ctx, host_copy.get(), hostname.size(), - hostname.size() - 1); - - hosts_.push_back(std::move(host_copy)); -} - -namespace { -SSL_CTX *cert_lookup_tree_lookup(CertNode *node, const StringRef &hostname, - int offset) { - int i, j; - for (i = node->first, j = offset; - i > node->last && j >= 0 && node->str[i] == util::lowcase(hostname[j]); - --i, --j) - ; - if (i != node->last) { - return nullptr; - } - if (j == -1) { - if (node->ssl_ctx) { - // exact match - return node->ssl_ctx; - } - - // Do not perform wildcard-match because '*' must match at least - // one character. - return nullptr; - } - - for (const auto &wildcert : node->wildcard_certs) { - if (tls_hostname_match(StringRef{wildcert.hostname, wildcert.hostnamelen}, - hostname)) { - return wildcert.ssl_ctx; + best_idx = wprefix.idx; + best_prefixlen = prefixlen; } } - 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(next_node.get(), hostname, j); - } - } - return nullptr; -} -} // namespace - -SSL_CTX *CertLookupTree::lookup(const StringRef &hostname) { - if (hostname.empty()) { - return nullptr; - } - return cert_lookup_tree_lookup(&root_, hostname, hostname.size() - 1); } -int cert_lookup_tree_add_cert_from_file(CertLookupTree *lt, SSL_CTX *ssl_ctx, - const char *certfile) { - auto bio = BIO_new(BIO_s_file()); - if (!bio) { - LOG(ERROR) << "BIO_new failed"; - return -1; - } - auto bio_deleter = defer(BIO_vfree, bio); - if (!BIO_read_filename(bio, certfile)) { - LOG(ERROR) << "Could not read certificate file '" << certfile << "'"; - return -1; - } - auto cert = PEM_read_bio_X509(bio, nullptr, nullptr, nullptr); - if (!cert) { - LOG(ERROR) << "Could not read X509 structure from file '" << certfile - << "'"; - return -1; - } - auto cert_deleter = defer(X509_free, cert); +void CertLookupTree::dump() const { + std::cerr << "exact:" << std::endl; + router_.dump(); + std::cerr << "wildcard suffix (reversed):" << std::endl; + rev_wildcard_router_.dump(); +} + +int cert_lookup_tree_add_cert_from_x509(CertLookupTree *lt, size_t idx, + X509 *cert) { + std::array buf; auto altnames = static_cast( X509_get_ext_d2i(cert, NID_subject_alt_name, nullptr, nullptr)); @@ -1285,7 +1259,15 @@ int cert_lookup_tree_add_cert_from_file(CertLookupTree *lt, SSL_CTX *ssl_ctx, } dns_found = true; - lt->add_cert(ssl_ctx, StringRef{name, static_cast(len)}); + + if (static_cast(len) + 1 > buf.size()) { + continue; + } + + auto end_buf = std::copy_n(name, len, std::begin(buf)); + util::inp_strlower(std::begin(buf), end_buf); + + lt->add_cert(StringRef{std::begin(buf), end_buf}, idx); } // Don't bother CN if we have dNSName. @@ -1309,10 +1291,14 @@ int cert_lookup_tree_add_cert_from_file(CertLookupTree *lt, SSL_CTX *ssl_ctx, cn = StringRef{cn.c_str(), cn.size() - 1}; } - lt->add_cert(ssl_ctx, cn); + auto end_buf = std::copy(std::begin(cn), std::end(cn), std::begin(buf)); OPENSSL_free(const_cast(cn.c_str())); + util::inp_strlower(std::begin(buf), end_buf); + + lt->add_cert(StringRef{std::begin(buf), end_buf}, idx); + return 0; } @@ -1365,6 +1351,13 @@ SSL_CTX *setup_server_ssl_context(std::vector &all_ssl_ctx, return ssl_ctx; } + if (ssl::cert_lookup_tree_add_cert_from_x509( + cert_tree, all_ssl_ctx.size() - 1, + SSL_CTX_get0_certificate(ssl_ctx)) == -1) { + LOG(FATAL) << "Failed to add default certificate."; + DIE(); + } + for (auto &keycert : tlsconf.subcerts) { auto ssl_ctx = ssl::create_ssl_context(keycert.first.c_str(), keycert.second.c_str() @@ -1374,19 +1367,14 @@ SSL_CTX *setup_server_ssl_context(std::vector &all_ssl_ctx, #endif // HAVE_NEVERBLEED ); all_ssl_ctx.push_back(ssl_ctx); - if (ssl::cert_lookup_tree_add_cert_from_file( - cert_tree, ssl_ctx, keycert.second.c_str()) == -1) { + if (ssl::cert_lookup_tree_add_cert_from_x509( + cert_tree, all_ssl_ctx.size() - 1, + SSL_CTX_get0_certificate(ssl_ctx)) == -1) { LOG(FATAL) << "Failed to add sub certificate."; DIE(); } } - if (ssl::cert_lookup_tree_add_cert_from_file( - cert_tree, ssl_ctx, tlsconf.cert_file.c_str()) == -1) { - LOG(FATAL) << "Failed to add default certificate."; - DIE(); - } - return ssl_ctx; } diff --git a/src/shrpx_ssl.h b/src/shrpx_ssl.h index 643f4ffa..294b4efe 100644 --- a/src/shrpx_ssl.h +++ b/src/shrpx_ssl.h @@ -40,6 +40,7 @@ #endif // HAVE_NEVERBLEED #include "network.h" +#include "shrpx_router.h" namespace shrpx { @@ -103,73 +104,69 @@ int check_cert(SSL *ssl, const DownstreamAddr *addr); void get_altnames(X509 *cert, std::vector &dns_names, std::vector &ip_addrs, std::string &common_name); -// CertLookupTree forms lookup tree to get SSL_CTX whose DNS or -// commonName matches hostname in query. The tree is patricia trie -// data structure formed from the tail of the hostname pattern. Each -// CertNode contains part of hostname str member in range [first, -// last) member and the next member contains the following CertNode -// pointers ('following' means character before the current one). The -// CertNode where a hostname pattern ends contains its SSL_CTX pointer -// in the ssl_ctx member. For wildcard hostname pattern, we store the -// its pattern and SSL_CTX in CertNode one before first "*" found from -// the tail. -// -// When querying SSL_CTX with particular hostname, we match from its -// tail in our lookup tree. If the query goes to the first character -// of the hostname and current CertNode has non-NULL ssl_ctx member, -// then it is the exact match. The ssl_ctx member is returned. Along -// the way, if CertNode which contains non-empty wildcard_certs member -// is encountered, wildcard hostname matching is performed against -// them. If there is a match, its SSL_CTX is returned. If none -// matches, query is continued to the next character. +struct WildcardRevPrefix { + WildcardRevPrefix(const StringRef &prefix, size_t idx) + : prefix(std::begin(prefix), std::end(prefix)), idx(idx) {} -struct WildcardCert { - SSL_CTX *ssl_ctx; - const char *hostname; - size_t hostnamelen; + // "Prefix" of wildcard pattern. It is reversed from original form. + // For example, if the original wildcard is "test*.nghttp2.org", + // prefix would be "tset". + ImmutableString prefix; + // The index of SSL_CTX. See ConnectionHandler::get_ssl_ctx(). + size_t idx; }; -struct CertNode { - // list of wildcard domain name and its SSL_CTX pair, the wildcard - // '*' appears in this position. - std::vector wildcard_certs; - // Next CertNode index of CertLookupTree::nodes - std::vector> next; - // SSL_CTX for exact match - SSL_CTX *ssl_ctx; - const char *str; - // [first, last) in the reverse direction in str, first >= - // last. This indices only work for str member. - int first, last; +struct WildcardPattern { + // Wildcard host sharing only suffix is probably rare, so we just do + // linear search. + std::vector rev_prefix; }; class CertLookupTree { public: CertLookupTree(); - // Adds |ssl_ctx| with hostname pattern |hostname| to the lookup - // tree. - void add_cert(SSL_CTX *ssl_ctx, const StringRef &hostname); + // Adds hostname pattern |hostname| to the lookup tree, associating + // value |index|. When the queried host matches this pattern, + // |index| is returned. We support wildcard pattern. The left most + // '*' is considered as wildcard character, and it must match at + // least one character. If the same pattern has been already added, + // this function is noop. + // + // The caller should lower-case |hostname| since this function does + // do that, and lookup function performs case-sensitive match. + // + // TODO Treat wildcard pattern described as RFC 6125. + void add_cert(const StringRef &hostname, size_t index); - // Looks up SSL_CTX using the given |hostname|. 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 StringRef &hostname); + // Looks up index using the given |hostname|. The exact match takes + // precedence over wildcard match. For wildcard match, longest + // match (sum of matched suffix and prefix length in bytes) is + // preferred, breaking a tie with longer suffix. + // + // The caller should lower-case |hostname| since this function + // performs case-sensitive match. + ssize_t lookup(const StringRef &hostname); + + // Dumps the contents of this lookup tree to stderr. + void dump() const; private: - CertNode root_; - // Stores pointers to copied hostname when adding hostname and - // ssl_ctx pair. - std::vector> hosts_; + // Exact match + Router router_; + // Wildcard reversed suffix match. The returned index is into + // wildcard_patterns_. + Router rev_wildcard_router_; + // Stores wildcard suffix patterns. + std::vector wildcard_patterns_; }; -// 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. -// Even if no ssl_ctx is added to tree, this function returns 0. -int cert_lookup_tree_add_cert_from_file(CertLookupTree *lt, SSL_CTX *ssl_ctx, - const char *certfile); +// Adds hostnames in |cert| to lookup tree |lt|. The subjectAltNames +// and commonName are considered as eligible hostname. If there is at +// least one dNSName in subjectAltNames, commonName is not considered. +// This function returns 0 if it succeeds, or -1. +int cert_lookup_tree_add_cert_from_x509(CertLookupTree *lt, size_t idx, + X509 *cert); // Returns true if |proto| is included in the // protocol list |protos|. diff --git a/src/shrpx_ssl_test.cc b/src/shrpx_ssl_test.cc index c1a50a35..63062f4c 100644 --- a/src/shrpx_ssl_test.cc +++ b/src/shrpx_ssl_test.cc @@ -36,83 +36,133 @@ namespace shrpx { void test_shrpx_ssl_create_lookup_tree(void) { auto tree = make_unique(); - SSL_CTX *ctxs[] = { - SSL_CTX_new(SSLv23_method()), SSL_CTX_new(SSLv23_method()), - SSL_CTX_new(SSLv23_method()), SSL_CTX_new(SSLv23_method()), - SSL_CTX_new(SSLv23_method()), SSL_CTX_new(SSLv23_method()), - SSL_CTX_new(SSLv23_method()), SSL_CTX_new(SSLv23_method()), - SSL_CTX_new(SSLv23_method()), SSL_CTX_new(SSLv23_method())}; constexpr StringRef hostnames[] = { - StringRef::from_lit("example.com"), - StringRef::from_lit("www.example.org"), - StringRef::from_lit("*www.example.org"), - StringRef::from_lit("x*.host.domain"), - StringRef::from_lit("*yy.host.domain"), - StringRef::from_lit("nghttp2.sourceforge.net"), - StringRef::from_lit("sourceforge.net"), - StringRef::from_lit("sourceforge.net"), // duplicate - StringRef::from_lit("*.foo.bar"), // oo.bar is suffix of *.foo.bar - StringRef::from_lit("oo.bar")}; - auto num = array_size(ctxs); - for (size_t i = 0; i < num; ++i) { - tree->add_cert(ctxs[i], hostnames[i]); + StringRef::from_lit("example.com"), // 0 + StringRef::from_lit("www.example.org"), // 1 + StringRef::from_lit("*www.example.org"), // 2 + StringRef::from_lit("xy*.host.domain"), // 3 + StringRef::from_lit("*yy.host.domain"), // 4 + StringRef::from_lit("nghttp2.sourceforge.net"), // 5 + StringRef::from_lit("sourceforge.net"), // 6 + StringRef::from_lit("sourceforge.net"), // 7, duplicate + StringRef::from_lit("*.foo.bar"), // 8, oo.bar is suffix of *.foo.bar + StringRef::from_lit("oo.bar") // 9 + }; + auto num = array_size(hostnames); + + for (size_t idx = 0; idx < num; ++idx) { + tree->add_cert(hostnames[idx], idx); } - CU_ASSERT(ctxs[0] == tree->lookup(hostnames[0])); - CU_ASSERT(ctxs[1] == tree->lookup(hostnames[1])); - CU_ASSERT(ctxs[2] == tree->lookup(StringRef::from_lit("2www.example.org"))); - CU_ASSERT(nullptr == tree->lookup(StringRef::from_lit("www2.example.org"))); - CU_ASSERT(ctxs[3] == tree->lookup(StringRef::from_lit("x1.host.domain"))); + tree->dump(); + + CU_ASSERT(0 == tree->lookup(hostnames[0])); + CU_ASSERT(1 == tree->lookup(hostnames[1])); + CU_ASSERT(2 == tree->lookup(StringRef::from_lit("2www.example.org"))); + CU_ASSERT(-1 == tree->lookup(StringRef::from_lit("www2.example.org"))); + CU_ASSERT(3 == tree->lookup(StringRef::from_lit("xy1.host.domain"))); // Does not match *yy.host.domain, because * must match at least 1 // character. - CU_ASSERT(nullptr == tree->lookup(StringRef::from_lit("yy.Host.domain"))); - CU_ASSERT(ctxs[4] == tree->lookup(StringRef::from_lit("zyy.host.domain"))); - CU_ASSERT(nullptr == tree->lookup(StringRef{})); - CU_ASSERT(ctxs[5] == tree->lookup(hostnames[5])); - CU_ASSERT(ctxs[6] == tree->lookup(hostnames[6])); + CU_ASSERT(-1 == tree->lookup(StringRef::from_lit("yy.host.domain"))); + CU_ASSERT(4 == tree->lookup(StringRef::from_lit("xyy.host.domain"))); + CU_ASSERT(-1 == tree->lookup(StringRef{})); + CU_ASSERT(5 == tree->lookup(hostnames[5])); + CU_ASSERT(6 == tree->lookup(hostnames[6])); constexpr char h6[] = "pdylay.sourceforge.net"; for (int i = 0; i < 7; ++i) { - CU_ASSERT(0 == tree->lookup(StringRef{h6 + i, str_size(h6) - i})); + CU_ASSERT(-1 == tree->lookup(StringRef{h6 + i, str_size(h6) - i})); } - CU_ASSERT(ctxs[8] == tree->lookup(StringRef::from_lit("x.foo.bar"))); - CU_ASSERT(ctxs[9] == tree->lookup(hostnames[9])); + CU_ASSERT(8 == tree->lookup(StringRef::from_lit("x.foo.bar"))); + CU_ASSERT(9 == tree->lookup(hostnames[9])); - for (size_t i = 0; i < num; ++i) { - SSL_CTX_free(ctxs[i]); - } - - SSL_CTX *ctxs2[] = { - SSL_CTX_new(SSLv23_method()), SSL_CTX_new(SSLv23_method()), - SSL_CTX_new(SSLv23_method()), SSL_CTX_new(SSLv23_method())}; constexpr StringRef names[] = { - StringRef::from_lit("rab"), StringRef::from_lit("zab"), - StringRef::from_lit("zzub"), StringRef::from_lit("ab")}; - num = array_size(ctxs2); + StringRef::from_lit("rab"), // 1 + StringRef::from_lit("zab"), // 2 + StringRef::from_lit("zzub"), // 3 + StringRef::from_lit("ab") // 4 + }; + num = array_size(names); tree = make_unique(); - for (size_t i = 0; i < num; ++i) { - tree->add_cert(ctxs2[i], names[i]); + for (size_t idx = 0; idx < num; ++idx) { + tree->add_cert(names[idx], idx); } for (size_t i = 0; i < num; ++i) { - CU_ASSERT(ctxs2[i] == tree->lookup(names[i])); - } - - for (size_t i = 0; i < num; ++i) { - SSL_CTX_free(ctxs2[i]); + CU_ASSERT(i == tree->lookup(names[i])); } } -void test_shrpx_ssl_cert_lookup_tree_add_cert_from_file(void) { - int rv; - 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); - CU_ASSERT(0 == rv); - CU_ASSERT(ssl_ctx == tree.lookup(StringRef::from_lit("localhost"))); +namespace { +X509 *load_certificate(const char *filename) { + auto bio = BIO_new(BIO_s_file()); + if (!bio) { + fprintf(stderr, "BIO_new() failed\n"); + return nullptr; + } + auto bio_deleter = defer(BIO_vfree, bio); + if (!BIO_read_filename(bio, filename)) { + fprintf(stderr, "Could not read certificate file '%s'\n", filename); + return nullptr; + } + auto cert = PEM_read_bio_X509(bio, nullptr, nullptr, nullptr); + if (!cert) { + fprintf(stderr, "Could not read X509 structure from file '%s'\n", filename); + return nullptr; + } - SSL_CTX_free(ssl_ctx); + return cert; +} +} // namespace + +// We use cfssl to generate key pairs. +// +// CA self-signed key pairs generation: +// +// $ cfssl genkey -initca ca.nghttp2.org.csr.json | \ +// cfssljson -bare ca.nghttp2.org +// +// Create CSR: +// +// $ cfssl genkey test.nghttp2.org.csr.json | cfssljson -bare test.nghttp2.org +// $ cfssl genkey test.example.com.csr.json | cfssljson -bare test.example.com +// +// Sign CSR: +// +// $ cfssl sign -ca ca.nghttp2.org.pem -ca-key ca.nghttp2.org-key.pem \ +// -config=ca-config.json -profile=server test.nghttp2.org.csr | \ +// cfssljson -bare test.nghttp2.org +// +// $ cfssl sign -ca ca.nghttp2.org.pem -ca-key ca.nghttp2.org-key.pem \ +// -config=ca-config.json -profile=server test.example.com.csr | \ +// cfssljson -bare test.example.com +// +void test_shrpx_ssl_cert_lookup_tree_add_cert_from_x509(void) { + int rv; + + constexpr char nghttp2_certfile[] = NGHTTP2_SRC_DIR "/test.nghttp2.org.pem"; + auto nghttp2_cert = load_certificate(nghttp2_certfile); + auto nghttp2_cert_deleter = defer(X509_free, nghttp2_cert); + + constexpr char examples_certfile[] = NGHTTP2_SRC_DIR "/test.example.com.pem"; + auto examples_cert = load_certificate(examples_certfile); + auto examples_cert_deleter = defer(X509_free, examples_cert); + + ssl::CertLookupTree tree; + + rv = ssl::cert_lookup_tree_add_cert_from_x509(&tree, 0, nghttp2_cert); + + CU_ASSERT(0 == rv); + + rv = ssl::cert_lookup_tree_add_cert_from_x509(&tree, 1, examples_cert); + + CU_ASSERT(0 == rv); + + CU_ASSERT(-1 == tree.lookup(StringRef::from_lit("not-used.nghttp2.org"))); + CU_ASSERT(0 == tree.lookup(StringRef::from_lit("test.nghttp2.org"))); + CU_ASSERT(0 == tree.lookup(StringRef::from_lit("w.test.nghttp2.org"))); + CU_ASSERT(0 == tree.lookup(StringRef::from_lit("www.test.nghttp2.org"))); + CU_ASSERT(1 == tree.lookup(StringRef::from_lit("test.example.com"))); } template diff --git a/src/shrpx_ssl_test.h b/src/shrpx_ssl_test.h index 0b3619e2..8128e8fd 100644 --- a/src/shrpx_ssl_test.h +++ b/src/shrpx_ssl_test.h @@ -32,7 +32,7 @@ namespace shrpx { void test_shrpx_ssl_create_lookup_tree(void); -void test_shrpx_ssl_cert_lookup_tree_add_cert_from_file(void); +void test_shrpx_ssl_cert_lookup_tree_add_cert_from_x509(void); void test_shrpx_ssl_tls_hostname_match(void); } // namespace shrpx diff --git a/src/test.example.com-key.pem b/src/test.example.com-key.pem new file mode 100644 index 00000000..6d5515cf --- /dev/null +++ b/src/test.example.com-key.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpQIBAAKCAQEArf2UBsEh/xwd/4WZfVFf5sMyWcns/1idF2FroLDwqVUYRlxp +U/KbrIG8X8v3w4cVP/xOXd1y9Q+W9OLK2YScAeHeE97mHZXbcpowUxvTDv/GNIHH +XK/yvYM8R2EEnZR71qXdoXsCakv/aG2ewkkvA108eEbk0u7RxNmIsYsO0Y4iBtwB +M/MSkAYPfWdrdHhY9z0l4M8GAyUOuZc0t6j0zw3fzkjqmVgGEJvcVzvSgZIzMLqS +zvC89viXtj1pyzQjMLgmuDbs0l47uRHpZXgMGVyF3UuQipPzvhO7G/ZbX/4kUU5b +PabdtvPbjju7dE5PfGrKOThM6HS93Y7QvYTUtwIDAQABAoIBAQCjUL69iFOs7muK +CZGFe/iU1uxQM6XuGPN7mso3z15W07UxdlS3o6ZUSoLTONWcBxP/N4knulHJjZSY +0LivbDYz3htic3t0kdGmxOxPVnLKRXN6ncbQTaeAE8tlBMAcWd/UH2Tlylz+Ac// +6cV3gNJMShwUmhb3l4v3Rml0nZ6PO1pFc/Chk5L9REAV8G6rNtc9bzgmgoFucRO/ +8ce/uJrENt1Pu3vBvmz42DTGfG48v5RZ0OY4qEPawZJ7p+QYiTf6h3Eilss/AllW +PPfQ0thdyB+yrZ3p6qb+ZUYphpGxgg6YlQxLfDKAikuo+EXwjPBPfeHhTO4kAj+h +opDCroZhAoGBANyVFbagCWqwguE6nVPmnCaiNQUIh8b7L2CnkkLfdbPQr/KvyIjg +Ua125bTJhe9Uk+ZBWsobQkjA0Baidiylx51pWYaxPVn5araVmkh2dqMluk2QE82X +AWemBgKhAqCLLLMVXbrRYlxpKUm1Fc/lJ8Ig2R/MJSntTMpQhJtIejUbAoGBAMnt +XMvlFABCoFbI9GMcteI/KkvNGQUy3OKEln/QCssnE4/XIu7LCxy6P+1lycbFy/mQ +0bnp525sPEIIkMpi6LeAbSzYN2O3BRjNrjPcbx6Khz9DweNhRIo5qTFRszZ+pHbV +N+9Oc9JVenwPw6EuW7uZRFKFhCHtsBFdUrWLJoSVAoGAQ3ytdwGBwA2fDW/UgL32 +mm9YT2DrwbpKJYU/X4xkw44ett6HOTGAa9ULtINPogi7c2AdeeZbIk0znSk5hLF3 +4DZCOM5zWdrQhmpBGNh9ta6uUFq7ZFRGDsMh5Z4DYsER/PyVf7neIS3ffviTYtbW +kjNgmrTnzesXanK2D5heI28CgYEAhl+qjRTYhoPP53C7EOmeL/0QzHij2c3LKAJL +lKqBREewwNvNp1L/BhL7T6OY7unZny48IpgBJn5oaxkAIW5IpzSTcnBAC99TSPo2 +ntRmLdDJx9PzRrkHv2Q3r1ZLCEymbV3eZyWx9ZpkdAKZkL0k1mZcDP5Eu79Ml4Ge +9Kiw7TECgYEAh+nTKwrCUFGbe4RIVCj/QG7FVPbq5PdxJ3gILZ3/1XkhPcNRFKJS +u5qPfA02tYEALz9KXATK1uRB/GlBM7Eap/g2GFiHpVxrw6wPpybLywJmNyNTwqiq +eJxQ0FRzW9Kwwn1ThPY38LdFe/wvXZFOcNvGD8hHCLQRdlBR4zuTsBk= +-----END RSA PRIVATE KEY----- diff --git a/src/test.example.com.csr b/src/test.example.com.csr new file mode 100644 index 00000000..81639354 --- /dev/null +++ b/src/test.example.com.csr @@ -0,0 +1,17 @@ +-----BEGIN CERTIFICATE REQUEST----- +MIICpTCCAY0CAQAwYDELMAkGA1UEBhMCQVUxEzARBgNVBAgTClNvbWUtU3RhdGUx +ITAfBgNVBAoTGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEZMBcGA1UEAxMQdGVz +dC5leGFtcGxlLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAK39 +lAbBIf8cHf+FmX1RX+bDMlnJ7P9YnRdha6Cw8KlVGEZcaVPym6yBvF/L98OHFT/8 +Tl3dcvUPlvTiytmEnAHh3hPe5h2V23KaMFMb0w7/xjSBx1yv8r2DPEdhBJ2Ue9al +3aF7AmpL/2htnsJJLwNdPHhG5NLu0cTZiLGLDtGOIgbcATPzEpAGD31na3R4WPc9 +JeDPBgMlDrmXNLeo9M8N385I6plYBhCb3Fc70oGSMzC6ks7wvPb4l7Y9acs0IzC4 +Jrg27NJeO7kR6WV4DBlchd1LkIqT874Tuxv2W1/+JFFOWz2m3bbz2447u3ROT3xq +yjk4TOh0vd2O0L2E1LcCAwEAAaAAMA0GCSqGSIb3DQEBCwUAA4IBAQBMAUqwty7R +/YWRrC8NuvrbSsW0r7Z7FXWxny5w5ImONCgVffc2wVydtBVQ0rfd3pDZLyu0P4sx +4bJ/KBz67t2MsOKbCMDS7SJuFwHu9AUzaYNh455HeBOVwb6LemJDNnCtMG9DgcRv +2BpwKqekUVTGDuUQmLibjE8qwDHw/p9k4gjQBxlfJe2sIZGs6oA/JGFJUU6ZIn8Y +M6aazrbjWexbWCnjhiXkNa8kfKiSHzU+2ct+GY5QxI221+63bXRiAi2/LK0gaY+p ++3vYu75F7+8oPZOfsGmYEyPz7c1jPqcwPgVDk+sdvl1MO1TGFRaFNIlRP1DhpHkj +fuJ/id6oUHhj +-----END CERTIFICATE REQUEST----- diff --git a/src/test.example.com.csr.json b/src/test.example.com.csr.json new file mode 100644 index 00000000..5cd3e1ef --- /dev/null +++ b/src/test.example.com.csr.json @@ -0,0 +1,14 @@ +{ + "CN": "test.example.com", + "key": { + "algo": "rsa", + "size": 2048 + }, + "names": [ + { + "C": "AU", + "ST": "Some-State", + "O": "Internet Widgits Pty Ltd" + } + ] +} diff --git a/src/test.example.com.pem b/src/test.example.com.pem new file mode 100644 index 00000000..1c7e71ef --- /dev/null +++ b/src/test.example.com.pem @@ -0,0 +1,23 @@ +-----BEGIN CERTIFICATE----- +MIIDwTCCAqmgAwIBAgIUDhKNhGRUq1TSHD6aG2k4TRR8iA0wDQYJKoZIhvcNAQEL +BQAwXjELMAkGA1UEBhMCQVUxEzARBgNVBAgTClNvbWUtU3RhdGUxITAfBgNVBAoT +GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEXMBUGA1UEAxMOY2EubmdodHRwMi5v +cmcwHhcNMTYwNjI1MDkzNzAwWhcNMjYwNjIzMDkzNzAwWjBgMQswCQYDVQQGEwJB +VTETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0 +cyBQdHkgTHRkMRkwFwYDVQQDExB0ZXN0LmV4YW1wbGUuY29tMIIBIjANBgkqhkiG +9w0BAQEFAAOCAQ8AMIIBCgKCAQEArf2UBsEh/xwd/4WZfVFf5sMyWcns/1idF2Fr +oLDwqVUYRlxpU/KbrIG8X8v3w4cVP/xOXd1y9Q+W9OLK2YScAeHeE97mHZXbcpow +UxvTDv/GNIHHXK/yvYM8R2EEnZR71qXdoXsCakv/aG2ewkkvA108eEbk0u7RxNmI +sYsO0Y4iBtwBM/MSkAYPfWdrdHhY9z0l4M8GAyUOuZc0t6j0zw3fzkjqmVgGEJvc +VzvSgZIzMLqSzvC89viXtj1pyzQjMLgmuDbs0l47uRHpZXgMGVyF3UuQipPzvhO7 +G/ZbX/4kUU5bPabdtvPbjju7dE5PfGrKOThM6HS93Y7QvYTUtwIDAQABo3UwczAO +BgNVHQ8BAf8EBAMCBaAwEwYDVR0lBAwwCgYIKwYBBQUHAwEwDAYDVR0TAQH/BAIw +ADAdBgNVHQ4EFgQUm8jn1FICope9qUce6ORQ0CtbmhYwHwYDVR0jBBgwFoAU0DnF +VHVlxrFEkv1ULqnO4ua924YwDQYJKoZIhvcNAQELBQADggEBAD7RPz/5rAnS1MNP +JfAj1TXZSBwlYgtmJL65yaFB6a1SNSTo15deAm/1Vl10LbmYdV4sVnGKeZjhKNk+ +bvVzetUSUS7Rh1fHtxlivJFkG1VrvPu9b416l2aKftBiaNyAWXbyjqXwLYli6Ehk +uu6jZd0040Ggh7bY+KMSnDFDrp7Rar7OvGu9Iovs+sPdkc/iEbvwEiXdMjf3gwkT +Wqx6br1VDLzhD83HAsFA9tt5fv6KTf91UgJnCmOi81Uo6fSEJG84g32T25gwwmCK +q4U049aGF/f4u3QuWDsfYqNePycurAg3m5PC0wCoqvpY2u/q+PGbjWMi2PfZsF8U +imgl/L0= +-----END CERTIFICATE----- diff --git a/src/test.nghttp2.org-key.pem b/src/test.nghttp2.org-key.pem new file mode 100644 index 00000000..25328957 --- /dev/null +++ b/src/test.nghttp2.org-key.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEA7p6KKa3ctS+Sr/nf2uTKNtTshuDVzTsBTbaGydj8q0YDmT3n +CnOPWXvvG1N+jJv5pcAXN2ZnV9UpGh3N5g/CaRcFTgQQ8o+NlCXYBdPIXAJ+Kkbx +limDw3n9xIXfeL6+V2QuPNrqh6n23xwDg5boKaNkpf7X5OrjT1Ph57SEfX1op3GX +bwkAP2+3WlxxYYs0htRq2gH97q9J4MlhHPDapi+uKGs+2b1y6Uxgf4nD5jEWdPmy +VqeKs+fT4ja2n+3gujpdOo2lg504p50gL4zP8zhAlcqlQCmeJGL1xFzCtm2wHQo7 +6XHSWca4pJ7rxf2oIdtE7ikvgFlTVXnG1T3TEQIDAQABAoIBAQDlX/UD96MPcDmb +e6EZ85AGgUsUpJAhBjVMlMagxTqtEVJoPj8XptoHdMD2DZ66XzztfedTU9bHcZpf +BoNkQYXqKzzoL7Ry1leML4ymnVweRi8tSKD2bdXBVEUCYoXctc6WhzCDQxTrcBBl +i7I9DhUB4ZTglEbIQJpdKQ8hAj/Rt55KWSxc+8X7ItSdtMrq+uz+pqg4PkysVAFS +3aDybOqiI/2hzOvwQU4HaB48uUQwpOU6EGidt0C5nAdWOQMbS8kkCJz6UODiUfdM +mLIyA4ygkQ45QthzrddKauUMhUd/y1SAFJOambR4ZiyA+bItomlbNq018sFx3FDr +Uvg7nz2ZAoGBAPC6aO4W0U7vsWyL/lgC7ybFbtPJ4emj4wK/W87qx3LW1/dRgl7Q +h6oblZTFK/oV6xA7J2/Foocz/s1ntnyIdxrtrZUYAIiBXlrWhDg9+MnnmErfXx7H +CkRyWH6i9JbTRUeiWRNBGQ9yMkwQPc2Ckytxrh7w9M+RVCpyzUh8lX+XAoGBAP3B +4V8cF3bVEUOk0tHshR5m2kcJ22qmUv8WUG+IdRUDb4tRpzVFC8HcKedEjk3jxkXR +UInRSD+hLhx0HIhjZKWqJffSZI/G3U8AqoKu9+eh/xHZCah/8KW1YWNsn4rROcyZ +5XFRiMn7psWTjLEZ17zQS4rk9g65SKc9u1wtTxeXAoGAIY3qOF2n2Tfh5D5zOnNW +QHI+q3i1a6qzZtujgWkKWgCGY+vRn0Oz1Us5A16kbZyGgmGscpD6wZvGxXzSW/Nt +nqxIiMKquFxH+aNzFJ/WwNXuTWlrSc/2p2nE2gn+y9MxEfYYMm3df2CskBuncbDk +sKaM3bU6eoBIWg5cfOEYuYsCgYACB2bR59uYK6PzsoGtBAMcdx4Pq1iBxcqsF3WV +LrYg8OIXbxOzLVYmuqfrHXU10jhnnoDSWUYGnDdOKu9/d6v6Vx3umVQMgj6Kvyqd +2OBKjdUIQ3/8ROmbqZOZw+iSp5GavTBEc65wTv7KXZ+mWtqKu++esK32+CxIignR +dttHCQKBgQDZUt94wj9s5p7H5LH6hxyqNr6P9JYEuYPao5l/3mOJ8N330wKuN2G+ +GUg7p/AhtQHwdoyErlsQavKZa791oCvfJiOURYa8gYU03sYsyI1tV45UexCwl40f +oS+VQYgU16UdYo9B2petecEPNpM+mgpne3qzVUwJ5NUNURgmWpyiQw== +-----END RSA PRIVATE KEY----- diff --git a/src/test.nghttp2.org.csr b/src/test.nghttp2.org.csr new file mode 100644 index 00000000..dc4bb105 --- /dev/null +++ b/src/test.nghttp2.org.csr @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE REQUEST----- +MIIDATCCAekCAQAwZDELMAkGA1UEBhMCQVUxEzARBgNVBAgTClNvbWUtU3RhdGUx +ITAfBgNVBAoTGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEdMBsGA1UEAxMUbm90 +LXVzZWQubmdodHRwMi5vcmcwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB +AQDunooprdy1L5Kv+d/a5Mo21OyG4NXNOwFNtobJ2PyrRgOZPecKc49Ze+8bU36M +m/mlwBc3ZmdX1SkaHc3mD8JpFwVOBBDyj42UJdgF08hcAn4qRvGWKYPDef3Ehd94 +vr5XZC482uqHqfbfHAODlugpo2Sl/tfk6uNPU+HntIR9fWincZdvCQA/b7daXHFh +izSG1GraAf3ur0ngyWEc8NqmL64oaz7ZvXLpTGB/icPmMRZ0+bJWp4qz59PiNraf +7eC6Ol06jaWDnTinnSAvjM/zOECVyqVAKZ4kYvXEXMK2bbAdCjvpcdJZxriknuvF +/agh20TuKS+AWVNVecbVPdMRAgMBAAGgWDBWBgkqhkiG9w0BCQ4xSTBHMEUGA1Ud +EQQ+MDyCEFRFU1QuTkdIVFRQMi5PUkeCEioudGVzdC5uZ2h0dHAyLm9yZ4IUdyp3 +LnRlc3QubmdodHRwMi5vcmcwDQYJKoZIhvcNAQELBQADggEBAIAEwnoM5moRwO5U +eaeVCuzpxw1qQsB769GyQu+ey1aa+2BYflirv/FW+8x/uzQpCWGEgHqd5w+MXyXA +PsyucHgKh5Ia6MUW6xxlHkkOtVtmZiH7lXWv90RNtdfHHGWnBzw8iGsk5WfEaNho +NlPiuYLiFqA7W6jR/c4kOg3zziDlwTXaH6SWLCuDzLTb7E7nGcrWkN6moYj+QlSx +viA4GsqDBoFgXT7cSfUzS8ZwIjrqbx7C1xkzPEt5jAiCD/UBX9ot0G+lEgCv3UQj +Q1KkY+TO3bzMkt/kQSX2Q6plKj8D77tlDfFCjd77VC2lL3Qmzaz+M6T7uF+wyl9W +AQJvoUg= +-----END CERTIFICATE REQUEST----- diff --git a/src/test.nghttp2.org.csr.json b/src/test.nghttp2.org.csr.json new file mode 100644 index 00000000..5ee30695 --- /dev/null +++ b/src/test.nghttp2.org.csr.json @@ -0,0 +1,19 @@ +{ + "CN": "not-used.nghttp2.org", + "hosts": [ + "TEST.NGHTTP2.ORG", + "*.test.nghttp2.org", + "w*w.test.nghttp2.org" + ], + "key": { + "algo": "rsa", + "size": 2048 + }, + "names": [ + { + "C": "AU", + "ST": "Some-State", + "O": "Internet Widgits Pty Ltd" + } + ] +} diff --git a/src/test.nghttp2.org.pem b/src/test.nghttp2.org.pem new file mode 100644 index 00000000..0c386fc0 --- /dev/null +++ b/src/test.nghttp2.org.pem @@ -0,0 +1,24 @@ +-----BEGIN CERTIFICATE----- +MIIEDjCCAvagAwIBAgIUQBCY8Nre85JT1c7P+HbXUF9yzg8wDQYJKoZIhvcNAQEL +BQAwXjELMAkGA1UEBhMCQVUxEzARBgNVBAgTClNvbWUtU3RhdGUxITAfBgNVBAoT +GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEXMBUGA1UEAxMOY2EubmdodHRwMi5v +cmcwHhcNMTYwNjI1MDkzMzAwWhcNMjYwNjIzMDkzMzAwWjBkMQswCQYDVQQGEwJB +VTETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0 +cyBQdHkgTHRkMR0wGwYDVQQDExRub3QtdXNlZC5uZ2h0dHAyLm9yZzCCASIwDQYJ +KoZIhvcNAQEBBQADggEPADCCAQoCggEBAO6eiimt3LUvkq/539rkyjbU7Ibg1c07 +AU22hsnY/KtGA5k95wpzj1l77xtTfoyb+aXAFzdmZ1fVKRodzeYPwmkXBU4EEPKP +jZQl2AXTyFwCfipG8ZYpg8N5/cSF33i+vldkLjza6oep9t8cA4OW6CmjZKX+1+Tq +409T4ee0hH19aKdxl28JAD9vt1pccWGLNIbUatoB/e6vSeDJYRzw2qYvrihrPtm9 +culMYH+Jw+YxFnT5slanirPn0+I2tp/t4Lo6XTqNpYOdOKedIC+Mz/M4QJXKpUAp +niRi9cRcwrZtsB0KO+lx0lnGuKSe68X9qCHbRO4pL4BZU1V5xtU90xECAwEAAaOB +vTCBujAOBgNVHQ8BAf8EBAMCBaAwEwYDVR0lBAwwCgYIKwYBBQUHAwEwDAYDVR0T +AQH/BAIwADAdBgNVHQ4EFgQUGlxgxowH6jrQiyyFpCbwPkCXXIYwHwYDVR0jBBgw +FoAU0DnFVHVlxrFEkv1ULqnO4ua924YwRQYDVR0RBD4wPIIQVEVTVC5OR0hUVFAy +Lk9SR4ISKi50ZXN0Lm5naHR0cDIub3JnghR3KncudGVzdC5uZ2h0dHAyLm9yZzAN +BgkqhkiG9w0BAQsFAAOCAQEANCqM6ocfqOpgDEHYOOQTGFHJIptQhS3kRYAdTIo2 +G8XvGCoy+CDYe1GAUWbxE090+a1I1rsYMHcWKJnjKaCBZid7KMhyayIvrmgEsOCh +L8iLf3bxkCoyIAmCpxJwa3LMxm2QQLtRx8AoMXWf+N8are4HY6MLNn6aP4zaTrTZ +H+WkjKIh7WjSHtW/ro666PCXJDCCdRXljOf8v/fff3bYiLg8o70RBp7OFM0HaPtK +wCfcLLxBeoVIncWswB6GtVUFhLeGjepDzWpuDHOdw6DtpghwSXvWFu9bRtl+x02m +LAGfJ0kJrpYGfr9UB51NFX3aM/D3p2zxrjKwR2b59vJEcA== +-----END CERTIFICATE-----