/* * * 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 #include #include #include #include #include #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_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 int check_property(const grpc_auth_context* ctx, const char* expected_property_name, const char* expected_property_value) { grpc_auth_property_iterator it = grpc_auth_context_find_properties_by_name(ctx, expected_property_name); const grpc_auth_property* prop = grpc_auth_property_iterator_next(&it); if (prop == nullptr) { gpr_log(GPR_ERROR, "Expected value %s not found.", expected_property_value); return 0; } if (strncmp(prop->value, expected_property_value, prop->value_length) != 0) { gpr_log(GPR_ERROR, "Expected value %s and got %s for property %s.", expected_property_value, prop->value, expected_property_name); return 0; } if (grpc_auth_property_iterator_next(&it) != nullptr) { gpr_log(GPR_ERROR, "Expected only one property for %s.", expected_property_name); return 0; } return 1; } static int check_properties( const grpc_auth_context* ctx, const char* expected_property_name, const std::vector& expected_property_values) { grpc_auth_property_iterator it = grpc_auth_context_find_properties_by_name(ctx, expected_property_name); for (const auto& property_value : expected_property_values) { const grpc_auth_property* prop = grpc_auth_property_iterator_next(&it); if (prop == nullptr) { gpr_log(GPR_ERROR, "Expected value %s not found.", property_value.c_str()); return 0; } if (strcmp(prop->name, expected_property_name) != 0) { gpr_log(GPR_ERROR, "Expected peer property name %s and got %s.", expected_property_name, prop->name); return 0; } if (strncmp(prop->value, property_value.c_str(), prop->value_length) != 0) { gpr_log(GPR_ERROR, "Expected peer property value %s and got %s.", property_value.c_str(), prop->value); return 0; } } if (grpc_auth_property_iterator_next(&it) != nullptr) { gpr_log(GPR_ERROR, "Expected only %zu property values.", expected_property_values.size()); 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_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 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_property(ctx.get(), GRPC_TRANSPORT_SECURITY_TYPE_PROPERTY_NAME, GRPC_SSL_TRANSPORT_SECURITY_TYPE)); 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_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 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_property(ctx.get(), GRPC_X509_CN_PROPERTY_NAME, expected_cn)); GPR_ASSERT(check_property(ctx.get(), GRPC_TRANSPORT_SECURITY_TYPE_PROPERTY_NAME, GRPC_SSL_TRANSPORT_SECURITY_TYPE)); GPR_ASSERT( check_property(ctx.get(), GRPC_X509_CN_PROPERTY_NAME, expected_cn)); GPR_ASSERT(check_property(ctx.get(), GRPC_X509_PEM_CERT_PROPERTY_NAME, expected_pem_cert)); GPR_ASSERT(check_property(ctx.get(), GRPC_X509_PEM_CERT_CHAIN_PROPERTY_NAME, 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 std::vector expected_sans = {"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_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 (size_t i = 0; i < expected_sans.size(); i++) { GPR_ASSERT(tsi_construct_string_peer_property_from_cstring( TSI_X509_SUBJECT_ALTERNATIVE_NAME_PEER_PROPERTY, expected_sans[i].c_str(), &peer.properties[5 + i]) == TSI_OK); } grpc_core::RefCountedPtr 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_properties(ctx.get(), GRPC_X509_SAN_PROPERTY_NAME, expected_sans)); GPR_ASSERT(check_property(ctx.get(), GRPC_TRANSPORT_SECURITY_TYPE_PROPERTY_NAME, GRPC_SSL_TRANSPORT_SECURITY_TYPE)); GPR_ASSERT( check_property(ctx.get(), GRPC_X509_CN_PROPERTY_NAME, expected_cn)); GPR_ASSERT(check_property(ctx.get(), GRPC_X509_PEM_CERT_PROPERTY_NAME, expected_pem_cert)); GPR_ASSERT(check_property(ctx.get(), GRPC_X509_PEM_CERT_CHAIN_PROPERTY_NAME, 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 std::vector 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 + expected_sans.size(), &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 < expected_sans.size(); i++) { GPR_ASSERT(tsi_construct_string_peer_property_from_cstring( TSI_X509_SUBJECT_ALTERNATIVE_NAME_PEER_PROPERTY, expected_sans[i].c_str(), &peer.properties[5 + i]) == TSI_OK); } grpc_core::RefCountedPtr 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_properties(ctx.get(), GRPC_X509_SAN_PROPERTY_NAME, expected_sans)); GPR_ASSERT(check_property(ctx.get(), GRPC_TRANSPORT_SECURITY_TYPE_PROPERTY_NAME, GRPC_SSL_TRANSPORT_SECURITY_TYPE)); GPR_ASSERT( check_property(ctx.get(), GRPC_X509_CN_PROPERTY_NAME, expected_cn)); GPR_ASSERT(check_property(ctx.get(), GRPC_X509_PEM_CERT_PROPERTY_NAME, expected_pem_cert)); GPR_ASSERT(check_property(ctx.get(), GRPC_X509_PEM_CERT_CHAIN_PROPERTY_NAME, 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 std::vector expected_sans = {"san1", "san2", "san3"}; size_t i; GPR_ASSERT(tsi_construct_peer(7 + expected_sans.size(), &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 < expected_sans.size(); i++) { GPR_ASSERT(tsi_construct_string_peer_property_from_cstring( TSI_X509_SUBJECT_ALTERNATIVE_NAME_PEER_PROPERTY, expected_sans[i].c_str(), &peer.properties[7 + i]) == TSI_OK); } grpc_core::RefCountedPtr 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_properties(ctx.get(), GRPC_X509_SAN_PROPERTY_NAME, expected_sans)); GPR_ASSERT(check_property(ctx.get(), GRPC_TRANSPORT_SECURITY_TYPE_PROPERTY_NAME, GRPC_SSL_TRANSPORT_SECURITY_TYPE)); GPR_ASSERT( check_property(ctx.get(), GRPC_X509_CN_PROPERTY_NAME, expected_cn)); GPR_ASSERT(check_property(ctx.get(), GRPC_X509_PEM_CERT_PROPERTY_NAME, expected_pem_cert)); GPR_ASSERT(check_property(ctx.get(), GRPC_X509_PEM_CERT_CHAIN_PROPERTY_NAME, 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_dns_peer_to_auth_context(void) { tsi_peer peer; const std::vector expected_dns = {"dns1", "dns2", "dns3"}; GPR_ASSERT(tsi_construct_peer(expected_dns.size(), &peer) == TSI_OK); for (size_t i = 0; i < expected_dns.size(); ++i) { GPR_ASSERT(tsi_construct_string_peer_property_from_cstring( TSI_X509_DNS_PEER_PROPERTY, expected_dns[i].c_str(), &peer.properties[i]) == TSI_OK); } grpc_core::RefCountedPtr ctx = grpc_ssl_peer_to_auth_context(&peer, GRPC_SSL_TRANSPORT_SECURITY_TYPE); GPR_ASSERT(ctx != nullptr); GPR_ASSERT( check_properties(ctx.get(), GRPC_PEER_DNS_PROPERTY_NAME, expected_dns)); tsi_peer_destruct(&peer); ctx.reset(DEBUG_LOCATION, "test"); } static void test_uri_peer_to_auth_context(void) { tsi_peer peer; const std::vector expected_uri = {"uri1", "uri2", "uri3"}; GPR_ASSERT(tsi_construct_peer(expected_uri.size(), &peer) == TSI_OK); for (size_t i = 0; i < expected_uri.size(); ++i) { GPR_ASSERT(tsi_construct_string_peer_property_from_cstring( TSI_X509_URI_PEER_PROPERTY, expected_uri[i].c_str(), &peer.properties[i]) == TSI_OK); } grpc_core::RefCountedPtr ctx = grpc_ssl_peer_to_auth_context(&peer, GRPC_SSL_TRANSPORT_SECURITY_TYPE); GPR_ASSERT(ctx != nullptr); GPR_ASSERT( check_properties(ctx.get(), GRPC_PEER_URI_PROPERTY_NAME, expected_uri)); tsi_peer_destruct(&peer); ctx.reset(DEBUG_LOCATION, "test"); } static void test_email_peer_to_auth_context(void) { tsi_peer peer; const std::vector expected_emails = {"email1", "email2"}; GPR_ASSERT(tsi_construct_peer(expected_emails.size(), &peer) == TSI_OK); for (size_t i = 0; i < expected_emails.size(); ++i) { GPR_ASSERT(tsi_construct_string_peer_property_from_cstring( TSI_X509_EMAIL_PEER_PROPERTY, expected_emails[i].c_str(), &peer.properties[i]) == TSI_OK); } grpc_core::RefCountedPtr ctx = grpc_ssl_peer_to_auth_context(&peer, GRPC_SSL_TRANSPORT_SECURITY_TYPE); GPR_ASSERT(ctx != nullptr); GPR_ASSERT(check_properties(ctx.get(), GRPC_PEER_EMAIL_PROPERTY_NAME, expected_emails)); tsi_peer_destruct(&peer); ctx.reset(DEBUG_LOCATION, "test"); } static void test_ip_peer_to_auth_context(void) { tsi_peer peer; const std::vector expected_ips = {"128.128.128.128", "255.255.255.255"}; GPR_ASSERT(tsi_construct_peer(expected_ips.size(), &peer) == TSI_OK); for (size_t i = 0; i < expected_ips.size(); ++i) { GPR_ASSERT(tsi_construct_string_peer_property_from_cstring( TSI_X509_IP_PEER_PROPERTY, expected_ips[i].c_str(), &peer.properties[i]) == TSI_OK); } grpc_core::RefCountedPtr ctx = grpc_ssl_peer_to_auth_context(&peer, GRPC_SSL_TRANSPORT_SECURITY_TYPE); GPR_ASSERT(ctx != nullptr); GPR_ASSERT( check_properties(ctx.get(), GRPC_PEER_IP_PROPERTY_NAME, expected_ips)); 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 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 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 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 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 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 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 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 void test_subject_to_auth_context(void) { tsi_peer peer; const char* expected_subject = "subject1"; GPR_ASSERT(tsi_construct_peer(1, &peer) == TSI_OK); GPR_ASSERT(tsi_construct_string_peer_property_from_cstring( TSI_X509_SUBJECT_PEER_PROPERTY, expected_subject, &peer.properties[0]) == TSI_OK); grpc_core::RefCountedPtr ctx = grpc_ssl_peer_to_auth_context(&peer, GRPC_SSL_TRANSPORT_SECURITY_TYPE); GPR_ASSERT(ctx != nullptr); GPR_ASSERT(check_property(ctx.get(), GRPC_X509_SUBJECT_PROPERTY_NAME, expected_subject)); tsi_peer_destruct(&peer); 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(unknown): 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_handle 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_dns_peer_to_auth_context(); test_uri_peer_to_auth_context(); test_email_peer_to_auth_context(); test_ip_peer_to_auth_context(); test_spiffe_id_peer_to_auth_context(); test_subject_to_auth_context(); test_ipv6_address_san(); test_default_ssl_roots(); test_peer_alpn_check(); grpc_shutdown(); return 0; }