nghttpx: Add --no-http2-cipher-black-list to allow black listed cipher suite

This commit is contained in:
Tatsuhiro Tsujikawa 2016-02-06 17:05:14 +09:00
parent eec409dba7
commit 8741503db1
7 changed files with 53 additions and 17 deletions

View File

@ -109,7 +109,8 @@ OPTIONS = [
"forwarded-by", "forwarded-by",
"forwarded-for", "forwarded-for",
"response-header-field-buffer", "response-header-field-buffer",
"max-response-header-fields" "max-response-header-fields",
"no-http2-cipher-black-list"
] ]
LOGVARS = [ LOGVARS = [

View File

@ -1571,6 +1571,10 @@ SSL/TLS:
TLS HTTP/2 backends. TLS HTTP/2 backends.
Default: )" Default: )"
<< util::duration_str(get_config()->tls.dyn_rec.idle_timeout) << R"( << util::duration_str(get_config()->tls.dyn_rec.idle_timeout) << R"(
--no-http2-cipher-black-list
Allow black listed cipher suite on HTTP/2 connection.
See https://tools.ietf.org/html/rfc7540#appendix-A for
the complete HTTP/2 cipher suites black list.
HTTP/2 and SPDY: HTTP/2 and SPDY:
-c, --http2-max-concurrent-streams=<N> -c, --http2-max-concurrent-streams=<N>
@ -2367,6 +2371,7 @@ int main(int argc, char **argv) {
{SHRPX_OPT_FORWARDED_FOR, required_argument, &flag, 100}, {SHRPX_OPT_FORWARDED_FOR, required_argument, &flag, 100},
{SHRPX_OPT_RESPONSE_HEADER_FIELD_BUFFER, required_argument, &flag, 101}, {SHRPX_OPT_RESPONSE_HEADER_FIELD_BUFFER, required_argument, &flag, 101},
{SHRPX_OPT_MAX_RESPONSE_HEADER_FIELDS, required_argument, &flag, 102}, {SHRPX_OPT_MAX_RESPONSE_HEADER_FIELDS, required_argument, &flag, 102},
{SHRPX_OPT_NO_HTTP2_CIPHER_BLACK_LIST, no_argument, &flag, 103},
{nullptr, 0, nullptr, 0}}; {nullptr, 0, nullptr, 0}};
int option_index = 0; int option_index = 0;
@ -2804,6 +2809,10 @@ int main(int argc, char **argv) {
// --max-response-header-fields // --max-response-header-fields
cmdcfgs.emplace_back(SHRPX_OPT_MAX_RESPONSE_HEADER_FIELDS, optarg); cmdcfgs.emplace_back(SHRPX_OPT_MAX_RESPONSE_HEADER_FIELDS, optarg);
break; break;
case 103:
// --no-http2-cipher-black-list
cmdcfgs.emplace_back(SHRPX_OPT_NO_HTTP2_CIPHER_BLACK_LIST, "yes");
break;
default: default:
break; break;
} }

View File

@ -729,6 +729,7 @@ enum {
SHRPX_OPTID_MAX_RESPONSE_HEADER_FIELDS, SHRPX_OPTID_MAX_RESPONSE_HEADER_FIELDS,
SHRPX_OPTID_MRUBY_FILE, SHRPX_OPTID_MRUBY_FILE,
SHRPX_OPTID_NO_HOST_REWRITE, SHRPX_OPTID_NO_HOST_REWRITE,
SHRPX_OPTID_NO_HTTP2_CIPHER_BLACK_LIST,
SHRPX_OPTID_NO_LOCATION_REWRITE, SHRPX_OPTID_NO_LOCATION_REWRITE,
SHRPX_OPTID_NO_OCSP, SHRPX_OPTID_NO_OCSP,
SHRPX_OPTID_NO_SERVER_PUSH, SHRPX_OPTID_NO_SERVER_PUSH,
@ -1268,6 +1269,9 @@ int option_lookup_token(const char *name, size_t namelen) {
if (util::strieq_l("backend-keep-alive-timeou", name, 25)) { if (util::strieq_l("backend-keep-alive-timeou", name, 25)) {
return SHRPX_OPTID_BACKEND_KEEP_ALIVE_TIMEOUT; return SHRPX_OPTID_BACKEND_KEEP_ALIVE_TIMEOUT;
} }
if (util::strieq_l("no-http2-cipher-black-lis", name, 25)) {
return SHRPX_OPTID_NO_HTTP2_CIPHER_BLACK_LIST;
}
break; break;
} }
break; break;
@ -2188,6 +2192,10 @@ int parse_config(const char *opt, const char *optarg,
return 0; return 0;
} }
case SHRPX_OPTID_NO_HTTP2_CIPHER_BLACK_LIST:
mod_config()->tls.no_http2_cipher_black_list = util::strieq(optarg, "yes");
return 0;
case SHRPX_OPTID_CONF: case SHRPX_OPTID_CONF:
LOG(WARN) << "conf: ignored"; LOG(WARN) << "conf: ignored";

View File

@ -200,6 +200,8 @@ constexpr char SHRPX_OPT_RESPONSE_HEADER_FIELD_BUFFER[] =
"response-header-field-buffer"; "response-header-field-buffer";
constexpr char SHRPX_OPT_MAX_RESPONSE_HEADER_FIELDS[] = constexpr char SHRPX_OPT_MAX_RESPONSE_HEADER_FIELDS[] =
"max-response-header-fields"; "max-response-header-fields";
constexpr char SHRPX_OPT_NO_HTTP2_CIPHER_BLACK_LIST[] =
"no-http2-cipher-black-list";
constexpr size_t SHRPX_OBFUSCATED_NODE_LENGTH = 8; constexpr size_t SHRPX_OBFUSCATED_NODE_LENGTH = 8;
@ -396,6 +398,7 @@ struct TLSConfig {
std::unique_ptr<char[]> ciphers; std::unique_ptr<char[]> ciphers;
std::unique_ptr<char[]> cacert; std::unique_ptr<char[]> cacert;
bool insecure; bool insecure;
bool no_http2_cipher_black_list;
}; };
struct HttpConfig { struct HttpConfig {

View File

@ -488,10 +488,17 @@ int Connection::check_http2_requirement() {
!util::check_h2_is_selected(next_proto, next_proto_len)) { !util::check_h2_is_selected(next_proto, next_proto_len)) {
return 0; return 0;
} }
if (!nghttp2::ssl::check_http2_requirement(tls.ssl)) { if (!nghttp2::ssl::check_http2_tls_version(tls.ssl)) {
if (LOG_ENABLED(INFO)) { if (LOG_ENABLED(INFO)) {
LOG(INFO) << "TLSv1.2 and/or black listed cipher suite was negotiated. " LOG(INFO) << "TLSv1.2 was not negotiated. HTTP/2 must not be used.";
"HTTP/2 must not be used."; }
return -1;
}
if (!get_config()->tls.no_http2_cipher_black_list &&
nghttp2::ssl::check_http2_cipher_black_list(tls.ssl)) {
if (LOG_ENABLED(INFO)) {
LOG(INFO) << "The negotiated cipher suite is in HTTP/2 cipher suite "
"black list. HTTP/2 must not be used.";
} }
return -1; return -1;
} }

View File

@ -397,19 +397,10 @@ enum {
TLS_PSK_WITH_AES_256_CCM_8 = 0xC0A9u, TLS_PSK_WITH_AES_256_CCM_8 = 0xC0A9u,
}; };
bool check_http2_requirement(SSL *ssl) { bool check_http2_cipher_black_list(SSL *ssl) {
auto tls_ver = SSL_version(ssl);
switch (tls_ver) {
case TLS1_2_VERSION:
break;
default:
return false;
}
auto cipher = SSL_get_current_cipher(ssl); auto cipher = SSL_get_current_cipher(ssl);
// Cipher suites in RFC 7540 balck list are not allowed in HTTP/2. // Cipher suites in RFC 7540 black list are not allowed in HTTP/2.
switch (SSL_CIPHER_get_id(cipher) & 0xffffu) { switch (SSL_CIPHER_get_id(cipher) & 0xffffu) {
case TLS_NULL_WITH_NULL_NULL: case TLS_NULL_WITH_NULL_NULL:
case TLS_RSA_WITH_NULL_MD5: case TLS_RSA_WITH_NULL_MD5:
@ -687,10 +678,20 @@ bool check_http2_requirement(SSL *ssl) {
case TLS_PSK_WITH_AES_256_CCM: case TLS_PSK_WITH_AES_256_CCM:
case TLS_PSK_WITH_AES_128_CCM_8: case TLS_PSK_WITH_AES_128_CCM_8:
case TLS_PSK_WITH_AES_256_CCM_8: case TLS_PSK_WITH_AES_256_CCM_8:
return false; return true;
} }
return true; return false;
}
bool check_http2_tls_version(SSL *ssl) {
auto tls_ver = SSL_version(ssl);
return tls_ver == TLS1_2_VERSION;
}
bool check_http2_requirement(SSL *ssl) {
return check_http2_tls_version(ssl) && !check_http2_cipher_black_list(ssl);
} }
void libssl_init() { void libssl_init() {

View File

@ -59,6 +59,13 @@ struct TLSSessionInfo {
TLSSessionInfo *get_tls_session_info(TLSSessionInfo *tls_info, SSL *ssl); TLSSessionInfo *get_tls_session_info(TLSSessionInfo *tls_info, SSL *ssl);
// Returns true iff the negotiated protocol is TLSv1.2.
bool check_http2_tls_version(SSL *ssl);
// Returns true iff the negotiated cipher suite is in HTTP/2 cipher
// black list.
bool check_http2_cipher_black_list(SSL *ssl);
// Returns true if SSL/TLS requirement for HTTP/2 is fulfilled. // Returns true if SSL/TLS requirement for HTTP/2 is fulfilled.
// To fulfill the requirement, the following 2 terms must be hold: // To fulfill the requirement, the following 2 terms must be hold:
// //