From e10d90ba274146eab5dc1073e0675fef07ef26ce Mon Sep 17 00:00:00 2001 From: Yihua Zhang Date: Mon, 6 Jan 2020 11:40:27 -0800 Subject: [PATCH] add security level negotiation between call creds and channel. --- include/grpc/grpc_security.h | 28 ++++++--- include/grpc/grpc_security_constants.h | 5 +- include/grpcpp/security/credentials_impl.h | 4 ++ .../composite/composite_credentials.cc | 7 +++ .../composite/composite_credentials.h | 6 +- .../lib/security/credentials/credentials.h | 11 +++- .../credentials/fake/fake_credentials.h | 3 +- .../credentials/plugin/plugin_credentials.cc | 10 ++-- .../credentials/plugin/plugin_credentials.h | 3 +- .../alts/alts_security_connector.cc | 13 +++++ .../fake/fake_security_connector.cc | 24 +++++++- .../local/local_security_connector.cc | 32 +++++++++- .../security/security_connector/ssl_utils.cc | 32 ++++++++++ .../security/security_connector/ssl_utils.h | 11 ++++ .../security/transport/client_auth_filter.cc | 33 +++++++++++ .../alts/handshaker/alts_tsi_handshaker.cc | 12 +++- .../tsi/alts/handshaker/alts_tsi_handshaker.h | 2 +- src/core/tsi/fake_transport_security.cc | 10 +++- src/core/tsi/fake_transport_security.h | 2 + src/core/tsi/ssl_transport_security.cc | 9 ++- src/core/tsi/transport_security.cc | 13 +++++ src/core/tsi/transport_security_interface.h | 12 ++++ src/cpp/client/secure_credentials.cc | 18 +++++- src/csharp/ext/grpc_csharp_ext.c | 6 +- src/php/ext/grpc/call_credentials.c | 5 +- .../grpc/_cython/_cygrpc/credentials.pyx.pxi | 4 +- .../grpcio/grpc/_cython/_cygrpc/grpc.pxi | 9 ++- .../tests/unit/_auth_context_test.py | 10 ++-- src/ruby/ext/grpc/rb_call_credentials.c | 5 +- src/ruby/ext/grpc/rb_grpc_imports.generated.h | 2 +- test/core/end2end/end2end_tests.h | 1 + test/core/end2end/fixtures/h2_fakesec.cc | 3 +- test/core/end2end/tests/call_creds.cc | 15 ++++- .../security/alts_security_connector_test.cc | 40 ++++++++++++- test/core/security/credentials_test.cc | 33 +++++++++-- test/core/security/security_connector_test.cc | 58 ++++++++++++++++--- .../handshaker/alts_tsi_handshaker_test.cc | 10 ++++ test/core/tsi/fake_transport_security_test.cc | 5 ++ test/core/tsi/ssl_transport_security_test.cc | 16 ++++- 39 files changed, 462 insertions(+), 60 deletions(-) diff --git a/include/grpc/grpc_security.h b/include/grpc/grpc_security.h index 164c6dacdb0..b56365c4ed9 100644 --- a/include/grpc/grpc_security.h +++ b/include/grpc/grpc_security.h @@ -132,7 +132,8 @@ GRPCAPI void grpc_channel_credentials_release(grpc_channel_credentials* creds); /** Creates default credentials to connect to a google gRPC service. WARNING: Do NOT use this credentials to connect to a non-google service as - this could result in an oauth2 token leak. */ + this could result in an oauth2 token leak. The security level of the + resulting connection is GRPC_PRIVACY_AND_INTEGRITY. */ GRPCAPI grpc_channel_credentials* grpc_google_default_credentials_create(void); /** Callback for getting the SSL roots override from the application. @@ -208,6 +209,7 @@ typedef struct { /** Deprecated in favor of grpc_ssl_server_credentials_create_ex. It will be removed after all of its call sites are migrated to grpc_ssl_server_credentials_create_ex. Creates an SSL credentials object. + The security level of the resulting connection is GRPC_PRIVACY_AND_INTEGRITY. - pem_root_certs is the NULL-terminated string containing the PEM encoding of the server root certificates. If this parameter is NULL, the implementation will first try to dereference the file pointed by the @@ -239,6 +241,7 @@ GRPCAPI grpc_channel_credentials* grpc_ssl_credentials_create( const verify_peer_options* verify_options, void* reserved); /* Creates an SSL credentials object. + The security level of the resulting connection is GRPC_PRIVACY_AND_INTEGRITY. - pem_root_certs is the NULL-terminated string containing the PEM encoding of the server root certificates. If this parameter is NULL, the implementation will first try to dereference the file pointed by the @@ -281,7 +284,8 @@ typedef struct grpc_call_credentials grpc_call_credentials; The creator of the credentials object is responsible for its release. */ GRPCAPI void grpc_call_credentials_release(grpc_call_credentials* creds); -/** Creates a composite channel credentials object. */ +/** Creates a composite channel credentials object. The security level of + * resulting connection is determined by channel_creds. */ GRPCAPI grpc_channel_credentials* grpc_composite_channel_credentials_create( grpc_channel_credentials* channel_creds, grpc_call_credentials* call_creds, void* reserved); @@ -431,9 +435,11 @@ typedef struct { const char* type; } grpc_metadata_credentials_plugin; -/** Creates a credentials object from a plugin. */ +/** Creates a credentials object from a plugin with a specified minimum security + * level. */ GRPCAPI grpc_call_credentials* grpc_metadata_credentials_create_from_plugin( - grpc_metadata_credentials_plugin plugin, void* reserved); + grpc_metadata_credentials_plugin plugin, + grpc_security_level min_security_level, void* reserved); /** --- Secure channel creation. --- */ @@ -653,8 +659,9 @@ GRPCAPI void grpc_alts_credentials_options_destroy( grpc_alts_credentials_options* options); /** - * This method creates an ALTS channel credential object. It is used for - * experimental purpose for now and subject to change. + * This method creates an ALTS channel credential object. The security + * level of the resulting connection is GRPC_PRIVACY_AND_INTEGRITY. + * It is used for experimental purpose for now and subject to change. * * - options: grpc ALTS credentials options instance for client. * @@ -677,8 +684,10 @@ GRPCAPI grpc_server_credentials* grpc_alts_server_credentials_create( /** --- Local channel/server credentials --- **/ /** - * This method creates a local channel credential object. It is used for - * experimental purpose for now and subject to change. + * This method creates a local channel credential object. The security level + * of the resulting connection is GRPC_PRIVACY_AND_INTEGRITY for UDS and + * GRPC_SECURITY_NONE for LOCAL_TCP. It is used for experimental purpose + * for now and subject to change. * * - type: local connection type * @@ -940,7 +949,8 @@ grpc_tls_server_authorization_check_config_create( /** * This method creates a TLS channel credential object. - * It takes ownership of the options parameter. + * It takes ownership of the options parameter. The security level + * of the resulting connection is GRPC_PRIVACY_AND_INTEGRITY. * * - options: grpc TLS credentials options instance. * diff --git a/include/grpc/grpc_security_constants.h b/include/grpc/grpc_security_constants.h index 13d6e9ff8e0..16288a21f46 100644 --- a/include/grpc/grpc_security_constants.h +++ b/include/grpc/grpc_security_constants.h @@ -30,6 +30,7 @@ extern "C" { #define GRPC_X509_SAN_PROPERTY_NAME "x509_subject_alternative_name" #define GRPC_X509_PEM_CERT_PROPERTY_NAME "x509_pem_cert" #define GRPC_SSL_SESSION_REUSED_PROPERTY "ssl_session_reused" +#define GRPC_TRANSPORT_SECURITY_LEVEL_PROPERTY_NAME "security_level" /** Environment variable that points to the default SSL roots file. This file must be a PEM encoded file with all the roots such as the one that can be @@ -105,7 +106,9 @@ typedef enum { GRPC_SSL_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_AND_VERIFY } grpc_ssl_client_certificate_request_type; -/* Security levels of grpc transport security */ +/* Security levels of grpc transport security. It represents an inherent + * property of a backend connection and is determined by a channel credential + * used to create the connection. */ typedef enum { GRPC_SECURITY_MIN, GRPC_SECURITY_NONE = GRPC_SECURITY_MIN, diff --git a/include/grpcpp/security/credentials_impl.h b/include/grpcpp/security/credentials_impl.h index ba26d60d141..08415171d21 100644 --- a/include/grpcpp/security/credentials_impl.h +++ b/include/grpcpp/security/credentials_impl.h @@ -321,6 +321,10 @@ grpc::Status StsCredentialsOptionsFromEnv(StsCredentialsOptions* options); std::shared_ptr StsCredentials( const StsCredentialsOptions& options); +std::shared_ptr MetadataCredentialsFromPlugin( + std::unique_ptr plugin, + grpc_security_level min_security_level); + /// Options used to build AltsCredentials. struct AltsCredentialsOptions { /// service accounts of target endpoint that will be acceptable diff --git a/src/core/lib/security/credentials/composite/composite_credentials.cc b/src/core/lib/security/credentials/composite/composite_credentials.cc index a2c0bebf871..dc19e234101 100644 --- a/src/core/lib/security/credentials/composite/composite_credentials.cc +++ b/src/core/lib/security/credentials/composite/composite_credentials.cc @@ -151,6 +151,13 @@ grpc_composite_call_credentials::grpc_composite_call_credentials( inner_.reserve(size); push_to_inner(std::move(creds1), creds1_is_composite); push_to_inner(std::move(creds2), creds2_is_composite); + min_security_level_ = GRPC_SECURITY_NONE; + for (size_t i = 0; i < inner_.size(); ++i) { + if (static_cast(min_security_level_) < + static_cast(inner_[i]->min_security_level())) { + min_security_level_ = inner_[i]->min_security_level(); + } + } } static grpc_core::RefCountedPtr diff --git a/src/core/lib/security/credentials/composite/composite_credentials.h b/src/core/lib/security/credentials/composite/composite_credentials.h index 0e6e5f9e14f..74d55bbc416 100644 --- a/src/core/lib/security/credentials/composite/composite_credentials.h +++ b/src/core/lib/security/credentials/composite/composite_credentials.h @@ -86,12 +86,16 @@ class grpc_composite_call_credentials : public grpc_call_credentials { void cancel_get_request_metadata(grpc_credentials_mdelem_array* md_array, grpc_error* error) override; + grpc_security_level min_security_level() const override { + return min_security_level_; + } + const CallCredentialsList& inner() const { return inner_; } private: void push_to_inner(grpc_core::RefCountedPtr creds, bool is_composite); - + grpc_security_level min_security_level_; CallCredentialsList inner_; }; diff --git a/src/core/lib/security/credentials/credentials.h b/src/core/lib/security/credentials/credentials.h index 4518f013e1b..65996fd09e4 100644 --- a/src/core/lib/security/credentials/credentials.h +++ b/src/core/lib/security/credentials/credentials.h @@ -225,7 +225,11 @@ void grpc_credentials_mdelem_array_destroy(grpc_credentials_mdelem_array* list); struct grpc_call_credentials : public grpc_core::RefCounted { public: - explicit grpc_call_credentials(const char* type) : type_(type) {} + explicit grpc_call_credentials( + const char* type, + grpc_security_level min_security_level = GRPC_PRIVACY_AND_INTEGRITY) + : type_(type), min_security_level_(min_security_level) {} + virtual ~grpc_call_credentials() = default; // Returns true if completed synchronously, in which case \a error will @@ -244,10 +248,15 @@ struct grpc_call_credentials virtual void cancel_get_request_metadata( grpc_credentials_mdelem_array* md_array, grpc_error* error) = 0; + virtual grpc_security_level min_security_level() const { + return min_security_level_; + } + const char* type() const { return type_; } private: const char* type_; + const grpc_security_level min_security_level_; }; /* Metadata-only credentials with the specified key and value where diff --git a/src/core/lib/security/credentials/fake/fake_credentials.h b/src/core/lib/security/credentials/fake/fake_credentials.h index b7f6a1909fc..411cfa95b54 100644 --- a/src/core/lib/security/credentials/fake/fake_credentials.h +++ b/src/core/lib/security/credentials/fake/fake_credentials.h @@ -59,7 +59,8 @@ class grpc_md_only_test_credentials : public grpc_call_credentials { public: grpc_md_only_test_credentials(const char* md_key, const char* md_value, bool is_async) - : grpc_call_credentials(GRPC_CALL_CREDENTIALS_TYPE_OAUTH2), + : grpc_call_credentials(GRPC_CALL_CREDENTIALS_TYPE_OAUTH2, + GRPC_SECURITY_NONE), md_(grpc_mdelem_from_slices(grpc_slice_from_copied_string(md_key), grpc_slice_from_copied_string(md_value))), is_async_(is_async) {} diff --git a/src/core/lib/security/credentials/plugin/plugin_credentials.cc b/src/core/lib/security/credentials/plugin/plugin_credentials.cc index 517bf277f37..24351bcf42b 100644 --- a/src/core/lib/security/credentials/plugin/plugin_credentials.cc +++ b/src/core/lib/security/credentials/plugin/plugin_credentials.cc @@ -240,15 +240,17 @@ void grpc_plugin_credentials::cancel_get_request_metadata( } grpc_plugin_credentials::grpc_plugin_credentials( - grpc_metadata_credentials_plugin plugin) - : grpc_call_credentials(plugin.type), plugin_(plugin) { + grpc_metadata_credentials_plugin plugin, + grpc_security_level min_security_level) + : grpc_call_credentials(plugin.type, min_security_level), plugin_(plugin) { gpr_mu_init(&mu_); } grpc_call_credentials* grpc_metadata_credentials_create_from_plugin( - grpc_metadata_credentials_plugin plugin, void* reserved) { + grpc_metadata_credentials_plugin plugin, + grpc_security_level min_security_level, void* reserved) { GRPC_API_TRACE("grpc_metadata_credentials_create_from_plugin(reserved=%p)", 1, (reserved)); GPR_ASSERT(reserved == nullptr); - return new grpc_plugin_credentials(plugin); + return new grpc_plugin_credentials(plugin, min_security_level); } diff --git a/src/core/lib/security/credentials/plugin/plugin_credentials.h b/src/core/lib/security/credentials/plugin/plugin_credentials.h index 77a957e5137..fb9a7b71348 100644 --- a/src/core/lib/security/credentials/plugin/plugin_credentials.h +++ b/src/core/lib/security/credentials/plugin/plugin_credentials.h @@ -39,7 +39,8 @@ struct grpc_plugin_credentials final : public grpc_call_credentials { struct pending_request* next; }; - explicit grpc_plugin_credentials(grpc_metadata_credentials_plugin plugin); + explicit grpc_plugin_credentials(grpc_metadata_credentials_plugin plugin, + grpc_security_level min_security_level); ~grpc_plugin_credentials() override; bool get_request_metadata(grpc_polling_entity* pollent, diff --git a/src/core/lib/security/security_connector/alts/alts_security_connector.cc b/src/core/lib/security/security_connector/alts/alts_security_connector.cc index cff80001d12..1274edb6e6e 100644 --- a/src/core/lib/security/security_connector/alts/alts_security_connector.cc +++ b/src/core/lib/security/security_connector/alts/alts_security_connector.cc @@ -178,6 +178,13 @@ grpc_alts_auth_context_from_tsi_peer(const tsi_peer* peer) { gpr_log(GPR_ERROR, "Invalid or missing certificate type property."); return nullptr; } + /* Check if security level exists. */ + const tsi_peer_property* security_level_prop = + tsi_peer_get_property_by_name(peer, TSI_SECURITY_LEVEL_PEER_PROPERTY); + if (security_level_prop == nullptr) { + gpr_log(GPR_ERROR, "Missing security level property."); + return nullptr; + } /* Validate RPC protocol versions. */ const tsi_peer_property* rpc_versions_prop = tsi_peer_get_property_by_name(peer, TSI_ALTS_RPC_VERSIONS); @@ -232,6 +239,12 @@ grpc_alts_auth_context_from_tsi_peer(const tsi_peer* peer) { tsi_prop->value.data, tsi_prop->value.length); } + /* Add security level to auth context. */ + if (strcmp(tsi_prop->name, TSI_SECURITY_LEVEL_PEER_PROPERTY) == 0) { + grpc_auth_context_add_property( + ctx.get(), GRPC_TRANSPORT_SECURITY_LEVEL_PROPERTY_NAME, + tsi_prop->value.data, tsi_prop->value.length); + } } if (!grpc_auth_context_peer_is_authenticated(ctx.get())) { gpr_log(GPR_ERROR, "Invalid unauthenticated peer."); diff --git a/src/core/lib/security/security_connector/fake/fake_security_connector.cc b/src/core/lib/security/security_connector/fake/fake_security_connector.cc index d4203d1dfe8..7a81e68d618 100644 --- a/src/core/lib/security/security_connector/fake/fake_security_connector.cc +++ b/src/core/lib/security/security_connector/fake/fake_security_connector.cc @@ -219,9 +219,9 @@ static void fake_check_peer( const char* prop_name; grpc_error* error = GRPC_ERROR_NONE; *auth_context = nullptr; - if (peer.property_count != 1) { + if (peer.property_count != 2) { error = GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "Fake peers should only have 1 property."); + "Fake peers should only have 2 properties."); goto end; } prop_name = peer.properties[0].name; @@ -240,10 +240,30 @@ static void fake_check_peer( "Invalid value for cert type property."); goto end; } + prop_name = peer.properties[1].name; + if (prop_name == nullptr || + strcmp(prop_name, TSI_SECURITY_LEVEL_PEER_PROPERTY) != 0) { + char* msg; + gpr_asprintf(&msg, "Unexpected property in fake peer: %s.", + prop_name == nullptr ? "" : prop_name); + error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg); + gpr_free(msg); + goto end; + } + if (strncmp(peer.properties[1].value.data, TSI_FAKE_SECURITY_LEVEL, + peer.properties[1].value.length) != 0) { + error = GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Invalid value for security level property."); + goto end; + } + *auth_context = grpc_core::MakeRefCounted(nullptr); grpc_auth_context_add_cstring_property( auth_context->get(), GRPC_TRANSPORT_SECURITY_TYPE_PROPERTY_NAME, GRPC_FAKE_TRANSPORT_SECURITY_TYPE); + grpc_auth_context_add_cstring_property( + auth_context->get(), GRPC_TRANSPORT_SECURITY_LEVEL_PROPERTY_NAME, + TSI_FAKE_SECURITY_LEVEL); end: grpc_core::ExecCtx::Run(DEBUG_LOCATION, on_peer_checked, error); tsi_peer_destruct(&peer); diff --git a/src/core/lib/security/security_connector/local/local_security_connector.cc b/src/core/lib/security/security_connector/local/local_security_connector.cc index 20d0888320b..6fb770d7ade 100644 --- a/src/core/lib/security/security_connector/local/local_security_connector.cc +++ b/src/core/lib/security/security_connector/local/local_security_connector.cc @@ -46,7 +46,8 @@ namespace { -grpc_core::RefCountedPtr local_auth_context_create() { +grpc_core::RefCountedPtr local_auth_context_create( + const tsi_peer* peer) { /* Create auth context. */ grpc_core::RefCountedPtr ctx = grpc_core::MakeRefCounted(nullptr); @@ -55,10 +56,17 @@ grpc_core::RefCountedPtr local_auth_context_create() { GRPC_LOCAL_TRANSPORT_SECURITY_TYPE); GPR_ASSERT(grpc_auth_context_set_peer_identity_property_name( ctx.get(), GRPC_TRANSPORT_SECURITY_TYPE_PROPERTY_NAME) == 1); + GPR_ASSERT(peer->property_count == 1); + const tsi_peer_property* prop = &peer->properties[0]; + GPR_ASSERT(prop != nullptr); + GPR_ASSERT(strcmp(prop->name, TSI_SECURITY_LEVEL_PEER_PROPERTY) == 0); + grpc_auth_context_add_property(ctx.get(), + GRPC_TRANSPORT_SECURITY_LEVEL_PROPERTY_NAME, + prop->value.data, prop->value.length); return ctx; } -void local_check_peer(grpc_security_connector* /*sc*/, tsi_peer /*peer*/, +void local_check_peer(grpc_security_connector* sc, tsi_peer peer, grpc_endpoint* ep, grpc_core::RefCountedPtr* auth_context, grpc_closure* on_peer_checked, @@ -103,12 +111,30 @@ void local_check_peer(grpc_security_connector* /*sc*/, tsi_peer /*peer*/, grpc_core::ExecCtx::Run(DEBUG_LOCATION, on_peer_checked, error); return; } + // Add TSI_SECURITY_LEVEL_PEER_PROPERTY type peer property. + size_t new_property_count = peer.property_count + 1; + tsi_peer_property* new_properties = static_cast( + gpr_zalloc(sizeof(*new_properties) * new_property_count)); + for (size_t i = 0; i < peer.property_count; i++) { + new_properties[i] = peer.properties[i]; + } + if (peer.properties != nullptr) gpr_free(peer.properties); + peer.properties = new_properties; + // TODO(yihuazhang): Set security level of local TCP to TSI_SECURITY_NONE. + const char* security_level = + tsi_security_level_to_string(TSI_PRIVACY_AND_INTEGRITY); + tsi_result result = tsi_construct_string_peer_property_from_cstring( + TSI_SECURITY_LEVEL_PEER_PROPERTY, security_level, + &peer.properties[peer.property_count]); + if (result != TSI_OK) return; + peer.property_count++; /* Create an auth context which is necessary to pass the santiy check in * {client, server}_auth_filter that verifies if the peer's auth context is * obtained during handshakes. The auth context is only checked for its * existence and not actually used. */ - *auth_context = local_auth_context_create(); + *auth_context = local_auth_context_create(&peer); + tsi_peer_destruct(&peer); error = *auth_context != nullptr ? GRPC_ERROR_NONE : GRPC_ERROR_CREATE_FROM_STATIC_STRING( "Could not create local auth context"); diff --git a/src/core/lib/security/security_connector/ssl_utils.cc b/src/core/lib/security/security_connector/ssl_utils.cc index f4bb70bef8d..01ae486fff7 100644 --- a/src/core/lib/security/security_connector/ssl_utils.cc +++ b/src/core/lib/security/security_connector/ssl_utils.cc @@ -84,6 +84,30 @@ const char* grpc_get_ssl_cipher_suites(void) { return cipher_suites; } +grpc_security_level grpc_tsi_security_level_string_to_enum( + const char* security_level) { + if (strcmp(security_level, "TSI_INTEGRITY_ONLY") == 0) { + return GRPC_INTEGRITY_ONLY; + } else if (strcmp(security_level, "TSI_PRIVACY_AND_INTEGRITY") == 0) { + return GRPC_PRIVACY_AND_INTEGRITY; + } + return GRPC_SECURITY_NONE; +} + +const char* grpc_security_level_to_string(grpc_security_level security_level) { + if (security_level == GRPC_PRIVACY_AND_INTEGRITY) { + return "GRPC_PRIVACY_AND_INTEGRITY"; + } else if (security_level == GRPC_INTEGRITY_ONLY) { + return "GRPC_INTEGRITY_ONLY"; + } + return "GRPC_SECURITY_NONE"; +} + +bool grpc_check_security_level(grpc_security_level channel_level, + grpc_security_level call_cred_level) { + return static_cast(channel_level) >= static_cast(call_cred_level); +} + tsi_client_certificate_request_type grpc_get_tsi_client_certificate_request_type( grpc_ssl_client_certificate_request_type grpc_request_type) { @@ -229,6 +253,10 @@ grpc_core::RefCountedPtr grpc_ssl_peer_to_auth_context( grpc_auth_context_add_property(ctx.get(), GRPC_SSL_SESSION_REUSED_PROPERTY, prop->value.data, prop->value.length); + } else if (strcmp(prop->name, TSI_SECURITY_LEVEL_PEER_PROPERTY) == 0) { + grpc_auth_context_add_property( + ctx.get(), GRPC_TRANSPORT_SECURITY_LEVEL_PROPERTY_NAME, + prop->value.data, prop->value.length); } } if (peer_identity_property_name != nullptr) { @@ -272,6 +300,10 @@ tsi_peer grpc_shallow_peer_from_ssl_auth_context( } else if (strcmp(prop->name, GRPC_X509_PEM_CERT_PROPERTY_NAME) == 0) { add_shallow_auth_property_to_peer(&peer, prop, TSI_X509_PEM_CERT_PROPERTY); + } else if (strcmp(prop->name, + GRPC_TRANSPORT_SECURITY_LEVEL_PROPERTY_NAME) == 0) { + add_shallow_auth_property_to_peer(&peer, prop, + TSI_SECURITY_LEVEL_PEER_PROPERTY); } } } diff --git a/src/core/lib/security/security_connector/ssl_utils.h b/src/core/lib/security/security_connector/ssl_utils.h index 6ee2c3c7248..9140db1115f 100644 --- a/src/core/lib/security/security_connector/ssl_utils.h +++ b/src/core/lib/security/security_connector/ssl_utils.h @@ -68,6 +68,17 @@ tsi_client_certificate_request_type grpc_get_tsi_client_certificate_request_type( grpc_ssl_client_certificate_request_type grpc_request_type); +/* Map tsi_security_level string to grpc_security_level enum. */ +grpc_security_level grpc_tsi_security_level_string_to_enum( + const char* security_level); + +/* Map grpc_security_level enum to a string. */ +const char* grpc_security_level_to_string(grpc_security_level security_level); + +/* Check security level of channel and call credential.*/ +bool grpc_check_security_level(grpc_security_level channel_level, + grpc_security_level call_cred_level); + /* Return an array of strings containing alpn protocols. */ const char** grpc_fill_alpn_protocol_strings(size_t* num_alpn_protocols); diff --git a/src/core/lib/security/transport/client_auth_filter.cc b/src/core/lib/security/transport/client_auth_filter.cc index 2ac73375c75..c3658a99d2e 100644 --- a/src/core/lib/security/transport/client_auth_filter.cc +++ b/src/core/lib/security/transport/client_auth_filter.cc @@ -266,6 +266,39 @@ static void send_security_metadata(grpc_call_element* elem, call_creds_has_md ? ctx->creds->Ref() : channel_call_creds->Ref(); } + /* Check security level of call credential and channel, and do not send + * metadata if the check fails. */ + grpc_auth_property_iterator it = grpc_auth_context_find_properties_by_name( + chand->auth_context.get(), GRPC_TRANSPORT_SECURITY_LEVEL_PROPERTY_NAME); + const grpc_auth_property* prop = grpc_auth_property_iterator_next(&it); + if (prop == nullptr) { + grpc_transport_stream_op_batch_finish_with_failure( + batch, + grpc_error_set_int( + GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Established channel does not have an auth property " + "representing a security level."), + GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_UNAUTHENTICATED), + calld->call_combiner); + return; + } + grpc_security_level call_cred_security_level = + calld->creds->min_security_level(); + int is_security_level_ok = grpc_check_security_level( + grpc_tsi_security_level_string_to_enum(prop->value), + call_cred_security_level); + if (!is_security_level_ok) { + grpc_transport_stream_op_batch_finish_with_failure( + batch, + grpc_error_set_int(GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Established channel does not have a sufficient " + "security level to transfer call credential."), + GRPC_ERROR_INT_GRPC_STATUS, + GRPC_STATUS_UNAUTHENTICATED), + calld->call_combiner); + return; + } + grpc_auth_metadata_context_build( chand->security_connector->url_scheme(), calld->host, calld->method, chand->auth_context.get(), &calld->auth_md_context); diff --git a/src/core/tsi/alts/handshaker/alts_tsi_handshaker.cc b/src/core/tsi/alts/handshaker/alts_tsi_handshaker.cc index 6be319e9a85..b24ed1f24ab 100644 --- a/src/core/tsi/alts/handshaker/alts_tsi_handshaker.cc +++ b/src/core/tsi/alts/handshaker/alts_tsi_handshaker.cc @@ -86,7 +86,7 @@ static tsi_result handshaker_result_extract_peer( alts_tsi_handshaker_result* result = reinterpret_cast( const_cast(self)); - GPR_ASSERT(kTsiAltsNumOfPeerProperties == 4); + GPR_ASSERT(kTsiAltsNumOfPeerProperties == 5); tsi_result ok = tsi_construct_peer(kTsiAltsNumOfPeerProperties, peer); int index = 0; if (ok != TSI_OK) { @@ -131,6 +131,16 @@ static tsi_result handshaker_result_extract_peer( tsi_peer_destruct(peer); gpr_log(GPR_ERROR, "Failed to set tsi peer property"); } + index++; + GPR_ASSERT(&peer->properties[index] != nullptr); + ok = tsi_construct_string_peer_property_from_cstring( + TSI_SECURITY_LEVEL_PEER_PROPERTY, + tsi_security_level_to_string(TSI_PRIVACY_AND_INTEGRITY), + &peer->properties[index]); + if (ok != TSI_OK) { + tsi_peer_destruct(peer); + gpr_log(GPR_ERROR, "Failed to set tsi peer property"); + } GPR_ASSERT(++index == kTsiAltsNumOfPeerProperties); return ok; } diff --git a/src/core/tsi/alts/handshaker/alts_tsi_handshaker.h b/src/core/tsi/alts/handshaker/alts_tsi_handshaker.h index 3508aa021e0..5bace9affa8 100644 --- a/src/core/tsi/alts/handshaker/alts_tsi_handshaker.h +++ b/src/core/tsi/alts/handshaker/alts_tsi_handshaker.h @@ -36,7 +36,7 @@ #define TSI_ALTS_RPC_VERSIONS "rpc_versions" #define TSI_ALTS_CONTEXT "alts_context" -const size_t kTsiAltsNumOfPeerProperties = 4; +const size_t kTsiAltsNumOfPeerProperties = 5; typedef struct alts_tsi_handshaker alts_tsi_handshaker; diff --git a/src/core/tsi/fake_transport_security.cc b/src/core/tsi/fake_transport_security.cc index b12ae9b8250..c34aa9c5f04 100644 --- a/src/core/tsi/fake_transport_security.cc +++ b/src/core/tsi/fake_transport_security.cc @@ -495,14 +495,18 @@ typedef struct { } fake_handshaker_result; static tsi_result fake_handshaker_result_extract_peer( - const tsi_handshaker_result* /*self*/, tsi_peer* peer) { - /* Construct a tsi_peer with 1 property: certificate type. */ - tsi_result result = tsi_construct_peer(1, peer); + const tsi_handshaker_result* self, tsi_peer* peer) { + /* Construct a tsi_peer with 1 property: certificate type, security_level. */ + tsi_result result = tsi_construct_peer(2, peer); if (result != TSI_OK) return result; result = tsi_construct_string_peer_property_from_cstring( TSI_CERTIFICATE_TYPE_PEER_PROPERTY, TSI_FAKE_CERTIFICATE_TYPE, &peer->properties[0]); if (result != TSI_OK) tsi_peer_destruct(peer); + result = tsi_construct_string_peer_property_from_cstring( + TSI_SECURITY_LEVEL_PEER_PROPERTY, + tsi_security_level_to_string(TSI_SECURITY_NONE), &peer->properties[1]); + if (result != TSI_OK) tsi_peer_destruct(peer); return result; } diff --git a/src/core/tsi/fake_transport_security.h b/src/core/tsi/fake_transport_security.h index 37791827e17..704d9fb1a12 100644 --- a/src/core/tsi/fake_transport_security.h +++ b/src/core/tsi/fake_transport_security.h @@ -25,6 +25,8 @@ /* Value for the TSI_CERTIFICATE_TYPE_PEER_PROPERTY property for FAKE certs. */ #define TSI_FAKE_CERTIFICATE_TYPE "FAKE" +/* Value of the TSI_SECURITY_LEVEL_PEER_PROPERTY property for FAKE certs. */ +#define TSI_FAKE_SECURITY_LEVEL "TSI_SECURITY_NONE" /* Creates a fake handshaker that will create a fake frame protector. diff --git a/src/core/tsi/ssl_transport_security.cc b/src/core/tsi/ssl_transport_security.cc index 8b6d9f39dd4..8332d5d7ec0 100644 --- a/src/core/tsi/ssl_transport_security.cc +++ b/src/core/tsi/ssl_transport_security.cc @@ -1049,7 +1049,7 @@ static tsi_result ssl_handshaker_result_extract_peer( } // 1 is for session reused property. - size_t new_property_count = peer->property_count + 1; + size_t new_property_count = peer->property_count + 2; if (alpn_selected != nullptr) new_property_count++; tsi_peer_property* new_properties = static_cast( gpr_zalloc(sizeof(*new_properties) * new_property_count)); @@ -1067,6 +1067,13 @@ static tsi_result ssl_handshaker_result_extract_peer( if (result != TSI_OK) return result; peer->property_count++; } + // Add security_level peer property. + result = tsi_construct_string_peer_property_from_cstring( + TSI_SECURITY_LEVEL_PEER_PROPERTY, + tsi_security_level_to_string(TSI_PRIVACY_AND_INTEGRITY), + &peer->properties[peer->property_count]); + if (result != TSI_OK) return result; + peer->property_count++; const char* session_reused = SSL_session_reused(impl->ssl) ? "true" : "false"; result = tsi_construct_string_peer_property_from_cstring( diff --git a/src/core/tsi/transport_security.cc b/src/core/tsi/transport_security.cc index 078a917bbac..035e7cc5454 100644 --- a/src/core/tsi/transport_security.cc +++ b/src/core/tsi/transport_security.cc @@ -67,6 +67,19 @@ const char* tsi_result_to_string(tsi_result result) { } } +const char* tsi_security_level_to_string(tsi_security_level security_level) { + switch (security_level) { + case TSI_SECURITY_NONE: + return "TSI_SECURITY_NONE"; + case TSI_INTEGRITY_ONLY: + return "TSI_INTEGRITY_ONLY"; + case TSI_PRIVACY_AND_INTEGRITY: + return "TSI_PRIVACY_AND_INTEGRITY"; + default: + return "UNKNOWN"; + } +} + /* --- tsi_frame_protector common implementation. --- Calls specific implementation after state/input validation. */ diff --git a/src/core/tsi/transport_security_interface.h b/src/core/tsi/transport_security_interface.h index 7a0cdc3453a..8b4c9299ed4 100644 --- a/src/core/tsi/transport_security_interface.h +++ b/src/core/tsi/transport_security_interface.h @@ -46,6 +46,14 @@ typedef enum { TSI_HANDSHAKE_SHUTDOWN = 14, } tsi_result; +typedef enum { + TSI_SECURITY_MIN, + TSI_SECURITY_NONE = TSI_SECURITY_MIN, + TSI_INTEGRITY_ONLY, + TSI_PRIVACY_AND_INTEGRITY, + TSI_SECURITY_MAX = TSI_PRIVACY_AND_INTEGRITY, +} tsi_security_level; + typedef enum { // Default option TSI_DONT_REQUEST_CLIENT_CERTIFICATE, @@ -56,6 +64,7 @@ typedef enum { } tsi_client_certificate_request_type; const char* tsi_result_to_string(tsi_result result); +const char* tsi_security_level_to_string(tsi_security_level security_level); /* --- tsi tracing --- */ @@ -185,6 +194,9 @@ void tsi_frame_protector_destroy(tsi_frame_protector* self); /* This property is of type TSI_PEER_PROPERTY_STRING. */ #define TSI_CERTIFICATE_TYPE_PEER_PROPERTY "certificate_type" +/* This property represents security level of a channel. */ +#define TSI_SECURITY_LEVEL_PEER_PROPERTY "security_level" + /* Property values may contain NULL characters just like C++ strings. The length field gives the length of the string. */ typedef struct tsi_peer_property { diff --git a/src/cpp/client/secure_credentials.cc b/src/cpp/client/secure_credentials.cc index ca326974f01..0e3d6d24f66 100644 --- a/src/cpp/client/secure_credentials.cc +++ b/src/cpp/client/secure_credentials.cc @@ -256,6 +256,20 @@ std::shared_ptr StsCredentials( return WrapCallCredentials(grpc_sts_credentials_create(&opts, nullptr)); } +std::shared_ptr MetadataCredentialsFromPlugin( + std::unique_ptr plugin, + grpc_security_level min_security_level) { + grpc::GrpcLibraryCodegen init; // To call grpc_init(). + const char* type = plugin->GetType(); + grpc::MetadataCredentialsPluginWrapper* wrapper = + new grpc::MetadataCredentialsPluginWrapper(std::move(plugin)); + grpc_metadata_credentials_plugin c_plugin = { + grpc::MetadataCredentialsPluginWrapper::GetMetadata, + grpc::MetadataCredentialsPluginWrapper::Destroy, wrapper, type}; + return WrapCallCredentials(grpc_metadata_credentials_create_from_plugin( + c_plugin, min_security_level, nullptr)); +} + // Builds ALTS Credentials given ALTS specific options std::shared_ptr AltsCredentials( const AltsCredentialsOptions& options) { @@ -374,8 +388,8 @@ std::shared_ptr MetadataCredentialsFromPlugin( grpc_metadata_credentials_plugin c_plugin = { grpc::MetadataCredentialsPluginWrapper::GetMetadata, grpc::MetadataCredentialsPluginWrapper::Destroy, wrapper, type}; - return WrapCallCredentials( - grpc_metadata_credentials_create_from_plugin(c_plugin, nullptr)); + return WrapCallCredentials(grpc_metadata_credentials_create_from_plugin( + c_plugin, GRPC_PRIVACY_AND_INTEGRITY, nullptr)); } } // namespace grpc_impl diff --git a/src/csharp/ext/grpc_csharp_ext.c b/src/csharp/ext/grpc_csharp_ext.c index d9bf85e02c7..e09ad694328 100644 --- a/src/csharp/ext/grpc_csharp_ext.c +++ b/src/csharp/ext/grpc_csharp_ext.c @@ -1136,7 +1136,11 @@ grpcsharp_metadata_credentials_create_from_plugin(void* callback_tag) { plugin.destroy = grpcsharp_metadata_credentials_destroy_handler; plugin.state = callback_tag; plugin.type = ""; - return grpc_metadata_credentials_create_from_plugin(plugin, NULL); + // TODO(yihuazhang): Expose min_security_level via the C# API so + // that applications can decide what minimum security level their + // plugins require. + return grpc_metadata_credentials_create_from_plugin( + plugin, GRPC_PRIVACY_AND_INTEGRITY, NULL); } /* Auth context */ diff --git a/src/php/ext/grpc/call_credentials.c b/src/php/ext/grpc/call_credentials.c index fabb12a7518..484b9b54bf4 100644 --- a/src/php/ext/grpc/call_credentials.c +++ b/src/php/ext/grpc/call_credentials.c @@ -126,9 +126,10 @@ PHP_METHOD(CallCredentials, createFromPlugin) { plugin.destroy = plugin_destroy_state; plugin.state = (void *)state; plugin.type = ""; - + // TODO(yihuazhang): Expose min_security_level via the PHP API so that + // applications can decide what minimum security level their plugins require. grpc_call_credentials *creds = - grpc_metadata_credentials_create_from_plugin(plugin, NULL); + grpc_metadata_credentials_create_from_plugin(plugin, GRPC_PRIVACY_AND_INTEGRITY, NULL); zval *creds_object = grpc_php_wrap_call_credentials(creds TSRMLS_CC); RETURN_DESTROY_ZVAL(creds_object); } diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/credentials.pyx.pxi b/src/python/grpcio/grpc/_cython/_cygrpc/credentials.pyx.pxi index b2aa7800295..3da31bbb275 100644 --- a/src/python/grpcio/grpc/_cython/_cygrpc/credentials.pyx.pxi +++ b/src/python/grpcio/grpc/_cython/_cygrpc/credentials.pyx.pxi @@ -76,7 +76,9 @@ cdef class MetadataPluginCallCredentials(CallCredentials): c_metadata_plugin.type = self._name cpython.Py_INCREF(self._metadata_plugin) fork_handlers_and_grpc_init() - return grpc_metadata_credentials_create_from_plugin(c_metadata_plugin, NULL) + # TODO(yihuazhang): Expose min_security_level via the Python API so that + # applications can decide what minimum security level their plugins require. + return grpc_metadata_credentials_create_from_plugin(c_metadata_plugin, GRPC_PRIVACY_AND_INTEGRITY, NULL) cdef grpc_call_credentials *_composition(call_credentialses): diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/grpc.pxi b/src/python/grpcio/grpc/_cython/_cygrpc/grpc.pxi index e2117905421..4a26fdc91d9 100644 --- a/src/python/grpcio/grpc/_cython/_cygrpc/grpc.pxi +++ b/src/python/grpcio/grpc/_cython/_cygrpc/grpc.pxi @@ -433,6 +433,13 @@ cdef extern from "grpc/grpc_security.h": GRPC_SSL_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_BUT_DONT_VERIFY GRPC_SSL_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_AND_VERIFY + ctypedef enum grpc_security_level: + GRPC_SECURITY_MIN + GRPC_SECURITY_NONE = GRPC_SECURITY_MIN + GRPC_INTEGRITY_ONLY + GRPC_PRIVACY_AND_INTEGRITY + GRPC_SECURITY_MAX = GRPC_PRIVACY_AND_INTEGRITY + ctypedef enum grpc_ssl_certificate_config_reload_status: GRPC_SSL_CERTIFICATE_CONFIG_RELOAD_UNCHANGED GRPC_SSL_CERTIFICATE_CONFIG_RELOAD_NEW @@ -562,7 +569,7 @@ cdef extern from "grpc/grpc_security.h": const char *type grpc_call_credentials *grpc_metadata_credentials_create_from_plugin( - grpc_metadata_credentials_plugin plugin, void *reserved) nogil + grpc_metadata_credentials_plugin plugin, grpc_security_level min_security_level, void *reserved) nogil ctypedef struct grpc_auth_property_iterator: pass diff --git a/src/python/grpcio_tests/tests/unit/_auth_context_test.py b/src/python/grpcio_tests/tests/unit/_auth_context_test.py index 531c1ee37d5..74269542b28 100644 --- a/src/python/grpcio_tests/tests/unit/_auth_context_test.py +++ b/src/python/grpcio_tests/tests/unit/_auth_context_test.py @@ -103,11 +103,11 @@ class AuthContextTest(unittest.TestCase): auth_data = pickle.loads(response) self.assertIsNone(auth_data[_ID]) self.assertIsNone(auth_data[_ID_KEY]) - self.assertDictEqual( - { - 'transport_security_type': [b'ssl'], - 'ssl_session_reused': [b'false'], - }, auth_data[_AUTH_CTX]) + self.assertDictEqual({ + 'security_level': [b'TSI_PRIVACY_AND_INTEGRITY'], + 'transport_security_type': [b'ssl'], + 'ssl_session_reused': [b'false'], + }, auth_data[_AUTH_CTX]) def testSecureClientCert(self): handler = grpc.method_handlers_generic_handler('test', { diff --git a/src/ruby/ext/grpc/rb_call_credentials.c b/src/ruby/ext/grpc/rb_call_credentials.c index cea9620081f..61170cc73b2 100644 --- a/src/ruby/ext/grpc/rb_call_credentials.c +++ b/src/ruby/ext/grpc/rb_call_credentials.c @@ -229,7 +229,10 @@ static VALUE grpc_rb_call_credentials_init(VALUE self, VALUE proc) { plugin.state = (void*)proc; plugin.type = ""; - creds = grpc_metadata_credentials_create_from_plugin(plugin, NULL); + // TODO(yihuazhang): Expose min_security_level via the Ruby API so that + // applications can decide what minimum security level their plugins require. + creds = grpc_metadata_credentials_create_from_plugin( + plugin, GRPC_PRIVACY_AND_INTEGRITY, NULL); if (creds == NULL) { rb_raise(rb_eRuntimeError, "could not create a credentials, not sure why"); return Qnil; diff --git a/src/ruby/ext/grpc/rb_grpc_imports.generated.h b/src/ruby/ext/grpc/rb_grpc_imports.generated.h index 0205f60d06f..4e8a2d28540 100644 --- a/src/ruby/ext/grpc/rb_grpc_imports.generated.h +++ b/src/ruby/ext/grpc/rb_grpc_imports.generated.h @@ -380,7 +380,7 @@ extern grpc_google_iam_credentials_create_type grpc_google_iam_credentials_creat typedef grpc_call_credentials*(*grpc_sts_credentials_create_type)(const grpc_sts_credentials_options* options, void* reserved); extern grpc_sts_credentials_create_type grpc_sts_credentials_create_import; #define grpc_sts_credentials_create grpc_sts_credentials_create_import -typedef grpc_call_credentials*(*grpc_metadata_credentials_create_from_plugin_type)(grpc_metadata_credentials_plugin plugin, void* reserved); +typedef grpc_call_credentials*(*grpc_metadata_credentials_create_from_plugin_type)(grpc_metadata_credentials_plugin plugin, grpc_security_level min_security_level, void* reserved); extern grpc_metadata_credentials_create_from_plugin_type grpc_metadata_credentials_create_from_plugin_import; #define grpc_metadata_credentials_create_from_plugin grpc_metadata_credentials_create_from_plugin_import typedef grpc_channel*(*grpc_secure_channel_create_type)(grpc_channel_credentials* creds, const char* target, const grpc_channel_args* args, void* reserved); diff --git a/test/core/end2end/end2end_tests.h b/test/core/end2end/end2end_tests.h index a1ebdedea83..6a12ca690e3 100644 --- a/test/core/end2end/end2end_tests.h +++ b/test/core/end2end/end2end_tests.h @@ -34,6 +34,7 @@ typedef struct grpc_end2end_test_config grpc_end2end_test_config; #define FEATURE_MASK_DOES_NOT_SUPPORT_RESOURCE_QUOTA_SERVER 64 #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 FAIL_AUTH_CHECK_SERVER_ARG_NAME "fail_auth_check" diff --git a/test/core/end2end/fixtures/h2_fakesec.cc b/test/core/end2end/fixtures/h2_fakesec.cc index d8d92bea3b5..5dacbe43ba4 100644 --- a/test/core/end2end/fixtures/h2_fakesec.cc +++ b/test/core/end2end/fixtures/h2_fakesec.cc @@ -130,7 +130,8 @@ static grpc_end2end_test_config configs[] = { {"chttp2/fake_secure_fullstack", FEATURE_MASK_SUPPORTS_DELAYED_CONNECTION | FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL | - FEATURE_MASK_SUPPORTS_AUTHORITY_HEADER, + FEATURE_MASK_SUPPORTS_AUTHORITY_HEADER | + FEATURE_MASK_DOES_NOT_SUPPORT_SEND_CALL_CREDENTIALS, nullptr, chttp2_create_fixture_secure_fullstack, chttp2_init_client_fake_secure_fullstack, chttp2_init_server_fake_secure_fullstack, diff --git a/test/core/end2end/tests/call_creds.cc b/test/core/end2end/tests/call_creds.cc index ead6ecb1f74..83802a65ee5 100644 --- a/test/core/end2end/tests/call_creds.cc +++ b/test/core/end2end/tests/call_creds.cc @@ -36,7 +36,7 @@ static const char iam_selector[] = "selector"; static const char overridden_iam_token[] = "overridden_token"; static const char overridden_iam_selector[] = "overridden_selector"; -typedef enum { NONE, OVERRIDE, DESTROY } override_mode; +typedef enum { NONE, OVERRIDE, DESTROY, FAIL } override_mode; static void* tag(intptr_t t) { return (void*)t; } @@ -174,6 +174,7 @@ static void request_response_with_payload_and_call_creds( GPR_ASSERT(grpc_call_set_credentials(c, creds) == GRPC_CALL_OK); break; case DESTROY: + case FAIL: GPR_ASSERT(grpc_call_set_credentials(c, nullptr) == GRPC_CALL_OK); break; } @@ -312,6 +313,7 @@ static void request_response_with_payload_and_call_creds( overridden_iam_selector)); break; case DESTROY: + case FAIL: GPR_ASSERT(!contains_metadata(&request_metadata_recv, GRPC_IAM_AUTHORIZATION_TOKEN_METADATA_KEY, iam_token)); @@ -367,6 +369,13 @@ static void test_request_response_with_payload_and_deleted_call_creds( DESTROY); } +static void test_request_response_with_payload_fail_to_send_call_creds( + grpc_end2end_test_config config) { + request_response_with_payload_and_call_creds( + "test_request_response_with_payload_fail_to_send_call_creds", config, + FAIL); +} + static void test_request_with_server_rejecting_client_creds( grpc_end2end_test_config config) { grpc_op ops[6]; @@ -472,6 +481,10 @@ void call_creds(grpc_end2end_test_config config) { test_request_response_with_payload_and_deleted_call_creds(config); test_request_with_server_rejecting_client_creds(config); } + if (config.feature_mask & + FEATURE_MASK_DOES_NOT_SUPPORT_SEND_CALL_CREDENTIALS) { + test_request_response_with_payload_fail_to_send_call_creds(config); + } } void call_creds_pre_init(void) {} diff --git a/test/core/security/alts_security_connector_test.cc b/test/core/security/alts_security_connector_test.cc index 10b0549ee07..ec1b1d31bb3 100644 --- a/test/core/security/alts_security_connector_test.cc +++ b/test/core/security/alts_security_connector_test.cc @@ -74,6 +74,39 @@ static void test_missing_rpc_protocol_versions_property_failure() { tsi_peer_destruct(&peer); } +static void test_missing_security_level_property_failure() { + tsi_peer peer; + GPR_ASSERT(tsi_construct_peer(kTsiAltsNumOfPeerProperties, &peer) == TSI_OK); + GPR_ASSERT(tsi_construct_string_peer_property_from_cstring( + TSI_CERTIFICATE_TYPE_PEER_PROPERTY, TSI_ALTS_CERTIFICATE_TYPE, + &peer.properties[0]) == TSI_OK); + GPR_ASSERT(tsi_construct_string_peer_property_from_cstring( + TSI_ALTS_SERVICE_ACCOUNT_PEER_PROPERTY, "alice", + &peer.properties[1]) == TSI_OK); + grpc_gcp_rpc_protocol_versions peer_versions; + grpc_gcp_rpc_protocol_versions_set_max(&peer_versions, + GRPC_PROTOCOL_VERSION_MAX_MAJOR, + GRPC_PROTOCOL_VERSION_MAX_MINOR); + grpc_gcp_rpc_protocol_versions_set_min(&peer_versions, + GRPC_PROTOCOL_VERSION_MIN_MAJOR, + GRPC_PROTOCOL_VERSION_MIN_MINOR); + grpc_slice serialized_peer_versions; + GPR_ASSERT(grpc_gcp_rpc_protocol_versions_encode(&peer_versions, + &serialized_peer_versions)); + + GPR_ASSERT(tsi_construct_string_peer_property( + TSI_ALTS_RPC_VERSIONS, + reinterpret_cast( + GRPC_SLICE_START_PTR(serialized_peer_versions)), + GRPC_SLICE_LENGTH(serialized_peer_versions), + &peer.properties[2]) == TSI_OK); + grpc_core::RefCountedPtr ctx = + grpc_alts_auth_context_from_tsi_peer(&peer); + GPR_ASSERT(ctx == nullptr); + grpc_slice_unref(serialized_peer_versions); + tsi_peer_destruct(&peer); +} + static void test_unknown_peer_property_failure() { tsi_peer peer; GPR_ASSERT(tsi_construct_peer(kTsiAltsNumOfPeerProperties, &peer) == TSI_OK); @@ -135,6 +168,10 @@ static void test_alts_peer_to_auth_context_success() { GRPC_SLICE_START_PTR(serialized_peer_versions)), GRPC_SLICE_LENGTH(serialized_peer_versions), &peer.properties[2]) == TSI_OK); + GPR_ASSERT(tsi_construct_string_peer_property_from_cstring( + TSI_SECURITY_LEVEL_PEER_PROPERTY, + tsi_security_level_to_string(TSI_PRIVACY_AND_INTEGRITY), + &peer.properties[3]) == TSI_OK); char test_ctx[] = "test serialized context"; grpc_slice serialized_alts_ctx = grpc_slice_from_copied_string(test_ctx); GPR_ASSERT( @@ -142,7 +179,7 @@ static void test_alts_peer_to_auth_context_success() { TSI_ALTS_CONTEXT, reinterpret_cast(GRPC_SLICE_START_PTR(serialized_alts_ctx)), GRPC_SLICE_LENGTH(serialized_alts_ctx), - &peer.properties[3]) == TSI_OK); + &peer.properties[4]) == TSI_OK); grpc_core::RefCountedPtr ctx = grpc_alts_auth_context_from_tsi_peer(&peer); GPR_ASSERT(ctx != nullptr); @@ -161,6 +198,7 @@ int main(int /*argc*/, char** /*argv*/) { test_empty_peer_property_failure(); test_unknown_peer_property_failure(); test_missing_rpc_protocol_versions_property_failure(); + test_missing_security_level_property_failure(); test_alts_peer_to_auth_context_success(); return 0; diff --git a/test/core/security/credentials_test.cc b/test/core/security/credentials_test.cc index 440009a44b8..d86d3c950f6 100644 --- a/test/core/security/credentials_test.cc +++ b/test/core/security/credentials_test.cc @@ -404,6 +404,8 @@ static void test_google_iam_creds(void) { grpc_call_credentials* creds = grpc_google_iam_credentials_create( test_google_iam_authorization_token, test_google_iam_authority_selector, nullptr); + /* Check security level. */ + GPR_ASSERT(creds->min_security_level() == GRPC_PRIVACY_AND_INTEGRITY); grpc_auth_metadata_context auth_md_ctx = {test_service_url, test_method, nullptr, nullptr}; run_request_metadata_test(creds, auth_md_ctx, state); @@ -420,6 +422,8 @@ static void test_access_token_creds(void) { grpc_auth_metadata_context auth_md_ctx = {test_service_url, test_method, nullptr, nullptr}; GPR_ASSERT(strcmp(creds->type(), GRPC_CALL_CREDENTIALS_TYPE_OAUTH2) == 0); + /* Check security level. */ + GPR_ASSERT(creds->min_security_level() == GRPC_PRIVACY_AND_INTEGRITY); run_request_metadata_test(creds, auth_md_ctx, state); creds->Unref(); } @@ -474,12 +478,20 @@ static void test_oauth2_google_iam_composite_creds(void) { nullptr, nullptr}; grpc_call_credentials* oauth2_creds = grpc_md_only_test_credentials_create( "authorization", test_oauth2_bearer_token, 0); + + /* Check security level of fake credentials. */ + GPR_ASSERT(oauth2_creds->min_security_level() == GRPC_SECURITY_NONE); + grpc_call_credentials* google_iam_creds = grpc_google_iam_credentials_create( test_google_iam_authorization_token, test_google_iam_authority_selector, nullptr); grpc_call_credentials* composite_creds = grpc_composite_call_credentials_create(oauth2_creds, google_iam_creds, nullptr); + /* Check security level of composite credentials. */ + GPR_ASSERT(composite_creds->min_security_level() == + GRPC_PRIVACY_AND_INTEGRITY); + oauth2_creds->Unref(); google_iam_creds->Unref(); GPR_ASSERT(strcmp(composite_creds->type(), @@ -536,6 +548,7 @@ static void test_channel_oauth2_google_iam_composite_creds(void) { grpc_call_credentials* google_iam_creds = grpc_google_iam_credentials_create( test_google_iam_authorization_token, test_google_iam_authority_selector, nullptr); + grpc_channel_credentials* channel_oauth2_iam_creds = grpc_composite_channel_credentials_create(channel_oauth2_creds, google_iam_creds, nullptr); @@ -604,6 +617,8 @@ static void test_compute_engine_creds_success() { grpc_google_compute_engine_credentials_create(nullptr); grpc_auth_metadata_context auth_md_ctx = {test_service_url, test_method, nullptr, nullptr}; + /* Check security level. */ + GPR_ASSERT(creds->min_security_level() == GRPC_PRIVACY_AND_INTEGRITY); /* First request: http get should be called. */ request_metadata_state* state = @@ -695,6 +710,9 @@ static void test_refresh_token_creds_success(void) { grpc_call_credentials* creds = grpc_google_refresh_token_credentials_create( test_refresh_token_str, nullptr); + /* Check security level. */ + GPR_ASSERT(creds->min_security_level() == GRPC_PRIVACY_AND_INTEGRITY); + /* First request: http put should be called. */ request_metadata_state* state = make_request_metadata_state(GRPC_ERROR_NONE, emd, GPR_ARRAY_SIZE(emd)); @@ -924,6 +942,9 @@ static void test_sts_creds_success(void) { grpc_call_credentials* creds = grpc_sts_credentials_create(&valid_options, nullptr); + /* Check security level. */ + GPR_ASSERT(creds->min_security_level() == GRPC_PRIVACY_AND_INTEGRITY); + /* First request: http put should be called. */ request_metadata_state* state = make_request_metadata_state(GRPC_ERROR_NONE, emd, GPR_ARRAY_SIZE(emd)); @@ -1067,6 +1088,8 @@ static void test_jwt_creds_lifetime(void) { json_key_string, grpc_max_auth_token_lifetime(), nullptr); GPR_ASSERT(gpr_time_cmp(creds_as_jwt(jwt_creds)->jwt_lifetime(), grpc_max_auth_token_lifetime()) == 0); + /* Check security level. */ + GPR_ASSERT(jwt_creds->min_security_level() == GRPC_PRIVACY_AND_INTEGRITY); grpc_call_credentials_release(jwt_creds); // Shorter lifetime. @@ -1408,8 +1431,10 @@ static void test_metadata_plugin_success(void) { plugin.get_metadata = plugin_get_metadata_success; plugin.destroy = plugin_destroy; - grpc_call_credentials* creds = - grpc_metadata_credentials_create_from_plugin(plugin, nullptr); + grpc_call_credentials* creds = grpc_metadata_credentials_create_from_plugin( + plugin, GRPC_PRIVACY_AND_INTEGRITY, nullptr); + /* Check security level. */ + GPR_ASSERT(creds->min_security_level() == GRPC_PRIVACY_AND_INTEGRITY); GPR_ASSERT(state == PLUGIN_INITIAL_STATE); run_request_metadata_test(creds, auth_md_ctx, md_state); GPR_ASSERT(state == PLUGIN_GET_METADATA_CALLED_STATE); @@ -1436,8 +1461,8 @@ static void test_metadata_plugin_failure(void) { plugin.get_metadata = plugin_get_metadata_failure; plugin.destroy = plugin_destroy; - grpc_call_credentials* creds = - grpc_metadata_credentials_create_from_plugin(plugin, nullptr); + grpc_call_credentials* creds = grpc_metadata_credentials_create_from_plugin( + plugin, GRPC_PRIVACY_AND_INTEGRITY, nullptr); GPR_ASSERT(state == PLUGIN_INITIAL_STATE); run_request_metadata_test(creds, auth_md_ctx, md_state); GPR_ASSERT(state == PLUGIN_GET_METADATA_CALLED_STATE); diff --git a/test/core/security/security_connector_test.cc b/test/core/security/security_connector_test.cc index a1404a05688..1c483d8b67b 100644 --- a/test/core/security/security_connector_test.cc +++ b/test/core/security/security_connector_test.cc @@ -84,13 +84,38 @@ static int check_ssl_peer_equivalence(const tsi_peer* original, return 1; } +static void test_check_security_level() { + GPR_ASSERT(grpc_check_security_level(GRPC_PRIVACY_AND_INTEGRITY, + GRPC_PRIVACY_AND_INTEGRITY) == true); + GPR_ASSERT(grpc_check_security_level(GRPC_PRIVACY_AND_INTEGRITY, + GRPC_INTEGRITY_ONLY) == true); + GPR_ASSERT(grpc_check_security_level(GRPC_PRIVACY_AND_INTEGRITY, + GRPC_SECURITY_NONE) == true); + GPR_ASSERT(grpc_check_security_level(GRPC_INTEGRITY_ONLY, + GRPC_PRIVACY_AND_INTEGRITY) == false); + GPR_ASSERT(grpc_check_security_level(GRPC_INTEGRITY_ONLY, + GRPC_INTEGRITY_ONLY) == true); + GPR_ASSERT(grpc_check_security_level(GRPC_INTEGRITY_ONLY, + GRPC_SECURITY_NONE) == true); + GPR_ASSERT(grpc_check_security_level(GRPC_SECURITY_NONE, + GRPC_PRIVACY_AND_INTEGRITY) == false); + GPR_ASSERT(grpc_check_security_level(GRPC_SECURITY_NONE, + GRPC_INTEGRITY_ONLY) == false); + GPR_ASSERT(grpc_check_security_level(GRPC_SECURITY_NONE, + GRPC_SECURITY_NONE) == true); +} + static void test_unauthenticated_ssl_peer(void) { tsi_peer peer; tsi_peer rpeer; - GPR_ASSERT(tsi_construct_peer(1, &peer) == TSI_OK); + GPR_ASSERT(tsi_construct_peer(2, &peer) == TSI_OK); GPR_ASSERT(tsi_construct_string_peer_property_from_cstring( TSI_CERTIFICATE_TYPE_PEER_PROPERTY, TSI_X509_CERTIFICATE_TYPE, &peer.properties[0]) == TSI_OK); + GPR_ASSERT(tsi_construct_string_peer_property_from_cstring( + TSI_SECURITY_LEVEL_PEER_PROPERTY, + tsi_security_level_to_string(TSI_PRIVACY_AND_INTEGRITY), + &peer.properties[1]) == TSI_OK); grpc_core::RefCountedPtr ctx = grpc_ssl_peer_to_auth_context(&peer, GRPC_SSL_TRANSPORT_SECURITY_TYPE); GPR_ASSERT(ctx != nullptr); @@ -181,7 +206,7 @@ static void test_cn_only_ssl_peer_to_auth_context(void) { tsi_peer rpeer; const char* expected_cn = "cn1"; const char* expected_pem_cert = "pem_cert1"; - GPR_ASSERT(tsi_construct_peer(3, &peer) == TSI_OK); + GPR_ASSERT(tsi_construct_peer(4, &peer) == TSI_OK); GPR_ASSERT(tsi_construct_string_peer_property_from_cstring( TSI_CERTIFICATE_TYPE_PEER_PROPERTY, TSI_X509_CERTIFICATE_TYPE, &peer.properties[0]) == TSI_OK); @@ -191,6 +216,11 @@ static void test_cn_only_ssl_peer_to_auth_context(void) { GPR_ASSERT(tsi_construct_string_peer_property_from_cstring( TSI_X509_PEM_CERT_PROPERTY, expected_pem_cert, &peer.properties[2]) == TSI_OK); + GPR_ASSERT(tsi_construct_string_peer_property_from_cstring( + TSI_SECURITY_LEVEL_PEER_PROPERTY, + tsi_security_level_to_string(TSI_PRIVACY_AND_INTEGRITY), + &peer.properties[3]) == TSI_OK); + grpc_core::RefCountedPtr ctx = grpc_ssl_peer_to_auth_context(&peer, GRPC_SSL_TRANSPORT_SECURITY_TYPE); GPR_ASSERT(ctx != nullptr); @@ -215,7 +245,7 @@ static void test_cn_and_one_san_ssl_peer_to_auth_context(void) { const char* expected_cn = "cn1"; const char* expected_san = "san1"; const char* expected_pem_cert = "pem_cert1"; - GPR_ASSERT(tsi_construct_peer(4, &peer) == TSI_OK); + GPR_ASSERT(tsi_construct_peer(5, &peer) == TSI_OK); GPR_ASSERT(tsi_construct_string_peer_property_from_cstring( TSI_CERTIFICATE_TYPE_PEER_PROPERTY, TSI_X509_CERTIFICATE_TYPE, &peer.properties[0]) == TSI_OK); @@ -228,7 +258,10 @@ static void test_cn_and_one_san_ssl_peer_to_auth_context(void) { GPR_ASSERT(tsi_construct_string_peer_property_from_cstring( TSI_X509_PEM_CERT_PROPERTY, expected_pem_cert, &peer.properties[3]) == TSI_OK); - + GPR_ASSERT(tsi_construct_string_peer_property_from_cstring( + TSI_SECURITY_LEVEL_PEER_PROPERTY, + tsi_security_level_to_string(TSI_PRIVACY_AND_INTEGRITY), + &peer.properties[4]) == TSI_OK); grpc_core::RefCountedPtr ctx = grpc_ssl_peer_to_auth_context(&peer, GRPC_SSL_TRANSPORT_SECURITY_TYPE); GPR_ASSERT(ctx != nullptr); @@ -254,7 +287,7 @@ static void test_cn_and_multiple_sans_ssl_peer_to_auth_context(void) { const char* expected_sans[] = {"san1", "san2", "san3"}; const char* expected_pem_cert = "pem_cert1"; size_t i; - GPR_ASSERT(tsi_construct_peer(3 + GPR_ARRAY_SIZE(expected_sans), &peer) == + GPR_ASSERT(tsi_construct_peer(4 + GPR_ARRAY_SIZE(expected_sans), &peer) == TSI_OK); GPR_ASSERT(tsi_construct_string_peer_property_from_cstring( TSI_CERTIFICATE_TYPE_PEER_PROPERTY, TSI_X509_CERTIFICATE_TYPE, @@ -265,10 +298,14 @@ static void test_cn_and_multiple_sans_ssl_peer_to_auth_context(void) { GPR_ASSERT(tsi_construct_string_peer_property_from_cstring( TSI_X509_PEM_CERT_PROPERTY, expected_pem_cert, &peer.properties[2]) == TSI_OK); + GPR_ASSERT(tsi_construct_string_peer_property_from_cstring( + TSI_SECURITY_LEVEL_PEER_PROPERTY, + tsi_security_level_to_string(TSI_PRIVACY_AND_INTEGRITY), + &peer.properties[3]) == TSI_OK); for (i = 0; i < GPR_ARRAY_SIZE(expected_sans); i++) { GPR_ASSERT(tsi_construct_string_peer_property_from_cstring( TSI_X509_SUBJECT_ALTERNATIVE_NAME_PEER_PROPERTY, - expected_sans[i], &peer.properties[3 + i]) == TSI_OK); + expected_sans[i], &peer.properties[4 + i]) == TSI_OK); } grpc_core::RefCountedPtr ctx = grpc_ssl_peer_to_auth_context(&peer, GRPC_SSL_TRANSPORT_SECURITY_TYPE); @@ -296,7 +333,7 @@ static void test_cn_and_multiple_sans_and_others_ssl_peer_to_auth_context( const char* expected_pem_cert = "pem_cert1"; const char* expected_sans[] = {"san1", "san2", "san3"}; size_t i; - GPR_ASSERT(tsi_construct_peer(5 + GPR_ARRAY_SIZE(expected_sans), &peer) == + GPR_ASSERT(tsi_construct_peer(6 + GPR_ARRAY_SIZE(expected_sans), &peer) == TSI_OK); GPR_ASSERT(tsi_construct_string_peer_property_from_cstring( TSI_CERTIFICATE_TYPE_PEER_PROPERTY, TSI_X509_CERTIFICATE_TYPE, @@ -311,10 +348,14 @@ static void test_cn_and_multiple_sans_and_others_ssl_peer_to_auth_context( GPR_ASSERT(tsi_construct_string_peer_property_from_cstring( TSI_X509_PEM_CERT_PROPERTY, expected_pem_cert, &peer.properties[4]) == TSI_OK); + GPR_ASSERT(tsi_construct_string_peer_property_from_cstring( + TSI_SECURITY_LEVEL_PEER_PROPERTY, + tsi_security_level_to_string(TSI_PRIVACY_AND_INTEGRITY), + &peer.properties[5]) == TSI_OK); for (i = 0; i < GPR_ARRAY_SIZE(expected_sans); i++) { GPR_ASSERT(tsi_construct_string_peer_property_from_cstring( TSI_X509_SUBJECT_ALTERNATIVE_NAME_PEER_PROPERTY, - expected_sans[i], &peer.properties[5 + i]) == TSI_OK); + expected_sans[i], &peer.properties[6 + i]) == TSI_OK); } grpc_core::RefCountedPtr ctx = grpc_ssl_peer_to_auth_context(&peer, GRPC_SSL_TRANSPORT_SECURITY_TYPE); @@ -485,6 +526,7 @@ int main(int argc, char** argv) { test_ipv6_address_san(); test_default_ssl_roots(); test_peer_alpn_check(); + test_check_security_level(); grpc_shutdown(); return 0; } diff --git a/test/core/tsi/alts/handshaker/alts_tsi_handshaker_test.cc b/test/core/tsi/alts/handshaker/alts_tsi_handshaker_test.cc index 5391877f46f..d270be3b4c0 100644 --- a/test/core/tsi/alts/handshaker/alts_tsi_handshaker_test.cc +++ b/test/core/tsi/alts/handshaker/alts_tsi_handshaker_test.cc @@ -35,6 +35,7 @@ #define ALTS_TSI_HANDSHAKER_TEST_CONSUMED_BYTES "Hello " #define ALTS_TSI_HANDSHAKER_TEST_REMAIN_BYTES "Google" #define ALTS_TSI_HANDSHAKER_TEST_PEER_IDENTITY "chapi@service.google.com" +#define ALTS_TSI_HANDSHAKER_TEST_SECURITY_LEVEL "TSI_PRIVACY_AND_INTEGRITY" #define ALTS_TSI_HANDSHAKER_TEST_KEY_DATA \ "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKL" #define ALTS_TSI_HANDSHAKER_TEST_BUFFER_SIZE 100 @@ -309,6 +310,10 @@ static void on_client_next_success_cb(tsi_result status, void* user_data, peer_account.size) == 0); GPR_ASSERT(memcmp(ALTS_TSI_HANDSHAKER_TEST_LOCAL_IDENTITY, local_account.data, local_account.size) == 0); + /* Validate security level. */ + GPR_ASSERT(memcmp(ALTS_TSI_HANDSHAKER_TEST_SECURITY_LEVEL, + peer.properties[4].value.data, + peer.properties[4].value.length) == 0); tsi_peer_destruct(&peer); /* Validate unused bytes. */ const unsigned char* bytes = nullptr; @@ -365,6 +370,11 @@ static void on_server_next_success_cb(tsi_result status, void* user_data, peer_account.size) == 0); GPR_ASSERT(memcmp(ALTS_TSI_HANDSHAKER_TEST_LOCAL_IDENTITY, local_account.data, local_account.size) == 0); + /* Check security level. */ + GPR_ASSERT(memcmp(ALTS_TSI_HANDSHAKER_TEST_SECURITY_LEVEL, + peer.properties[4].value.data, + peer.properties[4].value.length) == 0); + tsi_peer_destruct(&peer); /* Validate unused bytes. */ const unsigned char* bytes = nullptr; diff --git a/test/core/tsi/fake_transport_security_test.cc b/test/core/tsi/fake_transport_security_test.cc index 6ce53aa44f5..ac491ab07f7 100644 --- a/test/core/tsi/fake_transport_security_test.cc +++ b/test/core/tsi/fake_transport_security_test.cc @@ -50,6 +50,11 @@ static void validate_handshaker_peers(tsi_handshaker_result* result) { GPR_ASSERT(property != nullptr); GPR_ASSERT(memcmp(property->value.data, TSI_FAKE_CERTIFICATE_TYPE, property->value.length) == 0); + property = + tsi_peer_get_property_by_name(&peer, TSI_SECURITY_LEVEL_PEER_PROPERTY); + GPR_ASSERT(property != nullptr); + GPR_ASSERT(memcmp(property->value.data, TSI_FAKE_SECURITY_LEVEL, + property->value.length) == 0); tsi_peer_destruct(&peer); } diff --git a/test/core/tsi/ssl_transport_security_test.cc b/test/core/tsi/ssl_transport_security_test.cc index 8a5dd6e4a0a..f9e0be42161 100644 --- a/test/core/tsi/ssl_transport_security_test.cc +++ b/test/core/tsi/ssl_transport_security_test.cc @@ -187,6 +187,15 @@ static void check_alpn(ssl_tsi_test_fixture* ssl_fixture, } } +static void check_security_level(const tsi_peer* peer) { + const tsi_peer_property* security_level = + tsi_peer_get_property_by_name(peer, TSI_SECURITY_LEVEL_PEER_PROPERTY); + GPR_ASSERT(security_level != nullptr); + const char* expected_match = "TSI_PRIVACY_AND_INTEGRITY"; + GPR_ASSERT(memcmp(security_level->value.data, expected_match, + security_level->value.length) == 0); +} + static const tsi_peer_property* check_basic_authenticated_peer_and_get_common_name(const tsi_peer* peer) { const tsi_peer_property* cert_type_property = @@ -271,7 +280,7 @@ static void check_client_peer(ssl_tsi_test_fixture* ssl_fixture, ssl_alpn_lib* alpn_lib = ssl_fixture->alpn_lib; if (!ssl_fixture->force_client_auth) { GPR_ASSERT(peer->property_count == - (alpn_lib->alpn_mode == ALPN_CLIENT_SERVER_OK ? 2 : 1)); + (alpn_lib->alpn_mode == ALPN_CLIENT_SERVER_OK ? 3 : 2)); } else { const tsi_peer_property* property = check_basic_authenticated_peer_and_get_common_name(peer); @@ -297,6 +306,7 @@ static void ssl_test_check_handshaker_peers(tsi_test_fixture* fixture) { ssl_fixture->base.client_result, &peer) == TSI_OK); check_session_reusage(ssl_fixture, &peer); check_alpn(ssl_fixture, &peer); + check_security_level(&peer); if (ssl_fixture->server_name_indication != nullptr) { check_server1_peer(&peer); } else { @@ -310,6 +320,7 @@ static void ssl_test_check_handshaker_peers(tsi_test_fixture* fixture) { ssl_fixture->base.server_result, &peer) == TSI_OK); check_session_reusage(ssl_fixture, &peer); check_alpn(ssl_fixture, &peer); + check_security_level(&peer); check_client_peer(ssl_fixture, &peer); } else { GPR_ASSERT(ssl_fixture->base.server_result == nullptr); @@ -825,7 +836,8 @@ void ssl_tsi_test_extract_x509_subject_names() { tsi_peer peer; GPR_ASSERT(tsi_ssl_extract_x509_subject_names_from_pem_cert(cert, &peer) == TSI_OK); - // One for common name, one for certificate, and six for SAN fields. + // One for common name, one for certificate, one for security level, and six + // for SAN fields. size_t expected_property_count = 8; GPR_ASSERT(peer.property_count == expected_property_count); // Check common name