/
githubmirror
/
nghttp2
Обзор
Документация
Войти
/
githubmirror
/
nghttp2
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
src/shrpx_tls.h
335 строк
12 KB
Tatsuhiro Tsujikawa
nghttpx: Adopt std::expected for tls::get_x509_not_before and get_x509_not_after
28 апр 2026, 15:24
28 апр 2026, 15:24
ed9df9f
Код
Авторство
О чём код?
/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2012 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef SHRPX_TLS_H #define SHRPX_TLS_H #include "shrpx.h" #include <vector> #include <mutex> #include <expected> #include "ssl_compat.h" #ifdef NGHTTP2_OPENSSL_IS_WOLFSSL # include <wolfssl/options.h> # include <wolfssl/openssl/ssl.h> # include <wolfssl/openssl/err.h> #else // !defined(NGHTTP2_OPENSSL_IS_WOLFSSL) # include <openssl/ssl.h> # include <openssl/err.h> #endif // !defined(NGHTTP2_OPENSSL_IS_WOLFSSL) #include <ev.h> #ifdef HAVE_NEVERBLEED # include <neverbleed.h> #endif // defined(HAVE_NEVERBLEED) #include "network.h" #include "shrpx_config.h" #include "shrpx_router.h" #include "errors.h" using namespace nghttp2; namespace shrpx { class ClientHandler; class Worker; class DownstreamConnectionPool; struct DownstreamAddr; struct UpstreamAddr; namespace tls { struct TLSSessionCache { // ASN1 representation of SSL_SESSION object. See // i2d_SSL_SESSION(3SSL). std::vector<uint8_t> session_data; // The last time stamp when this cache entry is created or updated. std::chrono::steady_clock::time_point last_updated; }; // This struct stores the additional information per SSL_CTX. This is // attached to SSL_CTX using SSL_CTX_set_app_data(). struct TLSContextData { // SCT data formatted so that this can be directly sent as // extension_data of signed_certificate_timestamp. std::vector<uint8_t> sct_data; // cert_type is the type of certificate (e.g., // NGHTTP2_CERT_TYPE_ECDSA). int cert_type; }; // Create server side SSL_CTX SSL_CTX *create_ssl_context(const char *private_key_file, const char *cert_file, const std::vector<uint8_t> &sct_data #ifdef HAVE_NEVERBLEED , neverbleed_t *nb #endif // defined(HAVE_NEVERBLEED) ); // Create client side SSL_CTX. This does not configure ALPN settings. SSL_CTX *create_ssl_client_context( #ifdef HAVE_NEVERBLEED neverbleed_t *nb, #endif // defined(HAVE_NEVERBLEED) std::string_view cacert, std::string_view cert_file, std::string_view private_key_file); std::expected<std::unique_ptr<ClientHandler>, Error> accept_connection(Worker *worker, int fd, const sockaddr *addr, socklen_t addrlen, const UpstreamAddr *faddr); // Check peer's certificate against given |address| and |host|. std::expected<void, Error> check_cert(SSL *ssl, const Address *addr, std::string_view host); // Check peer's certificate against given host name described in // |addr| and numeric address in |raddr|. Note that |raddr| might not // point to &addr->addr. std::expected<void, Error> check_cert(SSL *ssl, const DownstreamAddr *addr, const Address *raddr); // Verify |cert| using numeric IP address. |hostname| and |addr| // should contain the same numeric IP address. std::expected<void, Error> verify_numeric_hostname(X509 *cert, std::string_view hostname, const Address *addr); // Verify |cert| using DNS name hostname. std::expected<void, Error> verify_dns_hostname(X509 *cert, std::string_view hostname); struct WildcardRevPrefix { WildcardRevPrefix(std::string_view prefix, size_t idx) : prefix(std::ranges::begin(prefix), std::ranges::end(prefix)), idx(idx) {} // "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 WildcardPattern { // Wildcard host sharing only suffix is probably rare, so we just do // linear search. std::vector<WildcardRevPrefix> rev_prefix; }; class CertLookupTree { public: CertLookupTree(); // 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 does not alter the tree, and returns the existing // matching index. // // 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. // // This function returns the index. It may fail with error (e.g., // hostname is too long). If the returned index equals to |index|, // then hostname is added to the tree with the value |index|. // Otherwise, same hostname has already been added to the tree. std::expected<size_t, Error> add_cert(std::string_view hostname, size_t index); // 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. std::expected<size_t, Error> lookup(std::string_view hostname); // Dumps the contents of this lookup tree to stderr. void dump() const; private: // 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<WildcardPattern> wildcard_patterns_; }; // Adds hostnames in certificate in |ssl_ctx| 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. |ssl_ctx| is also added to // |indexed_ssl_ctx|. void 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 // protocol list |protos|. bool in_proto_list(const std::vector<std::string_view> &protos, std::string_view proto); // Returns true if security requirement for HTTP/2 is fulfilled. bool check_http2_requirement(SSL *ssl); // Returns SSL/TLS option mask to disable SSL/TLS protocol version not // included in |tls_proto_list|. The returned mask can be directly // passed to SSL_CTX_set_options(). nghttp2_ssl_op_type create_tls_proto_mask(const std::vector<std::string_view> &tls_proto_list); std::expected<void, Error> set_alpn_prefs(std::vector<unsigned char> &out, const std::vector<std::string_view> &protos); // Setups server side SSL_CTX. This function inspects get_config() // and if upstream_no_tls is true, returns nullptr. Otherwise // construct default SSL_CTX. If subcerts are available // (get_config()->subcerts), caller should provide CertLookupTree // object as |cert_tree| parameter, otherwise SNI does not work. All // the created SSL_CTX is stored into |all_ssl_ctx|. They are also // added to |indexed_ssl_ctx|. |cert_tree| uses its index to // 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 , neverbleed_t *nb #endif // defined(HAVE_NEVERBLEED) ); #ifdef ENABLE_HTTP3 SSL_CTX *setup_quic_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 , neverbleed_t *nb # endif // defined(HAVE_NEVERBLEED) ); #endif // defined(ENABLE_HTTP3) // Setups client side SSL_CTX. SSL_CTX *setup_downstream_client_ssl_context( #ifdef HAVE_NEVERBLEED neverbleed_t *nb #endif // defined(HAVE_NEVERBLEED) ); // Sets ALPN settings in |SSL| suitable for HTTP/2 use. void setup_downstream_http2_alpn(SSL *ssl); // Sets ALPN settings in |SSL| suitable for HTTP/1.1 use. void setup_downstream_http1_alpn(SSL *ssl); // Creates CertLookupTree. If frontend is configured not to use TLS, // this function returns nullptr. std::unique_ptr<CertLookupTree> create_cert_lookup_tree(); std::expected<SSL *, Error> create_ssl(SSL_CTX *ssl_ctx); // Returns true if SSL/TLS is enabled on upstream bool upstream_tls_enabled(const ConnectionConfig &connconf); // Performs TLS hostname match. |pattern| can contain wildcard // character '*', which matches prefix of target hostname. There are // several restrictions to make wildcard work. The matching algorithm // is based on RFC 6125. bool tls_hostname_match(std::string_view pattern, std::string_view hostname); // Caches |session|. |session| is serialized into ASN1 // representation, and stored. |t| is used as a time stamp. // Depending on the existing cache's time stamp, |session| might not // be cached. void try_cache_tls_session(TLSSessionCache *cache, SSL_SESSION *session, std::chrono::steady_clock::time_point t); // Returns cached session associated |addr|. std::expected<SSL_SESSION *, Error> reuse_tls_session(const TLSSessionCache &addr); // Returns TLS version from |v|. The returned value is defined in // OpenSSL header file. std::expected<int, Error> proto_version_from_string(std::string_view v); // Stores fingerprint of |x| in |dst|. |md| specifies hash function // to use, and |dst| must be large enough to include hash value (e.g., // 32 bytes for SHA-256). This function returns the span that just // includes the fingerprint. std::expected<std::span<uint8_t>, Error> get_x509_fingerprint(std::span<uint8_t> dst, const X509 *x, const EVP_MD *md); // Returns subject name of |x|. If this function fails to get subject // name, it returns an empty string. std::string_view get_x509_subject_name(BlockAllocator &balloc, X509 *x); // Returns issuer name of |x|. If this function fails to get issuer // name, it returns an empty string. std::string_view get_x509_issuer_name(BlockAllocator &balloc, X509 *x); // Returns serial number of |x|. If this function fails to get serial // number, it returns an empty string. number std::string_view get_x509_serial(BlockAllocator &balloc, X509 *x); // Returns NotBefore of |x|. std::expected<time_t, Error> get_x509_not_before(X509 *x); // Returns NotAfter of |x|. std::expected<time_t, Error> get_x509_not_after(X509 *x); #ifdef NGHTTP2_OPENSSL_IS_BORINGSSL // Read HPKE private key from PEM file denoted by |path|. It only // reads the first private key. std::expected<HPKEPrivateKey, Error> read_hpke_private_key_pem(BlockAllocator &balloc, std::string_view path); // Read the specific |type| of content from PEM file denoted by // |path|. It only reads the first block of the specified type. std::expected<std::span<const uint8_t>, Error> read_pem(BlockAllocator &balloc, std::string_view path, std::string_view type); #endif // defined(NGHTTP2_OPENSSL_IS_BORINGSSL) // Return true if ECH was accepted in |ssl|. bool is_ech_accepted(SSL *ssl); } // namespace tls } // namespace shrpx #endif // !defined(SHRPX_TLS_H)