nghttpx: Add --client-psk-secret option to enable PSK in backend

This commit is contained in:
Tatsuhiro Tsujikawa 2017-01-08 00:24:33 +09:00
parent 83c759572c
commit 79a24f5dd9
5 changed files with 129 additions and 2 deletions

View File

@ -154,6 +154,7 @@ OPTIONS = [
"dns-max-try",
"frontend-keep-alive-timeout",
"psk-secrets",
"client-psk-secrets",
]
LOGVARS = [

View File

@ -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;
}

View File

@ -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";

View File

@ -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,

View 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);