Compare commits
7 Commits
master
...
tls13-earl
Author | SHA1 | Date |
---|---|---|
Tatsuhiro Tsujikawa | 66eba46c8e | |
Tatsuhiro Tsujikawa | abcdca91ba | |
Tatsuhiro Tsujikawa | 5e59577e93 | |
Tatsuhiro Tsujikawa | 8c6612d338 | |
Tatsuhiro Tsujikawa | b71d9ea58e | |
Tatsuhiro Tsujikawa | aca99d42f1 | |
Tatsuhiro Tsujikawa | 90a9a804d0 |
|
@ -31,6 +31,7 @@ HEADERS = [
|
|||
"user-agent",
|
||||
"date",
|
||||
"content-type",
|
||||
"nghttpx-0rtt-uniq",
|
||||
# disallowed h1 headers
|
||||
'connection',
|
||||
'keep-alive',
|
||||
|
|
|
@ -168,6 +168,11 @@ OPTIONS = [
|
|||
"no-strip-incoming-x-forwarded-proto",
|
||||
"ocsp-startup",
|
||||
"no-verify-ocsp",
|
||||
"tls-anti-replay-memcached",
|
||||
"tls-anti-replay-memcached-cert-file",
|
||||
"tls-anti-replay-memcached-private-key-file",
|
||||
"tls-anti-replay-memcached-address-family",
|
||||
"no-strip-incoming-nghttpx-0rtt-uniq",
|
||||
]
|
||||
|
||||
LOGVARS = [
|
||||
|
|
10
src/http2.cc
10
src/http2.cc
|
@ -434,6 +434,11 @@ void copy_headers_to_nva_internal(std::vector<nghttp2_nv> &nva,
|
|||
kv = &(*it_via);
|
||||
it_via = it;
|
||||
break;
|
||||
case HD_NGHTTPX_0RTT_UNIQ:
|
||||
if (flags & HDOP_STRIP_NGHTTPX_ZERO_RTT_UNIQ) {
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
nva.push_back(
|
||||
make_nv_internal(kv->name, kv->value, kv->no_index, nv_flags));
|
||||
|
@ -920,6 +925,11 @@ int lookup_token(const uint8_t *name, size_t namelen) {
|
|||
return HD_X_FORWARDED_PROTO;
|
||||
}
|
||||
break;
|
||||
case 'q':
|
||||
if (util::streq_l("nghttpx-0rtt-uni", name, 16)) {
|
||||
return HD_NGHTTPX_0RTT_UNIQ;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -203,9 +203,13 @@ enum HeaderBuildOp {
|
|||
// Via header fields must be stripped. If this flag is not set, all
|
||||
// Via header fields other than last one are added.
|
||||
HDOP_STRIP_VIA = 1 << 3,
|
||||
// nghttpx-0rtt-uniq header fields must be stripped. If this flag
|
||||
// is not set, all nghttpx-0rtt-uniq header fields are added.
|
||||
HDOP_STRIP_NGHTTPX_ZERO_RTT_UNIQ = 1 << 4,
|
||||
// Strip above all header fields.
|
||||
HDOP_STRIP_ALL = HDOP_STRIP_FORWARDED | HDOP_STRIP_X_FORWARDED_FOR |
|
||||
HDOP_STRIP_X_FORWARDED_PROTO | HDOP_STRIP_VIA,
|
||||
HDOP_STRIP_X_FORWARDED_PROTO | HDOP_STRIP_VIA |
|
||||
HDOP_STRIP_NGHTTPX_ZERO_RTT_UNIQ,
|
||||
};
|
||||
|
||||
// Appends headers in |headers| to |nv|. |headers| must be indexed
|
||||
|
@ -312,6 +316,7 @@ enum {
|
|||
HD_KEEP_ALIVE,
|
||||
HD_LINK,
|
||||
HD_LOCATION,
|
||||
HD_NGHTTPX_0RTT_UNIQ,
|
||||
HD_PROXY_CONNECTION,
|
||||
HD_SERVER,
|
||||
HD_TE,
|
||||
|
|
85
src/shrpx.cc
85
src/shrpx.cc
|
@ -1438,6 +1438,12 @@ void fill_default_config(Config *config) {
|
|||
memcachedconf.family = AF_UNSPEC;
|
||||
}
|
||||
|
||||
auto &anti_replayconf = tlsconf.anti_replay;
|
||||
{
|
||||
auto &memcachedconf = anti_replayconf.memcached;
|
||||
memcachedconf.family = AF_UNSPEC;
|
||||
}
|
||||
|
||||
ticketconf.cipher = EVP_aes_128_cbc();
|
||||
}
|
||||
|
||||
|
@ -1480,6 +1486,7 @@ void fill_default_config(Config *config) {
|
|||
httpconf.max_requests = std::numeric_limits<size_t>::max();
|
||||
httpconf.xfp.add = true;
|
||||
httpconf.xfp.strip_incoming = true;
|
||||
httpconf.zero_rtt_uniq.strip_incoming = true;
|
||||
|
||||
auto &http2conf = config->http2;
|
||||
{
|
||||
|
@ -2284,6 +2291,25 @@ SSL/TLS:
|
|||
--tls-session-cache-memcached-private-key-file=<PATH>
|
||||
Path to client private key for memcached connections to
|
||||
store session cache.
|
||||
--tls-anti-replay-memcached=<HOST>,<PORT>[;tls]
|
||||
Specify address of memcached server to store ClientHello
|
||||
to avoid 0-RTT early data replay. This enables shared
|
||||
storage between multiple nghttpx instances. Optionally,
|
||||
memcached connection can be encrypted with TLS by
|
||||
specifying "tls" parameter.
|
||||
--tls-anti-replay-memcached-address-family=(auto|IPv4|IPv6)
|
||||
Specify address family of memcached connections to store
|
||||
ClientHello to avoid 0-RTT early data replay. If "auto"
|
||||
is given, both IPv4 and IPv6 are considered. If "IPv4"
|
||||
is given, only IPv4 address is considered. If "IPv6" is
|
||||
given, only IPv6 address is considered.
|
||||
Default: auto
|
||||
--tls-anti-replay-memcached-cert-file=<PATH>
|
||||
Path to client certificate for memcached connections to
|
||||
store ClientHello to avoid 0-RTT early data replay.
|
||||
--tls-anti-replay-memcached-private-key-file=<PATH>
|
||||
Path to client private key for memcached connections to
|
||||
store ClientHello to avoid 0-RTT early data replay.
|
||||
--tls-dyn-rec-warmup-threshold=<SIZE>
|
||||
Specify the threshold size for TLS dynamic record size
|
||||
behaviour. During a TLS session, after the threshold
|
||||
|
@ -2590,6 +2616,9 @@ HTTP:
|
|||
Default: obfuscated
|
||||
--no-via Don't append to Via header field. If Via header field
|
||||
is received, it is left unaltered.
|
||||
--no-strip-incoming-nghttpx-0rtt-uniq
|
||||
Don't strip nghttpx-0rtt-uniq header field from inbound
|
||||
client requests.
|
||||
--no-location-rewrite
|
||||
Don't rewrite location header field in default mode.
|
||||
When --http2-proxy is used, location header field will
|
||||
|
@ -2995,6 +3024,26 @@ int process_options(Config *config,
|
|||
}
|
||||
}
|
||||
|
||||
{
|
||||
auto &memcachedconf = tlsconf.anti_replay.memcached;
|
||||
if (!memcachedconf.host.empty()) {
|
||||
auto hostport = util::make_hostport(StringRef{memcachedconf.host},
|
||||
memcachedconf.port);
|
||||
if (resolve_hostname(&memcachedconf.addr, memcachedconf.host.c_str(),
|
||||
memcachedconf.port, memcachedconf.family) == -1) {
|
||||
LOG(FATAL) << "Resolving memcached address for TLS anti-replay failed: "
|
||||
<< hostport;
|
||||
return -1;
|
||||
}
|
||||
LOG(NOTICE) << "Memcached address for TLS anti-replay: " << hostport
|
||||
<< " -> " << util::to_numeric_addr(&memcachedconf.addr);
|
||||
if (memcachedconf.tls) {
|
||||
LOG(NOTICE) << "Connection to memcached for TLS anti-replay will be "
|
||||
"encrypted by TLS";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (config->rlimit_nofile) {
|
||||
struct rlimit lim = {static_cast<rlim_t>(config->rlimit_nofile),
|
||||
static_cast<rlim_t>(config->rlimit_nofile)};
|
||||
|
@ -3404,6 +3453,16 @@ int main(int argc, char **argv) {
|
|||
{SHRPX_OPT_NO_STRIP_INCOMING_X_FORWARDED_PROTO.c_str(), no_argument,
|
||||
&flag, 158},
|
||||
{SHRPX_OPT_SINGLE_PROCESS.c_str(), no_argument, &flag, 159},
|
||||
{SHRPX_OPT_TLS_ANTI_REPLAY_MEMCACHED.c_str(), required_argument, &flag,
|
||||
160},
|
||||
{SHRPX_OPT_TLS_ANTI_REPLAY_MEMCACHED_ADDRESS_FAMILY.c_str(),
|
||||
required_argument, &flag, 161},
|
||||
{SHRPX_OPT_TLS_ANTI_REPLAY_MEMCACHED_CERT_FILE.c_str(),
|
||||
required_argument, &flag, 162},
|
||||
{SHRPX_OPT_TLS_ANTI_REPLAY_MEMCACHED_PRIVATE_KEY_FILE.c_str(),
|
||||
required_argument, &flag, 163},
|
||||
{SHRPX_OPT_NO_STRIP_INCOMING_NGHTTPX_0RTT_UNIQ.c_str(), no_argument,
|
||||
&flag, 164},
|
||||
{nullptr, 0, nullptr, 0}};
|
||||
|
||||
int option_index = 0;
|
||||
|
@ -4165,6 +4224,32 @@ int main(int argc, char **argv) {
|
|||
cmdcfgs.emplace_back(SHRPX_OPT_SINGLE_PROCESS,
|
||||
StringRef::from_lit("yes"));
|
||||
break;
|
||||
case 160:
|
||||
// --tls-anti-replay-memcached
|
||||
cmdcfgs.emplace_back(SHRPX_OPT_TLS_ANTI_REPLAY_MEMCACHED,
|
||||
StringRef{optarg});
|
||||
break;
|
||||
case 161:
|
||||
// --tls-anti-replay-memcached-address-family
|
||||
cmdcfgs.emplace_back(SHRPX_OPT_TLS_ANTI_REPLAY_MEMCACHED_ADDRESS_FAMILY,
|
||||
StringRef{optarg});
|
||||
break;
|
||||
case 162:
|
||||
// --tls-anti-replay-memcached-cert-file
|
||||
cmdcfgs.emplace_back(SHRPX_OPT_TLS_ANTI_REPLAY_MEMCACHED_CERT_FILE,
|
||||
StringRef{optarg});
|
||||
break;
|
||||
case 163:
|
||||
// --tls-anti-replay-memcached-private-key-file
|
||||
cmdcfgs.emplace_back(
|
||||
SHRPX_OPT_TLS_ANTI_REPLAY_MEMCACHED_PRIVATE_KEY_FILE,
|
||||
StringRef{optarg});
|
||||
break;
|
||||
case 164:
|
||||
// --no-strip-incoming-nghttpx-0rtt-uniq
|
||||
cmdcfgs.emplace_back(SHRPX_OPT_NO_STRIP_INCOMING_NGHTTPX_0RTT_UNIQ,
|
||||
StringRef::from_lit("yes"));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -2072,6 +2072,11 @@ int option_lookup_token(const char *name, size_t namelen) {
|
|||
break;
|
||||
case 25:
|
||||
switch (name[24]) {
|
||||
case 'd':
|
||||
if (util::strieq_l("tls-anti-replay-memcache", name, 24)) {
|
||||
return SHRPX_OPTID_TLS_ANTI_REPLAY_MEMCACHED;
|
||||
}
|
||||
break;
|
||||
case 'e':
|
||||
if (util::strieq_l("backend-http2-window-siz", name, 24)) {
|
||||
return SHRPX_OPTID_BACKEND_HTTP2_WINDOW_SIZE;
|
||||
|
@ -2255,12 +2260,20 @@ int option_lookup_token(const char *name, size_t namelen) {
|
|||
if (util::strieq_l("frontend-http2-optimize-window-siz", name, 34)) {
|
||||
return SHRPX_OPTID_FRONTEND_HTTP2_OPTIMIZE_WINDOW_SIZE;
|
||||
}
|
||||
if (util::strieq_l("tls-anti-replay-memcached-cert-fil", name, 34)) {
|
||||
return SHRPX_OPTID_TLS_ANTI_REPLAY_MEMCACHED_CERT_FILE;
|
||||
}
|
||||
break;
|
||||
case 'o':
|
||||
if (util::strieq_l("no-strip-incoming-x-forwarded-prot", name, 34)) {
|
||||
return SHRPX_OPTID_NO_STRIP_INCOMING_X_FORWARDED_PROTO;
|
||||
}
|
||||
break;
|
||||
case 'q':
|
||||
if (util::strieq_l("no-strip-incoming-nghttpx-0rtt-uni", name, 34)) {
|
||||
return SHRPX_OPTID_NO_STRIP_INCOMING_NGHTTPX_0RTT_UNIQ;
|
||||
}
|
||||
break;
|
||||
case 'r':
|
||||
if (util::strieq_l("frontend-http2-dump-response-heade", name, 34)) {
|
||||
return SHRPX_OPTID_FRONTEND_HTTP2_DUMP_RESPONSE_HEADER;
|
||||
|
@ -2338,6 +2351,11 @@ int option_lookup_token(const char *name, size_t namelen) {
|
|||
return SHRPX_OPTID_BACKEND_HTTP2_ENCODER_DYNAMIC_TABLE_SIZE;
|
||||
}
|
||||
break;
|
||||
case 'y':
|
||||
if (util::strieq_l("tls-anti-replay-memcached-address-famil", name, 39)) {
|
||||
return SHRPX_OPTID_TLS_ANTI_REPLAY_MEMCACHED_ADDRESS_FAMILY;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 41:
|
||||
|
@ -2364,6 +2382,12 @@ int option_lookup_token(const char *name, size_t namelen) {
|
|||
break;
|
||||
case 42:
|
||||
switch (name[41]) {
|
||||
case 'e':
|
||||
if (util::strieq_l("tls-anti-replay-memcached-private-key-fil", name,
|
||||
41)) {
|
||||
return SHRPX_OPTID_TLS_ANTI_REPLAY_MEMCACHED_PRIVATE_KEY_FILE;
|
||||
}
|
||||
break;
|
||||
case 'y':
|
||||
if (util::strieq_l("tls-session-cache-memcached-address-famil", name,
|
||||
41)) {
|
||||
|
@ -3153,7 +3177,8 @@ int parse_config(Config *config, int optid, const StringRef &opt,
|
|||
|
||||
return 0;
|
||||
case SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED:
|
||||
case SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED: {
|
||||
case SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED:
|
||||
case SHRPX_OPTID_TLS_ANTI_REPLAY_MEMCACHED: {
|
||||
auto addr_end = std::find(std::begin(optarg), std::end(optarg), ';');
|
||||
auto src_params = StringRef{addr_end, std::end(optarg)};
|
||||
|
||||
|
@ -3183,6 +3208,13 @@ int parse_config(Config *config, int optid, const StringRef &opt,
|
|||
memcachedconf.tls = params.tls;
|
||||
break;
|
||||
}
|
||||
case SHRPX_OPTID_TLS_ANTI_REPLAY_MEMCACHED: {
|
||||
auto &memcachedconf = config->tls.anti_replay.memcached;
|
||||
memcachedconf.host = make_string_ref(config->balloc, StringRef{host});
|
||||
memcachedconf.port = port;
|
||||
memcachedconf.tls = params.tls;
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
return 0;
|
||||
|
@ -3330,6 +3362,16 @@ int parse_config(Config *config, int optid, const StringRef &opt,
|
|||
config->tls.ticket.memcached.private_key_file =
|
||||
make_string_ref(config->balloc, optarg);
|
||||
|
||||
return 0;
|
||||
case SHRPX_OPTID_TLS_ANTI_REPLAY_MEMCACHED_CERT_FILE:
|
||||
config->tls.anti_replay.memcached.cert_file =
|
||||
make_string_ref(config->balloc, optarg);
|
||||
|
||||
return 0;
|
||||
case SHRPX_OPTID_TLS_ANTI_REPLAY_MEMCACHED_PRIVATE_KEY_FILE:
|
||||
config->tls.anti_replay.memcached.private_key_file =
|
||||
make_string_ref(config->balloc, optarg);
|
||||
|
||||
return 0;
|
||||
case SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_ADDRESS_FAMILY:
|
||||
return parse_address_family(&config->tls.ticket.memcached.family, opt,
|
||||
|
@ -3337,6 +3379,9 @@ int parse_config(Config *config, int optid, const StringRef &opt,
|
|||
case SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED_ADDRESS_FAMILY:
|
||||
return parse_address_family(&config->tls.session_cache.memcached.family,
|
||||
opt, optarg);
|
||||
case SHRPX_OPTID_TLS_ANTI_REPLAY_MEMCACHED_ADDRESS_FAMILY:
|
||||
return parse_address_family(&config->tls.anti_replay.memcached.family, opt,
|
||||
optarg);
|
||||
case SHRPX_OPTID_BACKEND_ADDRESS_FAMILY:
|
||||
return parse_address_family(&config->conn.downstream->family, opt, optarg);
|
||||
case SHRPX_OPTID_FRONTEND_HTTP2_MAX_CONCURRENT_STREAMS:
|
||||
|
@ -3550,6 +3595,10 @@ int parse_config(Config *config, int optid, const StringRef &opt,
|
|||
case SHRPX_OPTID_NO_VERIFY_OCSP:
|
||||
config->tls.ocsp.no_verify = util::strieq_l("yes", optarg);
|
||||
|
||||
return 0;
|
||||
case SHRPX_OPTID_NO_STRIP_INCOMING_NGHTTPX_0RTT_UNIQ:
|
||||
config->http.zero_rtt_uniq.strip_incoming = !util::strieq_l("yes", optarg);
|
||||
|
||||
return 0;
|
||||
case SHRPX_OPTID_CONF:
|
||||
LOG(WARN) << "conf: ignored";
|
||||
|
|
|
@ -343,6 +343,16 @@ constexpr auto SHRPX_OPT_NO_STRIP_INCOMING_X_FORWARDED_PROTO =
|
|||
StringRef::from_lit("no-strip-incoming-x-forwarded-proto");
|
||||
constexpr auto SHRPX_OPT_OCSP_STARTUP = StringRef::from_lit("ocsp-startup");
|
||||
constexpr auto SHRPX_OPT_NO_VERIFY_OCSP = StringRef::from_lit("no-verify-ocsp");
|
||||
constexpr auto SHRPX_OPT_TLS_ANTI_REPLAY_MEMCACHED =
|
||||
StringRef::from_lit("tls-anti-replay-memcached");
|
||||
constexpr auto SHRPX_OPT_TLS_ANTI_REPLAY_MEMCACHED_CERT_FILE =
|
||||
StringRef::from_lit("tls-anti-replay-memcached-cert-file");
|
||||
constexpr auto SHRPX_OPT_TLS_ANTI_REPLAY_MEMCACHED_PRIVATE_KEY_FILE =
|
||||
StringRef::from_lit("tls-anti-replay-memcached-private-key-file");
|
||||
constexpr auto SHRPX_OPT_TLS_ANTI_REPLAY_MEMCACHED_ADDRESS_FAMILY =
|
||||
StringRef::from_lit("tls-anti-replay-memcached-address-family");
|
||||
constexpr auto SHRPX_OPT_NO_STRIP_INCOMING_NGHTTPX_0RTT_UNIQ =
|
||||
StringRef::from_lit("no-strip-incoming-nghttpx-0rtt-uniq");
|
||||
|
||||
constexpr size_t SHRPX_OBFUSCATED_NODE_LENGTH = 8;
|
||||
|
||||
|
@ -577,6 +587,23 @@ struct TLSConfig {
|
|||
} memcached;
|
||||
} session_cache;
|
||||
|
||||
struct {
|
||||
struct {
|
||||
Address addr;
|
||||
uint16_t port;
|
||||
// Hostname of memcached server. This is also used as SNI field
|
||||
// if TLS is enabled.
|
||||
StringRef host;
|
||||
// Client private key and certificate for authentication
|
||||
StringRef private_key_file;
|
||||
StringRef cert_file;
|
||||
// Address family of memcached connection. One of either
|
||||
// AF_INET, AF_INET6 or AF_UNSPEC.
|
||||
int family;
|
||||
bool tls;
|
||||
} memcached;
|
||||
} anti_replay;
|
||||
|
||||
// Dynamic record sizing configurations
|
||||
struct {
|
||||
size_t warmup_threshold;
|
||||
|
@ -679,6 +706,9 @@ struct HttpConfig {
|
|||
bool add;
|
||||
bool strip_incoming;
|
||||
} xfp;
|
||||
struct {
|
||||
bool strip_incoming;
|
||||
} zero_rtt_uniq;
|
||||
std::vector<AltSvc> altsvcs;
|
||||
std::vector<ErrorPage> error_pages;
|
||||
HeaderRefs add_request_headers;
|
||||
|
@ -1071,6 +1101,7 @@ enum {
|
|||
SHRPX_OPTID_NO_OCSP,
|
||||
SHRPX_OPTID_NO_SERVER_PUSH,
|
||||
SHRPX_OPTID_NO_SERVER_REWRITE,
|
||||
SHRPX_OPTID_NO_STRIP_INCOMING_NGHTTPX_0RTT_UNIQ,
|
||||
SHRPX_OPTID_NO_STRIP_INCOMING_X_FORWARDED_PROTO,
|
||||
SHRPX_OPTID_NO_VERIFY_OCSP,
|
||||
SHRPX_OPTID_NO_VIA,
|
||||
|
@ -1097,6 +1128,10 @@ enum {
|
|||
SHRPX_OPTID_STRIP_INCOMING_X_FORWARDED_FOR,
|
||||
SHRPX_OPTID_SUBCERT,
|
||||
SHRPX_OPTID_SYSLOG_FACILITY,
|
||||
SHRPX_OPTID_TLS_ANTI_REPLAY_MEMCACHED,
|
||||
SHRPX_OPTID_TLS_ANTI_REPLAY_MEMCACHED_ADDRESS_FAMILY,
|
||||
SHRPX_OPTID_TLS_ANTI_REPLAY_MEMCACHED_CERT_FILE,
|
||||
SHRPX_OPTID_TLS_ANTI_REPLAY_MEMCACHED_PRIVATE_KEY_FILE,
|
||||
SHRPX_OPTID_TLS_DYN_REC_IDLE_TIMEOUT,
|
||||
SHRPX_OPTID_TLS_DYN_REC_WARMUP_THRESHOLD,
|
||||
SHRPX_OPTID_TLS_MAX_PROTO_VERSION,
|
||||
|
|
|
@ -38,7 +38,6 @@
|
|||
#include "shrpx_log.h"
|
||||
#include "memchunk.h"
|
||||
#include "util.h"
|
||||
#include "ssl_compat.h"
|
||||
|
||||
using namespace nghttp2;
|
||||
|
||||
|
@ -60,7 +59,8 @@ Connection::Connection(struct ev_loop *loop, int fd, SSL *ssl,
|
|||
IOCb readcb, TimerCb timeoutcb, void *data,
|
||||
size_t tls_dyn_rec_warmup_threshold,
|
||||
ev_tstamp tls_dyn_rec_idle_timeout, shrpx_proto proto)
|
||||
: tls{DefaultMemchunks(mcpool), DefaultPeekMemchunks(mcpool)},
|
||||
: tls{DefaultMemchunks(mcpool), DefaultPeekMemchunks(mcpool),
|
||||
DefaultMemchunks(mcpool)},
|
||||
wlimit(loop, &wev, write_limit.rate, write_limit.burst),
|
||||
rlimit(loop, &rev, read_limit.rate, read_limit.burst, this),
|
||||
loop(loop),
|
||||
|
@ -92,7 +92,15 @@ Connection::Connection(struct ev_loop *loop, int fd, SSL *ssl,
|
|||
}
|
||||
}
|
||||
|
||||
Connection::~Connection() { disconnect(); }
|
||||
Connection::~Connection() {
|
||||
disconnect();
|
||||
|
||||
#if OPENSSL_1_1_1_API
|
||||
if (tls.ch_md_ctx) {
|
||||
EVP_MD_CTX_free(tls.ch_md_ctx);
|
||||
}
|
||||
#endif // OPENSSL_1_1_1_API
|
||||
}
|
||||
|
||||
void Connection::disconnect() {
|
||||
if (tls.ssl) {
|
||||
|
@ -110,20 +118,34 @@ void Connection::disconnect() {
|
|||
tls.cached_session_lookup_req = nullptr;
|
||||
}
|
||||
|
||||
if (tls.anti_replay_req) {
|
||||
tls.anti_replay_req->canceled = true;
|
||||
tls.anti_replay_req = nullptr;
|
||||
}
|
||||
|
||||
SSL_shutdown(tls.ssl);
|
||||
SSL_free(tls.ssl);
|
||||
tls.ssl = nullptr;
|
||||
|
||||
#if OPENSSL_1_1_1_API
|
||||
if (tls.ch_md_ctx) {
|
||||
EVP_MD_CTX_reset(tls.ch_md_ctx);
|
||||
}
|
||||
#endif // OPENSSL_1_1_1_API
|
||||
|
||||
tls.wbuf.reset();
|
||||
tls.rbuf.reset();
|
||||
tls.last_write_idle = 0.;
|
||||
tls.warmup_writelen = 0;
|
||||
tls.last_writelen = 0;
|
||||
tls.last_readlen = 0;
|
||||
tls.handshake_state = 0;
|
||||
tls.handshake_state = TLS_CONN_NORMAL;
|
||||
tls.initial_handshake_done = false;
|
||||
tls.reneg_started = false;
|
||||
tls.sct_requested = false;
|
||||
tls.early_data_finish = false;
|
||||
tls.early_cb_called = false;
|
||||
tls.postpone_early_data = false;
|
||||
}
|
||||
|
||||
if (fd != -1) {
|
||||
|
@ -141,11 +163,23 @@ void Connection::disconnect() {
|
|||
wlimit.stopw();
|
||||
}
|
||||
|
||||
void Connection::prepare_client_handshake() { SSL_set_connect_state(tls.ssl); }
|
||||
void Connection::prepare_client_handshake() {
|
||||
SSL_set_connect_state(tls.ssl);
|
||||
// This prevents SSL_read_early_data from being called.
|
||||
tls.early_data_finish = true;
|
||||
}
|
||||
|
||||
void Connection::prepare_server_handshake() {
|
||||
SSL_set_accept_state(tls.ssl);
|
||||
tls.server_handshake = true;
|
||||
|
||||
#if OPENSSL_1_1_1_API
|
||||
if (!tls.ch_md_ctx) {
|
||||
tls.ch_md_ctx = EVP_MD_CTX_new();
|
||||
}
|
||||
|
||||
EVP_DigestInit_ex(tls.ch_md_ctx, EVP_sha256(), nullptr);
|
||||
#endif // OPENSSL_1_1_1_API
|
||||
}
|
||||
|
||||
// BIO implementation is inspired by openldap implementation:
|
||||
|
@ -219,7 +253,19 @@ int shrpx_bio_read(BIO *b, char *buf, int len) {
|
|||
return -1;
|
||||
}
|
||||
|
||||
return rbuf.remove(buf, len);
|
||||
len = rbuf.remove(buf, len);
|
||||
|
||||
if (conn->tls.early_cb_called) {
|
||||
return len;
|
||||
}
|
||||
|
||||
#if OPENSSL_1_1_1_API
|
||||
if (EVP_DigestUpdate(conn->tls.ch_md_ctx, buf, len) == 0) {
|
||||
return -1;
|
||||
}
|
||||
#endif // OPENSSL_1_1_1_API
|
||||
|
||||
return len;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
|
@ -327,8 +373,9 @@ int Connection::tls_handshake() {
|
|||
wlimit.stopw();
|
||||
ev_timer_stop(loop, &wt);
|
||||
|
||||
std::array<uint8_t, 16_k> buf;
|
||||
|
||||
if (ev_is_active(&rev)) {
|
||||
std::array<uint8_t, 8_k> buf;
|
||||
auto nread = read_clear(buf.data(), buf.size());
|
||||
if (nread < 0) {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
|
@ -348,6 +395,7 @@ int Connection::tls_handshake() {
|
|||
|
||||
switch (tls.handshake_state) {
|
||||
case TLS_CONN_WAIT_FOR_SESSION_CACHE:
|
||||
case TLS_CONN_WAIT_FOR_ANTI_REPLAY:
|
||||
return SHRPX_ERR_INPROGRESS;
|
||||
case TLS_CONN_GOT_SESSION_CACHE: {
|
||||
// Use the same trick invented by @kazuho in h2o project.
|
||||
|
@ -381,9 +429,73 @@ int Connection::tls_handshake() {
|
|||
break;
|
||||
}
|
||||
|
||||
int rv;
|
||||
|
||||
ERR_clear_error();
|
||||
|
||||
auto rv = SSL_do_handshake(tls.ssl);
|
||||
#if OPENSSL_1_1_1_API
|
||||
if (!tls.server_handshake || tls.early_data_finish) {
|
||||
rv = SSL_do_handshake(tls.ssl);
|
||||
} else {
|
||||
for (;;) {
|
||||
size_t nread;
|
||||
|
||||
rv = SSL_read_early_data(tls.ssl, buf.data(), buf.size(), &nread);
|
||||
if (rv == SSL_READ_EARLY_DATA_ERROR) {
|
||||
if (SSL_get_error(tls.ssl, rv) == SSL_ERROR_WANT_CLIENT_HELLO_CB) {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
LOG(INFO)
|
||||
<< "tls: early_cb returns negative return value; handshake "
|
||||
"interrupted";
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// If we have early data, and server sends ServerHello, assume
|
||||
// that handshake is completed in server side, and start
|
||||
// processing request. If we don't exit handshake code here,
|
||||
// server waits for EndOfEarlyData and Finished message from
|
||||
// client, which voids the purpose of 0-RTT data. The left
|
||||
// over of handshake is done through write_tls or read_tls.
|
||||
if (!tls.postpone_early_data &&
|
||||
(tls.handshake_state == TLS_CONN_WRITE_STARTED ||
|
||||
tls.wbuf.rleft()) &&
|
||||
tls.earlybuf.rleft()) {
|
||||
rv = 1;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
LOG(INFO) << "tls: read early data " << nread << " bytes";
|
||||
}
|
||||
|
||||
tls.earlybuf.append(buf.data(), nread);
|
||||
|
||||
if (rv == SSL_READ_EARLY_DATA_FINISH) {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
LOG(INFO) << "tls: read all early data; total "
|
||||
<< tls.earlybuf.rleft() << " bytes";
|
||||
}
|
||||
tls.early_data_finish = true;
|
||||
// The same reason stated above.
|
||||
if (!tls.postpone_early_data &&
|
||||
(tls.handshake_state == TLS_CONN_WRITE_STARTED ||
|
||||
tls.wbuf.rleft()) &&
|
||||
tls.earlybuf.rleft()) {
|
||||
rv = 1;
|
||||
} else {
|
||||
ERR_clear_error();
|
||||
rv = SSL_do_handshake(tls.ssl);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
#else // !OPENSSL_1_1_1_API
|
||||
rv = SSL_do_handshake(tls.ssl);
|
||||
#endif // !OPENSSL_1_1_1_API
|
||||
|
||||
if (rv <= 0) {
|
||||
auto err = SSL_get_error(tls.ssl, rv);
|
||||
|
@ -397,6 +509,9 @@ int Connection::tls_handshake() {
|
|||
}
|
||||
break;
|
||||
case SSL_ERROR_WANT_WRITE:
|
||||
#if OPENSSL_1_1_1_API
|
||||
case SSL_ERROR_WANT_CLIENT_HELLO_CB:
|
||||
#endif // OPENSSL_1_1_1_API
|
||||
break;
|
||||
case SSL_ERROR_SSL:
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
|
@ -412,7 +527,8 @@ int Connection::tls_handshake() {
|
|||
}
|
||||
}
|
||||
|
||||
if (tls.handshake_state == TLS_CONN_WAIT_FOR_SESSION_CACHE) {
|
||||
if (tls.handshake_state == TLS_CONN_WAIT_FOR_SESSION_CACHE ||
|
||||
tls.handshake_state == TLS_CONN_WAIT_FOR_ANTI_REPLAY) {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
LOG(INFO) << "tls: handshake is still in progress";
|
||||
}
|
||||
|
@ -619,7 +735,21 @@ ssize_t Connection::write_tls(const void *data, size_t len) {
|
|||
|
||||
ERR_clear_error();
|
||||
|
||||
#if OPENSSL_1_1_1_API
|
||||
int rv;
|
||||
if (SSL_is_init_finished(tls.ssl)) {
|
||||
rv = SSL_write(tls.ssl, data, len);
|
||||
} else {
|
||||
size_t nwrite;
|
||||
rv = SSL_write_early_data(tls.ssl, data, len, &nwrite);
|
||||
// Use the same semantics with SSL_write.
|
||||
if (rv == 1) {
|
||||
rv = nwrite;
|
||||
}
|
||||
}
|
||||
#else // !OPENSSL_1_1_1_API
|
||||
auto rv = SSL_write(tls.ssl, data, len);
|
||||
#endif // !OPENSSL_1_1_1_API
|
||||
|
||||
if (rv <= 0) {
|
||||
auto err = SSL_get_error(tls.ssl, rv);
|
||||
|
@ -654,6 +784,14 @@ ssize_t Connection::write_tls(const void *data, size_t len) {
|
|||
}
|
||||
|
||||
ssize_t Connection::read_tls(void *data, size_t len) {
|
||||
ERR_clear_error();
|
||||
|
||||
#if OPENSSL_1_1_1_API
|
||||
if (tls.earlybuf.rleft()) {
|
||||
return tls.earlybuf.remove(data, len);
|
||||
}
|
||||
#endif // OPENSSL_1_1_1_API
|
||||
|
||||
// SSL_read requires the same arguments (buf pointer and its
|
||||
// length) on SSL_ERROR_WANT_READ or SSL_ERROR_WANT_WRITE.
|
||||
// rlimit_.avail() or rlimit_.avail() may return different length
|
||||
|
@ -671,7 +809,46 @@ ssize_t Connection::read_tls(void *data, size_t len) {
|
|||
tls.last_readlen = 0;
|
||||
}
|
||||
|
||||
ERR_clear_error();
|
||||
#if OPENSSL_1_1_1_API
|
||||
if (!tls.early_data_finish) {
|
||||
// TLSv1.3 handshake is still going on.
|
||||
size_t nread;
|
||||
auto rv = SSL_read_early_data(tls.ssl, data, len, &nread);
|
||||
if (rv == SSL_READ_EARLY_DATA_ERROR) {
|
||||
auto err = SSL_get_error(tls.ssl, rv);
|
||||
switch (err) {
|
||||
case SSL_ERROR_WANT_READ:
|
||||
tls.last_readlen = len;
|
||||
return 0;
|
||||
case SSL_ERROR_SSL:
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
LOG(INFO) << "SSL_read: "
|
||||
<< ERR_error_string(ERR_get_error(), nullptr);
|
||||
}
|
||||
return SHRPX_ERR_NETWORK;
|
||||
default:
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
LOG(INFO) << "SSL_read: SSL_get_error returned " << err;
|
||||
}
|
||||
return SHRPX_ERR_NETWORK;
|
||||
}
|
||||
}
|
||||
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
LOG(INFO) << "tls: read early data " << nread << " bytes";
|
||||
}
|
||||
|
||||
if (rv == SSL_READ_EARLY_DATA_FINISH) {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
LOG(INFO) << "tls: read all early data";
|
||||
}
|
||||
tls.early_data_finish = true;
|
||||
// We may have stopped write watcher in write_tls.
|
||||
wlimit.startw();
|
||||
}
|
||||
return nread;
|
||||
}
|
||||
#endif // OPENSSL_1_1_1_API
|
||||
|
||||
auto rv = SSL_read(tls.ssl, data, len);
|
||||
|
||||
|
|
|
@ -32,10 +32,12 @@
|
|||
#include <ev.h>
|
||||
|
||||
#include <openssl/ssl.h>
|
||||
#include <openssl/evp.h>
|
||||
|
||||
#include "shrpx_rate_limit.h"
|
||||
#include "shrpx_error.h"
|
||||
#include "memchunk.h"
|
||||
#include "ssl_compat.h"
|
||||
|
||||
namespace shrpx {
|
||||
|
||||
|
@ -50,16 +52,26 @@ enum {
|
|||
TLS_CONN_WAIT_FOR_SESSION_CACHE,
|
||||
TLS_CONN_GOT_SESSION_CACHE,
|
||||
TLS_CONN_CANCEL_SESSION_CACHE,
|
||||
TLS_CONN_WAIT_FOR_ANTI_REPLAY,
|
||||
TLS_CONN_WRITE_STARTED,
|
||||
};
|
||||
|
||||
struct TLSConnection {
|
||||
DefaultMemchunks wbuf;
|
||||
DefaultPeekMemchunks rbuf;
|
||||
// Stores TLSv1.3 early data.
|
||||
DefaultMemchunks earlybuf;
|
||||
// Message digest of ClientHello in hex string.
|
||||
StringRef ch_hex_md;
|
||||
SSL *ssl;
|
||||
SSL_SESSION *cached_session;
|
||||
MemcachedRequest *cached_session_lookup_req;
|
||||
tls::TLSSessionCache *client_session_cache;
|
||||
#if OPENSSL_1_1_1_API
|
||||
// Message digest context to calculate ClientHello for anti-replay.
|
||||
EVP_MD_CTX *ch_md_ctx;
|
||||
#endif // !OPENSSL_1_1_1_API
|
||||
MemcachedRequest *anti_replay_req;
|
||||
ev_tstamp last_write_idle;
|
||||
size_t warmup_writelen;
|
||||
// length passed to SSL_write and SSL_read last time. This is
|
||||
|
@ -74,6 +86,17 @@ struct TLSConnection {
|
|||
// true if ssl is initialized as server, and client requested
|
||||
// signed_certificate_timestamp extension.
|
||||
bool sct_requested;
|
||||
// true if TLSv1.3 early data has been completely received. Since
|
||||
// SSL_read_early_data acts like SSL_do_handshake, this field may be
|
||||
// true even if the negotiated TLS version is TLSv1.2 or earlier.
|
||||
// This value is also true if this is client side connection for
|
||||
// convenience.
|
||||
bool early_data_finish;
|
||||
// true if early_cb gets called.
|
||||
bool early_cb_called;
|
||||
// true if processing early data should be postponed until handshake
|
||||
// finishes.
|
||||
bool postpone_early_data;
|
||||
};
|
||||
|
||||
struct TCPHint {
|
||||
|
|
|
@ -235,9 +235,24 @@ int ConnectionHandler::create_single_worker() {
|
|||
}
|
||||
}
|
||||
|
||||
SSL_CTX *anti_replay_ssl_ctx = nullptr;
|
||||
{
|
||||
auto &memcachedconf = config->tls.anti_replay.memcached;
|
||||
|
||||
if (memcachedconf.tls) {
|
||||
anti_replay_ssl_ctx = tls::create_ssl_client_context(
|
||||
#ifdef HAVE_NEVERBLEED
|
||||
nb_.get(),
|
||||
#endif // HAVE_NEVERBLEED
|
||||
tlsconf.cacert, memcachedconf.cert_file,
|
||||
memcachedconf.private_key_file, nullptr);
|
||||
all_ssl_ctx_.push_back(anti_replay_ssl_ctx);
|
||||
}
|
||||
}
|
||||
|
||||
single_worker_ = make_unique<Worker>(
|
||||
loop_, sv_ssl_ctx, cl_ssl_ctx, session_cache_ssl_ctx, cert_tree_.get(),
|
||||
ticket_keys_, this, config->conn.downstream);
|
||||
loop_, sv_ssl_ctx, cl_ssl_ctx, session_cache_ssl_ctx, anti_replay_ssl_ctx,
|
||||
cert_tree_.get(), ticket_keys_, this, config->conn.downstream);
|
||||
#ifdef HAVE_MRUBY
|
||||
if (single_worker_->create_mruby_context() != 0) {
|
||||
return -1;
|
||||
|
@ -293,12 +308,28 @@ int ConnectionHandler::create_worker_thread(size_t num) {
|
|||
}
|
||||
}
|
||||
|
||||
SSL_CTX *anti_replay_ssl_ctx = nullptr;
|
||||
{
|
||||
auto &memcachedconf = config->tls.anti_replay.memcached;
|
||||
|
||||
if (memcachedconf.tls) {
|
||||
anti_replay_ssl_ctx = tls::create_ssl_client_context(
|
||||
#ifdef HAVE_NEVERBLEED
|
||||
nb_.get(),
|
||||
#endif // HAVE_NEVERBLEED
|
||||
tlsconf.cacert, memcachedconf.cert_file,
|
||||
memcachedconf.private_key_file, nullptr);
|
||||
all_ssl_ctx_.push_back(anti_replay_ssl_ctx);
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < num; ++i) {
|
||||
auto loop = ev_loop_new(config->ev_loop_flags);
|
||||
|
||||
auto worker = make_unique<Worker>(
|
||||
loop, sv_ssl_ctx, cl_ssl_ctx, session_cache_ssl_ctx, cert_tree_.get(),
|
||||
ticket_keys_, this, config->conn.downstream);
|
||||
auto worker =
|
||||
make_unique<Worker>(loop, sv_ssl_ctx, cl_ssl_ctx, session_cache_ssl_ctx,
|
||||
anti_replay_ssl_ctx, cert_tree_.get(), ticket_keys_,
|
||||
this, config->conn.downstream);
|
||||
#ifdef HAVE_MRUBY
|
||||
if (worker->create_mruby_context() != 0) {
|
||||
return -1;
|
||||
|
|
|
@ -271,7 +271,7 @@ int Http2DownstreamConnection::push_request_headers() {
|
|||
num_cookies = downstream_->count_crumble_request_cookie();
|
||||
}
|
||||
|
||||
// 9 means:
|
||||
// 10 means:
|
||||
// 1. :method
|
||||
// 2. :scheme
|
||||
// 3. :path
|
||||
|
@ -281,8 +281,9 @@ int Http2DownstreamConnection::push_request_headers() {
|
|||
// 7. x-forwarded-proto (optional)
|
||||
// 8. te (optional)
|
||||
// 9. forwarded (optional)
|
||||
// 10. nghttpx-0rtt-uniq (optional)
|
||||
auto nva = std::vector<nghttp2_nv>();
|
||||
nva.reserve(req.fs.headers().size() + 9 + num_cookies +
|
||||
nva.reserve(req.fs.headers().size() + 10 + num_cookies +
|
||||
httpconf.add_request_headers.size());
|
||||
|
||||
nva.push_back(
|
||||
|
@ -311,11 +312,15 @@ int Http2DownstreamConnection::push_request_headers() {
|
|||
auto &fwdconf = httpconf.forwarded;
|
||||
auto &xffconf = httpconf.xff;
|
||||
auto &xfpconf = httpconf.xfp;
|
||||
auto &zero_rtt_uniqconf = httpconf.zero_rtt_uniq;
|
||||
|
||||
uint32_t build_flags =
|
||||
(fwdconf.strip_incoming ? http2::HDOP_STRIP_FORWARDED : 0) |
|
||||
(xffconf.strip_incoming ? http2::HDOP_STRIP_X_FORWARDED_FOR : 0) |
|
||||
(xfpconf.strip_incoming ? http2::HDOP_STRIP_X_FORWARDED_PROTO : 0);
|
||||
(xfpconf.strip_incoming ? http2::HDOP_STRIP_X_FORWARDED_PROTO : 0) |
|
||||
(zero_rtt_uniqconf.strip_incoming
|
||||
? http2::HDOP_STRIP_NGHTTPX_ZERO_RTT_UNIQ
|
||||
: 0);
|
||||
|
||||
http2::copy_headers_to_nva_nocopy(nva, req.fs.headers(), build_flags);
|
||||
|
||||
|
@ -326,6 +331,15 @@ int Http2DownstreamConnection::push_request_headers() {
|
|||
auto upstream = downstream_->get_upstream();
|
||||
auto handler = upstream->get_client_handler();
|
||||
|
||||
#if OPENSSL_1_1_1_API
|
||||
auto conn = handler->get_connection();
|
||||
|
||||
if (!SSL_is_init_finished(conn->tls.ssl)) {
|
||||
nva.push_back(
|
||||
http2::make_nv_ls_nocopy("nghttpx-0rtt-uniq", conn->tls.ch_hex_md));
|
||||
}
|
||||
#endif // OPENSSL_1_1_1_API
|
||||
|
||||
auto fwd =
|
||||
fwdconf.strip_incoming ? nullptr : req.fs.header(http2::HD_FORWARDED);
|
||||
|
||||
|
|
|
@ -535,11 +535,15 @@ int HttpDownstreamConnection::push_request_headers() {
|
|||
auto &fwdconf = httpconf.forwarded;
|
||||
auto &xffconf = httpconf.xff;
|
||||
auto &xfpconf = httpconf.xfp;
|
||||
auto &zero_rtt_uniqconf = httpconf.zero_rtt_uniq;
|
||||
|
||||
uint32_t build_flags =
|
||||
(fwdconf.strip_incoming ? http2::HDOP_STRIP_FORWARDED : 0) |
|
||||
(xffconf.strip_incoming ? http2::HDOP_STRIP_X_FORWARDED_FOR : 0) |
|
||||
(xfpconf.strip_incoming ? http2::HDOP_STRIP_X_FORWARDED_PROTO : 0);
|
||||
(xfpconf.strip_incoming ? http2::HDOP_STRIP_X_FORWARDED_PROTO : 0) |
|
||||
(zero_rtt_uniqconf.strip_incoming
|
||||
? http2::HDOP_STRIP_NGHTTPX_ZERO_RTT_UNIQ
|
||||
: 0);
|
||||
|
||||
http2::build_http1_headers_from_headers(buf, req.fs.headers(), build_flags);
|
||||
|
||||
|
@ -580,6 +584,16 @@ int HttpDownstreamConnection::push_request_headers() {
|
|||
auto upstream = downstream_->get_upstream();
|
||||
auto handler = upstream->get_client_handler();
|
||||
|
||||
#if OPENSSL_1_1_1_API
|
||||
auto conn = handler->get_connection();
|
||||
|
||||
if (!SSL_is_init_finished(conn->tls.ssl)) {
|
||||
buf->append("Nghttpx-0rtt-uniq: ");
|
||||
buf->append(conn->tls.ch_hex_md);
|
||||
buf->append("\r\n");
|
||||
}
|
||||
#endif // OPENSSL_1_1_1_API
|
||||
|
||||
auto fwd =
|
||||
fwdconf.strip_incoming ? nullptr : req.fs.header(http2::HD_FORWARDED);
|
||||
|
||||
|
|
|
@ -108,8 +108,9 @@ void RateLimit::stopw() {
|
|||
}
|
||||
|
||||
void RateLimit::handle_tls_pending_read() {
|
||||
if (!conn_ || !conn_->tls.ssl ||
|
||||
(SSL_pending(conn_->tls.ssl) == 0 && conn_->tls.rbuf.rleft() == 0)) {
|
||||
if (!conn_ || !conn_->tls.ssl || !conn_->tls.initial_handshake_done ||
|
||||
(SSL_pending(conn_->tls.ssl) == 0 && conn_->tls.rbuf.rleft() == 0 &&
|
||||
conn_->tls.earlybuf.rleft() == 0)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
110
src/shrpx_tls.cc
110
src/shrpx_tls.cc
|
@ -510,6 +510,13 @@ int ticket_key_cb(SSL *ssl, unsigned char *key_name, unsigned char *iv,
|
|||
|
||||
namespace {
|
||||
void info_callback(const SSL *ssl, int where, int ret) {
|
||||
#ifdef TLS1_3_VERSION
|
||||
// TLSv1.3 has no renegotiation.
|
||||
if (SSL_version(ssl) == TLS1_3_VERSION) {
|
||||
return;
|
||||
}
|
||||
#endif // TLS1_3_VERSION
|
||||
|
||||
// To mitigate possible DOS attack using lots of renegotiations, we
|
||||
// disable renegotiation. Since OpenSSL does not provide an easy way
|
||||
// to disable it, we check that renegotiation is started in this
|
||||
|
@ -527,6 +534,105 @@ void info_callback(const SSL *ssl, int where, int ret) {
|
|||
}
|
||||
} // namespace
|
||||
|
||||
#if OPENSSL_1_1_1_API
|
||||
constexpr auto MEMCACHED_ANTI_REPLY_KEY_PREFIX =
|
||||
StringRef::from_lit("nghttpx:anti-reply:");
|
||||
|
||||
namespace {
|
||||
int early_cb(SSL *ssl, int *al, void *arg) {
|
||||
auto conn = static_cast<Connection *>(SSL_get_app_data(ssl));
|
||||
if (conn->tls.early_cb_called) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
conn->tls.early_cb_called = true;
|
||||
|
||||
const unsigned char *ext;
|
||||
size_t extlen;
|
||||
|
||||
if (!SSL_client_hello_get0_ext(conn->tls.ssl, TLSEXT_TYPE_early_data, &ext,
|
||||
&extlen)) {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
LOG(INFO) << "early_data extension does not exist";
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!SSL_client_hello_get0_ext(conn->tls.ssl, TLSEXT_TYPE_psk, &ext,
|
||||
&extlen)) {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
LOG(INFO) << "pre_shared_key extension does not exist";
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::array<uint8_t, 32> md;
|
||||
unsigned int mdlen;
|
||||
if (EVP_DigestFinal_ex(conn->tls.ch_md_ctx, md.data(), &mdlen) == 0) {
|
||||
LOG(ERROR) << "EVP_DigestFinal_ex failed";
|
||||
return 0;
|
||||
}
|
||||
assert(md.size() == mdlen);
|
||||
|
||||
auto handler = static_cast<ClientHandler *>(conn->data);
|
||||
auto worker = handler->get_worker();
|
||||
auto dispatcher = worker->get_anti_replay_memcached_dispatcher();
|
||||
auto &balloc = handler->get_block_allocator();
|
||||
|
||||
auto &tlsconf = get_config()->tls;
|
||||
|
||||
conn->tls.ch_hex_md =
|
||||
util::format_hex(balloc, StringRef{std::begin(md), std::end(md)});
|
||||
|
||||
if (tlsconf.anti_replay.memcached.host.empty()) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
auto req = make_unique<MemcachedRequest>();
|
||||
req->op = MEMCACHED_OP_ADD;
|
||||
req->key = MEMCACHED_ANTI_REPLY_KEY_PREFIX.str();
|
||||
req->key += conn->tls.ch_hex_md;
|
||||
|
||||
// TODO No value at the moment
|
||||
|
||||
// Set the same timeout value for session with the hope that
|
||||
// OpenSSL library invalidates the outdated ticket.
|
||||
req->expiry = tlsconf.session_timeout.count();
|
||||
req->cb = [conn](MemcachedRequest *req, MemcachedResult res) {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
LOG(INFO) << "Memcached: ClientHello anti-replay registration done. key="
|
||||
<< req->key << ", status_code=" << res.status_code;
|
||||
}
|
||||
|
||||
// We might stop reading, so start it again
|
||||
conn->rlimit.startw();
|
||||
ev_timer_again(conn->loop, &conn->rt);
|
||||
|
||||
conn->wlimit.startw();
|
||||
ev_timer_again(conn->loop, &conn->wt);
|
||||
|
||||
conn->tls.anti_replay_req = nullptr;
|
||||
|
||||
if (res.status_code != 0) {
|
||||
// If we cannot add key/value, just postpone processing 0-RTT
|
||||
// early data until handshake finishes. Note that memcached
|
||||
// atomically adds key/value.
|
||||
conn->tls.postpone_early_data = true;
|
||||
}
|
||||
|
||||
conn->tls.handshake_state = TLS_CONN_NORMAL;
|
||||
};
|
||||
|
||||
conn->tls.handshake_state = TLS_CONN_WAIT_FOR_ANTI_REPLAY;
|
||||
conn->tls.anti_replay_req = req.get();
|
||||
|
||||
dispatcher->add_request(std::move(req));
|
||||
|
||||
return -1;
|
||||
}
|
||||
} // namespace
|
||||
#endif // OPENSSL_1_1_1_API
|
||||
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x10002000L
|
||||
namespace {
|
||||
int alpn_select_proto_cb(SSL *ssl, const unsigned char **out,
|
||||
|
@ -913,6 +1019,10 @@ SSL_CTX *create_ssl_context(const char *private_key_file, const char *cert_file,
|
|||
#endif // OPENSSL_IS_BORINGSSL
|
||||
SSL_CTX_set_info_callback(ssl_ctx, info_callback);
|
||||
|
||||
#if OPENSSL_1_1_1_API
|
||||
SSL_CTX_set_client_hello_cb(ssl_ctx, early_cb, nullptr);
|
||||
#endif // OPENSSL_1_1_1_API
|
||||
|
||||
#ifdef OPENSSL_IS_BORINGSSL
|
||||
SSL_CTX_set_early_data_enabled(ssl_ctx, 1);
|
||||
#endif // OPENSSL_IS_BORINGSSL
|
||||
|
|
|
@ -118,6 +118,7 @@ bool match_shared_downstream_addr(
|
|||
|
||||
Worker::Worker(struct ev_loop *loop, SSL_CTX *sv_ssl_ctx, SSL_CTX *cl_ssl_ctx,
|
||||
SSL_CTX *tls_session_cache_memcached_ssl_ctx,
|
||||
SSL_CTX *tls_anti_replay_memcached_ssl_ctx,
|
||||
tls::CertLookupTree *cert_tree,
|
||||
const std::shared_ptr<TicketKeys> &ticket_keys,
|
||||
ConnectionHandler *conn_handler,
|
||||
|
@ -153,6 +154,15 @@ Worker::Worker(struct ev_loop *loop, SSL_CTX *sv_ssl_ctx, SSL_CTX *cl_ssl_ctx,
|
|||
StringRef{session_cacheconf.memcached.host}, &mcpool_, randgen_);
|
||||
}
|
||||
|
||||
auto &anti_replayconf = get_config()->tls.anti_replay;
|
||||
|
||||
if (!anti_replayconf.memcached.host.empty()) {
|
||||
anti_replay_memcached_dispatcher_ = make_unique<MemcachedDispatcher>(
|
||||
&anti_replayconf.memcached.addr, loop,
|
||||
tls_anti_replay_memcached_ssl_ctx, anti_replayconf.memcached.host,
|
||||
&mcpool_, randgen_);
|
||||
}
|
||||
|
||||
replace_downstream_config(std::move(downstreamconf));
|
||||
}
|
||||
|
||||
|
@ -474,6 +484,10 @@ MemcachedDispatcher *Worker::get_session_cache_memcached_dispatcher() {
|
|||
return session_cache_memcached_dispatcher_.get();
|
||||
}
|
||||
|
||||
MemcachedDispatcher *Worker::get_anti_replay_memcached_dispatcher() const {
|
||||
return anti_replay_memcached_dispatcher_.get();
|
||||
}
|
||||
|
||||
std::mt19937 &Worker::get_randgen() { return randgen_; }
|
||||
|
||||
#ifdef HAVE_MRUBY
|
||||
|
|
|
@ -221,6 +221,7 @@ class Worker {
|
|||
public:
|
||||
Worker(struct ev_loop *loop, SSL_CTX *sv_ssl_ctx, SSL_CTX *cl_ssl_ctx,
|
||||
SSL_CTX *tls_session_cache_memcached_ssl_ctx,
|
||||
SSL_CTX *tls_anti_replay_memcached_ssl_ctx,
|
||||
tls::CertLookupTree *cert_tree,
|
||||
const std::shared_ptr<TicketKeys> &ticket_keys,
|
||||
ConnectionHandler *conn_handler,
|
||||
|
@ -250,6 +251,7 @@ public:
|
|||
void schedule_clear_mcpool();
|
||||
|
||||
MemcachedDispatcher *get_session_cache_memcached_dispatcher();
|
||||
MemcachedDispatcher *get_anti_replay_memcached_dispatcher() const;
|
||||
|
||||
std::mt19937 &get_randgen();
|
||||
|
||||
|
@ -289,6 +291,7 @@ private:
|
|||
|
||||
std::shared_ptr<DownstreamConfig> downstreamconf_;
|
||||
std::unique_ptr<MemcachedDispatcher> session_cache_memcached_dispatcher_;
|
||||
std::unique_ptr<MemcachedDispatcher> anti_replay_memcached_dispatcher_;
|
||||
#ifdef HAVE_MRUBY
|
||||
std::unique_ptr<mruby::MRubyContext> mruby_ctx_;
|
||||
#endif // HAVE_MRUBY
|
||||
|
|
Loading…
Reference in New Issue