diff --git a/include/grpc/grpc_security_constants.h b/include/grpc/grpc_security_constants.h index e50829409b3..a62f76753b9 100644 --- a/include/grpc/grpc_security_constants.h +++ b/include/grpc/grpc_security_constants.h @@ -139,6 +139,9 @@ typedef enum { */ typedef enum { UDS = 0, LOCAL_TCP } grpc_local_connect_type; +/** The TLS versions that are supported by the SSL stack. **/ +typedef enum { TLS1_2, TLS1_3 } grpc_tls_version; + #ifdef __cplusplus } #endif diff --git a/src/core/lib/security/credentials/ssl/ssl_credentials.cc b/src/core/lib/security/credentials/ssl/ssl_credentials.cc index 48d78f39583..3bb7790f5c9 100644 --- a/src/core/lib/security/credentials/ssl/ssl_credentials.cc +++ b/src/core/lib/security/credentials/ssl/ssl_credentials.cc @@ -117,6 +117,16 @@ void grpc_ssl_credentials::build_config( } } +void grpc_ssl_credentials::set_min_tls_version( + grpc_tls_version min_tls_version) { + config_.min_tls_version = min_tls_version; +} + +void grpc_ssl_credentials::set_max_tls_version( + grpc_tls_version max_tls_version) { + config_.max_tls_version = max_tls_version; +} + /* Deprecated in favor of grpc_ssl_credentials_create_ex. Will be removed * once all of its call sites are migrated to grpc_ssl_credentials_create_ex. */ grpc_channel_credentials* grpc_ssl_credentials_create( @@ -213,6 +223,16 @@ void grpc_ssl_server_credentials::build_config( config_.num_key_cert_pairs = num_key_cert_pairs; } +void grpc_ssl_server_credentials::set_min_tls_version( + grpc_tls_version min_tls_version) { + config_.min_tls_version = min_tls_version; +} + +void grpc_ssl_server_credentials::set_max_tls_version( + grpc_tls_version max_tls_version) { + config_.max_tls_version = max_tls_version; +} + grpc_ssl_server_certificate_config* grpc_ssl_server_certificate_config_create( const char* pem_root_certs, const grpc_ssl_pem_key_cert_pair* pem_key_cert_pairs, diff --git a/src/core/lib/security/credentials/ssl/ssl_credentials.h b/src/core/lib/security/credentials/ssl/ssl_credentials.h index 545a27f0be4..4c90813f8d2 100644 --- a/src/core/lib/security/credentials/ssl/ssl_credentials.h +++ b/src/core/lib/security/credentials/ssl/ssl_credentials.h @@ -38,6 +38,11 @@ class grpc_ssl_credentials : public grpc_channel_credentials { const char* target, const grpc_channel_args* args, grpc_channel_args** new_args) override; + // TODO(mattstev): Plumb to wrapped languages. Until then, setting the TLS + // version should be done for testing purposes only. + void set_min_tls_version(grpc_tls_version min_tls_version); + void set_max_tls_version(grpc_tls_version max_tls_version); + private: void build_config(const char* pem_root_certs, grpc_ssl_pem_key_cert_pair* pem_key_cert_pair, @@ -77,6 +82,11 @@ class grpc_ssl_server_credentials final : public grpc_server_credentials { config); } + // TODO(mattstev): Plumb to wrapped languages. Until then, setting the TLS + // version should be done for testing purposes only. + void set_min_tls_version(grpc_tls_version min_tls_version); + void set_max_tls_version(grpc_tls_version max_tls_version); + const grpc_ssl_server_config& config() const { return config_; } private: diff --git a/src/core/lib/security/security_connector/ssl/ssl_security_connector.cc b/src/core/lib/security/security_connector/ssl/ssl_security_connector.cc index ae93406d7bb..6dccda47d95 100644 --- a/src/core/lib/security/security_connector/ssl/ssl_security_connector.cc +++ b/src/core/lib/security/security_connector/ssl/ssl_security_connector.cc @@ -107,6 +107,8 @@ class grpc_ssl_channel_security_connector final } options.cipher_suites = grpc_get_ssl_cipher_suites(); options.session_cache = ssl_session_cache; + options.min_tls_version = config->min_tls_version; + options.max_tls_version = config->max_tls_version; const tsi_result result = tsi_create_ssl_client_handshaker_factory_with_options( &options, &client_handshaker_factory_); @@ -251,6 +253,8 @@ class grpc_ssl_server_security_connector options.cipher_suites = grpc_get_ssl_cipher_suites(); options.alpn_protocols = alpn_protocol_strings; options.num_alpn_protocols = static_cast(num_alpn_protocols); + options.min_tls_version = server_credentials->config().min_tls_version; + options.max_tls_version = server_credentials->config().max_tls_version; const tsi_result result = tsi_create_ssl_server_handshaker_factory_with_options( &options, &server_handshaker_factory_); diff --git a/src/core/lib/security/security_connector/ssl/ssl_security_connector.h b/src/core/lib/security/security_connector/ssl/ssl_security_connector.h index f11e8190a30..04c32bb2a8c 100644 --- a/src/core/lib/security/security_connector/ssl/ssl_security_connector.h +++ b/src/core/lib/security/security_connector/ssl/ssl_security_connector.h @@ -33,7 +33,10 @@ struct grpc_ssl_config { tsi_ssl_pem_key_cert_pair* pem_key_cert_pair; char* pem_root_certs; verify_peer_options verify_options; + grpc_tls_version min_tls_version = grpc_tls_version::TLS1_2; + grpc_tls_version max_tls_version = grpc_tls_version::TLS1_3; }; + /* Creates an SSL channel_security_connector. - request_metadata_creds is the credentials object which metadata will be sent with each request. This parameter can be NULL. @@ -62,6 +65,8 @@ struct grpc_ssl_server_config { char* pem_root_certs = nullptr; grpc_ssl_client_certificate_request_type client_certificate_request = GRPC_SSL_DONT_REQUEST_CLIENT_CERTIFICATE; + grpc_tls_version min_tls_version = grpc_tls_version::TLS1_2; + grpc_tls_version max_tls_version = grpc_tls_version::TLS1_3; }; /* Creates an SSL server_security_connector. - config is the SSL config to be used for the SSL channel establishment. diff --git a/src/core/tsi/ssl_transport_security.cc b/src/core/tsi/ssl_transport_security.cc index 476aec47144..c6c8c5749c1 100644 --- a/src/core/tsi/ssl_transport_security.cc +++ b/src/core/tsi/ssl_transport_security.cc @@ -888,6 +888,43 @@ static int NullVerifyCallback(int /*preverify_ok*/, X509_STORE_CTX* /*ctx*/) { return 1; } +// Sets the min and max TLS version of |ssl_context| to |min_tls_version| and +// |max_tls_version|, respectively. +static tsi_result tsi_set_min_and_max_tls_versions( + SSL_CTX* ssl_context, grpc_tls_version min_tls_version, + grpc_tls_version max_tls_version) { + if (ssl_context == nullptr) { + gpr_log(GPR_INFO, + "Invalid nullptr argument to |tsi_set_min_and_max_tls_versions|."); + return TSI_INVALID_ARGUMENT; + } + // Set the min TLS version of the SSL context. + switch (min_tls_version) { + case grpc_tls_version::TLS1_2: + SSL_CTX_set_min_proto_version(ssl_context, TLS1_2_VERSION); + break; + case grpc_tls_version::TLS1_3: + SSL_CTX_set_min_proto_version(ssl_context, TLS1_3_VERSION); + break; + default: + gpr_log(GPR_INFO, "TLS version is not supported."); + return TSI_FAILED_PRECONDITION; + } + // Set the max TLS version of the SSL context. + switch (max_tls_version) { + case grpc_tls_version::TLS1_2: + SSL_CTX_set_max_proto_version(ssl_context, TLS1_2_VERSION); + break; + case grpc_tls_version::TLS1_3: + SSL_CTX_set_max_proto_version(ssl_context, TLS1_3_VERSION); + break; + default: + gpr_log(GPR_INFO, "TLS version is not supported."); + return TSI_FAILED_PRECONDITION; + } + return TSI_OK; +} + /* --- tsi_ssl_root_certs_store methods implementation. ---*/ tsi_ssl_root_certs_store* tsi_ssl_root_certs_store_create( @@ -1843,10 +1880,10 @@ tsi_result tsi_create_ssl_client_handshaker_factory_with_options( } #if OPENSSL_VERSION_NUMBER >= 0x10100000 - // TODO(mattstev): Allow user to set min/max TLS version. - // https://github.com/grpc/grpc/issues/22403 ssl_context = SSL_CTX_new(TLS_method()); - SSL_CTX_set_min_proto_version(ssl_context, TLS1_2_VERSION); + result = tsi_set_min_and_max_tls_versions( + ssl_context, options->min_tls_version, options->max_tls_version); + if (result != TSI_OK) return result; #else ssl_context = SSL_CTX_new(TLSv1_2_method()); #endif @@ -2010,10 +2047,11 @@ tsi_result tsi_create_ssl_server_handshaker_factory_with_options( for (i = 0; i < options->num_key_cert_pairs; i++) { do { #if OPENSSL_VERSION_NUMBER >= 0x10100000 - // TODO(mattstev): Allow user to set min/max TLS version. - // https://github.com/grpc/grpc/issues/22403 impl->ssl_contexts[i] = SSL_CTX_new(TLS_method()); - SSL_CTX_set_min_proto_version(impl->ssl_contexts[i], TLS1_2_VERSION); + result = tsi_set_min_and_max_tls_versions(impl->ssl_contexts[i], + options->min_tls_version, + options->max_tls_version); + if (result != TSI_OK) return result; #else impl->ssl_contexts[i] = SSL_CTX_new(TLSv1_2_method()); #endif diff --git a/src/core/tsi/ssl_transport_security.h b/src/core/tsi/ssl_transport_security.h index 5ace7ff35be..e49e2731d12 100644 --- a/src/core/tsi/ssl_transport_security.h +++ b/src/core/tsi/ssl_transport_security.h @@ -21,6 +21,7 @@ #include +#include #include "absl/strings/string_view.h" #include "src/core/tsi/transport_security_interface.h" @@ -152,6 +153,10 @@ struct tsi_ssl_client_handshaker_options { /* skip server certificate verification. */ bool skip_server_certificate_verification; + /* The min and max TLS versions that will be negotiated by the handshaker. */ + grpc_tls_version min_tls_version; + grpc_tls_version max_tls_version; + tsi_ssl_client_handshaker_options() : pem_key_cert_pair(nullptr), pem_root_certs(nullptr), @@ -160,7 +165,9 @@ struct tsi_ssl_client_handshaker_options { alpn_protocols(nullptr), num_alpn_protocols(0), session_cache(nullptr), - skip_server_certificate_verification(false) {} + skip_server_certificate_verification(false), + min_tls_version(grpc_tls_version::TLS1_2), + max_tls_version(grpc_tls_version::TLS1_3) {} }; /* Creates a client handshaker factory. @@ -276,6 +283,9 @@ struct tsi_ssl_server_handshaker_options { const char* session_ticket_key; /* session_ticket_key_size is a size of session ticket encryption key. */ size_t session_ticket_key_size; + /* The min and max TLS versions that will be negotiated by the handshaker. */ + grpc_tls_version min_tls_version; + grpc_tls_version max_tls_version; tsi_ssl_server_handshaker_options() : pem_key_cert_pairs(nullptr), @@ -286,7 +296,9 @@ struct tsi_ssl_server_handshaker_options { alpn_protocols(nullptr), num_alpn_protocols(0), session_ticket_key(nullptr), - session_ticket_key_size(0) {} + session_ticket_key_size(0), + min_tls_version(grpc_tls_version::TLS1_2), + max_tls_version(grpc_tls_version::TLS1_3) {} }; /* Creates a server handshaker factory. diff --git a/test/core/end2end/end2end_tests.h b/test/core/end2end/end2end_tests.h index 6a12ca690e3..6be694c76c3 100644 --- a/test/core/end2end/end2end_tests.h +++ b/test/core/end2end/end2end_tests.h @@ -35,6 +35,7 @@ typedef struct grpc_end2end_test_config grpc_end2end_test_config; #define FEATURE_MASK_DOES_NOT_SUPPORT_NETWORK_STATUS_CHANGE 128 #define FEATURE_MASK_SUPPORTS_WORKAROUNDS 256 #define FEATURE_MASK_DOES_NOT_SUPPORT_SEND_CALL_CREDENTIALS 512 +#define FEATURE_MASK_DOES_NOT_SUPPORT_CLIENT_HANDSHAKE_COMPLETE_FIRST 1024 #define FAIL_AUTH_CHECK_SERVER_ARG_NAME "fail_auth_check" diff --git a/test/core/end2end/fixtures/h2_oauth2.cc b/test/core/end2end/fixtures/h2_oauth2.cc index ce37f89e478..2cc53d0cfa5 100644 --- a/test/core/end2end/fixtures/h2_oauth2.cc +++ b/test/core/end2end/fixtures/h2_oauth2.cc @@ -26,6 +26,7 @@ #include "src/core/lib/iomgr/iomgr.h" #include "src/core/lib/iomgr/load_file.h" #include "src/core/lib/security/credentials/credentials.h" +#include "src/core/lib/security/credentials/ssl/ssl_credentials.h" #include "test/core/end2end/end2end_tests.h" #include "test/core/util/port.h" #include "test/core/util/test_config.h" @@ -40,6 +41,7 @@ static const char* client_identity = "Brainy Smurf"; struct fullstack_secure_fixture_data { std::string localaddr; + grpc_tls_version tls_version; }; static const grpc_metadata* find_metadata(const grpc_metadata* md, @@ -93,18 +95,32 @@ static void process_oauth2_failure(void* state, grpc_auth_context* /*ctx*/, } static grpc_end2end_test_fixture chttp2_create_fixture_secure_fullstack( - grpc_channel_args* /*client_args*/, grpc_channel_args* /*server_args*/) { + grpc_channel_args* /*client_args*/, grpc_channel_args* /*server_args*/, + grpc_tls_version tls_version) { grpc_end2end_test_fixture f; int port = grpc_pick_unused_port_or_die(); fullstack_secure_fixture_data* ffd = new fullstack_secure_fixture_data(); memset(&f, 0, sizeof(f)); ffd->localaddr = grpc_core::JoinHostPort("localhost", port); + ffd->tls_version = tls_version; f.fixture_data = ffd; f.cq = grpc_completion_queue_create_for_next(nullptr); f.shutdown_cq = grpc_completion_queue_create_for_pluck(nullptr); return f; } +static grpc_end2end_test_fixture chttp2_create_fixture_secure_fullstack_tls1_2( + grpc_channel_args* client_args, grpc_channel_args* server_args) { + return chttp2_create_fixture_secure_fullstack(client_args, server_args, + grpc_tls_version::TLS1_2); +} + +static grpc_end2end_test_fixture chttp2_create_fixture_secure_fullstack_tls1_3( + grpc_channel_args* client_args, grpc_channel_args* server_args) { + return chttp2_create_fixture_secure_fullstack(client_args, server_args, + grpc_tls_version::TLS1_3); +} + static void chttp2_init_client_secure_fullstack( grpc_end2end_test_fixture* f, grpc_channel_args* client_args, grpc_channel_credentials* creds) { @@ -148,6 +164,15 @@ static void chttp2_init_client_simple_ssl_with_oauth2_secure_fullstack( reinterpret_cast GRPC_SLICE_START_PTR(ca_slice); grpc_channel_credentials* ssl_creds = grpc_ssl_credentials_create(test_root_cert, nullptr, nullptr, nullptr); + if (f != nullptr && ssl_creds != nullptr) { + // Set the min and max TLS version. + grpc_ssl_credentials* creds = + reinterpret_cast(ssl_creds); + fullstack_secure_fixture_data* ffd = + static_cast(f->fixture_data); + creds->set_min_tls_version(ffd->tls_version); + creds->set_max_tls_version(ffd->tls_version); + } grpc_call_credentials* oauth2_creds = grpc_md_only_test_credentials_create( "authorization", oauth2_md, true /* is_async */); grpc_channel_credentials* ssl_oauth2_creds = @@ -213,6 +238,15 @@ static void chttp2_init_server_simple_ssl_secure_fullstack( grpc_ssl_pem_key_cert_pair pem_key_cert_pair = {server_key, server_cert}; grpc_server_credentials* ssl_creds = grpc_ssl_server_credentials_create( nullptr, &pem_key_cert_pair, 1, 0, nullptr); + if (f != nullptr && ssl_creds != nullptr) { + // Set the min and max TLS version. + grpc_ssl_server_credentials* creds = + reinterpret_cast(ssl_creds); + fullstack_secure_fixture_data* ffd = + static_cast(f->fixture_data); + creds->set_min_tls_version(ffd->tls_version); + creds->set_max_tls_version(ffd->tls_version); + } grpc_server_credentials_set_auth_metadata_processor( ssl_creds, test_processor_create(fail_server_auth_check(server_args))); chttp2_init_server_secure_fullstack(f, server_args, ssl_creds); @@ -223,12 +257,22 @@ static void chttp2_init_server_simple_ssl_secure_fullstack( /* All test configurations */ static grpc_end2end_test_config configs[] = { - {"chttp2/simple_ssl_with_oauth2_fullstack", + {"chttp2/simple_ssl_with_oauth2_fullstack_tls1_2", FEATURE_MASK_SUPPORTS_DELAYED_CONNECTION | FEATURE_MASK_SUPPORTS_PER_CALL_CREDENTIALS | FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL | FEATURE_MASK_SUPPORTS_AUTHORITY_HEADER, - "foo.test.google.fr", chttp2_create_fixture_secure_fullstack, + "foo.test.google.fr", chttp2_create_fixture_secure_fullstack_tls1_2, + chttp2_init_client_simple_ssl_with_oauth2_secure_fullstack, + chttp2_init_server_simple_ssl_secure_fullstack, + chttp2_tear_down_secure_fullstack}, + {"chttp2/simple_ssl_with_oauth2_fullstack_tls1_3", + FEATURE_MASK_SUPPORTS_DELAYED_CONNECTION | + FEATURE_MASK_SUPPORTS_PER_CALL_CREDENTIALS | + FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL | + FEATURE_MASK_SUPPORTS_AUTHORITY_HEADER | + FEATURE_MASK_DOES_NOT_SUPPORT_CLIENT_HANDSHAKE_COMPLETE_FIRST, + "foo.test.google.fr", chttp2_create_fixture_secure_fullstack_tls1_3, chttp2_init_client_simple_ssl_with_oauth2_secure_fullstack, chttp2_init_server_simple_ssl_secure_fullstack, chttp2_tear_down_secure_fullstack}, diff --git a/test/core/end2end/fixtures/h2_ssl.cc b/test/core/end2end/fixtures/h2_ssl.cc index c44e0699a32..2d1ef42c995 100644 --- a/test/core/end2end/fixtures/h2_ssl.cc +++ b/test/core/end2end/fixtures/h2_ssl.cc @@ -27,6 +27,7 @@ #include "src/core/lib/gprpp/host_port.h" #include "src/core/lib/iomgr/load_file.h" #include "src/core/lib/security/credentials/credentials.h" +#include "src/core/lib/security/credentials/ssl/ssl_credentials.h" #include "src/core/lib/security/security_connector/ssl_utils_config.h" #include "test/core/end2end/end2end_tests.h" #include "test/core/util/port.h" @@ -38,16 +39,19 @@ struct fullstack_secure_fixture_data { std::string localaddr; + grpc_tls_version tls_version; }; static grpc_end2end_test_fixture chttp2_create_fixture_secure_fullstack( - grpc_channel_args* /*client_args*/, grpc_channel_args* /*server_args*/) { + grpc_channel_args* /*client_args*/, grpc_channel_args* /*server_args*/, + grpc_tls_version tls_version) { grpc_end2end_test_fixture f; int port = grpc_pick_unused_port_or_die(); fullstack_secure_fixture_data* ffd = new fullstack_secure_fixture_data(); memset(&f, 0, sizeof(f)); ffd->localaddr = grpc_core::JoinHostPort("localhost", port); + ffd->tls_version = tls_version; f.fixture_data = ffd; f.cq = grpc_completion_queue_create_for_next(nullptr); @@ -56,6 +60,18 @@ static grpc_end2end_test_fixture chttp2_create_fixture_secure_fullstack( return f; } +static grpc_end2end_test_fixture chttp2_create_fixture_secure_fullstack_tls1_2( + grpc_channel_args* client_args, grpc_channel_args* server_args) { + return chttp2_create_fixture_secure_fullstack(client_args, server_args, + grpc_tls_version::TLS1_2); +} + +static grpc_end2end_test_fixture chttp2_create_fixture_secure_fullstack_tls1_3( + grpc_channel_args* client_args, grpc_channel_args* server_args) { + return chttp2_create_fixture_secure_fullstack(client_args, server_args, + grpc_tls_version::TLS1_3); +} + static void process_auth_failure(void* state, grpc_auth_context* /*ctx*/, const grpc_metadata* /*md*/, size_t /*md_count*/, @@ -102,6 +118,15 @@ static void chttp2_init_client_simple_ssl_secure_fullstack( grpc_end2end_test_fixture* f, grpc_channel_args* client_args) { grpc_channel_credentials* ssl_creds = grpc_ssl_credentials_create(nullptr, nullptr, nullptr, nullptr); + if (f != nullptr && ssl_creds != nullptr) { + // Set the min and max TLS version. + grpc_ssl_credentials* creds = + reinterpret_cast(ssl_creds); + fullstack_secure_fixture_data* ffd = + static_cast(f->fixture_data); + creds->set_min_tls_version(ffd->tls_version); + creds->set_max_tls_version(ffd->tls_version); + } grpc_arg ssl_name_override = { GRPC_ARG_STRING, const_cast(GRPC_SSL_TARGET_NAME_OVERRIDE_ARG), @@ -138,6 +163,15 @@ static void chttp2_init_server_simple_ssl_secure_fullstack( grpc_ssl_pem_key_cert_pair pem_key_cert_pair = {server_key, server_cert}; grpc_server_credentials* ssl_creds = grpc_ssl_server_credentials_create( nullptr, &pem_key_cert_pair, 1, 0, nullptr); + if (f != nullptr && ssl_creds != nullptr) { + // Set the min and max TLS version. + grpc_ssl_server_credentials* creds = + reinterpret_cast(ssl_creds); + fullstack_secure_fixture_data* ffd = + static_cast(f->fixture_data); + creds->set_min_tls_version(ffd->tls_version); + creds->set_max_tls_version(ffd->tls_version); + } grpc_slice_unref(cert_slice); grpc_slice_unref(key_slice); if (fail_server_auth_check(server_args)) { @@ -151,12 +185,22 @@ static void chttp2_init_server_simple_ssl_secure_fullstack( /* All test configurations */ static grpc_end2end_test_config configs[] = { - {"chttp2/simple_ssl_fullstack", + {"chttp2/simple_ssl_fullstack_tls1_2", FEATURE_MASK_SUPPORTS_DELAYED_CONNECTION | FEATURE_MASK_SUPPORTS_PER_CALL_CREDENTIALS | FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL | FEATURE_MASK_SUPPORTS_AUTHORITY_HEADER, - "foo.test.google.fr", chttp2_create_fixture_secure_fullstack, + "foo.test.google.fr", chttp2_create_fixture_secure_fullstack_tls1_2, + chttp2_init_client_simple_ssl_secure_fullstack, + chttp2_init_server_simple_ssl_secure_fullstack, + chttp2_tear_down_secure_fullstack}, + {"chttp2/simple_ssl_fullstack_tls1_3", + FEATURE_MASK_SUPPORTS_DELAYED_CONNECTION | + FEATURE_MASK_SUPPORTS_PER_CALL_CREDENTIALS | + FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL | + FEATURE_MASK_SUPPORTS_AUTHORITY_HEADER | + FEATURE_MASK_DOES_NOT_SUPPORT_CLIENT_HANDSHAKE_COMPLETE_FIRST, + "foo.test.google.fr", chttp2_create_fixture_secure_fullstack_tls1_3, chttp2_init_client_simple_ssl_secure_fullstack, chttp2_init_server_simple_ssl_secure_fullstack, chttp2_tear_down_secure_fullstack}, diff --git a/test/core/end2end/fixtures/h2_ssl_cred_reload.cc b/test/core/end2end/fixtures/h2_ssl_cred_reload.cc index 96f796d0b27..bf78cda091a 100644 --- a/test/core/end2end/fixtures/h2_ssl_cred_reload.cc +++ b/test/core/end2end/fixtures/h2_ssl_cred_reload.cc @@ -27,6 +27,7 @@ #include "src/core/lib/gprpp/host_port.h" #include "src/core/lib/iomgr/load_file.h" #include "src/core/lib/security/credentials/credentials.h" +#include "src/core/lib/security/credentials/ssl/ssl_credentials.h" #include "src/core/lib/security/security_connector/ssl_utils_config.h" #include "test/core/end2end/end2end_tests.h" #include "test/core/util/port.h" @@ -38,6 +39,7 @@ struct fullstack_secure_fixture_data { std::string localaddr; + grpc_tls_version tls_version; bool server_credential_reloaded = false; }; @@ -77,12 +79,14 @@ ssl_server_certificate_config_callback( } static grpc_end2end_test_fixture chttp2_create_fixture_secure_fullstack( - grpc_channel_args* /*client_args*/, grpc_channel_args* /*server_args*/) { + grpc_channel_args* /*client_args*/, grpc_channel_args* /*server_args*/, + grpc_tls_version tls_version) { grpc_end2end_test_fixture f; int port = grpc_pick_unused_port_or_die(); fullstack_secure_fixture_data* ffd = new fullstack_secure_fixture_data(); memset(&f, 0, sizeof(f)); ffd->localaddr = grpc_core::JoinHostPort("localhost", port); + ffd->tls_version = tls_version; f.fixture_data = ffd; f.cq = grpc_completion_queue_create_for_next(nullptr); @@ -91,6 +95,18 @@ static grpc_end2end_test_fixture chttp2_create_fixture_secure_fullstack( return f; } +static grpc_end2end_test_fixture chttp2_create_fixture_secure_fullstack_tls1_2( + grpc_channel_args* client_args, grpc_channel_args* server_args) { + return chttp2_create_fixture_secure_fullstack(client_args, server_args, + grpc_tls_version::TLS1_2); +} + +static grpc_end2end_test_fixture chttp2_create_fixture_secure_fullstack_tls1_3( + grpc_channel_args* client_args, grpc_channel_args* server_args) { + return chttp2_create_fixture_secure_fullstack(client_args, server_args, + grpc_tls_version::TLS1_3); +} + static void process_auth_failure(void* state, grpc_auth_context* /*ctx*/, const grpc_metadata* /*md*/, size_t /*md_count*/, @@ -138,6 +154,15 @@ static void chttp2_init_client_simple_ssl_secure_fullstack( grpc_end2end_test_fixture* f, grpc_channel_args* client_args) { grpc_channel_credentials* ssl_creds = grpc_ssl_credentials_create(nullptr, nullptr, nullptr, nullptr); + if (f != nullptr && ssl_creds != nullptr) { + // Set the min and max TLS version. + grpc_ssl_credentials* creds = + reinterpret_cast(ssl_creds); + fullstack_secure_fixture_data* ffd = + static_cast(f->fixture_data); + creds->set_min_tls_version(ffd->tls_version); + creds->set_max_tls_version(ffd->tls_version); + } grpc_arg ssl_name_override = { GRPC_ARG_STRING, const_cast(GRPC_SSL_TARGET_NAME_OVERRIDE_ARG), @@ -168,6 +193,15 @@ static void chttp2_init_server_simple_ssl_secure_fullstack( ssl_server_certificate_config_callback, f->fixture_data); grpc_server_credentials* ssl_creds = grpc_ssl_server_credentials_create_with_options(options); + if (f != nullptr && ssl_creds != nullptr) { + // Set the min and max TLS version. + grpc_ssl_server_credentials* creds = + reinterpret_cast(ssl_creds); + fullstack_secure_fixture_data* ffd = + static_cast(f->fixture_data); + creds->set_min_tls_version(ffd->tls_version); + creds->set_max_tls_version(ffd->tls_version); + } if (fail_server_auth_check(server_args)) { grpc_auth_metadata_processor processor = {process_auth_failure, nullptr, nullptr}; @@ -179,12 +213,22 @@ static void chttp2_init_server_simple_ssl_secure_fullstack( /* All test configurations */ static grpc_end2end_test_config configs[] = { - {"chttp2/simple_ssl_fullstack", + {"chttp2/simple_ssl_fullstack_tls1_2", FEATURE_MASK_SUPPORTS_DELAYED_CONNECTION | FEATURE_MASK_SUPPORTS_PER_CALL_CREDENTIALS | FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL | FEATURE_MASK_SUPPORTS_AUTHORITY_HEADER, - "foo.test.google.fr", chttp2_create_fixture_secure_fullstack, + "foo.test.google.fr", chttp2_create_fixture_secure_fullstack_tls1_2, + chttp2_init_client_simple_ssl_secure_fullstack, + chttp2_init_server_simple_ssl_secure_fullstack, + chttp2_tear_down_secure_fullstack}, + {"chttp2/simple_ssl_fullstack_tls1_3", + FEATURE_MASK_SUPPORTS_DELAYED_CONNECTION | + FEATURE_MASK_SUPPORTS_PER_CALL_CREDENTIALS | + FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL | + FEATURE_MASK_SUPPORTS_AUTHORITY_HEADER | + FEATURE_MASK_DOES_NOT_SUPPORT_CLIENT_HANDSHAKE_COMPLETE_FIRST, + "foo.test.google.fr", chttp2_create_fixture_secure_fullstack_tls1_3, chttp2_init_client_simple_ssl_secure_fullstack, chttp2_init_server_simple_ssl_secure_fullstack, chttp2_tear_down_secure_fullstack}, diff --git a/test/core/end2end/tests/filter_call_init_fails.cc b/test/core/end2end/tests/filter_call_init_fails.cc index a5785d7338b..062c83632b5 100644 --- a/test/core/end2end/tests/filter_call_init_fails.cc +++ b/test/core/end2end/tests/filter_call_init_fails.cc @@ -508,7 +508,15 @@ void filter_call_init_fails(grpc_end2end_test_config config) { g_enable_client_channel_filter = true; test_client_channel_filter(config); g_enable_client_channel_filter = false; - if (config.feature_mask & FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL) { + // If the client handshake completes before the server handshake and the + // client is able to send application data before the server handshake + // completes, then testing the CLIENT_SUBCHANNEL filter will cause the server + // to hang waiting for the final handshake message from the client. This + // handshake message will never arrive because it would have been sent with + // the first application data message, which failed because of the filter. + if ((config.feature_mask & FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL) && + !(config.feature_mask & + FEATURE_MASK_DOES_NOT_SUPPORT_CLIENT_HANDSHAKE_COMPLETE_FIRST)) { gpr_log(GPR_INFO, "Testing CLIENT_SUBCHANNEL filter."); g_enable_client_subchannel_filter = true; test_client_subchannel_filter(config); diff --git a/test/core/tsi/ssl_transport_security_test.cc b/test/core/tsi/ssl_transport_security_test.cc index 6d2bc5e535c..1f1915c84b9 100644 --- a/test/core/tsi/ssl_transport_security_test.cc +++ b/test/core/tsi/ssl_transport_security_test.cc @@ -55,6 +55,9 @@ const size_t kSessionTicketEncryptionKeySize = 80; const size_t kSessionTicketEncryptionKeySize = 48; #endif +// Indicates the TLS version used for the test. +static grpc_tls_version test_tls_version = grpc_tls_version::TLS1_3; + typedef enum AlpnMode { NO_ALPN, ALPN_CLIENT_NO_SERVER, @@ -127,6 +130,8 @@ static void ssl_test_setup_handshakers(tsi_test_fixture* fixture) { if (ssl_fixture->session_cache != nullptr) { client_options.session_cache = ssl_fixture->session_cache; } + client_options.min_tls_version = test_tls_version; + client_options.max_tls_version = test_tls_version; GPR_ASSERT(tsi_create_ssl_client_handshaker_factory_with_options( &client_options, &ssl_fixture->client_handshaker_factory) == TSI_OK); @@ -159,6 +164,8 @@ static void ssl_test_setup_handshakers(tsi_test_fixture* fixture) { } server_options.session_ticket_key = ssl_fixture->session_ticket_key; server_options.session_ticket_key_size = ssl_fixture->session_ticket_key_size; + server_options.min_tls_version = test_tls_version; + server_options.max_tls_version = test_tls_version; GPR_ASSERT(tsi_create_ssl_server_handshaker_factory_with_options( &server_options, &ssl_fixture->server_handshaker_factory) == TSI_OK); @@ -317,13 +324,17 @@ static void ssl_test_check_handshaker_peers(tsi_test_fixture* fixture) { GPR_ASSERT(ssl_fixture->key_cert_lib != nullptr); ssl_key_cert_lib* key_cert_lib = ssl_fixture->key_cert_lib; tsi_peer peer; + // In TLS 1.3, the client-side handshake succeeds even if the client sends a + // bad certificate. In such a case, the server would fail the TLS handshake + // and send an alert to the client as the first application data message. In + // TLS 1.2, the client-side handshake will fail if the client sends a bad + // certificate. bool expect_server_success = !(key_cert_lib->use_bad_server_cert || (key_cert_lib->use_bad_client_cert && ssl_fixture->force_client_auth)); - // In TLS 1.3, the client-side handshake succeeds even if the client sends a - // bad certificate. In such a case, the server would fail the TLS handshake - // and send an alert to the client as the first application data message. - bool expect_client_success = !key_cert_lib->use_bad_server_cert; + bool expect_client_success = test_tls_version == grpc_tls_version::TLS1_2 + ? expect_server_success + : !key_cert_lib->use_bad_server_cert; if (expect_client_success) { GPR_ASSERT(tsi_handshaker_result_extract_peer( ssl_fixture->base.client_result, &peer) == TSI_OK); @@ -954,31 +965,39 @@ void ssl_tsi_test_extract_cert_chain() { int main(int argc, char** argv) { grpc::testing::TestEnvironment env(argc, argv); grpc_init(); - ssl_tsi_test_do_handshake_tiny_handshake_buffer(); - ssl_tsi_test_do_handshake_small_handshake_buffer(); - ssl_tsi_test_do_handshake(); - ssl_tsi_test_do_handshake_with_root_store(); - ssl_tsi_test_do_handshake_with_client_authentication(); - ssl_tsi_test_do_handshake_with_client_authentication_and_root_store(); - ssl_tsi_test_do_handshake_with_server_name_indication_exact_domain(); - ssl_tsi_test_do_handshake_with_server_name_indication_wild_star_domain(); - ssl_tsi_test_do_handshake_with_wrong_server_name_indication(); - ssl_tsi_test_do_handshake_with_bad_server_cert(); - ssl_tsi_test_do_handshake_with_bad_client_cert(); + const size_t number_tls_versions = 2; + const grpc_tls_version tls_versions[] = {grpc_tls_version::TLS1_2, + grpc_tls_version::TLS1_3}; + for (size_t i = 0; i < number_tls_versions; i++) { + // Set the TLS version to be used in the tests. + test_tls_version = tls_versions[i]; + // Run all the tests using that TLS version for both the client and server. + ssl_tsi_test_do_handshake_tiny_handshake_buffer(); + ssl_tsi_test_do_handshake_small_handshake_buffer(); + ssl_tsi_test_do_handshake(); + ssl_tsi_test_do_handshake_with_root_store(); + ssl_tsi_test_do_handshake_with_client_authentication(); + ssl_tsi_test_do_handshake_with_client_authentication_and_root_store(); + ssl_tsi_test_do_handshake_with_server_name_indication_exact_domain(); + ssl_tsi_test_do_handshake_with_server_name_indication_wild_star_domain(); + ssl_tsi_test_do_handshake_with_wrong_server_name_indication(); + ssl_tsi_test_do_handshake_with_bad_server_cert(); + ssl_tsi_test_do_handshake_with_bad_client_cert(); #ifdef OPENSSL_IS_BORINGSSL - // BoringSSL and OpenSSL have different behaviors on mismatched ALPN. - ssl_tsi_test_do_handshake_alpn_client_no_server(); - ssl_tsi_test_do_handshake_alpn_client_server_mismatch(); + // BoringSSL and OpenSSL have different behaviors on mismatched ALPN. + ssl_tsi_test_do_handshake_alpn_client_no_server(); + ssl_tsi_test_do_handshake_alpn_client_server_mismatch(); #endif - ssl_tsi_test_do_handshake_alpn_server_no_client(); - ssl_tsi_test_do_handshake_alpn_client_server_ok(); - ssl_tsi_test_do_handshake_session_cache(); - ssl_tsi_test_do_round_trip_for_all_configs(); - ssl_tsi_test_do_round_trip_odd_buffer_size(); - ssl_tsi_test_handshaker_factory_internals(); - ssl_tsi_test_duplicate_root_certificates(); - ssl_tsi_test_extract_x509_subject_names(); - ssl_tsi_test_extract_cert_chain(); + ssl_tsi_test_do_handshake_alpn_server_no_client(); + ssl_tsi_test_do_handshake_alpn_client_server_ok(); + ssl_tsi_test_do_handshake_session_cache(); + ssl_tsi_test_do_round_trip_for_all_configs(); + ssl_tsi_test_do_round_trip_odd_buffer_size(); + ssl_tsi_test_handshaker_factory_internals(); + ssl_tsi_test_duplicate_root_certificates(); + ssl_tsi_test_extract_x509_subject_names(); + ssl_tsi_test_extract_cert_chain(); + } grpc_shutdown(); return 0; }