diff --git a/src/core/lib/security/security_connector/ssl/ssl_security_connector.cc b/src/core/lib/security/security_connector/ssl/ssl_security_connector.cc index fbf59d23b9d..7158290b6b4 100644 --- a/src/core/lib/security/security_connector/ssl/ssl_security_connector.cc +++ b/src/core/lib/security/security_connector/ssl/ssl_security_connector.cc @@ -1,21 +1,3 @@ -/* - * - * Copyright 2018 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 #include "src/core/lib/security/security_connector/ssl/ssl_security_connector.h" @@ -41,6 +23,33 @@ #include "src/core/tsi/transport_security.h" namespace { +grpc_error* ssl_check_peer( + const char* peer_name, const tsi_peer* peer, + grpc_core::RefCountedPtr* auth_context) { +#if TSI_OPENSSL_ALPN_SUPPORT + /* Check the ALPN if ALPN is supported. */ + const tsi_peer_property* p = + tsi_peer_get_property_by_name(peer, TSI_SSL_ALPN_SELECTED_PROTOCOL); + if (p == nullptr) { + return GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Cannot check peer: missing selected ALPN property."); + } + if (!grpc_chttp2_is_alpn_version_supported(p->value.data, p->value.length)) { + return GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Cannot check peer: invalid ALPN value."); + } +#endif /* TSI_OPENSSL_ALPN_SUPPORT */ + /* Check the peer name if specified. */ + if (peer_name != nullptr && !grpc_ssl_host_matches_name(peer, peer_name)) { + char* msg; + gpr_asprintf(&msg, "Peer name %s is not in peer certificate", peer_name); + grpc_error* error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg); + gpr_free(msg); + return error; + } + *auth_context = grpc_ssl_peer_to_auth_context(peer); + return GRPC_ERROR_NONE; +} class grpc_ssl_channel_security_connector final : public grpc_channel_security_connector { @@ -69,10 +78,34 @@ class grpc_ssl_channel_security_connector final } grpc_security_status InitializeHandshakerFactory( - const grpc_ssl_config* config, tsi_ssl_session_cache* ssl_session_cache) { - return grpc_ssl_tsi_client_handshaker_factory_init( - config->pem_key_cert_pair, config->pem_root_certs, ssl_session_cache, - &client_handshaker_factory_); + const grpc_ssl_config* config, const char* pem_root_certs, + const tsi_ssl_root_certs_store* root_store, + tsi_ssl_session_cache* ssl_session_cache) { + bool has_key_cert_pair = + config->pem_key_cert_pair != nullptr && + config->pem_key_cert_pair->private_key != nullptr && + config->pem_key_cert_pair->cert_chain != nullptr; + tsi_ssl_client_handshaker_options options; + GPR_DEBUG_ASSERT(pem_root_certs != nullptr); + options.pem_root_certs = pem_root_certs; + options.root_store = root_store; + options.alpn_protocols = + grpc_fill_alpn_protocol_strings(&options.num_alpn_protocols); + if (has_key_cert_pair) { + options.pem_key_cert_pair = config->pem_key_cert_pair; + } + options.cipher_suites = grpc_get_ssl_cipher_suites(); + options.session_cache = ssl_session_cache; + const tsi_result result = + tsi_create_ssl_client_handshaker_factory_with_options( + &options, &client_handshaker_factory_); + gpr_free((void*)options.alpn_protocols); + if (result != TSI_OK) { + gpr_log(GPR_ERROR, "Handshaker factory creation failed with %s.", + tsi_result_to_string(result)); + return GRPC_SECURITY_ERROR; + } + return GRPC_SECURITY_OK; } void add_handshakers(grpc_pollset_set* interested_parties, @@ -99,35 +132,29 @@ class grpc_ssl_channel_security_connector final const char* target_name = overridden_target_name_ != nullptr ? overridden_target_name_ : target_name_; - grpc_error* error = grpc_ssl_check_alpn(&peer); - if (error == GRPC_ERROR_NONE) { - error = grpc_ssl_check_peer_name(target_name, &peer); - if (error == GRPC_ERROR_NONE) { - if (verify_options_->verify_peer_callback != nullptr) { - const tsi_peer_property* p = - tsi_peer_get_property_by_name(&peer, TSI_X509_PEM_CERT_PROPERTY); - if (p == nullptr) { - error = GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "Cannot check peer: missing pem cert property."); - } else { - char* peer_pem = - static_cast(gpr_malloc(p->value.length + 1)); - memcpy(peer_pem, p->value.data, p->value.length); - peer_pem[p->value.length] = '\0'; - int callback_status = verify_options_->verify_peer_callback( - target_name, peer_pem, - verify_options_->verify_peer_callback_userdata); - gpr_free(peer_pem); - if (callback_status) { - char* msg; - gpr_asprintf(&msg, "Verify peer callback returned a failure (%d)", - callback_status); - error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg); - gpr_free(msg); - } - } + grpc_error* error = ssl_check_peer(target_name, &peer, auth_context); + if (error == GRPC_ERROR_NONE && + verify_options_->verify_peer_callback != nullptr) { + const tsi_peer_property* p = + tsi_peer_get_property_by_name(&peer, TSI_X509_PEM_CERT_PROPERTY); + if (p == nullptr) { + error = GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Cannot check peer: missing pem cert property."); + } else { + char* peer_pem = static_cast(gpr_malloc(p->value.length + 1)); + memcpy(peer_pem, p->value.data, p->value.length); + peer_pem[p->value.length] = '\0'; + int callback_status = verify_options_->verify_peer_callback( + target_name, peer_pem, + verify_options_->verify_peer_callback_userdata); + gpr_free(peer_pem); + if (callback_status) { + char* msg; + gpr_asprintf(&msg, "Verify peer callback returned a failure (%d)", + callback_status); + error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg); + gpr_free(msg); } - *auth_context = grpc_ssl_peer_to_auth_context(&peer); } } GRPC_CLOSURE_SCHED(on_peer_checked, error); @@ -139,16 +166,34 @@ class grpc_ssl_channel_security_connector final reinterpret_cast(other_sc); int c = channel_security_connector_cmp(other); if (c != 0) return c; - return grpc_ssl_cmp_target_name(target_name_, other->target_name_, - overridden_target_name_, - other->overridden_target_name_); + c = strcmp(target_name_, other->target_name_); + if (c != 0) return c; + return (overridden_target_name_ == nullptr || + other->overridden_target_name_ == nullptr) + ? GPR_ICMP(overridden_target_name_, + other->overridden_target_name_) + : strcmp(overridden_target_name_, + other->overridden_target_name_); } bool check_call_host(const char* host, grpc_auth_context* auth_context, grpc_closure* on_call_host_checked, grpc_error** error) override { - return grpc_ssl_check_call_host(host, target_name_, overridden_target_name_, - auth_context, on_call_host_checked, error); + grpc_security_status status = GRPC_SECURITY_ERROR; + tsi_peer peer = grpc_shallow_peer_from_ssl_auth_context(auth_context); + if (grpc_ssl_host_matches_name(&peer, host)) status = GRPC_SECURITY_OK; + /* If the target name was overridden, then the original target_name was + 'checked' transitively during the previous peer check at the end of the + handshake. */ + if (overridden_target_name_ != nullptr && strcmp(host, target_name_) == 0) { + status = GRPC_SECURITY_OK; + } + if (status != GRPC_SECURITY_OK) { + *error = GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "call host does not match SSL server name"); + } + grpc_shallow_peer_destruct(&peer); + return true; } void cancel_check_call_host(grpc_closure* on_call_host_checked, @@ -185,25 +230,43 @@ class grpc_ssl_server_security_connector } grpc_security_status InitializeHandshakerFactory() { - grpc_security_status retval = GRPC_SECURITY_OK; if (has_cert_config_fetcher()) { // Load initial credentials from certificate_config_fetcher: if (!try_fetch_ssl_server_credentials()) { gpr_log(GPR_ERROR, "Failed loading SSL server credentials from fetcher."); - retval = GRPC_SECURITY_ERROR; + return GRPC_SECURITY_ERROR; } } else { auto* server_credentials = static_cast(server_creds()); - retval = grpc_ssl_tsi_server_handshaker_factory_init( - server_credentials->config().pem_key_cert_pairs, - server_credentials->config().num_key_cert_pairs, - server_credentials->config().pem_root_certs, - server_credentials->config().client_certificate_request, - &server_handshaker_factory_); + size_t num_alpn_protocols = 0; + const char** alpn_protocol_strings = + grpc_fill_alpn_protocol_strings(&num_alpn_protocols); + tsi_ssl_server_handshaker_options options; + options.pem_key_cert_pairs = + server_credentials->config().pem_key_cert_pairs; + options.num_key_cert_pairs = + server_credentials->config().num_key_cert_pairs; + options.pem_client_root_certs = + server_credentials->config().pem_root_certs; + options.client_certificate_request = + grpc_get_tsi_client_certificate_request_type( + server_credentials->config().client_certificate_request); + options.cipher_suites = grpc_get_ssl_cipher_suites(); + options.alpn_protocols = alpn_protocol_strings; + options.num_alpn_protocols = static_cast(num_alpn_protocols); + const tsi_result result = + tsi_create_ssl_server_handshaker_factory_with_options( + &options, &server_handshaker_factory_); + gpr_free((void*)alpn_protocol_strings); + if (result != TSI_OK) { + gpr_log(GPR_ERROR, "Handshaker factory creation failed with %s.", + tsi_result_to_string(result)); + return GRPC_SECURITY_ERROR; + } } - return retval; + return GRPC_SECURITY_OK; } void add_handshakers(grpc_pollset_set* interested_parties, @@ -225,8 +288,7 @@ class grpc_ssl_server_security_connector void check_peer(tsi_peer peer, grpc_endpoint* ep, grpc_core::RefCountedPtr* auth_context, grpc_closure* on_peer_checked) override { - grpc_error* error = grpc_ssl_check_alpn(&peer); - *auth_context = grpc_ssl_peer_to_auth_context(&peer); + grpc_error* error = ssl_check_peer(nullptr, &peer, auth_context); tsi_peer_destruct(&peer); GRPC_CLOSURE_SCHED(on_peer_checked, error); } @@ -243,7 +305,9 @@ class grpc_ssl_server_security_connector bool try_fetch_ssl_server_credentials() { grpc_ssl_server_certificate_config* certificate_config = nullptr; bool status; + if (!has_cert_config_fetcher()) return false; + grpc_ssl_server_credentials* server_creds = static_cast(this->mutable_server_creds()); grpc_ssl_certificate_config_reload_status cb_result = @@ -260,6 +324,7 @@ class grpc_ssl_server_security_connector "use previously-loaded credentials."); status = false; } + if (certificate_config != nullptr) { grpc_ssl_server_certificate_config_destroy(certificate_config); } @@ -278,18 +343,34 @@ class grpc_ssl_server_security_connector "config."); return false; } - tsi_ssl_pem_key_cert_pair* pem_key_cert_pairs = - grpc_convert_grpc_to_tsi_cert_pairs(config->pem_key_cert_pairs, - config->num_key_cert_pairs); - const grpc_ssl_server_credentials* server_credentials = - static_cast(this->server_creds()); + gpr_log(GPR_DEBUG, "Using new server certificate config (%p).", config); + + size_t num_alpn_protocols = 0; + const char** alpn_protocol_strings = + grpc_fill_alpn_protocol_strings(&num_alpn_protocols); tsi_ssl_server_handshaker_factory* new_handshaker_factory = nullptr; - grpc_security_status retval = grpc_ssl_tsi_server_handshaker_factory_init( - pem_key_cert_pairs, config->num_key_cert_pairs, config->pem_root_certs, - server_credentials->config().client_certificate_request, - &new_handshaker_factory); - gpr_free(pem_key_cert_pairs); - if (retval != GRPC_SECURITY_OK) { + const grpc_ssl_server_credentials* server_creds = + static_cast(this->server_creds()); + GPR_DEBUG_ASSERT(config->pem_root_certs != nullptr); + tsi_ssl_server_handshaker_options options; + options.pem_key_cert_pairs = grpc_convert_grpc_to_tsi_cert_pairs( + config->pem_key_cert_pairs, config->num_key_cert_pairs); + options.num_key_cert_pairs = config->num_key_cert_pairs; + options.pem_client_root_certs = config->pem_root_certs; + options.client_certificate_request = + grpc_get_tsi_client_certificate_request_type( + server_creds->config().client_certificate_request); + options.cipher_suites = grpc_get_ssl_cipher_suites(); + options.alpn_protocols = alpn_protocol_strings; + options.num_alpn_protocols = static_cast(num_alpn_protocols); + tsi_result result = tsi_create_ssl_server_handshaker_factory_with_options( + &options, &new_handshaker_factory); + gpr_free((void*)options.pem_key_cert_pairs); + gpr_free((void*)alpn_protocol_strings); + + if (result != TSI_OK) { + gpr_log(GPR_ERROR, "Handshaker factory creation failed with %s.", + tsi_result_to_string(result)); return false; } set_server_handshaker_factory(new_handshaker_factory); @@ -319,17 +400,28 @@ grpc_ssl_channel_security_connector_create( gpr_log(GPR_ERROR, "An ssl channel needs a config and a target name."); return nullptr; } - if (config->pem_root_certs == nullptr && - grpc_core::DefaultSslRootStore::GetPemRootCerts() == nullptr) { - gpr_log(GPR_ERROR, "Could not get pem root certs."); - return nullptr; + + const char* pem_root_certs; + const tsi_ssl_root_certs_store* root_store; + if (config->pem_root_certs == nullptr) { + // Use default root certificates. + pem_root_certs = grpc_core::DefaultSslRootStore::GetPemRootCerts(); + if (pem_root_certs == nullptr) { + gpr_log(GPR_ERROR, "Could not get default pem root certs."); + return nullptr; + } + root_store = grpc_core::DefaultSslRootStore::GetRootStore(); + } else { + pem_root_certs = config->pem_root_certs; + root_store = nullptr; } + grpc_core::RefCountedPtr c = grpc_core::MakeRefCounted( std::move(channel_creds), std::move(request_metadata_creds), config, target_name, overridden_target_name); - const grpc_security_status result = - c->InitializeHandshakerFactory(config, ssl_session_cache); + const grpc_security_status result = c->InitializeHandshakerFactory( + config, pem_root_certs, root_store, ssl_session_cache); if (result != GRPC_SECURITY_OK) { return nullptr; }