blob: c6b760be18a91a13efc25e549404c160cbb0ed8d [file] [log] [blame]
/*
*
* 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;
}