| /* |
| * |
| * Copyright 2015 gRPC authors. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| * |
| */ |
| |
| #include "src/core/lib/security/security_connector/security_connector.h" |
| |
| #include <grpc/grpc_security.h> |
| #include <grpc/support/alloc.h> |
| #include <grpc/support/log.h> |
| #include <grpc/support/string_util.h> |
| #include <stdio.h> |
| #include <string.h> |
| |
| #include "src/core/lib/gpr/string.h" |
| #include "src/core/lib/gpr/tmpfile.h" |
| #include "src/core/lib/gprpp/ref_counted_ptr.h" |
| #include "src/core/lib/security/context/security_context.h" |
| #include "src/core/lib/security/security_connector/ssl_utils.h" |
| #include "src/core/lib/slice/slice_string_helpers.h" |
| #include "src/core/tsi/ssl_transport_security.h" |
| #include "src/core/tsi/transport_security.h" |
| #include "test/core/util/test_config.h" |
| |
| #ifndef TSI_OPENSSL_ALPN_SUPPORT |
| #define TSI_OPENSSL_ALPN_SUPPORT 1 |
| #endif |
| |
| static int check_transport_security_type(const grpc_auth_context* ctx) { |
| grpc_auth_property_iterator it = grpc_auth_context_find_properties_by_name( |
| ctx, GRPC_TRANSPORT_SECURITY_TYPE_PROPERTY_NAME); |
| const grpc_auth_property* prop = grpc_auth_property_iterator_next(&it); |
| if (prop == nullptr) return 0; |
| if (strncmp(prop->value, GRPC_SSL_TRANSPORT_SECURITY_TYPE, |
| prop->value_length) != 0) { |
| return 0; |
| } |
| /* Check that we have only one property with this name. */ |
| if (grpc_auth_property_iterator_next(&it) != nullptr) return 0; |
| return 1; |
| } |
| |
| static int check_peer_property(const tsi_peer* peer, |
| const tsi_peer_property* expected) { |
| size_t i; |
| for (i = 0; i < peer->property_count; i++) { |
| const tsi_peer_property* prop = &peer->properties[i]; |
| if ((strcmp(prop->name, expected->name) == 0) && |
| (prop->value.length == expected->value.length) && |
| (memcmp(prop->value.data, expected->value.data, |
| expected->value.length) == 0)) { |
| return 1; |
| } |
| } |
| return 0; /* Not found... */ |
| } |
| |
| static int check_ssl_peer_equivalence(const tsi_peer* original, |
| const tsi_peer* reconstructed) { |
| /* The reconstructed peer only has CN, SAN and pem cert properties. */ |
| size_t i; |
| for (i = 0; i < original->property_count; i++) { |
| const tsi_peer_property* prop = &original->properties[i]; |
| if ((strcmp(prop->name, TSI_X509_SUBJECT_COMMON_NAME_PEER_PROPERTY) == 0) || |
| (strcmp(prop->name, TSI_X509_SUBJECT_ALTERNATIVE_NAME_PEER_PROPERTY) == |
| 0) || |
| (strcmp(prop->name, TSI_X509_PEM_CERT_PROPERTY) == 0)) { |
| if (!check_peer_property(reconstructed, prop)) return 0; |
| } |
| } |
| 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(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<grpc_auth_context> ctx = |
| grpc_ssl_peer_to_auth_context(&peer, GRPC_SSL_TRANSPORT_SECURITY_TYPE); |
| GPR_ASSERT(ctx != nullptr); |
| GPR_ASSERT(!grpc_auth_context_peer_is_authenticated(ctx.get())); |
| GPR_ASSERT(check_transport_security_type(ctx.get())); |
| |
| rpeer = grpc_shallow_peer_from_ssl_auth_context(ctx.get()); |
| GPR_ASSERT(check_ssl_peer_equivalence(&peer, &rpeer)); |
| |
| grpc_shallow_peer_destruct(&rpeer); |
| tsi_peer_destruct(&peer); |
| ctx.reset(DEBUG_LOCATION, "test"); |
| } |
| |
| static int check_identity(const grpc_auth_context* ctx, |
| const char* expected_property_name, |
| const char** expected_identities, |
| size_t num_identities) { |
| grpc_auth_property_iterator it; |
| const grpc_auth_property* prop; |
| size_t i; |
| GPR_ASSERT(grpc_auth_context_peer_is_authenticated(ctx)); |
| it = grpc_auth_context_peer_identity(ctx); |
| for (i = 0; i < num_identities; i++) { |
| prop = grpc_auth_property_iterator_next(&it); |
| if (prop == nullptr) { |
| gpr_log(GPR_ERROR, "Expected identity value %s not found.", |
| expected_identities[i]); |
| return 0; |
| } |
| if (strcmp(prop->name, expected_property_name) != 0) { |
| gpr_log(GPR_ERROR, "Expected peer identity property name %s and got %s.", |
| expected_property_name, prop->name); |
| return 0; |
| } |
| if (strncmp(prop->value, expected_identities[i], prop->value_length) != 0) { |
| gpr_log(GPR_ERROR, "Expected peer identity %s and got %s.", |
| expected_identities[i], prop->value); |
| return 0; |
| } |
| } |
| return 1; |
| } |
| |
| static int check_x509_cn(const grpc_auth_context* ctx, |
| const char* expected_cn) { |
| grpc_auth_property_iterator it = grpc_auth_context_find_properties_by_name( |
| ctx, GRPC_X509_CN_PROPERTY_NAME); |
| const grpc_auth_property* prop = grpc_auth_property_iterator_next(&it); |
| if (prop == nullptr) { |
| gpr_log(GPR_ERROR, "CN property not found."); |
| return 0; |
| } |
| if (strncmp(prop->value, expected_cn, prop->value_length) != 0) { |
| gpr_log(GPR_ERROR, "Expected CN %s and got %s", expected_cn, prop->value); |
| return 0; |
| } |
| if (grpc_auth_property_iterator_next(&it) != nullptr) { |
| gpr_log(GPR_ERROR, "Expected only one property for CN."); |
| return 0; |
| } |
| return 1; |
| } |
| |
| static int check_x509_pem_cert(const grpc_auth_context* ctx, |
| const char* expected_pem_cert) { |
| grpc_auth_property_iterator it = grpc_auth_context_find_properties_by_name( |
| ctx, GRPC_X509_PEM_CERT_PROPERTY_NAME); |
| const grpc_auth_property* prop = grpc_auth_property_iterator_next(&it); |
| if (prop == nullptr) { |
| gpr_log(GPR_ERROR, "Pem certificate property not found."); |
| return 0; |
| } |
| if (strncmp(prop->value, expected_pem_cert, prop->value_length) != 0) { |
| gpr_log(GPR_ERROR, "Expected pem cert %s and got %s", expected_pem_cert, |
| prop->value); |
| return 0; |
| } |
| if (grpc_auth_property_iterator_next(&it) != nullptr) { |
| gpr_log(GPR_ERROR, "Expected only one property for pem cert."); |
| return 0; |
| } |
| return 1; |
| } |
| |
| static int check_x509_pem_cert_chain(const grpc_auth_context* ctx, |
| const char* expected_pem_cert_chain) { |
| grpc_auth_property_iterator it = grpc_auth_context_find_properties_by_name( |
| ctx, GRPC_X509_PEM_CERT_CHAIN_PROPERTY_NAME); |
| const grpc_auth_property* prop = grpc_auth_property_iterator_next(&it); |
| if (prop == nullptr) { |
| gpr_log(GPR_ERROR, "Pem certificate chain property not found."); |
| return 0; |
| } |
| if (strncmp(prop->value, expected_pem_cert_chain, prop->value_length) != 0) { |
| gpr_log(GPR_ERROR, "Expected pem cert chain %s and got %s", |
| expected_pem_cert_chain, prop->value); |
| return 0; |
| } |
| if (grpc_auth_property_iterator_next(&it) != nullptr) { |
| gpr_log(GPR_ERROR, "Expected only one property for pem cert chain."); |
| return 0; |
| } |
| return 1; |
| } |
| |
| static int check_spiffe_id(const grpc_auth_context* ctx, |
| const char* expected_spiffe_id, |
| bool expect_spiffe_id) { |
| grpc_auth_property_iterator it = grpc_auth_context_find_properties_by_name( |
| ctx, GRPC_PEER_SPIFFE_ID_PROPERTY_NAME); |
| const grpc_auth_property* prop = grpc_auth_property_iterator_next(&it); |
| if (prop == nullptr && !expect_spiffe_id) { |
| return 1; |
| } |
| if (prop != nullptr && !expect_spiffe_id) { |
| gpr_log(GPR_ERROR, "SPIFFE ID not expected, but got %s.", prop->value); |
| return 0; |
| } |
| if (prop == nullptr && expect_spiffe_id) { |
| gpr_log(GPR_ERROR, "SPIFFE ID expected, but got nullptr."); |
| return 0; |
| } |
| if (strncmp(prop->value, expected_spiffe_id, prop->value_length) != 0) { |
| gpr_log(GPR_ERROR, "Expected SPIFFE ID %s but got %s.", expected_spiffe_id, |
| prop->value); |
| return 0; |
| } |
| if (grpc_auth_property_iterator_next(&it) != nullptr) { |
| gpr_log(GPR_ERROR, "Expected only one property for SPIFFE ID."); |
| return 0; |
| } |
| return 1; |
| } |
| |
| static void test_cn_only_ssl_peer_to_auth_context(void) { |
| tsi_peer peer; |
| tsi_peer rpeer; |
| const char* expected_cn = "cn1"; |
| const char* expected_pem_cert = "pem_cert1"; |
| const char* expected_pem_cert_chain = "pem_cert1_chain"; |
| 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); |
| GPR_ASSERT(tsi_construct_string_peer_property_from_cstring( |
| TSI_X509_SUBJECT_COMMON_NAME_PEER_PROPERTY, expected_cn, |
| &peer.properties[1]) == TSI_OK); |
| 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); |
| GPR_ASSERT(tsi_construct_string_peer_property_from_cstring( |
| TSI_X509_PEM_CERT_CHAIN_PROPERTY, expected_pem_cert_chain, |
| &peer.properties[4]) == TSI_OK); |
| grpc_core::RefCountedPtr<grpc_auth_context> ctx = |
| grpc_ssl_peer_to_auth_context(&peer, GRPC_SSL_TRANSPORT_SECURITY_TYPE); |
| GPR_ASSERT(ctx != nullptr); |
| GPR_ASSERT(grpc_auth_context_peer_is_authenticated(ctx.get())); |
| GPR_ASSERT( |
| check_identity(ctx.get(), GRPC_X509_CN_PROPERTY_NAME, &expected_cn, 1)); |
| GPR_ASSERT(check_transport_security_type(ctx.get())); |
| GPR_ASSERT(check_x509_cn(ctx.get(), expected_cn)); |
| GPR_ASSERT(check_x509_pem_cert(ctx.get(), expected_pem_cert)); |
| GPR_ASSERT(check_x509_pem_cert_chain(ctx.get(), expected_pem_cert_chain)); |
| |
| rpeer = grpc_shallow_peer_from_ssl_auth_context(ctx.get()); |
| GPR_ASSERT(check_ssl_peer_equivalence(&peer, &rpeer)); |
| |
| grpc_shallow_peer_destruct(&rpeer); |
| tsi_peer_destruct(&peer); |
| ctx.reset(DEBUG_LOCATION, "test"); |
| } |
| |
| static void test_cn_and_one_san_ssl_peer_to_auth_context(void) { |
| tsi_peer peer; |
| tsi_peer rpeer; |
| const char* expected_cn = "cn1"; |
| const char* expected_san = "san1"; |
| const char* expected_pem_cert = "pem_cert1"; |
| const char* expected_pem_cert_chain = "pem_cert1_chain"; |
| GPR_ASSERT(tsi_construct_peer(6, &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_X509_SUBJECT_COMMON_NAME_PEER_PROPERTY, expected_cn, |
| &peer.properties[1]) == TSI_OK); |
| GPR_ASSERT(tsi_construct_string_peer_property_from_cstring( |
| TSI_X509_SUBJECT_ALTERNATIVE_NAME_PEER_PROPERTY, expected_san, |
| &peer.properties[2]) == TSI_OK); |
| 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); |
| GPR_ASSERT(tsi_construct_string_peer_property_from_cstring( |
| TSI_X509_PEM_CERT_CHAIN_PROPERTY, expected_pem_cert_chain, |
| &peer.properties[5]) == TSI_OK); |
| grpc_core::RefCountedPtr<grpc_auth_context> ctx = |
| grpc_ssl_peer_to_auth_context(&peer, GRPC_SSL_TRANSPORT_SECURITY_TYPE); |
| GPR_ASSERT(ctx != nullptr); |
| GPR_ASSERT(grpc_auth_context_peer_is_authenticated(ctx.get())); |
| GPR_ASSERT( |
| check_identity(ctx.get(), GRPC_X509_SAN_PROPERTY_NAME, &expected_san, 1)); |
| GPR_ASSERT(check_transport_security_type(ctx.get())); |
| GPR_ASSERT(check_x509_cn(ctx.get(), expected_cn)); |
| GPR_ASSERT(check_x509_pem_cert(ctx.get(), expected_pem_cert)); |
| GPR_ASSERT(check_x509_pem_cert_chain(ctx.get(), expected_pem_cert_chain)); |
| |
| rpeer = grpc_shallow_peer_from_ssl_auth_context(ctx.get()); |
| GPR_ASSERT(check_ssl_peer_equivalence(&peer, &rpeer)); |
| |
| grpc_shallow_peer_destruct(&rpeer); |
| tsi_peer_destruct(&peer); |
| ctx.reset(DEBUG_LOCATION, "test"); |
| } |
| |
| static void test_cn_and_multiple_sans_ssl_peer_to_auth_context(void) { |
| tsi_peer peer; |
| tsi_peer rpeer; |
| const char* expected_cn = "cn1"; |
| const char* expected_sans[] = {"san1", "san2", "san3"}; |
| const char* expected_pem_cert = "pem_cert1"; |
| const char* expected_pem_cert_chain = "pem_cert1_chain"; |
| size_t i; |
| GPR_ASSERT(tsi_construct_peer(5 + 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, |
| &peer.properties[0]) == TSI_OK); |
| GPR_ASSERT(tsi_construct_string_peer_property_from_cstring( |
| TSI_X509_SUBJECT_COMMON_NAME_PEER_PROPERTY, expected_cn, |
| &peer.properties[1]) == TSI_OK); |
| 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); |
| GPR_ASSERT(tsi_construct_string_peer_property_from_cstring( |
| TSI_X509_PEM_CERT_CHAIN_PROPERTY, expected_pem_cert_chain, |
| &peer.properties[4]) == 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); |
| } |
| grpc_core::RefCountedPtr<grpc_auth_context> ctx = |
| grpc_ssl_peer_to_auth_context(&peer, GRPC_SSL_TRANSPORT_SECURITY_TYPE); |
| GPR_ASSERT(ctx != nullptr); |
| GPR_ASSERT(grpc_auth_context_peer_is_authenticated(ctx.get())); |
| GPR_ASSERT(check_identity(ctx.get(), GRPC_X509_SAN_PROPERTY_NAME, |
| expected_sans, GPR_ARRAY_SIZE(expected_sans))); |
| GPR_ASSERT(check_transport_security_type(ctx.get())); |
| GPR_ASSERT(check_x509_cn(ctx.get(), expected_cn)); |
| GPR_ASSERT(check_x509_pem_cert(ctx.get(), expected_pem_cert)); |
| GPR_ASSERT(check_x509_pem_cert_chain(ctx.get(), expected_pem_cert_chain)); |
| |
| rpeer = grpc_shallow_peer_from_ssl_auth_context(ctx.get()); |
| GPR_ASSERT(check_ssl_peer_equivalence(&peer, &rpeer)); |
| |
| grpc_shallow_peer_destruct(&rpeer); |
| tsi_peer_destruct(&peer); |
| ctx.reset(DEBUG_LOCATION, "test"); |
| } |
| |
| static void test_cn_and_multiple_sans_and_others_ssl_peer_to_auth_context( |
| void) { |
| tsi_peer peer; |
| tsi_peer rpeer; |
| const char* expected_cn = "cn1"; |
| const char* expected_pem_cert = "pem_cert1"; |
| const char* expected_pem_cert_chain = "pem_cert1_chain"; |
| const char* expected_sans[] = {"san1", "san2", "san3"}; |
| size_t i; |
| GPR_ASSERT(tsi_construct_peer(7 + 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, |
| &peer.properties[0]) == TSI_OK); |
| GPR_ASSERT(tsi_construct_string_peer_property_from_cstring( |
| "foo", "bar", &peer.properties[1]) == TSI_OK); |
| GPR_ASSERT(tsi_construct_string_peer_property_from_cstring( |
| TSI_X509_SUBJECT_COMMON_NAME_PEER_PROPERTY, expected_cn, |
| &peer.properties[2]) == TSI_OK); |
| GPR_ASSERT(tsi_construct_string_peer_property_from_cstring( |
| "chapi", "chapo", &peer.properties[3]) == TSI_OK); |
| 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); |
| GPR_ASSERT(tsi_construct_string_peer_property_from_cstring( |
| TSI_X509_PEM_CERT_CHAIN_PROPERTY, expected_pem_cert_chain, |
| &peer.properties[6]) == 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[7 + i]) == TSI_OK); |
| } |
| grpc_core::RefCountedPtr<grpc_auth_context> ctx = |
| grpc_ssl_peer_to_auth_context(&peer, GRPC_SSL_TRANSPORT_SECURITY_TYPE); |
| GPR_ASSERT(ctx != nullptr); |
| GPR_ASSERT(grpc_auth_context_peer_is_authenticated(ctx.get())); |
| GPR_ASSERT(check_identity(ctx.get(), GRPC_X509_SAN_PROPERTY_NAME, |
| expected_sans, GPR_ARRAY_SIZE(expected_sans))); |
| GPR_ASSERT(check_transport_security_type(ctx.get())); |
| GPR_ASSERT(check_x509_cn(ctx.get(), expected_cn)); |
| GPR_ASSERT(check_x509_pem_cert(ctx.get(), expected_pem_cert)); |
| GPR_ASSERT(check_x509_pem_cert_chain(ctx.get(), expected_pem_cert_chain)); |
| |
| rpeer = grpc_shallow_peer_from_ssl_auth_context(ctx.get()); |
| GPR_ASSERT(check_ssl_peer_equivalence(&peer, &rpeer)); |
| |
| grpc_shallow_peer_destruct(&rpeer); |
| tsi_peer_destruct(&peer); |
| ctx.reset(DEBUG_LOCATION, "test"); |
| } |
| |
| static void test_spiffe_id_peer_to_auth_context(void) { |
| // Invalid SPIFFE IDs should not be plumbed. |
| std::string long_id(2050, 'x'); |
| std::string long_domain(256, 'x'); |
| tsi_peer invalid_peer; |
| std::vector<std::string> invalid_spiffe_id = { |
| "", |
| "spi://", |
| "sfiffe://domain/wl", |
| "spiffe://domain", |
| "spiffe://domain/", |
| long_id, |
| "spiffe://" + long_domain + "/wl"}; |
| size_t i; |
| GPR_ASSERT(tsi_construct_peer(invalid_spiffe_id.size(), &invalid_peer) == |
| TSI_OK); |
| for (i = 0; i < invalid_spiffe_id.size(); i++) { |
| GPR_ASSERT(tsi_construct_string_peer_property_from_cstring( |
| TSI_X509_URI_PEER_PROPERTY, invalid_spiffe_id[i].c_str(), |
| &invalid_peer.properties[i]) == TSI_OK); |
| } |
| grpc_core::RefCountedPtr<grpc_auth_context> invalid_ctx = |
| grpc_ssl_peer_to_auth_context(&invalid_peer, |
| GRPC_SSL_TRANSPORT_SECURITY_TYPE); |
| GPR_ASSERT(invalid_ctx != nullptr); |
| GPR_ASSERT(check_spiffe_id(invalid_ctx.get(), nullptr, false)); |
| tsi_peer_destruct(&invalid_peer); |
| invalid_ctx.reset(DEBUG_LOCATION, "test"); |
| // A valid SPIFFE ID should be plumbed. |
| tsi_peer valid_peer; |
| std::string valid_spiffe_id = "spiffe://foo.bar.com/wl"; |
| GPR_ASSERT(tsi_construct_peer(1, &valid_peer) == TSI_OK); |
| GPR_ASSERT(tsi_construct_string_peer_property_from_cstring( |
| TSI_X509_URI_PEER_PROPERTY, valid_spiffe_id.c_str(), |
| &valid_peer.properties[0]) == TSI_OK); |
| grpc_core::RefCountedPtr<grpc_auth_context> valid_ctx = |
| grpc_ssl_peer_to_auth_context(&valid_peer, |
| GRPC_SSL_TRANSPORT_SECURITY_TYPE); |
| GPR_ASSERT(valid_ctx != nullptr); |
| GPR_ASSERT(check_spiffe_id(valid_ctx.get(), "spiffe://foo.bar.com/wl", true)); |
| tsi_peer_destruct(&valid_peer); |
| valid_ctx.reset(DEBUG_LOCATION, "test"); |
| // Multiple SPIFFE IDs should not be plumbed. |
| tsi_peer multiple_peer; |
| std::vector<std::string> multiple_spiffe_id = { |
| "spiffe://foo.bar.com/wl", "https://xyz", "spiffe://foo.bar.com/wl2"}; |
| GPR_ASSERT(tsi_construct_peer(multiple_spiffe_id.size(), &multiple_peer) == |
| TSI_OK); |
| for (i = 0; i < multiple_spiffe_id.size(); i++) { |
| GPR_ASSERT(tsi_construct_string_peer_property_from_cstring( |
| TSI_X509_URI_PEER_PROPERTY, multiple_spiffe_id[i].c_str(), |
| &multiple_peer.properties[i]) == TSI_OK); |
| } |
| grpc_core::RefCountedPtr<grpc_auth_context> multiple_ctx = |
| grpc_ssl_peer_to_auth_context(&multiple_peer, |
| GRPC_SSL_TRANSPORT_SECURITY_TYPE); |
| GPR_ASSERT(multiple_ctx != nullptr); |
| GPR_ASSERT(check_spiffe_id(multiple_ctx.get(), nullptr, false)); |
| tsi_peer_destruct(&multiple_peer); |
| multiple_ctx.reset(DEBUG_LOCATION, "test"); |
| // A valid SPIFFE certificate should only has one URI SAN field. |
| // SPIFFE ID should not be plumbed if there are multiple URIs. |
| tsi_peer multiple_uri_peer; |
| std::vector<std::string> multiple_uri = {"spiffe://foo.bar.com/wl", |
| "https://xyz", "ssh://foo.bar.com/"}; |
| GPR_ASSERT(tsi_construct_peer(multiple_uri.size(), &multiple_uri_peer) == |
| TSI_OK); |
| for (i = 0; i < multiple_spiffe_id.size(); i++) { |
| GPR_ASSERT(tsi_construct_string_peer_property_from_cstring( |
| TSI_X509_URI_PEER_PROPERTY, multiple_uri[i].c_str(), |
| &multiple_uri_peer.properties[i]) == TSI_OK); |
| } |
| grpc_core::RefCountedPtr<grpc_auth_context> multiple_uri_ctx = |
| grpc_ssl_peer_to_auth_context(&multiple_uri_peer, |
| GRPC_SSL_TRANSPORT_SECURITY_TYPE); |
| GPR_ASSERT(multiple_uri_ctx != nullptr); |
| GPR_ASSERT(check_spiffe_id(multiple_uri_ctx.get(), nullptr, false)); |
| tsi_peer_destruct(&multiple_uri_peer); |
| multiple_uri_ctx.reset(DEBUG_LOCATION, "test"); |
| } |
| |
| static const char* roots_for_override_api = "roots for override api"; |
| |
| static grpc_ssl_roots_override_result override_roots_success( |
| char** pem_root_certs) { |
| *pem_root_certs = gpr_strdup(roots_for_override_api); |
| return GRPC_SSL_ROOTS_OVERRIDE_OK; |
| } |
| |
| static grpc_ssl_roots_override_result override_roots_permanent_failure( |
| char** /*pem_root_certs*/) { |
| return GRPC_SSL_ROOTS_OVERRIDE_FAIL_PERMANENTLY; |
| } |
| |
| static void test_ipv6_address_san(void) { |
| const char* addresses[] = { |
| "2001:db8::1", "fe80::abcd:ef65:4321%em0", "fd11:feed:beef:0:cafe::4", |
| "128.10.0.1:8888", "[2001:db8::1]:8080", "[2001:db8::1%em1]:8080", |
| }; |
| const char* san_ips[] = { |
| "2001:db8::1", "fe80::abcd:ef65:4321", "fd11:feed:beef:0:cafe::4", |
| "128.10.0.1", "2001:db8::1", "2001:db8::1", |
| }; |
| tsi_peer peer; |
| GPR_ASSERT(tsi_construct_peer(1, &peer) == TSI_OK); |
| for (size_t i = 0; i < GPR_ARRAY_SIZE(addresses); i++) { |
| GPR_ASSERT(tsi_construct_string_peer_property_from_cstring( |
| TSI_X509_SUBJECT_ALTERNATIVE_NAME_PEER_PROPERTY, san_ips[i], |
| &peer.properties[0]) == TSI_OK); |
| GPR_ASSERT(grpc_ssl_host_matches_name(&peer, addresses[i])); |
| tsi_peer_property_destruct(&peer.properties[0]); |
| } |
| tsi_peer_destruct(&peer); |
| } |
| namespace grpc_core { |
| namespace { |
| |
| class TestDefaultSslRootStore : public DefaultSslRootStore { |
| public: |
| static grpc_slice ComputePemRootCertsForTesting() { |
| return ComputePemRootCerts(); |
| } |
| }; |
| |
| } // namespace |
| } // namespace grpc_core |
| |
| // TODO: Convert this test to C++ test when security_connector implementation |
| // is converted to C++. |
| static void test_default_ssl_roots(void) { |
| const char* roots_for_env_var = "roots for env var"; |
| |
| char* roots_env_var_file_path; |
| FILE* roots_env_var_file = |
| gpr_tmpfile("test_roots_for_env_var", &roots_env_var_file_path); |
| fwrite(roots_for_env_var, 1, strlen(roots_for_env_var), roots_env_var_file); |
| fclose(roots_env_var_file); |
| |
| /* First let's get the root through the override: set the env to an invalid |
| value. */ |
| GPR_GLOBAL_CONFIG_SET(grpc_default_ssl_roots_file_path, ""); |
| grpc_set_ssl_roots_override_callback(override_roots_success); |
| grpc_slice roots = |
| grpc_core::TestDefaultSslRootStore::ComputePemRootCertsForTesting(); |
| char* roots_contents = grpc_slice_to_c_string(roots); |
| grpc_slice_unref(roots); |
| GPR_ASSERT(strcmp(roots_contents, roots_for_override_api) == 0); |
| gpr_free(roots_contents); |
| |
| /* Now let's set the env var: We should get the contents pointed value |
| instead. */ |
| GPR_GLOBAL_CONFIG_SET(grpc_default_ssl_roots_file_path, |
| roots_env_var_file_path); |
| roots = grpc_core::TestDefaultSslRootStore::ComputePemRootCertsForTesting(); |
| roots_contents = grpc_slice_to_c_string(roots); |
| grpc_slice_unref(roots); |
| GPR_ASSERT(strcmp(roots_contents, roots_for_env_var) == 0); |
| gpr_free(roots_contents); |
| |
| /* Now reset the env var. We should fall back to the value overridden using |
| the api. */ |
| GPR_GLOBAL_CONFIG_SET(grpc_default_ssl_roots_file_path, ""); |
| roots = grpc_core::TestDefaultSslRootStore::ComputePemRootCertsForTesting(); |
| roots_contents = grpc_slice_to_c_string(roots); |
| grpc_slice_unref(roots); |
| GPR_ASSERT(strcmp(roots_contents, roots_for_override_api) == 0); |
| gpr_free(roots_contents); |
| |
| /* Now setup a permanent failure for the overridden roots and we should get |
| an empty slice. */ |
| GPR_GLOBAL_CONFIG_SET(grpc_not_use_system_ssl_roots, true); |
| grpc_set_ssl_roots_override_callback(override_roots_permanent_failure); |
| roots = grpc_core::TestDefaultSslRootStore::ComputePemRootCertsForTesting(); |
| GPR_ASSERT(GRPC_SLICE_IS_EMPTY(roots)); |
| const tsi_ssl_root_certs_store* root_store = |
| grpc_core::TestDefaultSslRootStore::GetRootStore(); |
| GPR_ASSERT(root_store == nullptr); |
| |
| /* Cleanup. */ |
| remove(roots_env_var_file_path); |
| gpr_free(roots_env_var_file_path); |
| } |
| |
| static void test_peer_alpn_check(void) { |
| #if TSI_OPENSSL_ALPN_SUPPORT |
| tsi_peer peer; |
| const char* alpn = "grpc"; |
| const char* wrong_alpn = "wrong"; |
| // peer does not have a TSI_SSL_ALPN_SELECTED_PROTOCOL property. |
| GPR_ASSERT(tsi_construct_peer(1, &peer) == TSI_OK); |
| GPR_ASSERT(tsi_construct_string_peer_property("wrong peer property name", |
| alpn, strlen(alpn), |
| &peer.properties[0]) == TSI_OK); |
| grpc_error* error = grpc_ssl_check_alpn(&peer); |
| GPR_ASSERT(error != GRPC_ERROR_NONE); |
| tsi_peer_destruct(&peer); |
| GRPC_ERROR_UNREF(error); |
| // peer has a TSI_SSL_ALPN_SELECTED_PROTOCOL property but with an incorrect |
| // property value. |
| GPR_ASSERT(tsi_construct_peer(1, &peer) == TSI_OK); |
| GPR_ASSERT(tsi_construct_string_peer_property(TSI_SSL_ALPN_SELECTED_PROTOCOL, |
| wrong_alpn, strlen(wrong_alpn), |
| &peer.properties[0]) == TSI_OK); |
| error = grpc_ssl_check_alpn(&peer); |
| GPR_ASSERT(error != GRPC_ERROR_NONE); |
| tsi_peer_destruct(&peer); |
| GRPC_ERROR_UNREF(error); |
| // peer has a TSI_SSL_ALPN_SELECTED_PROTOCOL property with a correct property |
| // value. |
| GPR_ASSERT(tsi_construct_peer(1, &peer) == TSI_OK); |
| GPR_ASSERT(tsi_construct_string_peer_property(TSI_SSL_ALPN_SELECTED_PROTOCOL, |
| alpn, strlen(alpn), |
| &peer.properties[0]) == TSI_OK); |
| GPR_ASSERT(grpc_ssl_check_alpn(&peer) == GRPC_ERROR_NONE); |
| tsi_peer_destruct(&peer); |
| #else |
| GPR_ASSERT(grpc_ssl_check_alpn(nullptr) == GRPC_ERROR_NONE); |
| #endif |
| } |
| |
| int main(int argc, char** argv) { |
| grpc::testing::TestEnvironment env(argc, argv); |
| grpc_init(); |
| test_unauthenticated_ssl_peer(); |
| test_cn_only_ssl_peer_to_auth_context(); |
| test_cn_and_one_san_ssl_peer_to_auth_context(); |
| test_cn_and_multiple_sans_ssl_peer_to_auth_context(); |
| test_cn_and_multiple_sans_and_others_ssl_peer_to_auth_context(); |
| test_spiffe_id_peer_to_auth_context(); |
| test_ipv6_address_san(); |
| test_default_ssl_roots(); |
| test_peer_alpn_check(); |
| test_check_security_level(); |
| grpc_shutdown(); |
| return 0; |
| } |