diff --git a/grpc.def b/grpc.def index c3f8af10860..0cc981d0fd5 100644 --- a/grpc.def +++ b/grpc.def @@ -157,6 +157,8 @@ EXPORTS grpc_tls_certificate_provider_file_watcher_create grpc_tls_certificate_provider_release grpc_tls_credentials_options_create + grpc_tls_credentials_options_set_min_tls_version + grpc_tls_credentials_options_set_max_tls_version grpc_tls_credentials_options_copy grpc_tls_credentials_options_destroy grpc_tls_credentials_options_set_certificate_provider diff --git a/include/grpc/grpc_security.h b/include/grpc/grpc_security.h index d099ff62933..c81b81a207b 100644 --- a/include/grpc/grpc_security.h +++ b/include/grpc/grpc_security.h @@ -815,6 +815,24 @@ GRPCAPI void grpc_tls_certificate_provider_release( */ GRPCAPI grpc_tls_credentials_options* grpc_tls_credentials_options_create(void); +/** + * EXPERIMENTAL API - Subject to change + * + * Sets the minimum TLS version that will be negotiated during the TLS + * handshake. If not set, the underlying SSL library will set it to TLS v1.2. + */ +GRPCAPI void grpc_tls_credentials_options_set_min_tls_version( + grpc_tls_credentials_options* options, grpc_tls_version min_tls_version); + +/** + * EXPERIMENTAL API - Subject to change + * + * Sets the maximum TLS version that will be negotiated during the TLS + * handshake. If not set, the underlying SSL library will set it to TLS v1.3. + */ +GRPCAPI void grpc_tls_credentials_options_set_max_tls_version( + grpc_tls_credentials_options* options, grpc_tls_version max_tls_version); + /** * EXPERIMENTAL API - Subject to change * diff --git a/include/grpcpp/security/tls_credentials_options.h b/include/grpcpp/security/tls_credentials_options.h index 7ad5b088568..7f5cb8208fa 100644 --- a/include/grpcpp/security/tls_credentials_options.h +++ b/include/grpcpp/security/tls_credentials_options.h @@ -109,6 +109,15 @@ class TlsCredentialsOptions { void set_crl_provider(std::shared_ptr crl_provider); + // Sets the minimum TLS version that will be negotiated during the TLS + // handshake. If not set, the underlying SSL library will use TLS v1.2. + // @param tls_version: The minimum TLS version. + void set_min_tls_version(grpc_tls_version tls_version); + // Sets the maximum TLS version that will be negotiated during the TLS + // handshake. If not set, the underlying SSL library will use TLS v1.3. + // @param tls_version: The maximum TLS version. + void set_max_tls_version(grpc_tls_version tls_version); + // ----- Getters for member fields ---- // Returns a deep copy of the internal c options. The caller takes ownership // of the returned pointer. This function shall be used only internally. diff --git a/src/core/lib/security/credentials/tls/grpc_tls_credentials_options.cc b/src/core/lib/security/credentials/tls/grpc_tls_credentials_options.cc index 3c090011f31..0dcc959bf3c 100644 --- a/src/core/lib/security/credentials/tls/grpc_tls_credentials_options.cc +++ b/src/core/lib/security/credentials/tls/grpc_tls_credentials_options.cc @@ -149,3 +149,15 @@ void grpc_tls_credentials_options_set_crl_provider( GPR_ASSERT(options != nullptr); options->set_crl_provider(provider); } + +void grpc_tls_credentials_options_set_min_tls_version( + grpc_tls_credentials_options* options, grpc_tls_version min_tls_version) { + GPR_ASSERT(options != nullptr); + options->set_min_tls_version(min_tls_version); +} + +void grpc_tls_credentials_options_set_max_tls_version( + grpc_tls_credentials_options* options, grpc_tls_version max_tls_version) { + GPR_ASSERT(options != nullptr); + options->set_max_tls_version(max_tls_version); +} diff --git a/src/core/lib/security/credentials/tls/tls_credentials.cc b/src/core/lib/security/credentials/tls/tls_credentials.cc index c1a27343fb7..eac64307f8d 100644 --- a/src/core/lib/security/credentials/tls/tls_credentials.cc +++ b/src/core/lib/security/credentials/tls/tls_credentials.cc @@ -46,6 +46,22 @@ bool CredentialOptionSanityCheck(grpc_tls_credentials_options* options, gpr_log(GPR_ERROR, "TLS credentials options is nullptr."); return false; } + // In this case, there will be non-retriable handshake errors. + if (options->min_tls_version() > options->max_tls_version()) { + gpr_log(GPR_ERROR, "TLS min version must not be higher than max version."); + grpc_tls_credentials_options_destroy(options); + return false; + } + if (options->max_tls_version() > grpc_tls_version::TLS1_3) { + gpr_log(GPR_ERROR, "TLS max version must not be higher than v1.3."); + grpc_tls_credentials_options_destroy(options); + return false; + } + if (options->min_tls_version() < grpc_tls_version::TLS1_2) { + gpr_log(GPR_ERROR, "TLS min version must not be lower than v1.2."); + grpc_tls_credentials_options_destroy(options); + return false; + } if (!options->crl_directory().empty() && options->crl_provider() != nullptr) { gpr_log(GPR_ERROR, "Setting crl_directory and crl_provider not supported. Using the " diff --git a/src/cpp/common/tls_credentials_options.cc b/src/cpp/common/tls_credentials_options.cc index 6e907704652..6732aca3454 100644 --- a/src/cpp/common/tls_credentials_options.cc +++ b/src/cpp/common/tls_credentials_options.cc @@ -95,6 +95,18 @@ void TlsCredentialsOptions::set_certificate_verifier( } } +void TlsCredentialsOptions::set_min_tls_version(grpc_tls_version tls_version) { + grpc_tls_credentials_options* options = mutable_c_credentials_options(); + GPR_ASSERT(options != nullptr); + grpc_tls_credentials_options_set_min_tls_version(options, tls_version); +} + +void TlsCredentialsOptions::set_max_tls_version(grpc_tls_version tls_version) { + grpc_tls_credentials_options* options = mutable_c_credentials_options(); + GPR_ASSERT(options != nullptr); + grpc_tls_credentials_options_set_max_tls_version(options, tls_version); +} + grpc_tls_credentials_options* TlsCredentialsOptions::c_credentials_options() const { return grpc_tls_credentials_options_copy(c_credentials_options_); diff --git a/src/ruby/ext/grpc/rb_grpc_imports.generated.c b/src/ruby/ext/grpc/rb_grpc_imports.generated.c index 641c9cb97cc..b28175e0165 100644 --- a/src/ruby/ext/grpc/rb_grpc_imports.generated.c +++ b/src/ruby/ext/grpc/rb_grpc_imports.generated.c @@ -180,6 +180,8 @@ grpc_tls_certificate_provider_static_data_create_type grpc_tls_certificate_provi grpc_tls_certificate_provider_file_watcher_create_type grpc_tls_certificate_provider_file_watcher_create_import; grpc_tls_certificate_provider_release_type grpc_tls_certificate_provider_release_import; grpc_tls_credentials_options_create_type grpc_tls_credentials_options_create_import; +grpc_tls_credentials_options_set_min_tls_version_type grpc_tls_credentials_options_set_min_tls_version_import; +grpc_tls_credentials_options_set_max_tls_version_type grpc_tls_credentials_options_set_max_tls_version_import; grpc_tls_credentials_options_copy_type grpc_tls_credentials_options_copy_import; grpc_tls_credentials_options_destroy_type grpc_tls_credentials_options_destroy_import; grpc_tls_credentials_options_set_certificate_provider_type grpc_tls_credentials_options_set_certificate_provider_import; @@ -469,6 +471,8 @@ void grpc_rb_load_imports(HMODULE library) { grpc_tls_certificate_provider_file_watcher_create_import = (grpc_tls_certificate_provider_file_watcher_create_type) GetProcAddress(library, "grpc_tls_certificate_provider_file_watcher_create"); grpc_tls_certificate_provider_release_import = (grpc_tls_certificate_provider_release_type) GetProcAddress(library, "grpc_tls_certificate_provider_release"); grpc_tls_credentials_options_create_import = (grpc_tls_credentials_options_create_type) GetProcAddress(library, "grpc_tls_credentials_options_create"); + grpc_tls_credentials_options_set_min_tls_version_import = (grpc_tls_credentials_options_set_min_tls_version_type) GetProcAddress(library, "grpc_tls_credentials_options_set_min_tls_version"); + grpc_tls_credentials_options_set_max_tls_version_import = (grpc_tls_credentials_options_set_max_tls_version_type) GetProcAddress(library, "grpc_tls_credentials_options_set_max_tls_version"); grpc_tls_credentials_options_copy_import = (grpc_tls_credentials_options_copy_type) GetProcAddress(library, "grpc_tls_credentials_options_copy"); grpc_tls_credentials_options_destroy_import = (grpc_tls_credentials_options_destroy_type) GetProcAddress(library, "grpc_tls_credentials_options_destroy"); grpc_tls_credentials_options_set_certificate_provider_import = (grpc_tls_credentials_options_set_certificate_provider_type) GetProcAddress(library, "grpc_tls_credentials_options_set_certificate_provider"); diff --git a/src/ruby/ext/grpc/rb_grpc_imports.generated.h b/src/ruby/ext/grpc/rb_grpc_imports.generated.h index 55da803ef6a..65c095a96e4 100644 --- a/src/ruby/ext/grpc/rb_grpc_imports.generated.h +++ b/src/ruby/ext/grpc/rb_grpc_imports.generated.h @@ -515,6 +515,12 @@ extern grpc_tls_certificate_provider_release_type grpc_tls_certificate_provider_ typedef grpc_tls_credentials_options*(*grpc_tls_credentials_options_create_type)(void); extern grpc_tls_credentials_options_create_type grpc_tls_credentials_options_create_import; #define grpc_tls_credentials_options_create grpc_tls_credentials_options_create_import +typedef void(*grpc_tls_credentials_options_set_min_tls_version_type)(grpc_tls_credentials_options* options, grpc_tls_version min_tls_version); +extern grpc_tls_credentials_options_set_min_tls_version_type grpc_tls_credentials_options_set_min_tls_version_import; +#define grpc_tls_credentials_options_set_min_tls_version grpc_tls_credentials_options_set_min_tls_version_import +typedef void(*grpc_tls_credentials_options_set_max_tls_version_type)(grpc_tls_credentials_options* options, grpc_tls_version max_tls_version); +extern grpc_tls_credentials_options_set_max_tls_version_type grpc_tls_credentials_options_set_max_tls_version_import; +#define grpc_tls_credentials_options_set_max_tls_version grpc_tls_credentials_options_set_max_tls_version_import typedef grpc_tls_credentials_options*(*grpc_tls_credentials_options_copy_type)(grpc_tls_credentials_options* options); extern grpc_tls_credentials_options_copy_type grpc_tls_credentials_options_copy_import; #define grpc_tls_credentials_options_copy grpc_tls_credentials_options_copy_import diff --git a/test/core/security/grpc_tls_credentials_options_test.cc b/test/core/security/grpc_tls_credentials_options_test.cc index b6708224fd5..9cafa1b98b2 100644 --- a/test/core/security/grpc_tls_credentials_options_test.cc +++ b/test/core/security/grpc_tls_credentials_options_test.cc @@ -67,6 +67,22 @@ class GrpcTlsCredentialsOptionsTest : public ::testing::Test { HostNameCertificateVerifier hostname_certificate_verifier_; }; +TEST_F(GrpcTlsCredentialsOptionsTest, BadTlsVersionsForChannelCredentials) { + auto options = grpc_tls_credentials_options_create(); + options->set_max_tls_version(grpc_tls_version::TLS1_2); + options->set_min_tls_version(grpc_tls_version::TLS1_3); + auto credentials = grpc_tls_credentials_create(options); + EXPECT_EQ(credentials, nullptr); +} + +TEST_F(GrpcTlsCredentialsOptionsTest, BadTlsVersionsForServerCredentials) { + auto server_options = grpc_tls_credentials_options_create(); + server_options->set_max_tls_version(grpc_tls_version::TLS1_2); + server_options->set_min_tls_version(grpc_tls_version::TLS1_3); + auto server_credentials = grpc_tls_server_credentials_create(server_options); + EXPECT_EQ(server_credentials, nullptr); +} + // // Tests for Default Root Certs. // diff --git a/test/cpp/client/credentials_test.cc b/test/cpp/client/credentials_test.cc index a6b1645e891..edf78b44f12 100644 --- a/test/cpp/client/credentials_test.cc +++ b/test/cpp/client/credentials_test.cc @@ -500,6 +500,22 @@ TEST(CredentialsTest, MultipleChannelCredentialsOneCrlProviderDoesNotLeak) { EXPECT_NE(channel_creds_2, nullptr); } +TEST(CredentialsTest, TlsChannelCredentialsWithGoodMinAndMaxTlsVersions) { + grpc::experimental::TlsChannelCredentialsOptions options; + options.set_min_tls_version(grpc_tls_version::TLS1_2); + options.set_max_tls_version(grpc_tls_version::TLS1_3); + auto channel_credentials = grpc::experimental::TlsCredentials(options); + EXPECT_NE(channel_credentials, nullptr); +} + +TEST(CredentialsTest, TlsChannelCredentialsWithBadMinAndMaxTlsVersions) { + grpc::experimental::TlsChannelCredentialsOptions options; + options.set_min_tls_version(grpc_tls_version::TLS1_3); + options.set_max_tls_version(grpc_tls_version::TLS1_2); + auto channel_credentials = grpc::experimental::TlsCredentials(options); + EXPECT_EQ(channel_credentials, nullptr); +} + } // namespace } // namespace testing } // namespace grpc diff --git a/test/cpp/server/credentials_test.cc b/test/cpp/server/credentials_test.cc index 8c61d3ce378..879c2c101dd 100644 --- a/test/cpp/server/credentials_test.cc +++ b/test/cpp/server/credentials_test.cc @@ -277,6 +277,24 @@ TEST(CredentialsTest, MultipleServerCredentialsOneCrlProviderDoesNotLeak) { EXPECT_NE(server_creds_2, nullptr); } +TEST(CredentialsTest, TlsServerCredentialsWithGoodMinMaxTlsVersions) { + grpc::experimental::TlsServerCredentialsOptions options( + /*certificate_provider=*/nullptr); + options.set_min_tls_version(grpc_tls_version::TLS1_2); + options.set_max_tls_version(grpc_tls_version::TLS1_3); + auto server_credentials = grpc::experimental::TlsServerCredentials(options); + EXPECT_NE(server_credentials, nullptr); +} + +TEST(CredentialsTest, TlsServerCredentialsWithBadMinMaxTlsVersions) { + grpc::experimental::TlsServerCredentialsOptions options( + /*certificate_provider=*/nullptr); + options.set_min_tls_version(grpc_tls_version::TLS1_3); + options.set_max_tls_version(grpc_tls_version::TLS1_2); + auto server_credentials = grpc::experimental::TlsServerCredentials(options); + EXPECT_EQ(server_credentials, nullptr); +} + } // namespace } // namespace testing } // namespace grpc