diff --git a/src/shrpx.cc b/src/shrpx.cc index 19516c81..ec7d3947 100644 --- a/src/shrpx.cc +++ b/src/shrpx.cc @@ -329,6 +329,10 @@ const char *DEFAULT_NPN_LIST = NGHTTP2_PROTO_VERSION_ID "," "http/1.1"; } // namespace +namespace { +const char *DEFAULT_TLS_PROTO_LIST = "TLSv1.2,TLSv1.1,TLSv1.0"; +} // namespace + namespace { void fill_default_config() { @@ -622,6 +626,17 @@ void print_help(std::ostream& out) << " Path to file that contains client\n" << " certificate used in backend client\n" << " authentication.\n" + << " --tls-proto-list=\n" + << " Comma delimited list of SSL/TLS protocol to\n" + << " be enabled.\n" + << " The following protocols are available:\n" + << " TLSv1.2, TLSv1.1, TLSv1.0, SSLv3\n" + << " The name matching is done in case-insensitive\n" + << " manner.\n" + << " The parameter must be delimited by a single\n" + << " comma only and any white spaces are treated\n" + << " as a part of protocol string.\n" + << " Default: " << DEFAULT_TLS_PROTO_LIST << "\n" << "\n" << " HTTP/2.0 and SPDY:\n" << " -c, --http2-max-concurrent-streams=\n" @@ -798,6 +813,7 @@ int main(int argc, char **argv) {"http2-no-cookie-crumbling", no_argument, &flag, 45}, {"frontend-http2-connection-window-bits", required_argument, &flag, 46}, {"backend-http2-connection-window-bits", required_argument, &flag, 47}, + {"tls-proto-list", required_argument, &flag, 48}, {nullptr, 0, nullptr, 0 } }; @@ -1049,6 +1065,10 @@ int main(int argc, char **argv) (SHRPX_OPT_BACKEND_HTTP2_CONNECTION_WINDOW_BITS, optarg)); break; + case 48: + // --tls-proto-list + cmdcfgs.push_back(std::make_pair(SHRPX_OPT_TLS_PROTO_LIST, optarg)); + break; default: break; } @@ -1091,6 +1111,10 @@ int main(int argc, char **argv) mod_config()->npn_list = parse_config_str_list(&mod_config()->npn_list_len, DEFAULT_NPN_LIST); } + if(!get_config()->tls_proto_list) { + mod_config()->tls_proto_list = parse_config_str_list + (&mod_config()->tls_proto_list_len, DEFAULT_TLS_PROTO_LIST); + } if(!get_config()->subcerts.empty()) { mod_config()->cert_tree = ssl::cert_lookup_tree_new(); diff --git a/src/shrpx_config.cc b/src/shrpx_config.cc index b60e02ac..9312d6e2 100644 --- a/src/shrpx_config.cc +++ b/src/shrpx_config.cc @@ -105,6 +105,7 @@ const char SHRPX_OPT_READ_BURST[] = "read-burst"; const char SHRPX_OPT_WRITE_RATE[] = "write-rate"; const char SHRPX_OPT_WRITE_BURST[] = "write-burst"; const char SHRPX_OPT_NPN_LIST[] = "npn-list"; +const char SHRPX_OPT_TLS_PROTO_LIST[] = "tls-proto-list"; const char SHRPX_OPT_VERIFY_CLIENT[] = "verify-client"; const char SHRPX_OPT_VERIFY_CLIENT_CACERT[] = "verify-client-cacert"; const char SHRPX_OPT_CLIENT_PRIVATE_KEY_FILE[] = "client-private-key-file"; @@ -453,6 +454,10 @@ int parse_config(const char *opt, const char *optarg) delete [] mod_config()->npn_list; mod_config()->npn_list = parse_config_str_list(&mod_config()->npn_list_len, optarg); + } else if(util::strieq(opt, SHRPX_OPT_TLS_PROTO_LIST)) { + delete [] mod_config()->tls_proto_list; + mod_config()->tls_proto_list = parse_config_str_list + (&mod_config()->tls_proto_list_len, optarg); } else if(util::strieq(opt, SHRPX_OPT_VERIFY_CLIENT)) { mod_config()->verify_client = util::strieq(optarg, "yes"); } else if(util::strieq(opt, SHRPX_OPT_VERIFY_CLIENT_CACERT)) { diff --git a/src/shrpx_config.h b/src/shrpx_config.h index 6534a0db..b801e7bb 100644 --- a/src/shrpx_config.h +++ b/src/shrpx_config.h @@ -94,6 +94,7 @@ extern const char SHRPX_OPT_READ_BURST[]; extern const char SHRPX_OPT_WRITE_RATE[]; extern const char SHRPX_OPT_WRITE_BURST[]; extern const char SHRPX_OPT_NPN_LIST[]; +extern const char SHRPX_OPT_TLS_PROTO_LIST[]; extern const char SHRPX_OPT_VERIFY_CLIENT[]; extern const char SHRPX_OPT_VERIFY_CLIENT_CACERT[]; extern const char SHRPX_OPT_CLIENT_PRIVATE_KEY_FILE[]; @@ -151,6 +152,9 @@ struct Config { // preference. The each element of this list is a NULL-terminated // string. char **npn_list; + // list of supported SSL/TLS protocol strings. The each element of + // this list is a NULL-terminated string. + char **tls_proto_list; // Path to file containing CA certificate solely used for client // certificate validation char *verify_client_cacert; @@ -173,6 +177,8 @@ struct Config { size_t write_burst; // The number of elements in npn_list size_t npn_list_len; + // The number of elements in tls_proto_list + size_t tls_proto_list_len; // downstream protocol; this will be determined by given options. shrpx_proto downstream_proto; int syslog_facility; diff --git a/src/shrpx_ssl.cc b/src/shrpx_ssl.cc index 14f5860d..f71b4799 100644 --- a/src/shrpx_ssl.cc +++ b/src/shrpx_ssl.cc @@ -149,6 +149,29 @@ int alpn_select_proto_cb(SSL* ssl, } // namespace #endif // OPENSSL_VERSION_NUMBER >= 0x10002000L +namespace { +const char *names[] = { "TLSv1.2", "TLSv1.1", "TLSv1.0", "SSLv3" }; +const size_t namelen = sizeof(names)/sizeof(names[0]); +const long int masks[] = { SSL_OP_NO_TLSv1_2, SSL_OP_NO_TLSv1_1, + SSL_OP_NO_TLSv1, SSL_OP_NO_SSLv3 }; +long int create_tls_proto_mask(char **tls_proto_list, size_t len) +{ + long int res = 0; + for(size_t i = 0; i < namelen; ++i) { + size_t j; + for(j = 0; j < len; ++j) { + if(strcasecmp(names[i], tls_proto_list[j]) == 0) { + break; + } + } + if(j == len) { + res |= masks[i]; + } + } + return res; +} +} // namespace + SSL_CTX* create_ssl_context(const char *private_key_file, const char *cert_file) { @@ -158,11 +181,14 @@ SSL_CTX* create_ssl_context(const char *private_key_file, LOG(FATAL) << ERR_error_string(ERR_get_error(), nullptr); DIE(); } + SSL_CTX_set_options(ssl_ctx, SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_COMPRESSION | SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION | SSL_OP_SINGLE_ECDH_USE | SSL_OP_SINGLE_DH_USE | - SSL_OP_NO_TICKET); + SSL_OP_NO_TICKET | + create_tls_proto_mask(get_config()->tls_proto_list, + get_config()->tls_proto_list_len)); const unsigned char sid_ctx[] = "shrpx"; SSL_CTX_set_session_id_context(ssl_ctx, sid_ctx, sizeof(sid_ctx)-1); @@ -295,7 +321,9 @@ SSL_CTX* create_ssl_client_context() } SSL_CTX_set_options(ssl_ctx, SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_COMPRESSION | - SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION); + SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION | + create_tls_proto_mask(get_config()->tls_proto_list, + get_config()->tls_proto_list_len)); if(get_config()->ciphers) { if(SSL_CTX_set_cipher_list(ssl_ctx, get_config()->ciphers) == 0) {