nghttpx: Add TLS signed_certificate_timestamp extension support
This commit is contained in:
parent
2795da840c
commit
412c8f9e67
|
@ -209,6 +209,24 @@ from the given file. In this case, nghttpx does not rotate key
|
||||||
automatically. To rotate key, one has to restart nghttpx (see
|
automatically. To rotate key, one has to restart nghttpx (see
|
||||||
SIGNALS).
|
SIGNALS).
|
||||||
|
|
||||||
|
CERTIFICATE TRANSPARENCY
|
||||||
|
------------------------
|
||||||
|
|
||||||
|
nghttpx supports TLS ``signed_certificate_timestamp`` extension (`RFC
|
||||||
|
6962 <https://tools.ietf.org/html/rfc6962>`_). The relevant options
|
||||||
|
are :option:`--tls-sct-dir` and ``sct-dir`` parameter in
|
||||||
|
:option:`--subcert`. They takes a directory, and nghttpx reads all
|
||||||
|
files whose extension is ``.sct`` under the directory. The ``*.sct``
|
||||||
|
files are encoded as ``SignedCertificateTimestamp`` struct described
|
||||||
|
in `section 3.2 of RFC 69662
|
||||||
|
<https://tools.ietf.org/html/rfc6962#section-3.2>`_. This format is
|
||||||
|
the same one used by `nginx-ct
|
||||||
|
<https://github.com/grahamedgecombe/nginx-ct>`_ and `mod_ssl_ct
|
||||||
|
<https://httpd.apache.org/docs/trunk/mod/mod_ssl_ct.html>`_.
|
||||||
|
`ct-submit <https://github.com/grahamedgecombe/ct-submit>`_ can be
|
||||||
|
used to submit certificates to log servers, and obtain the
|
||||||
|
``SignedCertificateTimestamp`` struct which can be used with nghttpx.
|
||||||
|
|
||||||
MRUBY SCRIPTING
|
MRUBY SCRIPTING
|
||||||
---------------
|
---------------
|
||||||
|
|
||||||
|
|
|
@ -147,6 +147,7 @@ OPTIONS = [
|
||||||
"backend-http2-encoder-dynamic-table-size",
|
"backend-http2-encoder-dynamic-table-size",
|
||||||
"backend-http2-decoder-dynamic-table-size",
|
"backend-http2-decoder-dynamic-table-size",
|
||||||
"ecdh-curves",
|
"ecdh-curves",
|
||||||
|
"tls-sct-dir",
|
||||||
]
|
]
|
||||||
|
|
||||||
LOGVARS = [
|
LOGVARS = [
|
||||||
|
|
25
src/shrpx.cc
25
src/shrpx.cc
|
@ -1843,12 +1843,21 @@ SSL/TLS:
|
||||||
Path to file that contains password for the server's
|
Path to file that contains password for the server's
|
||||||
private key. If none is given and the private key is
|
private key. If none is given and the private key is
|
||||||
password protected it'll be requested interactively.
|
password protected it'll be requested interactively.
|
||||||
--subcert=<KEYPATH>:<CERTPATH>
|
--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. This
|
||||||
option can be used multiple times. To make OCSP
|
option can be used multiple times. To make OCSP
|
||||||
stapling work, <CERTPATH> must be absolute path.
|
stapling work, <CERTPATH> must be absolute path.
|
||||||
|
|
||||||
|
Additional parameter can be specified in <PARAM>. The
|
||||||
|
available <PARAM> is "sct-dir=<DIR>".
|
||||||
|
|
||||||
|
"sct-dir=<DIR>" specifies the path to directory which
|
||||||
|
contains *.sct files for TLS
|
||||||
|
signed_certificate_timestamp extension (RFC 6962). This
|
||||||
|
feature requires OpenSSL >= 1.0.2. See also
|
||||||
|
--tls-sct-dir option.
|
||||||
--dh-param-file=<PATH>
|
--dh-param-file=<PATH>
|
||||||
Path to file that contains DH parameters in PEM format.
|
Path to file that contains DH parameters in PEM format.
|
||||||
Without this option, DHE cipher suites are not
|
Without this option, DHE cipher suites are not
|
||||||
|
@ -2004,6 +2013,15 @@ SSL/TLS:
|
||||||
Allow black listed cipher suite on HTTP/2 connection.
|
Allow black listed cipher suite on HTTP/2 connection.
|
||||||
See https://tools.ietf.org/html/rfc7540#appendix-A for
|
See https://tools.ietf.org/html/rfc7540#appendix-A for
|
||||||
the complete HTTP/2 cipher suites black list.
|
the complete HTTP/2 cipher suites black list.
|
||||||
|
--tls-sct-dir=<DIR>
|
||||||
|
Specifies the directory where *.sct files exist. All
|
||||||
|
*.sct files in <DIR> are read, and sent as
|
||||||
|
extension_data of TLS signed_certificate_timestamp (RFC
|
||||||
|
6962) to client. These *.sct files are for the
|
||||||
|
certificate specified in positional command-line
|
||||||
|
argument <CERT>, or certificate option in configuration
|
||||||
|
file. For additional certificates, use --subcert
|
||||||
|
option. This option requires OpenSSL >= 1.0.2.
|
||||||
|
|
||||||
HTTP/2 and SPDY:
|
HTTP/2 and SPDY:
|
||||||
-c, --frontend-http2-max-concurrent-streams=<N>
|
-c, --frontend-http2-max-concurrent-streams=<N>
|
||||||
|
@ -2937,6 +2955,7 @@ int main(int argc, char **argv) {
|
||||||
{SHRPX_OPT_BACKEND_HTTP2_DECODER_DYNAMIC_TABLE_SIZE.c_str(),
|
{SHRPX_OPT_BACKEND_HTTP2_DECODER_DYNAMIC_TABLE_SIZE.c_str(),
|
||||||
required_argument, &flag, 139},
|
required_argument, &flag, 139},
|
||||||
{SHRPX_OPT_ECDH_CURVES.c_str(), required_argument, &flag, 140},
|
{SHRPX_OPT_ECDH_CURVES.c_str(), required_argument, &flag, 140},
|
||||||
|
{SHRPX_OPT_TLS_SCT_DIR.c_str(), required_argument, &flag, 141},
|
||||||
{nullptr, 0, nullptr, 0}};
|
{nullptr, 0, nullptr, 0}};
|
||||||
|
|
||||||
int option_index = 0;
|
int option_index = 0;
|
||||||
|
@ -3601,6 +3620,10 @@ int main(int argc, char **argv) {
|
||||||
// --ecdh-curves
|
// --ecdh-curves
|
||||||
cmdcfgs.emplace_back(SHRPX_OPT_ECDH_CURVES, StringRef{optarg});
|
cmdcfgs.emplace_back(SHRPX_OPT_ECDH_CURVES, StringRef{optarg});
|
||||||
break;
|
break;
|
||||||
|
case 141:
|
||||||
|
// --tls-sct-dir
|
||||||
|
cmdcfgs.emplace_back(SHRPX_OPT_TLS_SCT_DIR, StringRef{optarg});
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,6 +41,7 @@
|
||||||
#ifdef HAVE_UNISTD_H
|
#ifdef HAVE_UNISTD_H
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#endif // HAVE_UNISTD_H
|
#endif // HAVE_UNISTD_H
|
||||||
|
#include <dirent.h>
|
||||||
|
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <cerrno>
|
#include <cerrno>
|
||||||
|
@ -1016,6 +1017,162 @@ int parse_error_page(std::vector<ErrorPage> &error_pages, const StringRef &opt,
|
||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
// Maximum size of SCT extension payload length.
|
||||||
|
constexpr size_t MAX_SCT_EXT_LEN = 16_k;
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
struct SubcertParams {
|
||||||
|
StringRef sct_dir;
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
// Parses subcert parameter |src_params|, and stores parsed results
|
||||||
|
// into |out|. This function returns 0 if it succeeds, or -1.
|
||||||
|
int parse_subcert_params(SubcertParams &out, const StringRef &src_params) {
|
||||||
|
auto last = std::end(src_params);
|
||||||
|
for (auto first = std::begin(src_params); first != last;) {
|
||||||
|
auto end = std::find(first, last, ';');
|
||||||
|
auto param = StringRef{first, end};
|
||||||
|
|
||||||
|
if (util::istarts_with_l(param, "sct-dir=")) {
|
||||||
|
#if !LIBRESSL_IN_USE && OPENSSL_VERSION_NUMBER >= 0x10002000L
|
||||||
|
auto sct_dir =
|
||||||
|
StringRef{std::begin(param) + str_size("sct-dir="), std::end(param)};
|
||||||
|
if (sct_dir.empty()) {
|
||||||
|
LOG(ERROR) << "subcert: " << param << ": empty sct-dir";
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
out.sct_dir = sct_dir;
|
||||||
|
#else // !(!LIBRESSL_IN_USE && OPENSSL_VERSION_NUMBER >= 0x10002000L)
|
||||||
|
LOG(WARN) << "subcert: sct-dir requires OpenSSL >= 1.0.2";
|
||||||
|
#endif // !(!LIBRESSL_IN_USE && OPENSSL_VERSION_NUMBER >= 0x10002000L)
|
||||||
|
} else if (!param.empty()) {
|
||||||
|
LOG(ERROR) << "subcert: " << param << ": unknown keyword";
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (end == last) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
first = end + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
// Reads *.sct files from directory denoted by |dir_path|. |dir_path|
|
||||||
|
// must be NULL-terminated string.
|
||||||
|
int read_tls_sct_from_dir(std::vector<uint8_t> &dst, const StringRef &opt,
|
||||||
|
const StringRef &dir_path) {
|
||||||
|
auto dir = opendir(dir_path.c_str());
|
||||||
|
if (dir == nullptr) {
|
||||||
|
auto error = errno;
|
||||||
|
LOG(ERROR) << opt << ": " << dir_path << ": " << strerror(error);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto closer = defer(closedir, dir);
|
||||||
|
|
||||||
|
// 2 bytes total length field
|
||||||
|
auto len_idx = std::distance(std::begin(dst), std::end(dst));
|
||||||
|
dst.insert(std::end(dst), 2, 0);
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
errno = 0;
|
||||||
|
auto ent = readdir(dir);
|
||||||
|
if (ent == nullptr) {
|
||||||
|
if (errno != 0) {
|
||||||
|
auto error = errno;
|
||||||
|
LOG(ERROR) << opt << ": failed to read directory " << dir_path << ": "
|
||||||
|
<< strerror(error);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto name = StringRef{ent->d_name};
|
||||||
|
|
||||||
|
if (name[0] == '.' || !util::iends_with_l(name, ".sct")) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string path;
|
||||||
|
path.resize(dir_path.size() + 1 + name.size());
|
||||||
|
{
|
||||||
|
auto p = std::begin(path);
|
||||||
|
p = std::copy(std::begin(dir_path), std::end(dir_path), p);
|
||||||
|
*p++ = '/';
|
||||||
|
std::copy(std::begin(name), std::end(name), p);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto fd = open(path.c_str(), O_RDONLY);
|
||||||
|
if (fd == -1) {
|
||||||
|
auto error = errno;
|
||||||
|
LOG(ERROR) << opt << ": failed to read SCT from " << path << ": "
|
||||||
|
<< strerror(error);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2 bytes length field for this SCT.
|
||||||
|
auto len_idx = std::distance(std::begin(dst), std::end(dst));
|
||||||
|
dst.insert(std::end(dst), 2, 0);
|
||||||
|
|
||||||
|
// *.sct file tends to be small; around 110+ bytes.
|
||||||
|
std::array<char, 256> buf;
|
||||||
|
for (;;) {
|
||||||
|
ssize_t nread;
|
||||||
|
while ((nread = read(fd, buf.data(), buf.size())) == -1 && errno == EINTR)
|
||||||
|
;
|
||||||
|
|
||||||
|
if (nread == -1) {
|
||||||
|
auto error = errno;
|
||||||
|
LOG(ERROR) << opt << ": failed to read SCT data from " << path << ": "
|
||||||
|
<< strerror(error);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nread == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
dst.insert(std::end(dst), std::begin(buf), std::begin(buf) + nread);
|
||||||
|
|
||||||
|
if (dst.size() > MAX_SCT_EXT_LEN) {
|
||||||
|
LOG(ERROR) << opt << ": the concatenated SCT data from " << dir_path
|
||||||
|
<< " is too large. Max " << MAX_SCT_EXT_LEN;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto len = dst.size() - len_idx - 2;
|
||||||
|
|
||||||
|
if (len == 0) {
|
||||||
|
dst.resize(dst.size() - 2);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
dst[len_idx] = len >> 8;
|
||||||
|
dst[len_idx + 1] = len;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto len = dst.size() - len_idx - 2;
|
||||||
|
|
||||||
|
if (len == 0) {
|
||||||
|
dst.resize(dst.size() - 2);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
dst[len_idx] = len >> 8;
|
||||||
|
dst[len_idx + 1] = len;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
// generated by gennghttpxfun.py
|
// generated by gennghttpxfun.py
|
||||||
int option_lookup_token(const char *name, size_t namelen) {
|
int option_lookup_token(const char *name, size_t namelen) {
|
||||||
switch (namelen) {
|
switch (namelen) {
|
||||||
|
@ -1171,6 +1328,11 @@ int option_lookup_token(const char *name, size_t namelen) {
|
||||||
return SHRPX_OPTID_SERVER_NAME;
|
return SHRPX_OPTID_SERVER_NAME;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case 'r':
|
||||||
|
if (util::strieq_l("tls-sct-di", name, 10)) {
|
||||||
|
return SHRPX_OPTID_TLS_SCT_DIR;
|
||||||
|
}
|
||||||
|
break;
|
||||||
case 's':
|
case 's':
|
||||||
if (util::strieq_l("backend-tl", name, 10)) {
|
if (util::strieq_l("backend-tl", name, 10)) {
|
||||||
return SHRPX_OPTID_BACKEND_TLS;
|
return SHRPX_OPTID_BACKEND_TLS;
|
||||||
|
@ -2165,30 +2327,51 @@ int parse_config(Config *config, int optid, const StringRef &opt,
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
case SHRPX_OPTID_SUBCERT: {
|
case SHRPX_OPTID_SUBCERT: {
|
||||||
|
auto end_keys = std::find(std::begin(optarg), std::end(optarg), ';');
|
||||||
|
auto src_params = StringRef{end_keys, std::end(optarg)};
|
||||||
|
|
||||||
|
SubcertParams params;
|
||||||
|
if (parse_subcert_params(params, src_params) != 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<uint8_t> sct_data;
|
||||||
|
|
||||||
|
if (!params.sct_dir.empty()) {
|
||||||
|
// Make sure that dir_path is NULL terminated string.
|
||||||
|
if (read_tls_sct_from_dir(sct_data, opt,
|
||||||
|
StringRef{params.sct_dir.str()}) != 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Private Key file and certificate file separated by ':'.
|
// Private Key file and certificate file separated by ':'.
|
||||||
auto sp = std::find(std::begin(optarg), std::end(optarg), ':');
|
auto sp = std::find(std::begin(optarg), end_keys, ':');
|
||||||
if (sp == std::end(optarg)) {
|
if (sp == end_keys) {
|
||||||
LOG(ERROR) << opt << ": missing ':' in " << optarg;
|
LOG(ERROR) << opt << ": missing ':' in "
|
||||||
|
<< StringRef{std::begin(optarg), end_keys};
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto private_key_file = StringRef{std::begin(optarg), sp};
|
auto private_key_file = StringRef{std::begin(optarg), sp};
|
||||||
|
|
||||||
if (private_key_file.empty()) {
|
if (private_key_file.empty()) {
|
||||||
LOG(ERROR) << opt << ": missing private key file: " << optarg;
|
LOG(ERROR) << opt << ": missing private key file: "
|
||||||
|
<< StringRef{std::begin(optarg), end_keys};
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto cert_file = StringRef{sp + 1, std::end(optarg)};
|
auto cert_file = StringRef{sp + 1, end_keys};
|
||||||
|
|
||||||
if (cert_file.empty()) {
|
if (cert_file.empty()) {
|
||||||
LOG(ERROR) << opt << ": missing certificate file: " << optarg;
|
LOG(ERROR) << opt << ": missing certificate file: "
|
||||||
|
<< StringRef{std::begin(optarg), end_keys};
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
config->tls.subcerts.emplace_back(
|
config->tls.subcerts.emplace_back(
|
||||||
make_string_ref(config->balloc, private_key_file),
|
make_string_ref(config->balloc, private_key_file),
|
||||||
make_string_ref(config->balloc, cert_file));
|
make_string_ref(config->balloc, cert_file), std::move(sct_data));
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -2875,6 +3058,13 @@ int parse_config(Config *config, int optid, const StringRef &opt,
|
||||||
LOG(WARN) << opt << ": This option requires OpenSSL >= 1.0.2";
|
LOG(WARN) << opt << ": This option requires OpenSSL >= 1.0.2";
|
||||||
#endif // !(!LIBRESSL_IN_USE && OPENSSL_VERSION_NUMBER >= 0x10002000L)
|
#endif // !(!LIBRESSL_IN_USE && OPENSSL_VERSION_NUMBER >= 0x10002000L)
|
||||||
return 0;
|
return 0;
|
||||||
|
case SHRPX_OPTID_TLS_SCT_DIR:
|
||||||
|
#if !LIBRESSL_IN_USE && OPENSSL_VERSION_NUMBER >= 0x10002000L
|
||||||
|
return read_tls_sct_from_dir(config->tls.sct_data, opt, optarg);
|
||||||
|
#else // !(!LIBRESSL_IN_USE && OPENSSL_VERSION_NUMBER >= 0x10002000L)
|
||||||
|
LOG(WARN) << opt << ": This option requires OpenSSL >= 1.0.2";
|
||||||
|
return 0;
|
||||||
|
#endif // !(!LIBRESSL_IN_USE && OPENSSL_VERSION_NUMBER >= 0x10002000L)
|
||||||
case SHRPX_OPTID_CONF:
|
case SHRPX_OPTID_CONF:
|
||||||
LOG(WARN) << "conf: ignored";
|
LOG(WARN) << "conf: ignored";
|
||||||
|
|
||||||
|
|
|
@ -309,6 +309,7 @@ constexpr auto SHRPX_OPT_BACKEND_HTTP2_ENCODER_DYNAMIC_TABLE_SIZE =
|
||||||
constexpr auto SHRPX_OPT_BACKEND_HTTP2_DECODER_DYNAMIC_TABLE_SIZE =
|
constexpr auto SHRPX_OPT_BACKEND_HTTP2_DECODER_DYNAMIC_TABLE_SIZE =
|
||||||
StringRef::from_lit("backend-http2-decoder-dynamic-table-size");
|
StringRef::from_lit("backend-http2-decoder-dynamic-table-size");
|
||||||
constexpr auto SHRPX_OPT_ECDH_CURVES = StringRef::from_lit("ecdh-curves");
|
constexpr auto SHRPX_OPT_ECDH_CURVES = StringRef::from_lit("ecdh-curves");
|
||||||
|
constexpr auto SHRPX_OPT_TLS_SCT_DIR = StringRef::from_lit("tls-sct-dir");
|
||||||
|
|
||||||
constexpr size_t SHRPX_OBFUSCATED_NODE_LENGTH = 8;
|
constexpr size_t SHRPX_OBFUSCATED_NODE_LENGTH = 8;
|
||||||
|
|
||||||
|
@ -437,6 +438,18 @@ struct TicketKeys {
|
||||||
std::vector<TicketKey> keys;
|
std::vector<TicketKey> keys;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct TLSCertificate {
|
||||||
|
TLSCertificate(StringRef private_key_file, StringRef cert_file,
|
||||||
|
std::vector<uint8_t> sct_data)
|
||||||
|
: private_key_file(std::move(private_key_file)),
|
||||||
|
cert_file(std::move(cert_file)),
|
||||||
|
sct_data(std::move(sct_data)) {}
|
||||||
|
|
||||||
|
StringRef private_key_file;
|
||||||
|
StringRef cert_file;
|
||||||
|
std::vector<uint8_t> sct_data;
|
||||||
|
};
|
||||||
|
|
||||||
struct HttpProxy {
|
struct HttpProxy {
|
||||||
Address addr;
|
Address addr;
|
||||||
// host in http proxy URI
|
// host in http proxy URI
|
||||||
|
@ -522,14 +535,15 @@ struct TLSConfig {
|
||||||
StringRef cert_file;
|
StringRef cert_file;
|
||||||
} client;
|
} client;
|
||||||
|
|
||||||
// The list of (private key file, certificate file) pair
|
// The list of additional TLS certificate pair
|
||||||
std::vector<std::pair<StringRef, StringRef>> subcerts;
|
std::vector<TLSCertificate> subcerts;
|
||||||
std::vector<unsigned char> alpn_prefs;
|
std::vector<unsigned char> alpn_prefs;
|
||||||
// list of supported NPN/ALPN protocol strings in the order of
|
// list of supported NPN/ALPN protocol strings in the order of
|
||||||
// preference.
|
// preference.
|
||||||
std::vector<StringRef> npn_list;
|
std::vector<StringRef> npn_list;
|
||||||
// list of supported SSL/TLS protocol strings.
|
// list of supported SSL/TLS protocol strings.
|
||||||
std::vector<StringRef> tls_proto_list;
|
std::vector<StringRef> tls_proto_list;
|
||||||
|
std::vector<uint8_t> sct_data;
|
||||||
BIO_METHOD *bio_method;
|
BIO_METHOD *bio_method;
|
||||||
// Bit mask to disable SSL/TLS protocol versions. This will be
|
// Bit mask to disable SSL/TLS protocol versions. This will be
|
||||||
// passed to SSL_CTX_set_options().
|
// passed to SSL_CTX_set_options().
|
||||||
|
@ -942,6 +956,7 @@ enum {
|
||||||
SHRPX_OPTID_TLS_DYN_REC_IDLE_TIMEOUT,
|
SHRPX_OPTID_TLS_DYN_REC_IDLE_TIMEOUT,
|
||||||
SHRPX_OPTID_TLS_DYN_REC_WARMUP_THRESHOLD,
|
SHRPX_OPTID_TLS_DYN_REC_WARMUP_THRESHOLD,
|
||||||
SHRPX_OPTID_TLS_PROTO_LIST,
|
SHRPX_OPTID_TLS_PROTO_LIST,
|
||||||
|
SHRPX_OPTID_TLS_SCT_DIR,
|
||||||
SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED,
|
SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED,
|
||||||
SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED_ADDRESS_FAMILY,
|
SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED_ADDRESS_FAMILY,
|
||||||
SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED_CERT_FILE,
|
SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED_CERT_FILE,
|
||||||
|
|
|
@ -485,6 +485,44 @@ int alpn_select_proto_cb(SSL *ssl, const unsigned char **out,
|
||||||
} // namespace
|
} // namespace
|
||||||
#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
|
#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
// https://tools.ietf.org/html/rfc6962#section-6
|
||||||
|
constexpr unsigned int TLS_EXT_SIGNED_CERTIFICATE_TIMESTAMP = 18;
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
int sct_add_cb(SSL *ssl, unsigned int ext_type, const unsigned char **out,
|
||||||
|
size_t *outlen, int *al, void *add_arg) {
|
||||||
|
assert(ext_type == TLS_EXT_SIGNED_CERTIFICATE_TIMESTAMP);
|
||||||
|
auto ssl_ctx = SSL_get_SSL_CTX(ssl);
|
||||||
|
auto tls_ctx_data =
|
||||||
|
static_cast<TLSContextData *>(SSL_CTX_get_app_data(ssl_ctx));
|
||||||
|
|
||||||
|
*out = tls_ctx_data->sct_data.data();
|
||||||
|
*outlen = tls_ctx_data->sct_data.size();
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
void sct_free_cb(SSL *ssl, unsigned int ext_type, const unsigned char *out,
|
||||||
|
void *add_arg) {
|
||||||
|
assert(ext_type == TLS_EXT_SIGNED_CERTIFICATE_TIMESTAMP);
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
int sct_parse_cb(SSL *ssl, unsigned int ext_type, const unsigned char *in,
|
||||||
|
size_t inlen, int *al, void *parse_arg) {
|
||||||
|
assert(ext_type == TLS_EXT_SIGNED_CERTIFICATE_TIMESTAMP);
|
||||||
|
// client SHOULD send 0 length extension_data, but it is still
|
||||||
|
// SHOULD, and not MUST.
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
struct TLSProtocol {
|
struct TLSProtocol {
|
||||||
StringRef name;
|
StringRef name;
|
||||||
long int mask;
|
long int mask;
|
||||||
|
@ -513,7 +551,8 @@ long int create_tls_proto_mask(const std::vector<StringRef> &tls_proto_list) {
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
SSL_CTX *create_ssl_context(const char *private_key_file, const char *cert_file
|
SSL_CTX *create_ssl_context(const char *private_key_file, const char *cert_file,
|
||||||
|
const std::vector<uint8_t> &sct_data
|
||||||
#ifdef HAVE_NEVERBLEED
|
#ifdef HAVE_NEVERBLEED
|
||||||
,
|
,
|
||||||
neverbleed_t *nb
|
neverbleed_t *nb
|
||||||
|
@ -678,8 +717,22 @@ SSL_CTX *create_ssl_context(const char *private_key_file, const char *cert_file
|
||||||
SSL_CTX_set_alpn_select_cb(ssl_ctx, alpn_select_proto_cb, nullptr);
|
SSL_CTX_set_alpn_select_cb(ssl_ctx, alpn_select_proto_cb, nullptr);
|
||||||
#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
|
#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
|
||||||
|
|
||||||
|
#if !LIBRESSL_IN_USE && OPENSSL_VERSION_NUMBER >= 0x10002000L
|
||||||
|
if (!sct_data.empty() &&
|
||||||
|
SSL_extension_supported(TLS_EXT_SIGNED_CERTIFICATE_TIMESTAMP) == 0) {
|
||||||
|
if (SSL_CTX_add_server_custom_ext(
|
||||||
|
ssl_ctx, TLS_EXT_SIGNED_CERTIFICATE_TIMESTAMP, sct_add_cb,
|
||||||
|
sct_free_cb, nullptr, sct_parse_cb, nullptr) != 1) {
|
||||||
|
LOG(FATAL) << "SSL_CTX_add_server_custom_ext failed: "
|
||||||
|
<< ERR_error_string(ERR_get_error(), nullptr);
|
||||||
|
DIE();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif // !LIBRESSL_IN_USE && OPENSSL_VERSION_NUMBER >= 0x10002000L
|
||||||
|
|
||||||
auto tls_ctx_data = new TLSContextData();
|
auto tls_ctx_data = new TLSContextData();
|
||||||
tls_ctx_data->cert_file = cert_file;
|
tls_ctx_data->cert_file = cert_file;
|
||||||
|
tls_ctx_data->sct_data = sct_data;
|
||||||
|
|
||||||
SSL_CTX_set_app_data(ssl_ctx, tls_ctx_data);
|
SSL_CTX_set_app_data(ssl_ctx, tls_ctx_data);
|
||||||
|
|
||||||
|
@ -1372,8 +1425,9 @@ SSL_CTX *setup_server_ssl_context(std::vector<SSL_CTX *> &all_ssl_ctx,
|
||||||
|
|
||||||
auto &tlsconf = get_config()->tls;
|
auto &tlsconf = get_config()->tls;
|
||||||
|
|
||||||
auto ssl_ctx = ssl::create_ssl_context(tlsconf.private_key_file.c_str(),
|
auto ssl_ctx =
|
||||||
tlsconf.cert_file.c_str()
|
ssl::create_ssl_context(tlsconf.private_key_file.c_str(),
|
||||||
|
tlsconf.cert_file.c_str(), tlsconf.sct_data
|
||||||
#ifdef HAVE_NEVERBLEED
|
#ifdef HAVE_NEVERBLEED
|
||||||
,
|
,
|
||||||
nb
|
nb
|
||||||
|
@ -1407,12 +1461,9 @@ SSL_CTX *setup_server_ssl_context(std::vector<SSL_CTX *> &all_ssl_ctx,
|
||||||
DIE();
|
DIE();
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto &keycert : tlsconf.subcerts) {
|
for (auto &c : tlsconf.subcerts) {
|
||||||
auto &priv_key_file = keycert.first;
|
auto ssl_ctx = ssl::create_ssl_context(c.private_key_file.c_str(),
|
||||||
auto &cert_file = keycert.second;
|
c.cert_file.c_str(), c.sct_data
|
||||||
|
|
||||||
auto ssl_ctx =
|
|
||||||
ssl::create_ssl_context(priv_key_file.c_str(), cert_file.c_str()
|
|
||||||
#ifdef HAVE_NEVERBLEED
|
#ifdef HAVE_NEVERBLEED
|
||||||
,
|
,
|
||||||
nb
|
nb
|
||||||
|
@ -1424,7 +1475,7 @@ SSL_CTX *setup_server_ssl_context(std::vector<SSL_CTX *> &all_ssl_ctx,
|
||||||
auto cert = SSL_CTX_get0_certificate(ssl_ctx);
|
auto cert = SSL_CTX_get0_certificate(ssl_ctx);
|
||||||
#else // defined(LIBRESSL_VERSION_NUMBER) || OPENSSL_VERSION_NUMBER <
|
#else // defined(LIBRESSL_VERSION_NUMBER) || OPENSSL_VERSION_NUMBER <
|
||||||
// 0x10002000L
|
// 0x10002000L
|
||||||
auto cert = load_certificate(cert_file.c_str());
|
auto cert = load_certificate(c.cert_file.c_str());
|
||||||
auto cert_deleter = defer(X509_free, cert);
|
auto cert_deleter = defer(X509_free, cert);
|
||||||
#endif // defined(LIBRESSL_VERSION_NUMBER) || OPENSSL_VERSION_NUMBER <
|
#endif // defined(LIBRESSL_VERSION_NUMBER) || OPENSSL_VERSION_NUMBER <
|
||||||
// 0x10002000L
|
// 0x10002000L
|
||||||
|
|
|
@ -63,6 +63,9 @@ struct TLSSessionCache {
|
||||||
// This struct stores the additional information per SSL_CTX. This is
|
// This struct stores the additional information per SSL_CTX. This is
|
||||||
// attached to SSL_CTX using SSL_CTX_set_app_data().
|
// attached to SSL_CTX using SSL_CTX_set_app_data().
|
||||||
struct TLSContextData {
|
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;
|
||||||
#ifndef HAVE_ATOMIC_STD_SHARED_PTR
|
#ifndef HAVE_ATOMIC_STD_SHARED_PTR
|
||||||
// Protects ocsp_data;
|
// Protects ocsp_data;
|
||||||
std::mutex mu;
|
std::mutex mu;
|
||||||
|
@ -75,7 +78,9 @@ struct TLSContextData {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Create server side SSL_CTX
|
// Create server side SSL_CTX
|
||||||
SSL_CTX *create_ssl_context(const char *private_key_file, const char *cert_file
|
SSL_CTX *create_ssl_context(const char *private_key_file, const char *cert_file,
|
||||||
|
const std::vector<uint8_t> &sct_data
|
||||||
|
|
||||||
#ifdef HAVE_NEVERBLEED
|
#ifdef HAVE_NEVERBLEED
|
||||||
,
|
,
|
||||||
neverbleed_t *nb
|
neverbleed_t *nb
|
||||||
|
|
Loading…
Reference in New Issue