From 20877b1107877310907dc4b78e3bfb7fe287c4b6 Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Thu, 2 Jan 2014 00:53:07 +0900 Subject: [PATCH] nghttpx: Don't allow application protocol not listed in --npn-list option --- src/shrpx.cc | 2 +- src/shrpx_client_handler.cc | 24 +++++++++++++++++++----- src/shrpx_config.h | 5 +++-- src/shrpx_ssl.cc | 12 ++++++++++++ src/shrpx_ssl.h | 6 ++++++ 5 files changed, 41 insertions(+), 8 deletions(-) diff --git a/src/shrpx.cc b/src/shrpx.cc index 21ab2bfc..a53da086 100644 --- a/src/shrpx.cc +++ b/src/shrpx.cc @@ -602,7 +602,7 @@ void print_help(std::ostream& out) << " Path to file that contains DH parameters in\n" << " PEM format. Without this option, DHE cipher\n" << " suites are not available.\n" - << " --npn-list= Comma delimited list of NPN protocol sorted\n" + << " --npn-list= Comma delimited list of NPN/ALPN protocol sorted\n" << " in the order of preference. That means\n" << " most desirable protocol comes first.\n" << " The parameter must be delimited by a single\n" diff --git a/src/shrpx_client_handler.cc b/src/shrpx_client_handler.cc index a5667053..396eab62 100644 --- a/src/shrpx_client_handler.cc +++ b/src/shrpx_client_handler.cc @@ -34,6 +34,7 @@ #include "shrpx_http_downstream_connection.h" #include "shrpx_http2_downstream_connection.h" #include "shrpx_accesslog.h" +#include "shrpx_ssl.h" #ifdef HAVE_SPDYLAY #include "shrpx_spdy_upstream.h" #endif // HAVE_SPDYLAY @@ -108,7 +109,10 @@ void upstream_eventcb(bufferevent *bev, short events, void *arg) if(LOG_ENABLED(INFO)) { CLOG(INFO, handler) << "SSL/TLS handleshake completed"; } - handler->validate_next_proto(); + if(handler->validate_next_proto() != 0) { + delete handler; + return; + } if(LOG_ENABLED(INFO)) { if(SSL_session_reused(handler->get_ssl())) { CLOG(INFO, handler) << "SSL/TLS session reused"; @@ -305,6 +309,11 @@ int ClientHandler::validate_next_proto() std::string proto(next_proto, next_proto+next_proto_len); CLOG(INFO, this) << "The negotiated next protocol: " << proto; } + if(!ssl::in_proto_list(get_config()->npn_list, + get_config()->npn_list_len, + next_proto, next_proto_len)) { + break; + } if(next_proto_len == NGHTTP2_PROTO_VERSION_ID_LEN && memcmp(NGHTTP2_PROTO_VERSION_ID, next_proto, NGHTTP2_PROTO_VERSION_ID_LEN) == 0) { @@ -320,6 +329,10 @@ int ClientHandler::validate_next_proto() return 0; } #endif // HAVE_SPDYLAY + if(next_proto_len == 8 && memcmp("http/1.1", next_proto, 8) == 0) { + upstream_ = util::make_unique(this); + return 0; + } } break; } @@ -331,14 +344,15 @@ int ClientHandler::validate_next_proto() } if(!next_proto) { if(LOG_ENABLED(INFO)) { - CLOG(INFO, this) << "No proto negotiated."; + CLOG(INFO, this) << "No protocol negotiated. Fallback to HTTP/1.1"; } + upstream_ = util::make_unique(this); + return 0; } if(LOG_ENABLED(INFO)) { - CLOG(INFO, this) << "Use HTTP/1.1"; + CLOG(INFO, this) << "The negotiated protocol is not supported"; } - upstream_ = util::make_unique(this); - return 0; + return -1; } int ClientHandler::on_read() diff --git a/src/shrpx_config.h b/src/shrpx_config.h index 009e5e7e..8d112e7b 100644 --- a/src/shrpx_config.h +++ b/src/shrpx_config.h @@ -147,8 +147,9 @@ struct Config { char *downstream_http_proxy_host; // Rate limit configuration ev_token_bucket_cfg *rate_limit_cfg; - // Comma delimited list of NPN protocol strings in the order of - // preference. + // list of supported NPN/ALPN protocol strings in the order of + // preference. The each element of this list is a NULL-terminated + // string. char **npn_list; // Path to file containing CA certificate solely used for client // certificate validation diff --git a/src/shrpx_ssl.cc b/src/shrpx_ssl.cc index 9f513e7e..14f5860d 100644 --- a/src/shrpx_ssl.cc +++ b/src/shrpx_ssl.cc @@ -831,6 +831,18 @@ int cert_lookup_tree_add_cert_from_file(CertLookupTree *lt, SSL_CTX *ssl_ctx, return 0; } +bool in_proto_list(char **protos, size_t len, + const unsigned char *proto, size_t protolen) +{ + for(size_t i = 0; i < len; ++i) { + if(strlen(protos[i]) == protolen && + memcmp(protos[i], proto, protolen) == 0) { + return true; + } + } + return false; +} + } // namespace ssl } // namespace shrpx diff --git a/src/shrpx_ssl.h b/src/shrpx_ssl.h index fb1a2eca..44bbbfdc 100644 --- a/src/shrpx_ssl.h +++ b/src/shrpx_ssl.h @@ -126,6 +126,12 @@ SSL_CTX* cert_lookup_tree_lookup(CertLookupTree *lt, const char *hostname, int cert_lookup_tree_add_cert_from_file(CertLookupTree *lt, SSL_CTX *ssl_ctx, const char *certfile); +// Returns true if |proto| which has |protolen| bytes is included in +// the protocol list |protos|, which has |len| elements. The format of +// the |protos| is the one used in Config::npn_list. +bool in_proto_list(char **protos, size_t len, + const unsigned char *proto, size_t protolen); + } // namespace ssl } // namespace shrpx