nghttpx: Add --client-psk-secret option to enable PSK in backend
This commit is contained in:
parent
83c759572c
commit
79a24f5dd9
|
@ -154,6 +154,7 @@ OPTIONS = [
|
|||
"dns-max-try",
|
||||
"frontend-keep-alive-timeout",
|
||||
"psk-secrets",
|
||||
"client-psk-secrets",
|
||||
]
|
||||
|
||||
LOGVARS = [
|
||||
|
|
12
src/shrpx.cc
12
src/shrpx.cc
|
@ -2112,6 +2112,13 @@ SSL/TLS:
|
|||
are skipped. The default enabled cipher list might not
|
||||
contain any PSK cipher suite. In that case, desired PSK
|
||||
cipher suites must be enabled using --ciphers option.
|
||||
--client-psk-secrets=<PATH>
|
||||
Read PSK identity and secrets from <PATH>. This is used
|
||||
for backend connection. The each line of input file is
|
||||
formatted as <identity>:<hex-secret>, where <identity>
|
||||
is PSK identity, and <hex-secret> is secret in hex. An
|
||||
empty line, and line which starts with '#' are skipped.
|
||||
The first identity and secret pair encountered is used.
|
||||
|
||||
HTTP/2 and SPDY:
|
||||
-c, --frontend-http2-max-concurrent-streams=<N>
|
||||
|
@ -3088,6 +3095,7 @@ int main(int argc, char **argv) {
|
|||
{SHRPX_OPT_FRONTEND_KEEP_ALIVE_TIMEOUT.c_str(), required_argument,
|
||||
&flag, 146},
|
||||
{SHRPX_OPT_PSK_SECRETS.c_str(), required_argument, &flag, 147},
|
||||
{SHRPX_OPT_CLIENT_PSK_SECRETS.c_str(), required_argument, &flag, 148},
|
||||
{nullptr, 0, nullptr, 0}};
|
||||
|
||||
int option_index = 0;
|
||||
|
@ -3782,6 +3790,10 @@ int main(int argc, char **argv) {
|
|||
// --psk-secrets
|
||||
cmdcfgs.emplace_back(SHRPX_OPT_PSK_SECRETS, StringRef{optarg});
|
||||
break;
|
||||
case 148:
|
||||
// --client-psk-secrets
|
||||
cmdcfgs.emplace_back(SHRPX_OPT_CLIENT_PSK_SECRETS, StringRef{optarg});
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -1263,6 +1263,66 @@ int parse_psk_secrets(Config *config, const StringRef &path) {
|
|||
}
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
// Reads PSK secrets from path, and parses each line. The result is
|
||||
// directly stored into config->tls.client_psk. This function returns
|
||||
// 0 if it succeeds, or -1.
|
||||
int parse_client_psk_secrets(Config *config, const StringRef &path) {
|
||||
auto &tlsconf = config->tls;
|
||||
|
||||
std::ifstream f(path.c_str(), std::ios::binary);
|
||||
if (!f) {
|
||||
LOG(ERROR) << SHRPX_OPT_CLIENT_PSK_SECRETS << ": could not open file "
|
||||
<< path;
|
||||
return -1;
|
||||
}
|
||||
|
||||
size_t lineno = 0;
|
||||
std::string line;
|
||||
while (std::getline(f, line)) {
|
||||
++lineno;
|
||||
if (line.empty() || line[0] == '#') {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto sep_it = std::find(std::begin(line), std::end(line), ':');
|
||||
if (sep_it == std::end(line)) {
|
||||
LOG(ERROR) << SHRPX_OPT_CLIENT_PSK_SECRETS
|
||||
<< ": could not fine separator at line " << lineno;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (sep_it == std::begin(line)) {
|
||||
LOG(ERROR) << SHRPX_OPT_CLIENT_PSK_SECRETS << ": empty identity at line "
|
||||
<< lineno;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (sep_it + 1 == std::end(line)) {
|
||||
LOG(ERROR) << SHRPX_OPT_CLIENT_PSK_SECRETS << ": empty secret at line "
|
||||
<< lineno;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!util::is_hex_string(StringRef{sep_it + 1, std::end(line)})) {
|
||||
LOG(ERROR) << SHRPX_OPT_CLIENT_PSK_SECRETS
|
||||
<< ": secret must be hex string at line " << lineno;
|
||||
return -1;
|
||||
}
|
||||
|
||||
tlsconf.client_psk.identity =
|
||||
make_string_ref(config->balloc, StringRef{std::begin(line), sep_it});
|
||||
|
||||
tlsconf.client_psk.secret =
|
||||
util::decode_hex(config->balloc, StringRef{sep_it + 1, std::end(line)});
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
// generated by gennghttpxfun.py
|
||||
int option_lookup_token(const char *name, size_t namelen) {
|
||||
switch (namelen) {
|
||||
|
@ -1620,6 +1680,11 @@ int option_lookup_token(const char *name, size_t namelen) {
|
|||
return SHRPX_OPTID_ADD_REQUEST_HEADER;
|
||||
}
|
||||
break;
|
||||
case 's':
|
||||
if (util::strieq_l("client-psk-secret", name, 17)) {
|
||||
return SHRPX_OPTID_CLIENT_PSK_SECRETS;
|
||||
}
|
||||
break;
|
||||
case 't':
|
||||
if (util::strieq_l("dns-lookup-timeou", name, 17)) {
|
||||
return SHRPX_OPTID_DNS_LOOKUP_TIMEOUT;
|
||||
|
@ -3207,6 +3272,8 @@ int parse_config(Config *config, int optid, const StringRef &opt,
|
|||
optarg);
|
||||
case SHRPX_OPTID_PSK_SECRETS:
|
||||
return parse_psk_secrets(config, optarg);
|
||||
case SHRPX_OPTID_CLIENT_PSK_SECRETS:
|
||||
return parse_client_psk_secrets(config, optarg);
|
||||
case SHRPX_OPTID_CONF:
|
||||
LOG(WARN) << "conf: ignored";
|
||||
|
||||
|
|
|
@ -320,6 +320,8 @@ constexpr auto SHRPX_OPT_DNS_MAX_TRY = StringRef::from_lit("dns-max-try");
|
|||
constexpr auto SHRPX_OPT_FRONTEND_KEEP_ALIVE_TIMEOUT =
|
||||
StringRef::from_lit("frontend-keep-alive-timeout");
|
||||
constexpr auto SHRPX_OPT_PSK_SECRETS = StringRef::from_lit("psk-secrets");
|
||||
constexpr auto SHRPX_OPT_CLIENT_PSK_SECRETS =
|
||||
StringRef::from_lit("client-psk-secrets");
|
||||
|
||||
constexpr size_t SHRPX_OBFUSCATED_NODE_LENGTH = 8;
|
||||
|
||||
|
@ -550,6 +552,13 @@ struct TLSConfig {
|
|||
StringRef cert_file;
|
||||
} client;
|
||||
|
||||
// Client PSK configuration
|
||||
struct {
|
||||
// identity must be NULL terminated string.
|
||||
StringRef identity;
|
||||
StringRef secret;
|
||||
} client_psk;
|
||||
|
||||
// PSK secrets. The key is identity, and the associated value is
|
||||
// its secret.
|
||||
std::map<StringRef, StringRef> psk_secrets;
|
||||
|
@ -918,6 +927,7 @@ enum {
|
|||
SHRPX_OPTID_CLIENT_CERT_FILE,
|
||||
SHRPX_OPTID_CLIENT_PRIVATE_KEY_FILE,
|
||||
SHRPX_OPTID_CLIENT_PROXY,
|
||||
SHRPX_OPTID_CLIENT_PSK_SECRETS,
|
||||
SHRPX_OPTID_CONF,
|
||||
SHRPX_OPTID_DAEMON,
|
||||
SHRPX_OPTID_DH_PARAM_FILE,
|
||||
|
|
|
@ -549,6 +549,39 @@ unsigned int psk_server_cb(SSL *ssl, const char *identity, unsigned char *psk,
|
|||
}
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
unsigned int psk_client_cb(SSL *ssl, const char *hint, char *identity_out,
|
||||
unsigned int max_identity_len, unsigned char *psk,
|
||||
unsigned int max_psk_len) {
|
||||
auto config = get_config();
|
||||
auto &tlsconf = config->tls;
|
||||
|
||||
auto &identity = tlsconf.client_psk.identity;
|
||||
auto &secret = tlsconf.client_psk.secret;
|
||||
|
||||
if (identity.empty()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (identity.size() + 1 > max_identity_len) {
|
||||
LOG(ERROR) << "The size of PSK identity is " << identity.size()
|
||||
<< ", but the acceptable maximum size is " << max_identity_len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (secret.size() > max_psk_len) {
|
||||
LOG(ERROR) << "The size of PSK secret is " << secret.size()
|
||||
<< ", but the acceptable maximum size is " << max_psk_len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
*std::copy(std::begin(identity), std::end(identity), identity_out) = '\0';
|
||||
std::copy(std::begin(secret), std::end(secret), psk);
|
||||
|
||||
return (unsigned int)secret.size();
|
||||
}
|
||||
} // namespace
|
||||
|
||||
struct TLSProtocol {
|
||||
StringRef name;
|
||||
long int mask;
|
||||
|
@ -899,6 +932,8 @@ SSL_CTX *create_ssl_client_context(
|
|||
#endif // HAVE_NEVERBLEED
|
||||
}
|
||||
|
||||
SSL_CTX_set_psk_client_callback(ssl_ctx, psk_client_cb);
|
||||
|
||||
// NPN selection callback. This is required to set SSL_CTX because
|
||||
// OpenSSL does not offer SSL_set_next_proto_select_cb.
|
||||
SSL_CTX_set_next_proto_select_cb(ssl_ctx, next_proto_select_cb, nullptr);
|
||||
|
@ -1181,8 +1216,10 @@ int verify_hostname(X509 *cert, const StringRef &hostname,
|
|||
int check_cert(SSL *ssl, const Address *addr, const StringRef &host) {
|
||||
auto cert = SSL_get_peer_certificate(ssl);
|
||||
if (!cert) {
|
||||
LOG(ERROR) << "No certificate found";
|
||||
return -1;
|
||||
// By the protocol definition, TLS server always sends certificate
|
||||
// if it has. If certificate cannot be retrieved, authentication
|
||||
// without certificate is used, such as PSK.
|
||||
return 0;
|
||||
}
|
||||
auto cert_deleter = defer(X509_free, cert);
|
||||
auto verify_res = SSL_get_verify_result(ssl);
|
||||
|
|
Loading…
Reference in New Issue