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", "dns-max-try",
"frontend-keep-alive-timeout", "frontend-keep-alive-timeout",
"psk-secrets", "psk-secrets",
"client-psk-secrets",
] ]
LOGVARS = [ LOGVARS = [

View File

@ -2112,6 +2112,13 @@ SSL/TLS:
are skipped. The default enabled cipher list might not are skipped. The default enabled cipher list might not
contain any PSK cipher suite. In that case, desired PSK contain any PSK cipher suite. In that case, desired PSK
cipher suites must be enabled using --ciphers option. 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: HTTP/2 and SPDY:
-c, --frontend-http2-max-concurrent-streams=<N> -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, {SHRPX_OPT_FRONTEND_KEEP_ALIVE_TIMEOUT.c_str(), required_argument,
&flag, 146}, &flag, 146},
{SHRPX_OPT_PSK_SECRETS.c_str(), required_argument, &flag, 147}, {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}}; {nullptr, 0, nullptr, 0}};
int option_index = 0; int option_index = 0;
@ -3782,6 +3790,10 @@ int main(int argc, char **argv) {
// --psk-secrets // --psk-secrets
cmdcfgs.emplace_back(SHRPX_OPT_PSK_SECRETS, StringRef{optarg}); cmdcfgs.emplace_back(SHRPX_OPT_PSK_SECRETS, StringRef{optarg});
break; break;
case 148:
// --client-psk-secrets
cmdcfgs.emplace_back(SHRPX_OPT_CLIENT_PSK_SECRETS, StringRef{optarg});
break;
default: default:
break; break;
} }

View File

@ -1263,6 +1263,66 @@ int parse_psk_secrets(Config *config, const StringRef &path) {
} }
} // namespace } // 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 // 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) {
@ -1620,6 +1680,11 @@ int option_lookup_token(const char *name, size_t namelen) {
return SHRPX_OPTID_ADD_REQUEST_HEADER; return SHRPX_OPTID_ADD_REQUEST_HEADER;
} }
break; break;
case 's':
if (util::strieq_l("client-psk-secret", name, 17)) {
return SHRPX_OPTID_CLIENT_PSK_SECRETS;
}
break;
case 't': case 't':
if (util::strieq_l("dns-lookup-timeou", name, 17)) { if (util::strieq_l("dns-lookup-timeou", name, 17)) {
return SHRPX_OPTID_DNS_LOOKUP_TIMEOUT; return SHRPX_OPTID_DNS_LOOKUP_TIMEOUT;
@ -3207,6 +3272,8 @@ int parse_config(Config *config, int optid, const StringRef &opt,
optarg); optarg);
case SHRPX_OPTID_PSK_SECRETS: case SHRPX_OPTID_PSK_SECRETS:
return parse_psk_secrets(config, optarg); return parse_psk_secrets(config, optarg);
case SHRPX_OPTID_CLIENT_PSK_SECRETS:
return parse_client_psk_secrets(config, optarg);
case SHRPX_OPTID_CONF: case SHRPX_OPTID_CONF:
LOG(WARN) << "conf: ignored"; 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 = constexpr auto SHRPX_OPT_FRONTEND_KEEP_ALIVE_TIMEOUT =
StringRef::from_lit("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_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; constexpr size_t SHRPX_OBFUSCATED_NODE_LENGTH = 8;
@ -550,6 +552,13 @@ struct TLSConfig {
StringRef cert_file; StringRef cert_file;
} client; } 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 // PSK secrets. The key is identity, and the associated value is
// its secret. // its secret.
std::map<StringRef, StringRef> psk_secrets; std::map<StringRef, StringRef> psk_secrets;
@ -918,6 +927,7 @@ enum {
SHRPX_OPTID_CLIENT_CERT_FILE, SHRPX_OPTID_CLIENT_CERT_FILE,
SHRPX_OPTID_CLIENT_PRIVATE_KEY_FILE, SHRPX_OPTID_CLIENT_PRIVATE_KEY_FILE,
SHRPX_OPTID_CLIENT_PROXY, SHRPX_OPTID_CLIENT_PROXY,
SHRPX_OPTID_CLIENT_PSK_SECRETS,
SHRPX_OPTID_CONF, SHRPX_OPTID_CONF,
SHRPX_OPTID_DAEMON, SHRPX_OPTID_DAEMON,
SHRPX_OPTID_DH_PARAM_FILE, 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
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 { struct TLSProtocol {
StringRef name; StringRef name;
long int mask; long int mask;
@ -899,6 +932,8 @@ SSL_CTX *create_ssl_client_context(
#endif // HAVE_NEVERBLEED #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 // NPN selection callback. This is required to set SSL_CTX because
// OpenSSL does not offer SSL_set_next_proto_select_cb. // OpenSSL does not offer SSL_set_next_proto_select_cb.
SSL_CTX_set_next_proto_select_cb(ssl_ctx, next_proto_select_cb, nullptr); 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) { int check_cert(SSL *ssl, const Address *addr, const StringRef &host) {
auto cert = SSL_get_peer_certificate(ssl); auto cert = SSL_get_peer_certificate(ssl);
if (!cert) { if (!cert) {
LOG(ERROR) << "No certificate found"; // By the protocol definition, TLS server always sends certificate
return -1; // 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 cert_deleter = defer(X509_free, cert);
auto verify_res = SSL_get_verify_result(ssl); auto verify_res = SSL_get_verify_result(ssl);