blob: 29030f07ad6980bb536f411bf287e0aa9c3e1b16 [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 <grpc/support/port_platform.h>
#include "src/core/lib/security/security_connector/ssl_utils.h"
#include <grpc/slice_buffer.h>
#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
#include <grpc/support/string_util.h>
#include "src/core/ext/transport/chttp2/alpn/alpn.h"
#include "src/core/lib/channel/channel_args.h"
#include "src/core/lib/gpr/env.h"
#include "src/core/lib/gpr/host_port.h"
#include "src/core/lib/gpr/string.h"
#include "src/core/lib/gprpp/ref_counted_ptr.h"
#include "src/core/lib/iomgr/load_file.h"
#include "src/core/lib/security/context/security_context.h"
#include "src/core/lib/security/security_connector/load_system_roots.h"
#include "src/core/tsi/ssl_transport_security.h"
/* -- Constants. -- */
#ifndef INSTALL_PREFIX
static const char* installed_roots_path = "/usr/share/grpc/roots.pem";
#else
static const char* installed_roots_path =
INSTALL_PREFIX "/share/grpc/roots.pem";
#endif
/** Environment variable used as a flag to enable/disable loading system root
certificates from the OS trust store. */
#ifndef GRPC_NOT_USE_SYSTEM_SSL_ROOTS_ENV_VAR
#define GRPC_NOT_USE_SYSTEM_SSL_ROOTS_ENV_VAR "GRPC_NOT_USE_SYSTEM_SSL_ROOTS"
#endif
#ifndef TSI_OPENSSL_ALPN_SUPPORT
#define TSI_OPENSSL_ALPN_SUPPORT 1
#endif
/* -- Overridden default roots. -- */
static grpc_ssl_roots_override_callback ssl_roots_override_cb = nullptr;
void grpc_set_ssl_roots_override_callback(grpc_ssl_roots_override_callback cb) {
ssl_roots_override_cb = cb;
}
/* -- Cipher suites. -- */
/* Defines the cipher suites that we accept by default. All these cipher suites
are compliant with HTTP2. */
#define GRPC_SSL_CIPHER_SUITES \
"ECDHE-ECDSA-AES128-GCM-SHA256:" \
"ECDHE-ECDSA-AES256-GCM-SHA384:" \
"ECDHE-RSA-AES128-GCM-SHA256:" \
"ECDHE-RSA-AES256-GCM-SHA384"
static gpr_once cipher_suites_once = GPR_ONCE_INIT;
static const char* cipher_suites = nullptr;
static void init_cipher_suites(void) {
char* overridden = gpr_getenv("GRPC_SSL_CIPHER_SUITES");
cipher_suites = overridden != nullptr ? overridden : GRPC_SSL_CIPHER_SUITES;
}
/* --- Util --- */
const char* grpc_get_ssl_cipher_suites(void) {
gpr_once_init(&cipher_suites_once, init_cipher_suites);
return cipher_suites;
}
tsi_client_certificate_request_type
grpc_get_tsi_client_certificate_request_type(
grpc_ssl_client_certificate_request_type grpc_request_type) {
switch (grpc_request_type) {
case GRPC_SSL_DONT_REQUEST_CLIENT_CERTIFICATE:
return TSI_DONT_REQUEST_CLIENT_CERTIFICATE;
case GRPC_SSL_REQUEST_CLIENT_CERTIFICATE_BUT_DONT_VERIFY:
return TSI_REQUEST_CLIENT_CERTIFICATE_BUT_DONT_VERIFY;
case GRPC_SSL_REQUEST_CLIENT_CERTIFICATE_AND_VERIFY:
return TSI_REQUEST_CLIENT_CERTIFICATE_AND_VERIFY;
case GRPC_SSL_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_BUT_DONT_VERIFY:
return TSI_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_BUT_DONT_VERIFY;
case GRPC_SSL_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_AND_VERIFY:
return TSI_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_AND_VERIFY;
default:
return TSI_DONT_REQUEST_CLIENT_CERTIFICATE;
}
}
const char** grpc_fill_alpn_protocol_strings(size_t* num_alpn_protocols) {
GPR_ASSERT(num_alpn_protocols != nullptr);
*num_alpn_protocols = grpc_chttp2_num_alpn_versions();
const char** alpn_protocol_strings = static_cast<const char**>(
gpr_malloc(sizeof(const char*) * (*num_alpn_protocols)));
for (size_t i = 0; i < *num_alpn_protocols; i++) {
alpn_protocol_strings[i] = grpc_chttp2_get_alpn_version_index(i);
}
return alpn_protocol_strings;
}
int grpc_ssl_host_matches_name(const tsi_peer* peer, const char* peer_name) {
char* allocated_name = nullptr;
int r;
char* ignored_port;
gpr_split_host_port(peer_name, &allocated_name, &ignored_port);
gpr_free(ignored_port);
peer_name = allocated_name;
if (!peer_name) return 0;
// IPv6 zone-id should not be included in comparisons.
char* const zone_id = strchr(allocated_name, '%');
if (zone_id != nullptr) *zone_id = '\0';
r = tsi_ssl_peer_matches_name(peer, peer_name);
gpr_free(allocated_name);
return r;
}
grpc_core::RefCountedPtr<grpc_auth_context> grpc_ssl_peer_to_auth_context(
const tsi_peer* peer) {
size_t i;
const char* peer_identity_property_name = nullptr;
/* The caller has checked the certificate type property. */
GPR_ASSERT(peer->property_count >= 1);
grpc_core::RefCountedPtr<grpc_auth_context> ctx =
grpc_core::MakeRefCounted<grpc_auth_context>(nullptr);
grpc_auth_context_add_cstring_property(
ctx.get(), GRPC_TRANSPORT_SECURITY_TYPE_PROPERTY_NAME,
GRPC_SSL_TRANSPORT_SECURITY_TYPE);
for (i = 0; i < peer->property_count; i++) {
const tsi_peer_property* prop = &peer->properties[i];
if (prop->name == nullptr) continue;
if (strcmp(prop->name, TSI_X509_SUBJECT_COMMON_NAME_PEER_PROPERTY) == 0) {
/* If there is no subject alt name, have the CN as the identity. */
if (peer_identity_property_name == nullptr) {
peer_identity_property_name = GRPC_X509_CN_PROPERTY_NAME;
}
grpc_auth_context_add_property(ctx.get(), GRPC_X509_CN_PROPERTY_NAME,
prop->value.data, prop->value.length);
} else if (strcmp(prop->name,
TSI_X509_SUBJECT_ALTERNATIVE_NAME_PEER_PROPERTY) == 0) {
peer_identity_property_name = GRPC_X509_SAN_PROPERTY_NAME;
grpc_auth_context_add_property(ctx.get(), GRPC_X509_SAN_PROPERTY_NAME,
prop->value.data, prop->value.length);
} else if (strcmp(prop->name, TSI_X509_PEM_CERT_PROPERTY) == 0) {
grpc_auth_context_add_property(ctx.get(),
GRPC_X509_PEM_CERT_PROPERTY_NAME,
prop->value.data, prop->value.length);
} else if (strcmp(prop->name, TSI_SSL_SESSION_REUSED_PEER_PROPERTY) == 0) {
grpc_auth_context_add_property(ctx.get(),
GRPC_SSL_SESSION_REUSED_PROPERTY,
prop->value.data, prop->value.length);
}
}
if (peer_identity_property_name != nullptr) {
GPR_ASSERT(grpc_auth_context_set_peer_identity_property_name(
ctx.get(), peer_identity_property_name) == 1);
}
return ctx;
}
static void add_shallow_auth_property_to_peer(tsi_peer* peer,
const grpc_auth_property* prop,
const char* tsi_prop_name) {
tsi_peer_property* tsi_prop = &peer->properties[peer->property_count++];
tsi_prop->name = const_cast<char*>(tsi_prop_name);
tsi_prop->value.data = prop->value;
tsi_prop->value.length = prop->value_length;
}
tsi_peer grpc_shallow_peer_from_ssl_auth_context(
const grpc_auth_context* auth_context) {
size_t max_num_props = 0;
grpc_auth_property_iterator it;
const grpc_auth_property* prop;
tsi_peer peer;
memset(&peer, 0, sizeof(peer));
it = grpc_auth_context_property_iterator(auth_context);
while (grpc_auth_property_iterator_next(&it) != nullptr) max_num_props++;
if (max_num_props > 0) {
peer.properties = static_cast<tsi_peer_property*>(
gpr_malloc(max_num_props * sizeof(tsi_peer_property)));
it = grpc_auth_context_property_iterator(auth_context);
while ((prop = grpc_auth_property_iterator_next(&it)) != nullptr) {
if (strcmp(prop->name, GRPC_X509_SAN_PROPERTY_NAME) == 0) {
add_shallow_auth_property_to_peer(
&peer, prop, TSI_X509_SUBJECT_ALTERNATIVE_NAME_PEER_PROPERTY);
} else if (strcmp(prop->name, GRPC_X509_CN_PROPERTY_NAME) == 0) {
add_shallow_auth_property_to_peer(
&peer, prop, TSI_X509_SUBJECT_COMMON_NAME_PEER_PROPERTY);
} 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);
}
}
}
return peer;
}
void grpc_shallow_peer_destruct(tsi_peer* peer) {
if (peer->properties != nullptr) gpr_free(peer->properties);
}
/* --- Ssl cache implementation. --- */
grpc_ssl_session_cache* grpc_ssl_session_cache_create_lru(size_t capacity) {
tsi_ssl_session_cache* cache = tsi_ssl_session_cache_create_lru(capacity);
return reinterpret_cast<grpc_ssl_session_cache*>(cache);
}
void grpc_ssl_session_cache_destroy(grpc_ssl_session_cache* cache) {
tsi_ssl_session_cache* tsi_cache =
reinterpret_cast<tsi_ssl_session_cache*>(cache);
tsi_ssl_session_cache_unref(tsi_cache);
}
static void* grpc_ssl_session_cache_arg_copy(void* p) {
tsi_ssl_session_cache* tsi_cache =
reinterpret_cast<tsi_ssl_session_cache*>(p);
// destroy call below will unref the pointer.
tsi_ssl_session_cache_ref(tsi_cache);
return p;
}
static void grpc_ssl_session_cache_arg_destroy(void* p) {
tsi_ssl_session_cache* tsi_cache =
reinterpret_cast<tsi_ssl_session_cache*>(p);
tsi_ssl_session_cache_unref(tsi_cache);
}
static int grpc_ssl_session_cache_arg_cmp(void* p, void* q) {
return GPR_ICMP(p, q);
}
grpc_arg grpc_ssl_session_cache_create_channel_arg(
grpc_ssl_session_cache* cache) {
static const grpc_arg_pointer_vtable vtable = {
grpc_ssl_session_cache_arg_copy,
grpc_ssl_session_cache_arg_destroy,
grpc_ssl_session_cache_arg_cmp,
};
return grpc_channel_arg_pointer_create(
const_cast<char*>(GRPC_SSL_SESSION_CACHE_ARG), cache, &vtable);
}
/* --- Default SSL root store implementation. --- */
namespace grpc_core {
tsi_ssl_root_certs_store* DefaultSslRootStore::default_root_store_;
grpc_slice DefaultSslRootStore::default_pem_root_certs_;
const tsi_ssl_root_certs_store* DefaultSslRootStore::GetRootStore() {
InitRootStore();
return default_root_store_;
}
const char* DefaultSslRootStore::GetPemRootCerts() {
InitRootStore();
return GRPC_SLICE_IS_EMPTY(default_pem_root_certs_)
? nullptr
: reinterpret_cast<const char*>
GRPC_SLICE_START_PTR(default_pem_root_certs_);
}
grpc_slice DefaultSslRootStore::ComputePemRootCerts() {
grpc_slice result = grpc_empty_slice();
char* not_use_system_roots_env_value =
gpr_getenv(GRPC_NOT_USE_SYSTEM_SSL_ROOTS_ENV_VAR);
const bool not_use_system_roots = gpr_is_true(not_use_system_roots_env_value);
gpr_free(not_use_system_roots_env_value);
// First try to load the roots from the environment.
char* default_root_certs_path =
gpr_getenv(GRPC_DEFAULT_SSL_ROOTS_FILE_PATH_ENV_VAR);
if (default_root_certs_path != nullptr) {
GRPC_LOG_IF_ERROR("load_file",
grpc_load_file(default_root_certs_path, 1, &result));
gpr_free(default_root_certs_path);
}
// Try overridden roots if needed.
grpc_ssl_roots_override_result ovrd_res = GRPC_SSL_ROOTS_OVERRIDE_FAIL;
if (GRPC_SLICE_IS_EMPTY(result) && ssl_roots_override_cb != nullptr) {
char* pem_root_certs = nullptr;
ovrd_res = ssl_roots_override_cb(&pem_root_certs);
if (ovrd_res == GRPC_SSL_ROOTS_OVERRIDE_OK) {
GPR_ASSERT(pem_root_certs != nullptr);
result = grpc_slice_from_copied_buffer(
pem_root_certs,
strlen(pem_root_certs) + 1); // nullptr terminator.
}
gpr_free(pem_root_certs);
}
// Try loading roots from OS trust store if flag is enabled.
if (GRPC_SLICE_IS_EMPTY(result) && !not_use_system_roots) {
result = LoadSystemRootCerts();
}
// Fallback to roots manually shipped with gRPC.
if (GRPC_SLICE_IS_EMPTY(result) &&
ovrd_res != GRPC_SSL_ROOTS_OVERRIDE_FAIL_PERMANENTLY) {
GRPC_LOG_IF_ERROR("load_file",
grpc_load_file(installed_roots_path, 1, &result));
}
return result;
}
void DefaultSslRootStore::InitRootStore() {
static gpr_once once = GPR_ONCE_INIT;
gpr_once_init(&once, DefaultSslRootStore::InitRootStoreOnce);
}
void DefaultSslRootStore::InitRootStoreOnce() {
default_pem_root_certs_ = ComputePemRootCerts();
if (!GRPC_SLICE_IS_EMPTY(default_pem_root_certs_)) {
default_root_store_ =
tsi_ssl_root_certs_store_create(reinterpret_cast<const char*>(
GRPC_SLICE_START_PTR(default_pem_root_certs_)));
}
}
} // namespace grpc_core