| /* |
| * |
| * 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 <grpc/support/port_platform.h> |
| |
| #include "src/core/lib/security/security_connector/local/local_security_connector.h" |
| |
| #include <stdbool.h> |
| #include <string.h> |
| |
| #include <grpc/grpc.h> |
| #include <grpc/support/alloc.h> |
| #include <grpc/support/log.h> |
| #include <grpc/support/string_util.h> |
| |
| #include "src/core/ext/filters/client_channel/client_channel.h" |
| #include "src/core/lib/channel/channel_args.h" |
| #include "src/core/lib/gprpp/ref_counted_ptr.h" |
| #include "src/core/lib/iomgr/pollset.h" |
| #include "src/core/lib/iomgr/resolve_address.h" |
| #include "src/core/lib/iomgr/sockaddr.h" |
| #include "src/core/lib/iomgr/sockaddr_utils.h" |
| #include "src/core/lib/iomgr/socket_utils.h" |
| #include "src/core/lib/iomgr/unix_sockets_posix.h" |
| #include "src/core/lib/security/credentials/local/local_credentials.h" |
| #include "src/core/lib/security/transport/security_handshaker.h" |
| #include "src/core/tsi/local_transport_security.h" |
| |
| #define GRPC_UDS_URI_PATTERN "unix:" |
| #define GRPC_LOCAL_TRANSPORT_SECURITY_TYPE "local" |
| |
| namespace { |
| |
| grpc_core::RefCountedPtr<grpc_auth_context> local_auth_context_create() { |
| /* Create auth context. */ |
| 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_LOCAL_TRANSPORT_SECURITY_TYPE); |
| GPR_ASSERT(grpc_auth_context_set_peer_identity_property_name( |
| ctx.get(), GRPC_TRANSPORT_SECURITY_TYPE_PROPERTY_NAME) == 1); |
| return ctx; |
| } |
| |
| void local_check_peer(grpc_security_connector* sc, tsi_peer peer, |
| grpc_endpoint* ep, |
| grpc_core::RefCountedPtr<grpc_auth_context>* auth_context, |
| grpc_closure* on_peer_checked, |
| grpc_local_connect_type type) { |
| int fd = grpc_endpoint_get_fd(ep); |
| grpc_resolved_address resolved_addr; |
| memset(&resolved_addr, 0, sizeof(resolved_addr)); |
| resolved_addr.len = GRPC_MAX_SOCKADDR_SIZE; |
| bool is_endpoint_local = false; |
| if (getsockname(fd, reinterpret_cast<grpc_sockaddr*>(resolved_addr.addr), |
| &resolved_addr.len) == 0) { |
| grpc_resolved_address addr_normalized; |
| grpc_resolved_address* addr = |
| grpc_sockaddr_is_v4mapped(&resolved_addr, &addr_normalized) |
| ? &addr_normalized |
| : &resolved_addr; |
| grpc_sockaddr* sock_addr = reinterpret_cast<grpc_sockaddr*>(&addr->addr); |
| // UDS |
| if (type == UDS && grpc_is_unix_socket(addr)) { |
| is_endpoint_local = true; |
| // IPV4 |
| } else if (type == LOCAL_TCP && sock_addr->sa_family == GRPC_AF_INET) { |
| const grpc_sockaddr_in* addr4 = |
| reinterpret_cast<const grpc_sockaddr_in*>(sock_addr); |
| if (grpc_htonl(addr4->sin_addr.s_addr) == INADDR_LOOPBACK) { |
| is_endpoint_local = true; |
| } |
| // IPv6 |
| } else if (type == LOCAL_TCP && sock_addr->sa_family == GRPC_AF_INET6) { |
| const grpc_sockaddr_in6* addr6 = |
| reinterpret_cast<const grpc_sockaddr_in6*>(addr); |
| if (memcmp(&addr6->sin6_addr, &in6addr_loopback, |
| sizeof(in6addr_loopback)) == 0) { |
| is_endpoint_local = true; |
| } |
| } |
| } |
| grpc_error* error = GRPC_ERROR_NONE; |
| if (!is_endpoint_local) { |
| error = GRPC_ERROR_CREATE_FROM_STATIC_STRING( |
| "Endpoint is neither UDS or TCP loopback address."); |
| GRPC_CLOSURE_SCHED(on_peer_checked, error); |
| return; |
| } |
| /* Create an auth context which is necessary to pass the santiy check in |
| * {client, server}_auth_filter that verifies if the peer's auth context is |
| * obtained during handshakes. The auth context is only checked for its |
| * existence and not actually used. |
| */ |
| *auth_context = local_auth_context_create(); |
| error = *auth_context != nullptr ? GRPC_ERROR_NONE |
| : GRPC_ERROR_CREATE_FROM_STATIC_STRING( |
| "Could not create local auth context"); |
| GRPC_CLOSURE_SCHED(on_peer_checked, error); |
| } |
| |
| class grpc_local_channel_security_connector final |
| : public grpc_channel_security_connector { |
| public: |
| grpc_local_channel_security_connector( |
| grpc_core::RefCountedPtr<grpc_channel_credentials> channel_creds, |
| grpc_core::RefCountedPtr<grpc_call_credentials> request_metadata_creds, |
| const char* target_name) |
| : grpc_channel_security_connector(nullptr, std::move(channel_creds), |
| std::move(request_metadata_creds)), |
| target_name_(gpr_strdup(target_name)) {} |
| |
| ~grpc_local_channel_security_connector() override { gpr_free(target_name_); } |
| |
| void add_handshakers(grpc_pollset_set* interested_parties, |
| grpc_handshake_manager* handshake_manager) override { |
| tsi_handshaker* handshaker = nullptr; |
| GPR_ASSERT(local_tsi_handshaker_create(true /* is_client */, &handshaker) == |
| TSI_OK); |
| grpc_handshake_manager_add( |
| handshake_manager, grpc_security_handshaker_create(handshaker, this)); |
| } |
| |
| int cmp(const grpc_security_connector* other_sc) const override { |
| auto* other = |
| reinterpret_cast<const grpc_local_channel_security_connector*>( |
| other_sc); |
| int c = channel_security_connector_cmp(other); |
| if (c != 0) return c; |
| return strcmp(target_name_, other->target_name_); |
| } |
| |
| void check_peer(tsi_peer peer, grpc_endpoint* ep, |
| grpc_core::RefCountedPtr<grpc_auth_context>* auth_context, |
| grpc_closure* on_peer_checked) override { |
| grpc_local_credentials* creds = |
| reinterpret_cast<grpc_local_credentials*>(mutable_channel_creds()); |
| local_check_peer(this, peer, ep, auth_context, on_peer_checked, |
| creds->connect_type()); |
| } |
| |
| bool check_call_host(const char* host, grpc_auth_context* auth_context, |
| grpc_closure* on_call_host_checked, |
| grpc_error** error) override { |
| if (host == nullptr || strcmp(host, target_name_) != 0) { |
| *error = GRPC_ERROR_CREATE_FROM_STATIC_STRING( |
| "local call host does not match target name"); |
| } |
| return true; |
| } |
| |
| void cancel_check_call_host(grpc_closure* on_call_host_checked, |
| grpc_error* error) override { |
| GRPC_ERROR_UNREF(error); |
| } |
| |
| const char* target_name() const { return target_name_; } |
| |
| private: |
| char* target_name_; |
| }; |
| |
| class grpc_local_server_security_connector final |
| : public grpc_server_security_connector { |
| public: |
| grpc_local_server_security_connector( |
| grpc_core::RefCountedPtr<grpc_server_credentials> server_creds) |
| : grpc_server_security_connector(nullptr, std::move(server_creds)) {} |
| ~grpc_local_server_security_connector() override = default; |
| |
| void add_handshakers(grpc_pollset_set* interested_parties, |
| grpc_handshake_manager* handshake_manager) override { |
| tsi_handshaker* handshaker = nullptr; |
| GPR_ASSERT(local_tsi_handshaker_create(false /* is_client */, |
| &handshaker) == TSI_OK); |
| grpc_handshake_manager_add( |
| handshake_manager, grpc_security_handshaker_create(handshaker, this)); |
| } |
| |
| void check_peer(tsi_peer peer, grpc_endpoint* ep, |
| grpc_core::RefCountedPtr<grpc_auth_context>* auth_context, |
| grpc_closure* on_peer_checked) override { |
| grpc_local_server_credentials* creds = |
| static_cast<grpc_local_server_credentials*>(mutable_server_creds()); |
| local_check_peer(this, peer, ep, auth_context, on_peer_checked, |
| creds->connect_type()); |
| } |
| |
| int cmp(const grpc_security_connector* other) const override { |
| return server_security_connector_cmp( |
| static_cast<const grpc_server_security_connector*>(other)); |
| } |
| }; |
| } // namespace |
| |
| grpc_core::RefCountedPtr<grpc_channel_security_connector> |
| grpc_local_channel_security_connector_create( |
| grpc_core::RefCountedPtr<grpc_channel_credentials> channel_creds, |
| grpc_core::RefCountedPtr<grpc_call_credentials> request_metadata_creds, |
| const grpc_channel_args* args, const char* target_name) { |
| if (channel_creds == nullptr || target_name == nullptr) { |
| gpr_log( |
| GPR_ERROR, |
| "Invalid arguments to grpc_local_channel_security_connector_create()"); |
| return nullptr; |
| } |
| // Perform sanity check on UDS address. For TCP local connection, the check |
| // will be done during check_peer procedure. |
| grpc_local_credentials* creds = |
| static_cast<grpc_local_credentials*>(channel_creds.get()); |
| const grpc_arg* server_uri_arg = |
| grpc_channel_args_find(args, GRPC_ARG_SERVER_URI); |
| const char* server_uri_str = grpc_channel_arg_get_string(server_uri_arg); |
| if (creds->connect_type() == UDS && |
| strncmp(GRPC_UDS_URI_PATTERN, server_uri_str, |
| strlen(GRPC_UDS_URI_PATTERN)) != 0) { |
| gpr_log(GPR_ERROR, |
| "Invalid UDS target name to " |
| "grpc_local_channel_security_connector_create()"); |
| return nullptr; |
| } |
| return grpc_core::MakeRefCounted<grpc_local_channel_security_connector>( |
| channel_creds, request_metadata_creds, target_name); |
| } |
| |
| grpc_core::RefCountedPtr<grpc_server_security_connector> |
| grpc_local_server_security_connector_create( |
| grpc_core::RefCountedPtr<grpc_server_credentials> server_creds) { |
| if (server_creds == nullptr) { |
| gpr_log( |
| GPR_ERROR, |
| "Invalid arguments to grpc_local_server_security_connector_create()"); |
| return nullptr; |
| } |
| return grpc_core::MakeRefCounted<grpc_local_server_security_connector>( |
| std::move(server_creds)); |
| } |