diff options
author | apolcyn <apolcyn@google.com> | 2017-11-14 12:24:39 -0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-11-14 12:24:39 -0800 |
commit | 71d5ec7cffa2ccc0ce6448525f90de9be3e8c9be (patch) | |
tree | 0948cef35d2826048b6259139114ee1658dbb497 | |
parent | 42fc8d4a9375fb0814526fb0f4fd20441dba1088 (diff) | |
parent | f51b7e16ee96cbcd23a85aef982ca7a4bada2727 (diff) |
Merge pull request #13189 from apolcyn/pass_args_to_tsi
Use existing read buffer in security handshaker if present
-rw-r--r-- | CMakeLists.txt | 38 | ||||
-rw-r--r-- | Makefile | 42 | ||||
-rw-r--r-- | build.yaml | 21 | ||||
-rw-r--r-- | src/core/lib/security/transport/security_handshaker.cc | 45 | ||||
-rw-r--r-- | test/core/handshake/BUILD | 31 | ||||
-rw-r--r-- | test/core/handshake/readahead_handshaker_server_ssl.cc | 103 | ||||
-rw-r--r-- | test/core/handshake/server_ssl.cc | 201 | ||||
-rw-r--r-- | test/core/handshake/server_ssl_common.cc | 238 | ||||
-rw-r--r-- | test/core/handshake/server_ssl_common.h | 36 | ||||
-rw-r--r-- | tools/run_tests/generated/sources_and_headers.json | 29 | ||||
-rw-r--r-- | tools/run_tests/generated/tests.json | 20 |
11 files changed, 582 insertions, 222 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 4daff95efd..7eb0eb79c8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -462,6 +462,9 @@ endif() if(_gRPC_PLATFORM_LINUX) add_dependencies(buildtests_c handshake_server) endif() +if(_gRPC_PLATFORM_LINUX) +add_dependencies(buildtests_c handshake_server_with_readahead_handshaker) +endif() add_dependencies(buildtests_c hpack_parser_test) add_dependencies(buildtests_c hpack_table_test) add_dependencies(buildtests_c http_parser_test) @@ -7236,6 +7239,7 @@ if(_gRPC_PLATFORM_LINUX) add_executable(handshake_server test/core/handshake/server_ssl.cc + test/core/handshake/server_ssl_common.cc ) @@ -7265,6 +7269,40 @@ target_link_libraries(handshake_server endif() endif (gRPC_BUILD_TESTS) if (gRPC_BUILD_TESTS) +if(_gRPC_PLATFORM_LINUX) + +add_executable(handshake_server_with_readahead_handshaker + test/core/handshake/readahead_handshaker_server_ssl.cc + test/core/handshake/server_ssl_common.cc +) + + +target_include_directories(handshake_server_with_readahead_handshaker + PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} + PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include + PRIVATE ${BORINGSSL_ROOT_DIR}/include + PRIVATE ${PROTOBUF_ROOT_DIR}/src + PRIVATE ${BENCHMARK_ROOT_DIR}/include + PRIVATE ${ZLIB_ROOT_DIR} + PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/third_party/zlib + PRIVATE ${CARES_INCLUDE_DIR} + PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/third_party/cares/cares + PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/third_party/gflags/include + PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/third_party/abseil-cpp +) + +target_link_libraries(handshake_server_with_readahead_handshaker + ${_gRPC_SSL_LIBRARIES} + ${_gRPC_ALLTARGETS_LIBRARIES} + grpc_test_util + grpc + gpr_test_util + gpr +) + +endif() +endif (gRPC_BUILD_TESTS) +if (gRPC_BUILD_TESTS) add_executable(hpack_parser_test test/core/transport/chttp2/hpack_parser_test.cc @@ -1019,6 +1019,7 @@ grpc_ssl_credentials_test: $(BINDIR)/$(CONFIG)/grpc_ssl_credentials_test grpc_verify_jwt: $(BINDIR)/$(CONFIG)/grpc_verify_jwt handshake_client: $(BINDIR)/$(CONFIG)/handshake_client handshake_server: $(BINDIR)/$(CONFIG)/handshake_server +handshake_server_with_readahead_handshaker: $(BINDIR)/$(CONFIG)/handshake_server_with_readahead_handshaker hpack_parser_fuzzer_test: $(BINDIR)/$(CONFIG)/hpack_parser_fuzzer_test hpack_parser_test: $(BINDIR)/$(CONFIG)/hpack_parser_test hpack_table_test: $(BINDIR)/$(CONFIG)/hpack_table_test @@ -1408,6 +1409,7 @@ buildtests_c: privatelibs_c \ $(BINDIR)/$(CONFIG)/grpc_ssl_credentials_test \ $(BINDIR)/$(CONFIG)/handshake_client \ $(BINDIR)/$(CONFIG)/handshake_server \ + $(BINDIR)/$(CONFIG)/handshake_server_with_readahead_handshaker \ $(BINDIR)/$(CONFIG)/hpack_parser_test \ $(BINDIR)/$(CONFIG)/hpack_table_test \ $(BINDIR)/$(CONFIG)/http_parser_test \ @@ -1876,6 +1878,8 @@ test_c: buildtests_c $(Q) $(BINDIR)/$(CONFIG)/handshake_client || ( echo test handshake_client failed ; exit 1 ) $(E) "[RUN] Testing handshake_server" $(Q) $(BINDIR)/$(CONFIG)/handshake_server || ( echo test handshake_server failed ; exit 1 ) + $(E) "[RUN] Testing handshake_server_with_readahead_handshaker" + $(Q) $(BINDIR)/$(CONFIG)/handshake_server_with_readahead_handshaker || ( echo test handshake_server_with_readahead_handshaker failed ; exit 1 ) $(E) "[RUN] Testing hpack_parser_test" $(Q) $(BINDIR)/$(CONFIG)/hpack_parser_test || ( echo test hpack_parser_test failed ; exit 1 ) $(E) "[RUN] Testing hpack_table_test" @@ -11040,6 +11044,7 @@ endif HANDSHAKE_SERVER_SRC = \ test/core/handshake/server_ssl.cc \ + test/core/handshake/server_ssl_common.cc \ HANDSHAKE_SERVER_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(HANDSHAKE_SERVER_SRC)))) ifeq ($(NO_SECURE),true) @@ -11061,6 +11066,8 @@ endif $(OBJDIR)/$(CONFIG)/test/core/handshake/server_ssl.o: $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a +$(OBJDIR)/$(CONFIG)/test/core/handshake/server_ssl_common.o: $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + deps_handshake_server: $(HANDSHAKE_SERVER_OBJS:.o=.dep) ifneq ($(NO_SECURE),true) @@ -11070,6 +11077,41 @@ endif endif +HANDSHAKE_SERVER_WITH_READAHEAD_HANDSHAKER_SRC = \ + test/core/handshake/readahead_handshaker_server_ssl.cc \ + test/core/handshake/server_ssl_common.cc \ + +HANDSHAKE_SERVER_WITH_READAHEAD_HANDSHAKER_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(HANDSHAKE_SERVER_WITH_READAHEAD_HANDSHAKER_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/handshake_server_with_readahead_handshaker: openssl_dep_error + +else + + + +$(BINDIR)/$(CONFIG)/handshake_server_with_readahead_handshaker: $(HANDSHAKE_SERVER_WITH_READAHEAD_HANDSHAKER_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(HANDSHAKE_SERVER_WITH_READAHEAD_HANDSHAKER_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/handshake_server_with_readahead_handshaker + +endif + +$(OBJDIR)/$(CONFIG)/test/core/handshake/readahead_handshaker_server_ssl.o: $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + +$(OBJDIR)/$(CONFIG)/test/core/handshake/server_ssl_common.o: $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + +deps_handshake_server_with_readahead_handshaker: $(HANDSHAKE_SERVER_WITH_READAHEAD_HANDSHAKER_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(HANDSHAKE_SERVER_WITH_READAHEAD_HANDSHAKER_OBJS:.o=.dep) +endif +endif + + HPACK_PARSER_FUZZER_TEST_SRC = \ test/core/transport/chttp2/hpack_parser_fuzzer_test.cc \ diff --git a/build.yaml b/build.yaml index cd6486ea29..910b00ebe2 100644 --- a/build.yaml +++ b/build.yaml @@ -2511,8 +2511,29 @@ targets: - name: handshake_server build: test language: c + headers: + - test/core/handshake/server_ssl_common.h src: - test/core/handshake/server_ssl.cc + - test/core/handshake/server_ssl_common.cc + deps: + - grpc_test_util + - grpc + - gpr_test_util + - gpr + exclude_iomgrs: + - uv + platforms: + - linux + secure: true +- name: handshake_server_with_readahead_handshaker + build: test + language: c + headers: + - test/core/handshake/server_ssl_common.h + src: + - test/core/handshake/readahead_handshaker_server_ssl.cc + - test/core/handshake/server_ssl_common.cc deps: - grpc_test_util - grpc diff --git a/src/core/lib/security/transport/security_handshaker.cc b/src/core/lib/security/transport/security_handshaker.cc index c540445792..7067b70cb6 100644 --- a/src/core/lib/security/transport/security_handshaker.cc +++ b/src/core/lib/security/transport/security_handshaker.cc @@ -65,6 +65,25 @@ typedef struct { tsi_handshaker_result* handshaker_result; } security_handshaker; +static size_t move_read_buffer_into_handshake_buffer(grpc_exec_ctx* exec_ctx, + security_handshaker* h) { + size_t bytes_in_read_buffer = h->args->read_buffer->length; + if (h->handshake_buffer_size < bytes_in_read_buffer) { + h->handshake_buffer = + (uint8_t*)gpr_realloc(h->handshake_buffer, bytes_in_read_buffer); + h->handshake_buffer_size = bytes_in_read_buffer; + } + size_t offset = 0; + while (h->args->read_buffer->count > 0) { + grpc_slice next_slice = grpc_slice_buffer_take_first(h->args->read_buffer); + memcpy(h->handshake_buffer + offset, GRPC_SLICE_START_PTR(next_slice), + GRPC_SLICE_LENGTH(next_slice)); + offset += GRPC_SLICE_LENGTH(next_slice); + grpc_slice_unref_internal(exec_ctx, next_slice); + } + return bytes_in_read_buffer; +} + static void security_handshaker_unref(grpc_exec_ctx* exec_ctx, security_handshaker* h) { if (gpr_unref(&h->refs)) { @@ -177,8 +196,6 @@ static void on_peer_checked_inner(grpc_exec_ctx* exec_ctx, } tsi_handshaker_result_destroy(h->handshaker_result); h->handshaker_result = nullptr; - // Clear out the read buffer before it gets passed to the transport. - grpc_slice_buffer_reset_and_unref_internal(exec_ctx, h->args->read_buffer); // Add auth context to channel args. grpc_arg auth_context_arg = grpc_auth_context_to_arg(h->auth_context); grpc_channel_args* tmp_args = h->args->args; @@ -312,23 +329,8 @@ static void on_handshake_data_received_from_peer(grpc_exec_ctx* exec_ctx, return; } // Copy all slices received. - size_t i; - size_t bytes_received_size = 0; - for (i = 0; i < h->args->read_buffer->count; i++) { - bytes_received_size += GRPC_SLICE_LENGTH(h->args->read_buffer->slices[i]); - } - if (bytes_received_size > h->handshake_buffer_size) { - h->handshake_buffer = - (uint8_t*)gpr_realloc(h->handshake_buffer, bytes_received_size); - h->handshake_buffer_size = bytes_received_size; - } - size_t offset = 0; - for (i = 0; i < h->args->read_buffer->count; i++) { - size_t slice_size = GPR_SLICE_LENGTH(h->args->read_buffer->slices[i]); - memcpy(h->handshake_buffer + offset, - GRPC_SLICE_START_PTR(h->args->read_buffer->slices[i]), slice_size); - offset += slice_size; - } + size_t bytes_received_size = + move_read_buffer_into_handshake_buffer(exec_ctx, h); // Call TSI handshaker. error = do_handshaker_next_locked(exec_ctx, h, h->handshake_buffer, bytes_received_size); @@ -405,7 +407,10 @@ static void security_handshaker_do_handshake(grpc_exec_ctx* exec_ctx, h->args = args; h->on_handshake_done = on_handshake_done; gpr_ref(&h->refs); - grpc_error* error = do_handshaker_next_locked(exec_ctx, h, nullptr, 0); + size_t bytes_received_size = + move_read_buffer_into_handshake_buffer(exec_ctx, h); + grpc_error* error = do_handshaker_next_locked( + exec_ctx, h, h->handshake_buffer, bytes_received_size); if (error != GRPC_ERROR_NONE) { security_handshake_failed_locked(exec_ctx, h, error); gpr_mu_unlock(&h->mu); diff --git a/test/core/handshake/BUILD b/test/core/handshake/BUILD index aea4a27e99..a3276b9343 100644 --- a/test/core/handshake/BUILD +++ b/test/core/handshake/BUILD @@ -35,6 +35,18 @@ grpc_cc_test( ], ) +grpc_cc_library( + name = "server_ssl_common", + hdrs = ["server_ssl_common.h"], + srcs = ["server_ssl_common.cc"], + deps = [ + "//:gpr", + "//:grpc", + "//test/core/util:gpr_test_util", + "//test/core/util:grpc_test_util", + ], +) + grpc_cc_test( name = "server_ssl", srcs = ["server_ssl.cc"], @@ -45,6 +57,25 @@ grpc_cc_test( "//src/core/tsi/test_creds:server1.pem", ], deps = [ + ":server_ssl_common", + "//:gpr", + "//:grpc", + "//test/core/util:gpr_test_util", + "//test/core/util:grpc_test_util", + ], +) + +grpc_cc_test( + name = "handshake_server_with_readahead_handshaker", + srcs = ["readahead_handshaker_server_ssl.cc"], + language = "C++", + data = [ + "//src/core/tsi/test_creds:ca.pem", + "//src/core/tsi/test_creds:server1.key", + "//src/core/tsi/test_creds:server1.pem", + ], + deps = [ + ":server_ssl_common", "//:gpr", "//:grpc", "//test/core/util:gpr_test_util", diff --git a/test/core/handshake/readahead_handshaker_server_ssl.cc b/test/core/handshake/readahead_handshaker_server_ssl.cc new file mode 100644 index 0000000000..2810082837 --- /dev/null +++ b/test/core/handshake/readahead_handshaker_server_ssl.cc @@ -0,0 +1,103 @@ +/* + * + * Copyright 2016 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 <arpa/inet.h> +#include <openssl/err.h> +#include <openssl/ssl.h> +#include <string.h> +#include <sys/socket.h> +#include <unistd.h> + +#include <grpc/grpc.h> +#include <grpc/grpc_security.h> +#include <grpc/support/alloc.h> +#include <grpc/support/log.h> +#include <grpc/support/string_util.h> +#include <grpc/support/sync.h> +#include <grpc/support/thd.h> +#include "src/core/lib/iomgr/load_file.h" +#include "test/core/util/port.h" +#include "test/core/util/test_config.h" + +#include "src/core/lib/channel/handshaker_factory.h" +#include "src/core/lib/channel/handshaker_registry.h" +#include "src/core/lib/security/transport/security_handshaker.h" + +#include "test/core/handshake/server_ssl_common.h" + +/* The purpose of this test is to exercise the case when a + * grpc *security_handshaker* begins its handshake with data already + * in the read buffer of the handshaker arg. This scenario is created by + * adding a fake "readahead" handshaker at the beginning of the server's + * handshaker list, which just reads from the connection and then places + * read bytes into the read buffer of the handshake arg (to be passed down + * to the security_handshaker). This test is meant to protect code relying on + * this functionality that lives outside of this repo. */ + +static void readahead_handshaker_destroy(grpc_exec_ctx* ctx, + grpc_handshaker* handshaker) { + gpr_free(handshaker); +} + +static void readahead_handshaker_shutdown(grpc_exec_ctx* ctx, + grpc_handshaker* handshaker, + grpc_error* error) {} + +static void readahead_handshaker_do_handshake( + grpc_exec_ctx* ctx, grpc_handshaker* handshaker, + grpc_tcp_server_acceptor* acceptor, grpc_closure* on_handshake_done, + grpc_handshaker_args* args) { + grpc_endpoint_read(ctx, args->endpoint, args->read_buffer, on_handshake_done); +} + +const grpc_handshaker_vtable readahead_handshaker_vtable = { + readahead_handshaker_destroy, readahead_handshaker_shutdown, + readahead_handshaker_do_handshake}; + +static grpc_handshaker* readahead_handshaker_create(grpc_exec_ctx* ctx) { + grpc_handshaker* h = (grpc_handshaker*)gpr_zalloc(sizeof(grpc_handshaker)); + grpc_handshaker_init(&readahead_handshaker_vtable, h); + return h; +} + +static void readahead_handshaker_factory_add_handshakers( + grpc_exec_ctx* exec_ctx, grpc_handshaker_factory* hf, + const grpc_channel_args* args, grpc_handshake_manager* handshake_mgr) { + grpc_handshake_manager_add(handshake_mgr, + readahead_handshaker_create(exec_ctx)); +} + +static void readahead_handshaker_factory_destroy( + grpc_exec_ctx* exec_ctx, grpc_handshaker_factory* handshaker_factory) {} + +static const grpc_handshaker_factory_vtable + readahead_handshaker_factory_vtable = { + readahead_handshaker_factory_add_handshakers, + readahead_handshaker_factory_destroy}; + +int main(int argc, char* argv[]) { + grpc_handshaker_factory readahead_handshaker_factory = { + &readahead_handshaker_factory_vtable}; + grpc_init(); + grpc_handshaker_factory_register(true /* at_start */, HANDSHAKER_SERVER, + &readahead_handshaker_factory); + const char* full_alpn_list[] = {"grpc-exp", "h2"}; + GPR_ASSERT(server_ssl_test(full_alpn_list, 2, "grpc-exp")); + grpc_shutdown(); + return 0; +} diff --git a/test/core/handshake/server_ssl.cc b/test/core/handshake/server_ssl.cc index 69c4fdc11b..736d3e578e 100644 --- a/test/core/handshake/server_ssl.cc +++ b/test/core/handshake/server_ssl.cc @@ -34,206 +34,7 @@ #include "test/core/util/port.h" #include "test/core/util/test_config.h" -#define SSL_CERT_PATH "src/core/tsi/test_creds/server1.pem" -#define SSL_KEY_PATH "src/core/tsi/test_creds/server1.key" -#define SSL_CA_PATH "src/core/tsi/test_creds/ca.pem" - -// Handshake completed signal to server thread. -static gpr_event client_handshake_complete; - -static int create_socket(int port) { - int s; - struct sockaddr_in addr; - - addr.sin_family = AF_INET; - addr.sin_port = htons((uint16_t)port); - addr.sin_addr.s_addr = htonl(INADDR_ANY); - - s = socket(AF_INET, SOCK_STREAM, 0); - if (s < 0) { - perror("Unable to create socket"); - return -1; - } - - if (connect(s, (struct sockaddr*)&addr, sizeof(addr)) < 0) { - perror("Unable to connect"); - return -1; - } - - return s; -} - -// Simple gRPC server. This listens until client_handshake_complete occurs. -static void server_thread(void* arg) { - const int port = *(int*)arg; - - // Load key pair and establish server SSL credentials. - grpc_ssl_pem_key_cert_pair pem_key_cert_pair; - grpc_slice ca_slice, cert_slice, key_slice; - GPR_ASSERT(GRPC_LOG_IF_ERROR("load_file", - grpc_load_file(SSL_CA_PATH, 1, &ca_slice))); - GPR_ASSERT(GRPC_LOG_IF_ERROR("load_file", - grpc_load_file(SSL_CERT_PATH, 1, &cert_slice))); - GPR_ASSERT(GRPC_LOG_IF_ERROR("load_file", - grpc_load_file(SSL_KEY_PATH, 1, &key_slice))); - const char* ca_cert = (const char*)GRPC_SLICE_START_PTR(ca_slice); - pem_key_cert_pair.private_key = (const char*)GRPC_SLICE_START_PTR(key_slice); - pem_key_cert_pair.cert_chain = (const char*)GRPC_SLICE_START_PTR(cert_slice); - grpc_server_credentials* ssl_creds = grpc_ssl_server_credentials_create( - ca_cert, &pem_key_cert_pair, 1, 0, nullptr); - - // Start server listening on local port. - char* addr; - gpr_asprintf(&addr, "127.0.0.1:%d", port); - grpc_server* server = grpc_server_create(nullptr, nullptr); - GPR_ASSERT(grpc_server_add_secure_http2_port(server, addr, ssl_creds)); - free(addr); - - grpc_completion_queue* cq = grpc_completion_queue_create_for_next(nullptr); - - grpc_server_register_completion_queue(server, cq, nullptr); - grpc_server_start(server); - - // Wait a bounded number of time until client_handshake_complete is set, - // sleeping between polls. - int retries = 10; - while (!gpr_event_get(&client_handshake_complete) && retries-- > 0) { - const gpr_timespec cq_deadline = grpc_timeout_seconds_to_deadline(1); - grpc_event ev = grpc_completion_queue_next(cq, cq_deadline, nullptr); - GPR_ASSERT(ev.type == GRPC_QUEUE_TIMEOUT); - } - - gpr_log(GPR_INFO, "Shutting down server"); - grpc_server_shutdown_and_notify(server, cq, nullptr); - grpc_completion_queue_shutdown(cq); - - const gpr_timespec cq_deadline = grpc_timeout_seconds_to_deadline(5); - grpc_event ev = grpc_completion_queue_next(cq, cq_deadline, nullptr); - GPR_ASSERT(ev.type == GRPC_OP_COMPLETE); - - grpc_server_destroy(server); - grpc_completion_queue_destroy(cq); - grpc_server_credentials_release(ssl_creds); - grpc_slice_unref(cert_slice); - grpc_slice_unref(key_slice); - grpc_slice_unref(ca_slice); -} - -// This test launches a gRPC server on a separate thread and then establishes a -// TLS handshake via a minimal TLS client. The TLS client has configurable (via -// alpn_list) ALPN settings and can probe at the supported ALPN preferences -// using this (via alpn_expected). -static bool server_ssl_test(const char* alpn_list[], unsigned int alpn_list_len, - const char* alpn_expected) { - bool success = true; - - grpc_init(); - int port = grpc_pick_unused_port_or_die(); - gpr_event_init(&client_handshake_complete); - - // Launch the gRPC server thread. - gpr_thd_options thdopt = gpr_thd_options_default(); - gpr_thd_id thdid; - gpr_thd_options_set_joinable(&thdopt); - GPR_ASSERT(gpr_thd_new(&thdid, server_thread, &port, &thdopt)); - - SSL_load_error_strings(); - OpenSSL_add_ssl_algorithms(); - - const SSL_METHOD* method = TLSv1_2_client_method(); - SSL_CTX* ctx = SSL_CTX_new(method); - if (!ctx) { - perror("Unable to create SSL context"); - ERR_print_errors_fp(stderr); - abort(); - } - - // Load key pair. - if (SSL_CTX_use_certificate_file(ctx, SSL_CERT_PATH, SSL_FILETYPE_PEM) < 0) { - ERR_print_errors_fp(stderr); - abort(); - } - if (SSL_CTX_use_PrivateKey_file(ctx, SSL_KEY_PATH, SSL_FILETYPE_PEM) < 0) { - ERR_print_errors_fp(stderr); - abort(); - } - - // Set the cipher list to match the one expressed in - // src/core/tsi/ssl_transport_security.c. - const char* cipher_list = - "ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-" - "SHA384:ECDHE-RSA-AES256-GCM-SHA384"; - if (!SSL_CTX_set_cipher_list(ctx, cipher_list)) { - ERR_print_errors_fp(stderr); - gpr_log(GPR_ERROR, "Couldn't set server cipher list."); - abort(); - } - - // Configure ALPN list the client will send to the server. This must match the - // wire format, see documentation for SSL_CTX_set_alpn_protos. - unsigned int alpn_protos_len = alpn_list_len; - for (unsigned int i = 0; i < alpn_list_len; ++i) { - alpn_protos_len += (unsigned int)strlen(alpn_list[i]); - } - unsigned char* alpn_protos = - static_cast<unsigned char*>(gpr_malloc(alpn_protos_len)); - unsigned char* p = alpn_protos; - for (unsigned int i = 0; i < alpn_list_len; ++i) { - const uint8_t len = (uint8_t)strlen(alpn_list[i]); - *p++ = len; - memcpy(p, alpn_list[i], len); - p += len; - } - GPR_ASSERT(SSL_CTX_set_alpn_protos(ctx, alpn_protos, alpn_protos_len) == 0); - - // Try and connect to server. We allow a bounded number of retries as we might - // be racing with the server setup on its separate thread. - int retries = 10; - int sock = -1; - while (sock == -1 && retries-- > 0) { - sock = create_socket(port); - if (sock < 0) { - sleep(1); - } - } - GPR_ASSERT(sock > 0); - gpr_log(GPR_INFO, "Connected to server on port %d", port); - - // Establish a SSL* and connect at SSL layer. - SSL* ssl = SSL_new(ctx); - GPR_ASSERT(ssl); - SSL_set_fd(ssl, sock); - if (SSL_connect(ssl) <= 0) { - ERR_print_errors_fp(stderr); - gpr_log(GPR_ERROR, "Handshake failed."); - success = false; - } else { - gpr_log(GPR_INFO, "Handshake successful."); - // Validate ALPN preferred by server matches alpn_expected. - const unsigned char* alpn_selected; - unsigned int alpn_selected_len; - SSL_get0_alpn_selected(ssl, &alpn_selected, &alpn_selected_len); - if (strlen(alpn_expected) != alpn_selected_len || - strncmp((const char*)alpn_selected, alpn_expected, alpn_selected_len) != - 0) { - gpr_log(GPR_ERROR, "Unexpected ALPN protocol preference"); - success = false; - } - } - gpr_event_set(&client_handshake_complete, &client_handshake_complete); - - SSL_free(ssl); - gpr_free(alpn_protos); - SSL_CTX_free(ctx); - EVP_cleanup(); - close(sock); - - gpr_thd_join(thdid); - - grpc_shutdown(); - - return success; -} +#include "test/core/handshake/server_ssl_common.h" int main(int argc, char* argv[]) { // Handshake succeeeds when the client supplies the standard ALPN list. diff --git a/test/core/handshake/server_ssl_common.cc b/test/core/handshake/server_ssl_common.cc new file mode 100644 index 0000000000..599b2814e0 --- /dev/null +++ b/test/core/handshake/server_ssl_common.cc @@ -0,0 +1,238 @@ +/* + * + * Copyright 2016 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 "test/core/handshake/server_ssl_common.h" + +#include <arpa/inet.h> +#include <openssl/err.h> +#include <openssl/ssl.h> +#include <string.h> +#include <sys/socket.h> +#include <unistd.h> + +#include <grpc/grpc.h> +#include <grpc/grpc_security.h> +#include <grpc/support/alloc.h> +#include <grpc/support/log.h> +#include <grpc/support/string_util.h> +#include <grpc/support/sync.h> +#include <grpc/support/thd.h> +#include "src/core/lib/iomgr/load_file.h" +#include "test/core/util/port.h" +#include "test/core/util/test_config.h" + +#define SSL_CERT_PATH "src/core/tsi/test_creds/server1.pem" +#define SSL_KEY_PATH "src/core/tsi/test_creds/server1.key" +#define SSL_CA_PATH "src/core/tsi/test_creds/ca.pem" + +// Handshake completed signal to server thread. +static gpr_event client_handshake_complete; + +static int create_socket(int port) { + int s; + struct sockaddr_in addr; + + addr.sin_family = AF_INET; + addr.sin_port = htons((uint16_t)port); + addr.sin_addr.s_addr = htonl(INADDR_ANY); + + s = socket(AF_INET, SOCK_STREAM, 0); + if (s < 0) { + perror("Unable to create socket"); + return -1; + } + + if (connect(s, (struct sockaddr*)&addr, sizeof(addr)) < 0) { + perror("Unable to connect"); + return -1; + } + + return s; +} + +// Simple gRPC server. This listens until client_handshake_complete occurs. +static void server_thread(void* arg) { + const int port = *(int*)arg; + + // Load key pair and establish server SSL credentials. + grpc_ssl_pem_key_cert_pair pem_key_cert_pair; + grpc_slice ca_slice, cert_slice, key_slice; + GPR_ASSERT(GRPC_LOG_IF_ERROR("load_file", + grpc_load_file(SSL_CA_PATH, 1, &ca_slice))); + GPR_ASSERT(GRPC_LOG_IF_ERROR("load_file", + grpc_load_file(SSL_CERT_PATH, 1, &cert_slice))); + GPR_ASSERT(GRPC_LOG_IF_ERROR("load_file", + grpc_load_file(SSL_KEY_PATH, 1, &key_slice))); + const char* ca_cert = (const char*)GRPC_SLICE_START_PTR(ca_slice); + pem_key_cert_pair.private_key = (const char*)GRPC_SLICE_START_PTR(key_slice); + pem_key_cert_pair.cert_chain = (const char*)GRPC_SLICE_START_PTR(cert_slice); + grpc_server_credentials* ssl_creds = grpc_ssl_server_credentials_create( + ca_cert, &pem_key_cert_pair, 1, 0, nullptr); + + // Start server listening on local port. + char* addr; + gpr_asprintf(&addr, "127.0.0.1:%d", port); + grpc_server* server = grpc_server_create(nullptr, nullptr); + GPR_ASSERT(grpc_server_add_secure_http2_port(server, addr, ssl_creds)); + free(addr); + + grpc_completion_queue* cq = grpc_completion_queue_create_for_next(nullptr); + + grpc_server_register_completion_queue(server, cq, nullptr); + grpc_server_start(server); + + // Wait a bounded number of time until client_handshake_complete is set, + // sleeping between polls. + int retries = 10; + while (!gpr_event_get(&client_handshake_complete) && retries-- > 0) { + const gpr_timespec cq_deadline = grpc_timeout_seconds_to_deadline(1); + grpc_event ev = grpc_completion_queue_next(cq, cq_deadline, nullptr); + GPR_ASSERT(ev.type == GRPC_QUEUE_TIMEOUT); + } + + gpr_log(GPR_INFO, "Shutting down server"); + grpc_server_shutdown_and_notify(server, cq, nullptr); + grpc_completion_queue_shutdown(cq); + + const gpr_timespec cq_deadline = grpc_timeout_seconds_to_deadline(5); + grpc_event ev = grpc_completion_queue_next(cq, cq_deadline, nullptr); + GPR_ASSERT(ev.type == GRPC_OP_COMPLETE); + + grpc_server_destroy(server); + grpc_completion_queue_destroy(cq); + grpc_server_credentials_release(ssl_creds); + grpc_slice_unref(cert_slice); + grpc_slice_unref(key_slice); + grpc_slice_unref(ca_slice); +} + +// This test launches a gRPC server on a separate thread and then establishes a +// TLS handshake via a minimal TLS client. The TLS client has configurable (via +// alpn_list) ALPN settings and can probe at the supported ALPN preferences +// using this (via alpn_expected). +bool server_ssl_test(const char* alpn_list[], unsigned int alpn_list_len, + const char* alpn_expected) { + bool success = true; + + grpc_init(); + int port = grpc_pick_unused_port_or_die(); + gpr_event_init(&client_handshake_complete); + + // Launch the gRPC server thread. + gpr_thd_options thdopt = gpr_thd_options_default(); + gpr_thd_id thdid; + gpr_thd_options_set_joinable(&thdopt); + GPR_ASSERT(gpr_thd_new(&thdid, server_thread, &port, &thdopt)); + + SSL_load_error_strings(); + OpenSSL_add_ssl_algorithms(); + + const SSL_METHOD* method = TLSv1_2_client_method(); + SSL_CTX* ctx = SSL_CTX_new(method); + if (!ctx) { + perror("Unable to create SSL context"); + ERR_print_errors_fp(stderr); + abort(); + } + + // Load key pair. + if (SSL_CTX_use_certificate_file(ctx, SSL_CERT_PATH, SSL_FILETYPE_PEM) < 0) { + ERR_print_errors_fp(stderr); + abort(); + } + if (SSL_CTX_use_PrivateKey_file(ctx, SSL_KEY_PATH, SSL_FILETYPE_PEM) < 0) { + ERR_print_errors_fp(stderr); + abort(); + } + + // Set the cipher list to match the one expressed in + // src/core/tsi/ssl_transport_security.c. + const char* cipher_list = + "ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-" + "SHA384:ECDHE-RSA-AES256-GCM-SHA384"; + if (!SSL_CTX_set_cipher_list(ctx, cipher_list)) { + ERR_print_errors_fp(stderr); + gpr_log(GPR_ERROR, "Couldn't set server cipher list."); + abort(); + } + + // Configure ALPN list the client will send to the server. This must match the + // wire format, see documentation for SSL_CTX_set_alpn_protos. + unsigned int alpn_protos_len = alpn_list_len; + for (unsigned int i = 0; i < alpn_list_len; ++i) { + alpn_protos_len += (unsigned int)strlen(alpn_list[i]); + } + unsigned char* alpn_protos = + static_cast<unsigned char*>(gpr_malloc(alpn_protos_len)); + unsigned char* p = alpn_protos; + for (unsigned int i = 0; i < alpn_list_len; ++i) { + const uint8_t len = (uint8_t)strlen(alpn_list[i]); + *p++ = len; + memcpy(p, alpn_list[i], len); + p += len; + } + GPR_ASSERT(SSL_CTX_set_alpn_protos(ctx, alpn_protos, alpn_protos_len) == 0); + + // Try and connect to server. We allow a bounded number of retries as we might + // be racing with the server setup on its separate thread. + int retries = 10; + int sock = -1; + while (sock == -1 && retries-- > 0) { + sock = create_socket(port); + if (sock < 0) { + sleep(1); + } + } + GPR_ASSERT(sock > 0); + gpr_log(GPR_INFO, "Connected to server on port %d", port); + + // Establish a SSL* and connect at SSL layer. + SSL* ssl = SSL_new(ctx); + GPR_ASSERT(ssl); + SSL_set_fd(ssl, sock); + if (SSL_connect(ssl) <= 0) { + ERR_print_errors_fp(stderr); + gpr_log(GPR_ERROR, "Handshake failed."); + success = false; + } else { + gpr_log(GPR_INFO, "Handshake successful."); + // Validate ALPN preferred by server matches alpn_expected. + const unsigned char* alpn_selected; + unsigned int alpn_selected_len; + SSL_get0_alpn_selected(ssl, &alpn_selected, &alpn_selected_len); + if (strlen(alpn_expected) != alpn_selected_len || + strncmp((const char*)alpn_selected, alpn_expected, alpn_selected_len) != + 0) { + gpr_log(GPR_ERROR, "Unexpected ALPN protocol preference"); + success = false; + } + } + gpr_event_set(&client_handshake_complete, &client_handshake_complete); + + SSL_free(ssl); + gpr_free(alpn_protos); + SSL_CTX_free(ctx); + EVP_cleanup(); + close(sock); + + gpr_thd_join(thdid); + + grpc_shutdown(); + + return success; +} diff --git a/test/core/handshake/server_ssl_common.h b/test/core/handshake/server_ssl_common.h new file mode 100644 index 0000000000..77865a408f --- /dev/null +++ b/test/core/handshake/server_ssl_common.h @@ -0,0 +1,36 @@ +/* + * + * Copyright 2016 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. + * + */ + +#ifndef GRPC_SERVER_SSL_COMMON_H +#define GRPC_SERVER_SSL_COMMON_H + +#include <grpc/grpc.h> +#include <grpc/grpc_security.h> +#include <grpc/support/alloc.h> +#include <grpc/support/log.h> +#include <grpc/support/string_util.h> +#include <grpc/support/sync.h> +#include <grpc/support/thd.h> +#include "src/core/lib/iomgr/load_file.h" +#include "test/core/util/port.h" +#include "test/core/util/test_config.h" + +bool server_ssl_test(const char* alpn_list[], unsigned int alpn_list_len, + const char* alpn_expected); + +#endif // GRPC_SERVER_SSL_COMMON_H diff --git a/tools/run_tests/generated/sources_and_headers.json b/tools/run_tests/generated/sources_and_headers.json index 6cd9a04056..fce01bf8ff 100644 --- a/tools/run_tests/generated/sources_and_headers.json +++ b/tools/run_tests/generated/sources_and_headers.json @@ -1207,12 +1207,37 @@ "grpc", "grpc_test_util" ], - "headers": [], + "headers": [ + "test/core/handshake/server_ssl_common.h" + ], "is_filegroup": false, "language": "c", "name": "handshake_server", "src": [ - "test/core/handshake/server_ssl.cc" + "test/core/handshake/server_ssl.cc", + "test/core/handshake/server_ssl_common.cc", + "test/core/handshake/server_ssl_common.h" + ], + "third_party": false, + "type": "target" + }, + { + "deps": [ + "gpr", + "gpr_test_util", + "grpc", + "grpc_test_util" + ], + "headers": [ + "test/core/handshake/server_ssl_common.h" + ], + "is_filegroup": false, + "language": "c", + "name": "handshake_server_with_readahead_handshaker", + "src": [ + "test/core/handshake/readahead_handshaker_server_ssl.cc", + "test/core/handshake/server_ssl_common.cc", + "test/core/handshake/server_ssl_common.h" ], "third_party": false, "type": "target" diff --git a/tools/run_tests/generated/tests.json b/tools/run_tests/generated/tests.json index 5df5a7477b..5d7d3b3525 100644 --- a/tools/run_tests/generated/tests.json +++ b/tools/run_tests/generated/tests.json @@ -1507,6 +1507,26 @@ "args": [], "benchmark": false, "ci_platforms": [ + "linux" + ], + "cpu_cost": 1.0, + "exclude_configs": [], + "exclude_iomgrs": [ + "uv" + ], + "flaky": false, + "gtest": false, + "language": "c", + "name": "handshake_server_with_readahead_handshaker", + "platforms": [ + "linux" + ], + "uses_polling": true + }, + { + "args": [], + "benchmark": false, + "ci_platforms": [ "linux", "mac", "posix", |