diff options
43 files changed, 1809 insertions, 140 deletions
@@ -1704,6 +1704,9 @@ grpc_cc_library( "src/core/tsi/alts/handshaker/alts_tsi_handshaker.cc", "src/core/tsi/alts/handshaker/alts_tsi_utils.cc", "src/core/tsi/fake_transport_security.cc", + "src/core/tsi/ssl/session_cache/ssl_session_boringssl.cc", + "src/core/tsi/ssl/session_cache/ssl_session_cache.cc", + "src/core/tsi/ssl/session_cache/ssl_session_openssl.cc", "src/core/tsi/ssl_transport_security.cc", "src/core/tsi/transport_security_grpc.cc", ], @@ -1715,6 +1718,8 @@ grpc_cc_library( "src/core/tsi/alts/handshaker/alts_tsi_handshaker_private.h", "src/core/tsi/alts/handshaker/alts_tsi_utils.h", "src/core/tsi/fake_transport_security.h", + "src/core/tsi/ssl/session_cache/ssl_session.h", + "src/core/tsi/ssl/session_cache/ssl_session_cache.h", "src/core/tsi/ssl_transport_security.h", "src/core/tsi/ssl_types.h", "src/core/tsi/transport_security_grpc.h", diff --git a/CMakeLists.txt b/CMakeLists.txt index 77e84a0df8..c9333f5220 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -561,6 +561,7 @@ add_dependencies(buildtests_cxx grpc_tool_test) add_dependencies(buildtests_cxx grpclb_api_test) add_dependencies(buildtests_cxx grpclb_end2end_test) add_dependencies(buildtests_cxx h2_ssl_cert_test) +add_dependencies(buildtests_cxx h2_ssl_session_reuse_test) add_dependencies(buildtests_cxx health_service_end2end_test) if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX) add_dependencies(buildtests_cxx http2_client) @@ -1114,6 +1115,9 @@ add_library(grpc src/core/ext/filters/deadline/deadline_filter.cc src/core/tsi/alts_transport_security.cc src/core/tsi/fake_transport_security.cc + src/core/tsi/ssl/session_cache/ssl_session_boringssl.cc + src/core/tsi/ssl/session_cache/ssl_session_cache.cc + src/core/tsi/ssl/session_cache/ssl_session_openssl.cc src/core/tsi/ssl_transport_security.cc src/core/tsi/transport_security_grpc.cc src/core/ext/transport/chttp2/server/chttp2_server.cc @@ -1503,6 +1507,9 @@ add_library(grpc_cronet src/core/ext/transport/chttp2/client/chttp2_connector.cc src/core/tsi/alts_transport_security.cc src/core/tsi/fake_transport_security.cc + src/core/tsi/ssl/session_cache/ssl_session_boringssl.cc + src/core/tsi/ssl/session_cache/ssl_session_cache.cc + src/core/tsi/ssl/session_cache/ssl_session_openssl.cc src/core/tsi/ssl_transport_security.cc src/core/tsi/transport_security_grpc.cc src/core/ext/filters/load_reporting/server_load_reporting_filter.cc @@ -11493,6 +11500,43 @@ target_link_libraries(h2_ssl_cert_test endif (gRPC_BUILD_TESTS) if (gRPC_BUILD_TESTS) +add_executable(h2_ssl_session_reuse_test + test/core/end2end/h2_ssl_session_reuse_test.cc + third_party/googletest/googletest/src/gtest-all.cc + third_party/googletest/googlemock/src/gmock-all.cc +) + + +target_include_directories(h2_ssl_session_reuse_test + PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} + PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include + PRIVATE ${_gRPC_SSL_INCLUDE_DIR} + PRIVATE ${_gRPC_PROTOBUF_INCLUDE_DIR} + PRIVATE ${_gRPC_ZLIB_INCLUDE_DIR} + PRIVATE ${_gRPC_BENCHMARK_INCLUDE_DIR} + PRIVATE ${_gRPC_CARES_INCLUDE_DIR} + PRIVATE ${_gRPC_GFLAGS_INCLUDE_DIR} + PRIVATE third_party/googletest/googletest/include + PRIVATE third_party/googletest/googletest + PRIVATE third_party/googletest/googlemock/include + PRIVATE third_party/googletest/googlemock + PRIVATE ${_gRPC_PROTO_GENS_DIR} +) + +target_link_libraries(h2_ssl_session_reuse_test + ${_gRPC_PROTOBUF_LIBRARIES} + ${_gRPC_ALLTARGETS_LIBRARIES} + grpc_test_util + grpc++ + grpc + gpr_test_util + gpr + ${_gRPC_GFLAGS_LIBRARIES} +) + +endif (gRPC_BUILD_TESTS) +if (gRPC_BUILD_TESTS) + add_executable(health_service_end2end_test test/cpp/end2end/health_service_end2end_test.cc third_party/googletest/googletest/src/gtest-all.cc @@ -1163,6 +1163,7 @@ grpc_tool_test: $(BINDIR)/$(CONFIG)/grpc_tool_test grpclb_api_test: $(BINDIR)/$(CONFIG)/grpclb_api_test grpclb_end2end_test: $(BINDIR)/$(CONFIG)/grpclb_end2end_test h2_ssl_cert_test: $(BINDIR)/$(CONFIG)/h2_ssl_cert_test +h2_ssl_session_reuse_test: $(BINDIR)/$(CONFIG)/h2_ssl_session_reuse_test health_service_end2end_test: $(BINDIR)/$(CONFIG)/health_service_end2end_test http2_client: $(BINDIR)/$(CONFIG)/http2_client hybrid_end2end_test: $(BINDIR)/$(CONFIG)/hybrid_end2end_test @@ -1641,6 +1642,7 @@ buildtests_cxx: privatelibs_cxx \ $(BINDIR)/$(CONFIG)/grpclb_api_test \ $(BINDIR)/$(CONFIG)/grpclb_end2end_test \ $(BINDIR)/$(CONFIG)/h2_ssl_cert_test \ + $(BINDIR)/$(CONFIG)/h2_ssl_session_reuse_test \ $(BINDIR)/$(CONFIG)/health_service_end2end_test \ $(BINDIR)/$(CONFIG)/http2_client \ $(BINDIR)/$(CONFIG)/hybrid_end2end_test \ @@ -1807,6 +1809,7 @@ buildtests_cxx: privatelibs_cxx \ $(BINDIR)/$(CONFIG)/grpclb_api_test \ $(BINDIR)/$(CONFIG)/grpclb_end2end_test \ $(BINDIR)/$(CONFIG)/h2_ssl_cert_test \ + $(BINDIR)/$(CONFIG)/h2_ssl_session_reuse_test \ $(BINDIR)/$(CONFIG)/health_service_end2end_test \ $(BINDIR)/$(CONFIG)/http2_client \ $(BINDIR)/$(CONFIG)/hybrid_end2end_test \ @@ -2255,6 +2258,8 @@ test_cxx: buildtests_cxx $(Q) $(BINDIR)/$(CONFIG)/grpclb_end2end_test || ( echo test grpclb_end2end_test failed ; exit 1 ) $(E) "[RUN] Testing h2_ssl_cert_test" $(Q) $(BINDIR)/$(CONFIG)/h2_ssl_cert_test || ( echo test h2_ssl_cert_test failed ; exit 1 ) + $(E) "[RUN] Testing h2_ssl_session_reuse_test" + $(Q) $(BINDIR)/$(CONFIG)/h2_ssl_session_reuse_test || ( echo test h2_ssl_session_reuse_test failed ; exit 1 ) $(E) "[RUN] Testing health_service_end2end_test" $(Q) $(BINDIR)/$(CONFIG)/health_service_end2end_test || ( echo test health_service_end2end_test failed ; exit 1 ) $(E) "[RUN] Testing inlined_vector_test" @@ -3419,6 +3424,9 @@ LIBGRPC_SRC = \ src/core/ext/filters/deadline/deadline_filter.cc \ src/core/tsi/alts_transport_security.cc \ src/core/tsi/fake_transport_security.cc \ + src/core/tsi/ssl/session_cache/ssl_session_boringssl.cc \ + src/core/tsi/ssl/session_cache/ssl_session_cache.cc \ + src/core/tsi/ssl/session_cache/ssl_session_openssl.cc \ src/core/tsi/ssl_transport_security.cc \ src/core/tsi/transport_security_grpc.cc \ src/core/ext/transport/chttp2/server/chttp2_server.cc \ @@ -3810,6 +3818,9 @@ LIBGRPC_CRONET_SRC = \ src/core/ext/transport/chttp2/client/chttp2_connector.cc \ src/core/tsi/alts_transport_security.cc \ src/core/tsi/fake_transport_security.cc \ + src/core/tsi/ssl/session_cache/ssl_session_boringssl.cc \ + src/core/tsi/ssl/session_cache/ssl_session_cache.cc \ + src/core/tsi/ssl/session_cache/ssl_session_openssl.cc \ src/core/tsi/ssl_transport_security.cc \ src/core/tsi/transport_security_grpc.cc \ src/core/ext/filters/load_reporting/server_load_reporting_filter.cc \ @@ -17430,6 +17441,49 @@ endif endif +H2_SSL_SESSION_REUSE_TEST_SRC = \ + test/core/end2end/h2_ssl_session_reuse_test.cc \ + +H2_SSL_SESSION_REUSE_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(H2_SSL_SESSION_REUSE_TEST_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/h2_ssl_session_reuse_test: openssl_dep_error + +else + + + + +ifeq ($(NO_PROTOBUF),true) + +# You can't build the protoc plugins or protobuf-enabled targets if you don't have protobuf 3.0.0+. + +$(BINDIR)/$(CONFIG)/h2_ssl_session_reuse_test: protobuf_dep_error + +else + +$(BINDIR)/$(CONFIG)/h2_ssl_session_reuse_test: $(PROTOBUF_DEP) $(H2_SSL_SESSION_REUSE_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LDXX) $(LDFLAGS) $(H2_SSL_SESSION_REUSE_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBSXX) $(LDLIBS_PROTOBUF) $(LDLIBS) $(LDLIBS_SECURE) $(GTEST_LIB) -o $(BINDIR)/$(CONFIG)/h2_ssl_session_reuse_test + +endif + +endif + +$(OBJDIR)/$(CONFIG)/test/core/end2end/h2_ssl_session_reuse_test.o: $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + +deps_h2_ssl_session_reuse_test: $(H2_SSL_SESSION_REUSE_TEST_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(H2_SSL_SESSION_REUSE_TEST_OBJS:.o=.dep) +endif +endif + + HEALTH_SERVICE_END2END_TEST_SRC = \ test/cpp/end2end/health_service_end2end_test.cc \ @@ -23505,6 +23559,9 @@ src/core/tsi/alts/zero_copy_frame_protector/alts_iovec_record_protocol.cc: $(OPE src/core/tsi/alts/zero_copy_frame_protector/alts_zero_copy_grpc_protector.cc: $(OPENSSL_DEP) src/core/tsi/alts_transport_security.cc: $(OPENSSL_DEP) src/core/tsi/fake_transport_security.cc: $(OPENSSL_DEP) +src/core/tsi/ssl/session_cache/ssl_session_boringssl.cc: $(OPENSSL_DEP) +src/core/tsi/ssl/session_cache/ssl_session_cache.cc: $(OPENSSL_DEP) +src/core/tsi/ssl/session_cache/ssl_session_openssl.cc: $(OPENSSL_DEP) src/core/tsi/ssl_transport_security.cc: $(OPENSSL_DEP) src/core/tsi/transport_security.cc: $(OPENSSL_DEP) src/core/tsi/transport_security_adapter.cc: $(OPENSSL_DEP) diff --git a/build.yaml b/build.yaml index a4875df9e5..1291f266e6 100644 --- a/build.yaml +++ b/build.yaml @@ -1037,12 +1037,17 @@ filegroups: headers: - src/core/tsi/alts_transport_security.h - src/core/tsi/fake_transport_security.h + - src/core/tsi/ssl/session_cache/ssl_session.h + - src/core/tsi/ssl/session_cache/ssl_session_cache.h - src/core/tsi/ssl_transport_security.h - src/core/tsi/ssl_types.h - src/core/tsi/transport_security_grpc.h src: - src/core/tsi/alts_transport_security.cc - src/core/tsi/fake_transport_security.cc + - src/core/tsi/ssl/session_cache/ssl_session_boringssl.cc + - src/core/tsi/ssl/session_cache/ssl_session_cache.cc + - src/core/tsi/ssl/session_cache/ssl_session_openssl.cc - src/core/tsi/ssl_transport_security.cc - src/core/tsi/transport_security_grpc.cc deps: @@ -4532,6 +4537,22 @@ targets: - gpr uses: - grpc++_test +- name: h2_ssl_session_reuse_test + gtest: true + build: test + language: c++ + headers: + - test/core/end2end/end2end_tests.h + src: + - test/core/end2end/h2_ssl_session_reuse_test.cc + deps: + - grpc_test_util + - grpc++ + - grpc + - gpr_test_util + - gpr + uses: + - grpc++_test - name: health_service_end2end_test gtest: true build: test @@ -343,6 +343,9 @@ if test "$PHP_GRPC" != "no"; then src/core/ext/filters/deadline/deadline_filter.cc \ src/core/tsi/alts_transport_security.cc \ src/core/tsi/fake_transport_security.cc \ + src/core/tsi/ssl/session_cache/ssl_session_boringssl.cc \ + src/core/tsi/ssl/session_cache/ssl_session_cache.cc \ + src/core/tsi/ssl/session_cache/ssl_session_openssl.cc \ src/core/tsi/ssl_transport_security.cc \ src/core/tsi/transport_security_grpc.cc \ src/core/ext/transport/chttp2/server/chttp2_server.cc \ @@ -700,6 +703,7 @@ if test "$PHP_GRPC" != "no"; then PHP_ADD_BUILD_DIR($ext_builddir/src/core/tsi/alts/frame_protector) PHP_ADD_BUILD_DIR($ext_builddir/src/core/tsi/alts/handshaker) PHP_ADD_BUILD_DIR($ext_builddir/src/core/tsi/alts/zero_copy_frame_protector) + PHP_ADD_BUILD_DIR($ext_builddir/src/core/tsi/ssl/session_cache) PHP_ADD_BUILD_DIR($ext_builddir/third_party/boringssl/crypto) PHP_ADD_BUILD_DIR($ext_builddir/third_party/boringssl/crypto/asn1) PHP_ADD_BUILD_DIR($ext_builddir/third_party/boringssl/crypto/base64) diff --git a/config.w32 b/config.w32 index 78f2fe079d..85c5a5f3fe 100644 --- a/config.w32 +++ b/config.w32 @@ -320,6 +320,9 @@ if (PHP_GRPC != "no") { "src\\core\\ext\\filters\\deadline\\deadline_filter.cc " + "src\\core\\tsi\\alts_transport_security.cc " + "src\\core\\tsi\\fake_transport_security.cc " + + "src\\core\\tsi\\ssl\\session_cache\\ssl_session_boringssl.cc " + + "src\\core\\tsi\\ssl\\session_cache\\ssl_session_cache.cc " + + "src\\core\\tsi\\ssl\\session_cache\\ssl_session_openssl.cc " + "src\\core\\tsi\\ssl_transport_security.cc " + "src\\core\\tsi\\transport_security_grpc.cc " + "src\\core\\ext\\transport\\chttp2\\server\\chttp2_server.cc " + @@ -714,6 +717,8 @@ if (PHP_GRPC != "no") { FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\core\\tsi\\alts\\frame_protector"); FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\core\\tsi\\alts\\handshaker"); FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\core\\tsi\\alts\\zero_copy_frame_protector"); + FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\core\\tsi\\ssl"); + FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\core\\tsi\\ssl\\session_cache"); FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\php"); FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\php\\ext"); FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\php\\ext\\grpc"); diff --git a/gRPC-C++.podspec b/gRPC-C++.podspec index e1282611b0..6dd67f5134 100644 --- a/gRPC-C++.podspec +++ b/gRPC-C++.podspec @@ -333,6 +333,8 @@ Pod::Spec.new do |s| 'src/core/ext/filters/deadline/deadline_filter.h', 'src/core/tsi/alts_transport_security.h', 'src/core/tsi/fake_transport_security.h', + 'src/core/tsi/ssl/session_cache/ssl_session.h', + 'src/core/tsi/ssl/session_cache/ssl_session_cache.h', 'src/core/tsi/ssl_transport_security.h', 'src/core/tsi/ssl_types.h', 'src/core/tsi/transport_security_grpc.h', diff --git a/gRPC-Core.podspec b/gRPC-Core.podspec index 39433ff819..1159835341 100644 --- a/gRPC-Core.podspec +++ b/gRPC-Core.podspec @@ -344,6 +344,8 @@ Pod::Spec.new do |s| 'src/core/ext/filters/deadline/deadline_filter.h', 'src/core/tsi/alts_transport_security.h', 'src/core/tsi/fake_transport_security.h', + 'src/core/tsi/ssl/session_cache/ssl_session.h', + 'src/core/tsi/ssl/session_cache/ssl_session_cache.h', 'src/core/tsi/ssl_transport_security.h', 'src/core/tsi/ssl_types.h', 'src/core/tsi/transport_security_grpc.h', @@ -756,6 +758,9 @@ Pod::Spec.new do |s| 'src/core/ext/filters/deadline/deadline_filter.cc', 'src/core/tsi/alts_transport_security.cc', 'src/core/tsi/fake_transport_security.cc', + 'src/core/tsi/ssl/session_cache/ssl_session_boringssl.cc', + 'src/core/tsi/ssl/session_cache/ssl_session_cache.cc', + 'src/core/tsi/ssl/session_cache/ssl_session_openssl.cc', 'src/core/tsi/ssl_transport_security.cc', 'src/core/tsi/transport_security_grpc.cc', 'src/core/ext/transport/chttp2/server/chttp2_server.cc', @@ -911,6 +916,8 @@ Pod::Spec.new do |s| 'src/core/ext/filters/deadline/deadline_filter.h', 'src/core/tsi/alts_transport_security.h', 'src/core/tsi/fake_transport_security.h', + 'src/core/tsi/ssl/session_cache/ssl_session.h', + 'src/core/tsi/ssl/session_cache/ssl_session_cache.h', 'src/core/tsi/ssl_transport_security.h', 'src/core/tsi/ssl_types.h', 'src/core/tsi/transport_security_grpc.h', @@ -83,6 +83,9 @@ EXPORTS grpc_auth_context_add_property grpc_auth_context_add_cstring_property grpc_auth_context_set_peer_identity_property_name + grpc_ssl_session_cache_create_lru + grpc_ssl_session_cache_destroy + grpc_ssl_session_cache_create_channel_arg grpc_channel_credentials_release grpc_google_default_credentials_create grpc_set_ssl_roots_override_callback diff --git a/grpc.gemspec b/grpc.gemspec index 3a57829943..e1106bc9a1 100644 --- a/grpc.gemspec +++ b/grpc.gemspec @@ -274,6 +274,8 @@ Gem::Specification.new do |s| s.files += %w( src/core/ext/filters/deadline/deadline_filter.h ) s.files += %w( src/core/tsi/alts_transport_security.h ) s.files += %w( src/core/tsi/fake_transport_security.h ) + s.files += %w( src/core/tsi/ssl/session_cache/ssl_session.h ) + s.files += %w( src/core/tsi/ssl/session_cache/ssl_session_cache.h ) s.files += %w( src/core/tsi/ssl_transport_security.h ) s.files += %w( src/core/tsi/ssl_types.h ) s.files += %w( src/core/tsi/transport_security_grpc.h ) @@ -689,6 +691,9 @@ Gem::Specification.new do |s| s.files += %w( src/core/ext/filters/deadline/deadline_filter.cc ) s.files += %w( src/core/tsi/alts_transport_security.cc ) s.files += %w( src/core/tsi/fake_transport_security.cc ) + s.files += %w( src/core/tsi/ssl/session_cache/ssl_session_boringssl.cc ) + s.files += %w( src/core/tsi/ssl/session_cache/ssl_session_cache.cc ) + s.files += %w( src/core/tsi/ssl/session_cache/ssl_session_openssl.cc ) s.files += %w( src/core/tsi/ssl_transport_security.cc ) s.files += %w( src/core/tsi/transport_security_grpc.cc ) s.files += %w( src/core/ext/transport/chttp2/server/chttp2_server.cc ) @@ -496,6 +496,9 @@ 'src/core/ext/filters/deadline/deadline_filter.cc', 'src/core/tsi/alts_transport_security.cc', 'src/core/tsi/fake_transport_security.cc', + 'src/core/tsi/ssl/session_cache/ssl_session_boringssl.cc', + 'src/core/tsi/ssl/session_cache/ssl_session_cache.cc', + 'src/core/tsi/ssl/session_cache/ssl_session_openssl.cc', 'src/core/tsi/ssl_transport_security.cc', 'src/core/tsi/transport_security_grpc.cc', 'src/core/ext/transport/chttp2/server/chttp2_server.cc', diff --git a/include/grpc/grpc_security.h b/include/grpc/grpc_security.h index abc591fd75..7c069b39d5 100644 --- a/include/grpc/grpc_security.h +++ b/include/grpc/grpc_security.h @@ -100,6 +100,25 @@ GRPCAPI void grpc_auth_context_add_cstring_property(grpc_auth_context* ctx, GRPCAPI int grpc_auth_context_set_peer_identity_property_name( grpc_auth_context* ctx, const char* name); +/** --- SSL Session Cache. --- + + A SSL session cache object represents a way to cache client sessions + between connections. Only ticket-based resumption is supported. */ + +typedef struct grpc_ssl_session_cache grpc_ssl_session_cache; + +/** Create LRU cache for client-side SSL sessions with the given capacity. + If capacity is < 1, a default capacity is used instead. */ +GRPCAPI grpc_ssl_session_cache* grpc_ssl_session_cache_create_lru( + size_t capacity); + +/** Destroy SSL session cache. */ +GRPCAPI void grpc_ssl_session_cache_destroy(grpc_ssl_session_cache* cache); + +/** Create a channel arg with the given cache object. */ +GRPCAPI grpc_arg +grpc_ssl_session_cache_create_channel_arg(grpc_ssl_session_cache* cache); + /** --- grpc_channel_credentials object. --- A channel credentials object represents a way to authenticate a client on a diff --git a/include/grpc/grpc_security_constants.h b/include/grpc/grpc_security_constants.h index 60e167eb88..92580ea35e 100644 --- a/include/grpc/grpc_security_constants.h +++ b/include/grpc/grpc_security_constants.h @@ -29,6 +29,7 @@ extern "C" { #define GRPC_X509_CN_PROPERTY_NAME "x509_common_name" #define GRPC_X509_SAN_PROPERTY_NAME "x509_subject_alternative_name" #define GRPC_X509_PEM_CERT_PROPERTY_NAME "x509_pem_cert" +#define GRPC_SSL_SESSION_REUSED_PROPERTY "ssl_session_reused" /** Environment variable that points to the default SSL roots file. This file must be a PEM encoded file with all the roots such as the one that can be diff --git a/include/grpc/impl/codegen/grpc_types.h b/include/grpc/impl/codegen/grpc_types.h index dcce2e7f9a..e1a3f0dcf1 100644 --- a/include/grpc/impl/codegen/grpc_types.h +++ b/include/grpc/impl/codegen/grpc_types.h @@ -258,6 +258,10 @@ typedef struct { secure channel is an SSL channel). If this parameter is specified and the underlying is not an SSL channel, it will just be ignored. */ #define GRPC_SSL_TARGET_NAME_OVERRIDE_ARG "grpc.ssl_target_name_override" +/** If non-zero, a pointer to a session cache (a pointer of type + grpc_ssl_session_cache*). (use grpc_ssl_session_cache_arg_vtable() to fetch + an appropriate pointer arg vtable) */ +#define GRPC_SSL_SESSION_CACHE_ARG "grpc.ssl_session_cache" /** Maximum metadata size, in bytes. Note this limit applies to the max sum of all metadata key-value entries in a batch of headers. */ #define GRPC_ARG_MAX_METADATA_SIZE "grpc.max_metadata_size" diff --git a/package.xml b/package.xml index 8f0dc55dbf..2a38df8406 100644 --- a/package.xml +++ b/package.xml @@ -281,6 +281,8 @@ <file baseinstalldir="/" name="src/core/ext/filters/deadline/deadline_filter.h" role="src" /> <file baseinstalldir="/" name="src/core/tsi/alts_transport_security.h" role="src" /> <file baseinstalldir="/" name="src/core/tsi/fake_transport_security.h" role="src" /> + <file baseinstalldir="/" name="src/core/tsi/ssl/session_cache/ssl_session.h" role="src" /> + <file baseinstalldir="/" name="src/core/tsi/ssl/session_cache/ssl_session_cache.h" role="src" /> <file baseinstalldir="/" name="src/core/tsi/ssl_transport_security.h" role="src" /> <file baseinstalldir="/" name="src/core/tsi/ssl_types.h" role="src" /> <file baseinstalldir="/" name="src/core/tsi/transport_security_grpc.h" role="src" /> @@ -696,6 +698,9 @@ <file baseinstalldir="/" name="src/core/ext/filters/deadline/deadline_filter.cc" role="src" /> <file baseinstalldir="/" name="src/core/tsi/alts_transport_security.cc" role="src" /> <file baseinstalldir="/" name="src/core/tsi/fake_transport_security.cc" role="src" /> + <file baseinstalldir="/" name="src/core/tsi/ssl/session_cache/ssl_session_boringssl.cc" role="src" /> + <file baseinstalldir="/" name="src/core/tsi/ssl/session_cache/ssl_session_cache.cc" role="src" /> + <file baseinstalldir="/" name="src/core/tsi/ssl/session_cache/ssl_session_openssl.cc" role="src" /> <file baseinstalldir="/" name="src/core/tsi/ssl_transport_security.cc" role="src" /> <file baseinstalldir="/" name="src/core/tsi/transport_security_grpc.cc" role="src" /> <file baseinstalldir="/" name="src/core/ext/transport/chttp2/server/chttp2_server.cc" role="src" /> diff --git a/src/core/lib/http/httpcli_security_connector.cc b/src/core/lib/http/httpcli_security_connector.cc index 180912383f..886357fc00 100644 --- a/src/core/lib/http/httpcli_security_connector.cc +++ b/src/core/lib/http/httpcli_security_connector.cc @@ -121,8 +121,11 @@ static grpc_security_status httpcli_ssl_channel_security_connector_create( if (secure_peer_name != nullptr) { c->secure_peer_name = gpr_strdup(secure_peer_name); } - result = tsi_create_ssl_client_handshaker_factory( - nullptr, pem_root_certs, nullptr, nullptr, 0, &c->handshaker_factory); + tsi_ssl_client_handshaker_options options; + memset(&options, 0, sizeof(options)); + options.pem_root_certs = pem_root_certs; + result = tsi_create_ssl_client_handshaker_factory_with_options( + &options, &c->handshaker_factory); if (result != TSI_OK) { gpr_log(GPR_ERROR, "Handshaker factory creation failed with %s.", tsi_result_to_string(result)); diff --git a/src/core/lib/security/credentials/ssl/ssl_credentials.cc b/src/core/lib/security/credentials/ssl/ssl_credentials.cc index 252b25bc0a..2b6377d3ec 100644 --- a/src/core/lib/security/credentials/ssl/ssl_credentials.cc +++ b/src/core/lib/security/credentials/ssl/ssl_credentials.cc @@ -24,6 +24,7 @@ #include "src/core/lib/channel/channel_args.h" #include "src/core/lib/surface/api_trace.h" +#include "src/core/tsi/ssl_transport_security.h" #include <grpc/support/alloc.h> #include <grpc/support/log.h> @@ -56,16 +57,22 @@ static grpc_security_status ssl_create_security_connector( grpc_ssl_credentials* c = reinterpret_cast<grpc_ssl_credentials*>(creds); grpc_security_status status = GRPC_SECURITY_OK; const char* overridden_target_name = nullptr; + tsi_ssl_session_cache* ssl_session_cache = nullptr; for (size_t i = 0; args && i < args->num_args; i++) { grpc_arg* arg = &args->args[i]; if (strcmp(arg->key, GRPC_SSL_TARGET_NAME_OVERRIDE_ARG) == 0 && arg->type == GRPC_ARG_STRING) { overridden_target_name = arg->value.string; - break; + } + if (strcmp(arg->key, GRPC_SSL_SESSION_CACHE_ARG) == 0 && + arg->type == GRPC_ARG_POINTER) { + ssl_session_cache = + static_cast<tsi_ssl_session_cache*>(arg->value.pointer.p); } } status = grpc_ssl_channel_security_connector_create( - creds, call_creds, &c->config, target, overridden_target_name, sc); + creds, call_creds, &c->config, target, overridden_target_name, + ssl_session_cache, sc); if (status != GRPC_SECURITY_OK) { return status; } diff --git a/src/core/lib/security/security_connector/security_connector.cc b/src/core/lib/security/security_connector/security_connector.cc index 3cc151bec7..cbe77d5a69 100644 --- a/src/core/lib/security/security_connector/security_connector.cc +++ b/src/core/lib/security/security_connector/security_connector.cc @@ -542,6 +542,46 @@ grpc_server_security_connector* grpc_fake_server_security_connector_create( /* --- Ssl 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); +} + typedef struct { grpc_channel_security_connector base; tsi_ssl_client_handshaker_factory* client_handshaker_factory; @@ -760,6 +800,9 @@ grpc_auth_context* tsi_ssl_peer_to_auth_context(const tsi_peer* peer) { } else if (strcmp(prop->name, TSI_X509_PEM_CERT_PROPERTY) == 0) { grpc_auth_context_add_property(ctx, 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, GRPC_SSL_SESSION_REUSED_PROPERTY, + prop->value.data, prop->value.length); } } if (peer_identity_property_name != nullptr) { @@ -983,28 +1026,30 @@ grpc_security_status grpc_ssl_channel_security_connector_create( grpc_channel_credentials* channel_creds, grpc_call_credentials* request_metadata_creds, const grpc_ssl_config* config, const char* target_name, - const char* overridden_target_name, grpc_channel_security_connector** sc) { - size_t num_alpn_protocols = 0; - const char** alpn_protocol_strings = - fill_alpn_protocol_strings(&num_alpn_protocols); + const char* overridden_target_name, + tsi_ssl_session_cache* ssl_session_cache, + grpc_channel_security_connector** sc) { tsi_result result = TSI_OK; grpc_ssl_channel_security_connector* c; - const char* pem_root_certs; char* port; bool has_key_cert_pair; + tsi_ssl_client_handshaker_options options; + memset(&options, 0, sizeof(options)); + options.alpn_protocols = + fill_alpn_protocol_strings(&options.num_alpn_protocols); if (config == nullptr || target_name == nullptr) { gpr_log(GPR_ERROR, "An ssl channel needs a config and a target name."); goto error; } if (config->pem_root_certs == nullptr) { - pem_root_certs = grpc_get_default_ssl_roots(); - if (pem_root_certs == nullptr) { + options.pem_root_certs = grpc_get_default_ssl_roots(); + if (options.pem_root_certs == nullptr) { gpr_log(GPR_ERROR, "Could not get default pem root certs."); goto error; } } else { - pem_root_certs = config->pem_root_certs; + options.pem_root_certs = config->pem_root_certs; } c = static_cast<grpc_ssl_channel_security_connector*>( @@ -1028,10 +1073,13 @@ grpc_security_status grpc_ssl_channel_security_connector_create( 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; - result = tsi_create_ssl_client_handshaker_factory( - has_key_cert_pair ? config->pem_key_cert_pair : nullptr, pem_root_certs, - ssl_cipher_suites(), alpn_protocol_strings, - static_cast<uint16_t>(num_alpn_protocols), &c->client_handshaker_factory); + if (has_key_cert_pair) { + options.pem_key_cert_pair = config->pem_key_cert_pair; + } + options.cipher_suites = ssl_cipher_suites(); + options.session_cache = ssl_session_cache; + result = tsi_create_ssl_client_handshaker_factory_with_options( + &options, &c->client_handshaker_factory); if (result != TSI_OK) { gpr_log(GPR_ERROR, "Handshaker factory creation failed with %s.", tsi_result_to_string(result)); @@ -1040,11 +1088,11 @@ grpc_security_status grpc_ssl_channel_security_connector_create( goto error; } *sc = &c->base; - gpr_free((void*)alpn_protocol_strings); + gpr_free((void*)options.alpn_protocols); return GRPC_SECURITY_OK; error: - gpr_free((void*)alpn_protocol_strings); + gpr_free((void*)options.alpn_protocols); return GRPC_SECURITY_ERROR; } diff --git a/src/core/lib/security/security_connector/security_connector.h b/src/core/lib/security/security_connector/security_connector.h index 130c8ecd3e..dc847d94f9 100644 --- a/src/core/lib/security/security_connector/security_connector.h +++ b/src/core/lib/security/security_connector/security_connector.h @@ -212,7 +212,9 @@ grpc_security_status grpc_ssl_channel_security_connector_create( grpc_channel_credentials* channel_creds, grpc_call_credentials* request_metadata_creds, const grpc_ssl_config* config, const char* target_name, - const char* overridden_target_name, grpc_channel_security_connector** sc); + const char* overridden_target_name, + tsi_ssl_session_cache* ssl_session_cache, + grpc_channel_security_connector** sc); /* Gets the default ssl roots. Returns NULL if not found. */ const char* grpc_get_default_ssl_roots(void); diff --git a/src/core/tsi/ssl/session_cache/ssl_session.h b/src/core/tsi/ssl/session_cache/ssl_session.h new file mode 100644 index 0000000000..115221ec06 --- /dev/null +++ b/src/core/tsi/ssl/session_cache/ssl_session.h @@ -0,0 +1,73 @@ +/* + * + * 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. + * + */ + +#ifndef GRPC_CORE_TSI_SSL_SESSION_CACHE_SSL_SESSION_H +#define GRPC_CORE_TSI_SSL_SESSION_CACHE_SSL_SESSION_H + +#include <grpc/support/port_platform.h> + +#include <grpc/slice.h> + +extern "C" { +#include <openssl/ssl.h> +} + +#include "src/core/lib/gprpp/ref_counted.h" + +// The main purpose of code here is to provide means to cache SSL sessions +// in a way that they can be shared between connections. +// +// SSL_SESSION stands for single instance of session and is not generally safe +// to share between SSL contexts with different lifetimes. It happens because +// not all SSL implementations guarantee immutability of SSL_SESSION object. +// See SSL_SESSION documentation in BoringSSL and OpenSSL for more details. + +namespace tsi { + +struct SslSessionDeleter { + void operator()(SSL_SESSION* session) { SSL_SESSION_free(session); } +}; + +typedef std::unique_ptr<SSL_SESSION, SslSessionDeleter> SslSessionPtr; + +/// SslCachedSession is an immutable thread-safe storage for single session +/// representation. It provides means to share SSL session data (e.g. TLS +/// ticket) between encrypted connections regardless of SSL context lifetime. +class SslCachedSession { + public: + // Not copyable nor movable. + SslCachedSession(const SslCachedSession&) = delete; + SslCachedSession& operator=(const SslCachedSession&) = delete; + + /// Create single cached instance of \a session. + static grpc_core::UniquePtr<SslCachedSession> Create(SslSessionPtr session); + + virtual ~SslCachedSession() = default; + + /// Returns a copy of previously cached session. + virtual SslSessionPtr CopySession() const GRPC_ABSTRACT; + + GRPC_ABSTRACT_BASE_CLASS + + protected: + SslCachedSession() = default; +}; + +} // namespace tsi + +#endif /* GRPC_CORE_TSI_SSL_SESSION_CACHE_SSL_SESSION_H */ diff --git a/src/core/tsi/ssl/session_cache/ssl_session_boringssl.cc b/src/core/tsi/ssl/session_cache/ssl_session_boringssl.cc new file mode 100644 index 0000000000..0da5a96164 --- /dev/null +++ b/src/core/tsi/ssl/session_cache/ssl_session_boringssl.cc @@ -0,0 +1,58 @@ +/* + * + * 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/tsi/ssl/session_cache/ssl_session.h" + +#ifdef OPENSSL_IS_BORINGSSL + +// BoringSSL allows SSL_SESSION to outlive SSL and SSL_CTX objects which are +// re-created by gRPC on every certificate rotation or subchannel creation. +// BoringSSL guarantees that SSL_SESSION is immutable so it's safe to share +// the same original session object between different threads and connections. + +namespace tsi { +namespace { + +class BoringSslCachedSession : public SslCachedSession { + public: + BoringSslCachedSession(SslSessionPtr session) + : session_(std::move(session)) {} + + SslSessionPtr CopySession() const override { + // SslSessionPtr will dereference on destruction. + SSL_SESSION_up_ref(session_.get()); + return SslSessionPtr(session_.get()); + } + + private: + SslSessionPtr session_; +}; + +} // namespace + +grpc_core::UniquePtr<SslCachedSession> SslCachedSession::Create( + SslSessionPtr session) { + return grpc_core::UniquePtr<SslCachedSession>( + grpc_core::New<BoringSslCachedSession>(std::move(session))); +} + +} // namespace tsi + +#endif /* OPENSSL_IS_BORINGSSL */ diff --git a/src/core/tsi/ssl/session_cache/ssl_session_cache.cc b/src/core/tsi/ssl/session_cache/ssl_session_cache.cc new file mode 100644 index 0000000000..fe4f83a13d --- /dev/null +++ b/src/core/tsi/ssl/session_cache/ssl_session_cache.cc @@ -0,0 +1,211 @@ +/* + * + * 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/tsi/ssl/session_cache/ssl_session_cache.h" + +#include "src/core/tsi/ssl/session_cache/ssl_session.h" + +#include <grpc/support/log.h> +#include <grpc/support/string_util.h> + +namespace tsi { + +static void cache_key_avl_destroy(void* key, void* unused) {} + +static void* cache_key_avl_copy(void* key, void* unused) { return key; } + +static long cache_key_avl_compare(void* key1, void* key2, void* unused) { + return grpc_slice_cmp(*static_cast<grpc_slice*>(key1), + *static_cast<grpc_slice*>(key2)); +} + +static void cache_value_avl_destroy(void* value, void* unused) {} + +static void* cache_value_avl_copy(void* value, void* unused) { return value; } + +// AVL only stores pointers, ownership belonges to the linked list. +static const grpc_avl_vtable cache_avl_vtable = { + cache_key_avl_destroy, cache_key_avl_copy, cache_key_avl_compare, + cache_value_avl_destroy, cache_value_avl_copy, +}; + +/// Node for single cached session. +class SslSessionLRUCache::Node { + public: + Node(const grpc_slice& key, SslSessionPtr session) : key_(key) { + SetSession(std::move(session)); + } + + ~Node() { grpc_slice_unref(key_); } + + // Not copyable nor movable. + Node(const Node&) = delete; + Node& operator=(const Node&) = delete; + + void* AvlKey() { return &key_; } + + /// Returns a copy of the node's cache session. + SslSessionPtr CopySession() const { return session_->CopySession(); } + + /// Set the \a session (which is moved) for the node. + void SetSession(SslSessionPtr session) { + session_ = SslCachedSession::Create(std::move(session)); + } + + private: + friend class SslSessionLRUCache; + + grpc_slice key_; + grpc_core::UniquePtr<SslCachedSession> session_; + + Node* next_ = nullptr; + Node* prev_ = nullptr; +}; + +SslSessionLRUCache::SslSessionLRUCache(size_t capacity) : capacity_(capacity) { + GPR_ASSERT(capacity > 0); + gpr_mu_init(&lock_); + entry_by_key_ = grpc_avl_create(&cache_avl_vtable); +} + +SslSessionLRUCache::~SslSessionLRUCache() { + Node* node = use_order_list_head_; + while (node) { + Node* next = node->next_; + grpc_core::Delete(node); + node = next; + } + grpc_avl_unref(entry_by_key_, nullptr); + gpr_mu_destroy(&lock_); +} + +size_t SslSessionLRUCache::Size() { + grpc_core::mu_guard guard(&lock_); + return use_order_list_size_; +} + +SslSessionLRUCache::Node* SslSessionLRUCache::FindLocked( + const grpc_slice& key) { + void* value = + grpc_avl_get(entry_by_key_, const_cast<grpc_slice*>(&key), nullptr); + if (value == nullptr) { + return nullptr; + } + Node* node = static_cast<Node*>(value); + // Move to the beginning. + Remove(node); + PushFront(node); + AssertInvariants(); + return node; +} + +void SslSessionLRUCache::Put(const char* key, SslSessionPtr session) { + grpc_core::mu_guard guard(&lock_); + Node* node = FindLocked(grpc_slice_from_static_string(key)); + if (node != nullptr) { + node->SetSession(std::move(session)); + return; + } + grpc_slice key_slice = grpc_slice_from_copied_string(key); + node = grpc_core::New<Node>(key_slice, std::move(session)); + PushFront(node); + entry_by_key_ = grpc_avl_add(entry_by_key_, node->AvlKey(), node, nullptr); + AssertInvariants(); + if (use_order_list_size_ > capacity_) { + GPR_ASSERT(use_order_list_tail_); + node = use_order_list_tail_; + Remove(node); + // Order matters, key is destroyed after deleting node. + entry_by_key_ = grpc_avl_remove(entry_by_key_, node->AvlKey(), nullptr); + grpc_core::Delete(node); + AssertInvariants(); + } +} + +SslSessionPtr SslSessionLRUCache::Get(const char* key) { + grpc_core::mu_guard guard(&lock_); + // Key is only used for lookups. + grpc_slice key_slice = grpc_slice_from_static_string(key); + Node* node = FindLocked(key_slice); + if (node == nullptr) { + return nullptr; + } + return node->CopySession(); +} + +void SslSessionLRUCache::Remove(SslSessionLRUCache::Node* node) { + if (node->prev_ == nullptr) { + use_order_list_head_ = node->next_; + } else { + node->prev_->next_ = node->next_; + } + if (node->next_ == nullptr) { + use_order_list_tail_ = node->prev_; + } else { + node->next_->prev_ = node->prev_; + } + GPR_ASSERT(use_order_list_size_ >= 1); + use_order_list_size_--; +} + +void SslSessionLRUCache::PushFront(SslSessionLRUCache::Node* node) { + if (use_order_list_head_ == nullptr) { + use_order_list_head_ = node; + use_order_list_tail_ = node; + node->next_ = nullptr; + node->prev_ = nullptr; + } else { + node->next_ = use_order_list_head_; + node->next_->prev_ = node; + use_order_list_head_ = node; + node->prev_ = nullptr; + } + use_order_list_size_++; +} + +#ifndef NDEBUG +static size_t calculate_tree_size(grpc_avl_node* node) { + if (node == nullptr) { + return 0; + } + return 1 + calculate_tree_size(node->left) + calculate_tree_size(node->right); +} + +void SslSessionLRUCache::AssertInvariants() { + size_t size = 0; + Node* prev = nullptr; + Node* current = use_order_list_head_; + while (current != nullptr) { + size++; + GPR_ASSERT(current->prev_ == prev); + void* node = grpc_avl_get(entry_by_key_, current->AvlKey(), nullptr); + GPR_ASSERT(node == current); + prev = current; + current = current->next_; + } + GPR_ASSERT(prev == use_order_list_tail_); + GPR_ASSERT(size == use_order_list_size_); + GPR_ASSERT(calculate_tree_size(entry_by_key_.root) == use_order_list_size_); +} +#else +void SslSessionLRUCache::AssertInvariants() {} +#endif + +} // namespace tsi diff --git a/src/core/tsi/ssl/session_cache/ssl_session_cache.h b/src/core/tsi/ssl/session_cache/ssl_session_cache.h new file mode 100644 index 0000000000..488638c9bb --- /dev/null +++ b/src/core/tsi/ssl/session_cache/ssl_session_cache.h @@ -0,0 +1,93 @@ +/* + * + * 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. + * + */ + +#ifndef GRPC_CORE_TSI_SSL_SESSION_CACHE_SSL_SESSION_CACHE_H +#define GRPC_CORE_TSI_SSL_SESSION_CACHE_SSL_SESSION_CACHE_H + +#include <grpc/support/port_platform.h> + +#include <grpc/slice.h> +#include <grpc/support/sync.h> + +extern "C" { +#include <openssl/ssl.h> +} + +#include "src/core/lib/avl/avl.h" +#include "src/core/lib/gprpp/memory.h" +#include "src/core/lib/gprpp/ref_counted.h" +#include "src/core/tsi/ssl/session_cache/ssl_session.h" + +/// Cache for SSL sessions for sessions resumption. +/// +/// Older sessions may be evicted from the cache using LRU policy if capacity +/// limit is hit. All sessions are associated with some key, usually server +/// name. Note that servers are required to share session ticket encryption keys +/// in order for cache to be effective. +/// +/// This class is thread safe. + +namespace tsi { + +class SslSessionLRUCache : public grpc_core::RefCounted<SslSessionLRUCache> { + public: + /// Create new LRU cache with the given capacity. + static grpc_core::RefCountedPtr<SslSessionLRUCache> Create(size_t capacity) { + return grpc_core::MakeRefCounted<SslSessionLRUCache>(capacity); + } + + // Not copyable nor movable. + SslSessionLRUCache(const SslSessionLRUCache&) = delete; + SslSessionLRUCache& operator=(const SslSessionLRUCache&) = delete; + + /// Returns current number of sessions in the cache. + size_t Size(); + /// Add \a session in the cache using \a key. This operation may discard older + /// sessions. + void Put(const char* key, SslSessionPtr session); + /// Returns the session from the cache associated with \a key or null if not + /// found. + SslSessionPtr Get(const char* key); + + private: + // So New() can call our private ctor. + template <typename T, typename... Args> + friend T* grpc_core::New(Args&&... args); + + class Node; + + explicit SslSessionLRUCache(size_t capacity); + ~SslSessionLRUCache(); + + Node* FindLocked(const grpc_slice& key); + void Remove(Node* node); + void PushFront(Node* node); + void AssertInvariants(); + + gpr_mu lock_; + size_t capacity_; + + Node* use_order_list_head_ = nullptr; + Node* use_order_list_tail_ = nullptr; + size_t use_order_list_size_ = 0; + grpc_avl entry_by_key_; +}; + +} // namespace tsi + +#endif /* GRPC_CORE_TSI_SSL_SESSION_CACHE_SSL_SESSION_CACHE_H */ diff --git a/src/core/tsi/ssl/session_cache/ssl_session_openssl.cc b/src/core/tsi/ssl/session_cache/ssl_session_openssl.cc new file mode 100644 index 0000000000..61c036c7eb --- /dev/null +++ b/src/core/tsi/ssl/session_cache/ssl_session_openssl.cc @@ -0,0 +1,76 @@ +/* + * + * 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/tsi/ssl/session_cache/ssl_session.h" + +#include <grpc/support/log.h> + +#ifndef OPENSSL_IS_BORINGSSL + +// OpenSSL invalidates SSL_SESSION on SSL destruction making it pointless +// to cache sessions. The workaround is to serialize (relatively expensive) +// session into binary blob and re-create it from blob on every handshake. +// Note that it's safe to keep serialized session outside of SSL lifetime +// as openssl performs all necessary validation while attempting to use a +// session and creates a new one if something is wrong (e.g. server changed +// set of allowed codecs). + +namespace tsi { +namespace { + +class OpenSslCachedSession : public SslCachedSession { + public: + OpenSslCachedSession(SslSessionPtr session) { + int size = i2d_SSL_SESSION(session.get(), nullptr); + GPR_ASSERT(size > 0); + grpc_slice slice = grpc_slice_malloc(size_t(size)); + unsigned char* start = GRPC_SLICE_START_PTR(slice); + int second_size = i2d_SSL_SESSION(session.get(), &start); + GPR_ASSERT(size == second_size); + serialized_session_ = slice; + } + + virtual ~OpenSslCachedSession() { grpc_slice_unref(serialized_session_); } + + SslSessionPtr CopySession() const override { + const unsigned char* data = GRPC_SLICE_START_PTR(serialized_session_); + size_t length = GRPC_SLICE_LENGTH(serialized_session_); + SSL_SESSION* session = d2i_SSL_SESSION(nullptr, &data, length); + if (session == nullptr) { + return SslSessionPtr(); + } + return SslSessionPtr(session); + } + + private: + grpc_slice serialized_session_; +}; + +} // namespace + +grpc_core::UniquePtr<SslCachedSession> SslCachedSession::Create( + SslSessionPtr session) { + return grpc_core::UniquePtr<SslCachedSession>( + grpc_core::New<OpenSslCachedSession>(std::move(session))); +} + +} // namespace tsi + +#endif /* OPENSSL_IS_BORINGSSL */ diff --git a/src/core/tsi/ssl_transport_security.cc b/src/core/tsi/ssl_transport_security.cc index 971170b7c5..0fc2926cf6 100644 --- a/src/core/tsi/ssl_transport_security.cc +++ b/src/core/tsi/ssl_transport_security.cc @@ -35,6 +35,7 @@ #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_id.h> @@ -47,6 +48,8 @@ extern "C" { #include <openssl/x509v3.h> } +#include "src/core/lib/gpr/useful.h" +#include "src/core/tsi/ssl/session_cache/ssl_session_cache.h" #include "src/core/tsi/ssl_types.h" #include "src/core/tsi/transport_security.h" @@ -78,6 +81,7 @@ struct tsi_ssl_client_handshaker_factory { SSL_CTX* ssl_context; unsigned char* alpn_protocol_list; size_t alpn_protocol_list_length; + grpc_core::RefCountedPtr<tsi::SslSessionLRUCache> session_cache; }; struct tsi_ssl_server_handshaker_factory { @@ -111,17 +115,19 @@ typedef struct { /* --- Library Initialization. ---*/ -static gpr_once init_openssl_once = GPR_ONCE_INIT; -static gpr_mu* openssl_mutexes = nullptr; +static gpr_once g_init_openssl_once = GPR_ONCE_INIT; +static gpr_mu* g_openssl_mutexes = nullptr; +static int g_ssl_ctx_ex_factory_index = -1; static void openssl_locking_cb(int mode, int type, const char* file, int line) GRPC_UNUSED; static unsigned long openssl_thread_id_cb(void) GRPC_UNUSED; +static const unsigned char kSslSessionIdContext[] = {'g', 'r', 'p', 'c'}; static void openssl_locking_cb(int mode, int type, const char* file, int line) { if (mode & CRYPTO_LOCK) { - gpr_mu_lock(&openssl_mutexes[type]); + gpr_mu_lock(&g_openssl_mutexes[type]); } else { - gpr_mu_unlock(&openssl_mutexes[type]); + gpr_mu_unlock(&g_openssl_mutexes[type]); } } @@ -137,13 +143,16 @@ static void init_openssl(void) { OpenSSL_add_all_algorithms(); num_locks = CRYPTO_num_locks(); GPR_ASSERT(num_locks > 0); - openssl_mutexes = static_cast<gpr_mu*>( + g_openssl_mutexes = static_cast<gpr_mu*>( gpr_malloc(static_cast<size_t>(num_locks) * sizeof(gpr_mu))); for (i = 0; i < CRYPTO_num_locks(); i++) { - gpr_mu_init(&openssl_mutexes[i]); + gpr_mu_init(&g_openssl_mutexes[i]); } CRYPTO_set_locking_callback(openssl_locking_cb); CRYPTO_set_id_callback(openssl_thread_id_cb); + g_ssl_ctx_ex_factory_index = + SSL_CTX_get_ex_new_index(0, nullptr, nullptr, nullptr, nullptr); + GPR_ASSERT(g_ssl_ctx_ex_factory_index != -1); } /* --- Ssl utils. ---*/ @@ -721,6 +730,23 @@ static int NullVerifyCallback(int preverify_ok, X509_STORE_CTX* ctx) { return 1; } +/* --- tsi_ssl_session_cache methods implementation. ---*/ + +tsi_ssl_session_cache* tsi_ssl_session_cache_create_lru(size_t capacity) { + /* Pointer will be dereferenced by unref call. */ + return reinterpret_cast<tsi_ssl_session_cache*>( + tsi::SslSessionLRUCache::Create(capacity).release()); +} + +void tsi_ssl_session_cache_ref(tsi_ssl_session_cache* cache) { + /* Pointer will be dereferenced by unref call. */ + reinterpret_cast<tsi::SslSessionLRUCache*>(cache)->Ref().release(); +} + +void tsi_ssl_session_cache_unref(tsi_ssl_session_cache* cache) { + reinterpret_cast<tsi::SslSessionLRUCache*>(cache)->Unref(); +} + /* --- tsi_frame_protector methods implementation. ---*/ static tsi_result ssl_protector_protect(tsi_frame_protector* self, @@ -1015,25 +1041,34 @@ static tsi_result ssl_handshaker_extract_peer(tsi_handshaker* self, SSL_get0_next_proto_negotiated(impl->ssl, &alpn_selected, &alpn_selected_len); } + + // 1 is for session reused property. + size_t new_property_count = peer->property_count + 1; + if (alpn_selected != nullptr) new_property_count++; + tsi_peer_property* new_properties = static_cast<tsi_peer_property*>( + gpr_zalloc(sizeof(*new_properties) * new_property_count)); + for (size_t i = 0; i < peer->property_count; i++) { + new_properties[i] = peer->properties[i]; + } + if (peer->properties != nullptr) gpr_free(peer->properties); + peer->properties = new_properties; + if (alpn_selected != nullptr) { - size_t i; - tsi_peer_property* new_properties = static_cast<tsi_peer_property*>( - gpr_zalloc(sizeof(*new_properties) * (peer->property_count + 1))); - for (i = 0; i < peer->property_count; i++) { - new_properties[i] = peer->properties[i]; - } result = tsi_construct_string_peer_property( TSI_SSL_ALPN_SELECTED_PROTOCOL, reinterpret_cast<const char*>(alpn_selected), alpn_selected_len, - &new_properties[peer->property_count]); - if (result != TSI_OK) { - gpr_free(new_properties); - return result; - } - if (peer->properties != nullptr) gpr_free(peer->properties); + &peer->properties[peer->property_count]); + if (result != TSI_OK) return result; peer->property_count++; - peer->properties = new_properties; } + + const char* session_reused = SSL_session_reused(impl->ssl) ? "true" : "false"; + result = tsi_construct_string_peer_property( + TSI_SSL_SESSION_REUSED_PEER_PROPERTY, session_reused, + strlen(session_reused) + 1, &peer->properties[peer->property_count]); + if (result != TSI_OK) return result; + peer->property_count++; + return result; } @@ -1103,6 +1138,19 @@ static const tsi_handshaker_vtable handshaker_vtable = { /* --- tsi_ssl_handshaker_factory common methods. --- */ +static void tsi_ssl_handshaker_resume_session( + SSL* ssl, tsi::SslSessionLRUCache* session_cache) { + const char* server_name = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name); + if (server_name == nullptr) { + return; + } + tsi::SslSessionPtr session = session_cache->Get(server_name); + if (session != nullptr) { + // SSL_set_session internally increments reference counter. + SSL_set_session(ssl, session.get()); + } +} + static tsi_result create_tsi_ssl_handshaker(SSL_CTX* ctx, int is_client, const char* server_name_indication, tsi_ssl_handshaker_factory* factory, @@ -1139,6 +1187,12 @@ static tsi_result create_tsi_ssl_handshaker(SSL_CTX* ctx, int is_client, return TSI_INTERNAL_ERROR; } } + tsi_ssl_client_handshaker_factory* client_factory = + reinterpret_cast<tsi_ssl_client_handshaker_factory*>(factory); + if (client_factory->session_cache != nullptr) { + tsi_ssl_handshaker_resume_session(ssl, + client_factory->session_cache.get()); + } ssl_result = SSL_do_handshake(ssl); ssl_result = SSL_get_error(ssl, ssl_result); if (ssl_result != SSL_ERROR_WANT_READ) { @@ -1214,6 +1268,7 @@ static void tsi_ssl_client_handshaker_factory_destroy( reinterpret_cast<tsi_ssl_client_handshaker_factory*>(factory); if (self->ssl_context != nullptr) SSL_CTX_free(self->ssl_context); if (self->alpn_protocol_list != nullptr) gpr_free(self->alpn_protocol_list); + self->session_cache.reset(); gpr_free(self); } @@ -1357,6 +1412,30 @@ static int server_handshaker_factory_npn_advertised_callback( return SSL_TLSEXT_ERR_OK; } +/// This callback is called when new \a session is established and ready to +/// be cached. This session can be reused for new connections to similar +/// servers at later point of time. +/// It's intended to be used with SSL_CTX_sess_set_new_cb function. +/// +/// It returns 1 if callback takes ownership over \a session and 0 otherwise. +static int server_handshaker_factory_new_session_callback( + SSL* ssl, SSL_SESSION* session) { + SSL_CTX* ssl_context = SSL_get_SSL_CTX(ssl); + if (ssl_context == nullptr) { + return 0; + } + void* arg = SSL_CTX_get_ex_data(ssl_context, g_ssl_ctx_ex_factory_index); + tsi_ssl_client_handshaker_factory* factory = + static_cast<tsi_ssl_client_handshaker_factory*>(arg); + const char* server_name = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name); + if (server_name == nullptr) { + return 0; + } + factory->session_cache->Put(server_name, tsi::SslSessionPtr(session)); + // Return 1 to indicate transfered ownership over the given session. + return 1; +} + /* --- tsi_ssl_handshaker_factory constructors. --- */ static tsi_ssl_handshaker_factory_vtable client_handshaker_factory_vtable = { @@ -1367,15 +1446,29 @@ tsi_result tsi_create_ssl_client_handshaker_factory( const char* pem_root_certs, const char* cipher_suites, const char** alpn_protocols, uint16_t num_alpn_protocols, tsi_ssl_client_handshaker_factory** factory) { + tsi_ssl_client_handshaker_options options; + memset(&options, 0, sizeof(options)); + options.pem_key_cert_pair = pem_key_cert_pair; + options.pem_root_certs = pem_root_certs; + options.cipher_suites = cipher_suites; + options.alpn_protocols = alpn_protocols; + options.num_alpn_protocols = num_alpn_protocols; + return tsi_create_ssl_client_handshaker_factory_with_options(&options, + factory); +} + +tsi_result tsi_create_ssl_client_handshaker_factory_with_options( + const tsi_ssl_client_handshaker_options* options, + tsi_ssl_client_handshaker_factory** factory) { SSL_CTX* ssl_context = nullptr; tsi_ssl_client_handshaker_factory* impl = nullptr; tsi_result result = TSI_OK; - gpr_once_init(&init_openssl_once, init_openssl); + gpr_once_init(&g_init_openssl_once, init_openssl); if (factory == nullptr) return TSI_INVALID_ARGUMENT; *factory = nullptr; - if (pem_root_certs == nullptr) return TSI_INVALID_ARGUMENT; + if (options->pem_root_certs == nullptr) return TSI_INVALID_ARGUMENT; ssl_context = SSL_CTX_new(TLSv1_2_method()); if (ssl_context == nullptr) { @@ -1390,21 +1483,33 @@ tsi_result tsi_create_ssl_client_handshaker_factory( impl->ssl_context = ssl_context; + if (options->session_cache != nullptr) { + // Unref is called manually on factory destruction. + impl->session_cache = + reinterpret_cast<tsi::SslSessionLRUCache*>(options->session_cache) + ->Ref(); + SSL_CTX_set_ex_data(ssl_context, g_ssl_ctx_ex_factory_index, impl); + SSL_CTX_sess_set_new_cb(ssl_context, + server_handshaker_factory_new_session_callback); + SSL_CTX_set_session_cache_mode(ssl_context, SSL_SESS_CACHE_CLIENT); + } + do { - result = - populate_ssl_context(ssl_context, pem_key_cert_pair, cipher_suites); + result = populate_ssl_context(ssl_context, options->pem_key_cert_pair, + options->cipher_suites); if (result != TSI_OK) break; - result = ssl_ctx_load_verification_certs(ssl_context, pem_root_certs, - strlen(pem_root_certs), nullptr); + result = ssl_ctx_load_verification_certs( + ssl_context, options->pem_root_certs, strlen(options->pem_root_certs), + nullptr); if (result != TSI_OK) { gpr_log(GPR_ERROR, "Cannot load server root certificates."); break; } - if (num_alpn_protocols != 0) { - result = build_alpn_protocol_name_list(alpn_protocols, num_alpn_protocols, - &impl->alpn_protocol_list, - &impl->alpn_protocol_list_length); + if (options->num_alpn_protocols != 0) { + result = build_alpn_protocol_name_list( + options->alpn_protocols, options->num_alpn_protocols, + &impl->alpn_protocol_list, &impl->alpn_protocol_list_length); if (result != TSI_OK) { gpr_log(GPR_ERROR, "Building alpn list failed with error %s.", tsi_result_to_string(result)); @@ -1457,15 +1562,32 @@ tsi_result tsi_create_ssl_server_handshaker_factory_ex( tsi_client_certificate_request_type client_certificate_request, const char* cipher_suites, const char** alpn_protocols, uint16_t num_alpn_protocols, tsi_ssl_server_handshaker_factory** factory) { + tsi_ssl_server_handshaker_options options; + memset(&options, 0, sizeof(options)); + options.pem_key_cert_pairs = pem_key_cert_pairs; + options.num_key_cert_pairs = num_key_cert_pairs; + options.pem_client_root_certs = pem_client_root_certs; + options.client_certificate_request = client_certificate_request; + options.cipher_suites = cipher_suites; + options.alpn_protocols = alpn_protocols; + options.num_alpn_protocols = num_alpn_protocols; + return tsi_create_ssl_server_handshaker_factory_with_options(&options, + factory); +} + +tsi_result tsi_create_ssl_server_handshaker_factory_with_options( + const tsi_ssl_server_handshaker_options* options, + tsi_ssl_server_handshaker_factory** factory) { tsi_ssl_server_handshaker_factory* impl = nullptr; tsi_result result = TSI_OK; size_t i = 0; - gpr_once_init(&init_openssl_once, init_openssl); + gpr_once_init(&g_init_openssl_once, init_openssl); if (factory == nullptr) return TSI_INVALID_ARGUMENT; *factory = nullptr; - if (num_key_cert_pairs == 0 || pem_key_cert_pairs == nullptr) { + if (options->num_key_cert_pairs == 0 || + options->pem_key_cert_pairs == nullptr) { return TSI_INVALID_ARGUMENT; } @@ -1474,28 +1596,28 @@ tsi_result tsi_create_ssl_server_handshaker_factory_ex( tsi_ssl_handshaker_factory_init(&impl->base); impl->base.vtable = &server_handshaker_factory_vtable; - impl->ssl_contexts = - static_cast<SSL_CTX**>(gpr_zalloc(num_key_cert_pairs * sizeof(SSL_CTX*))); - impl->ssl_context_x509_subject_names = - static_cast<tsi_peer*>(gpr_zalloc(num_key_cert_pairs * sizeof(tsi_peer))); + impl->ssl_contexts = static_cast<SSL_CTX**>( + gpr_zalloc(options->num_key_cert_pairs * sizeof(SSL_CTX*))); + impl->ssl_context_x509_subject_names = static_cast<tsi_peer*>( + gpr_zalloc(options->num_key_cert_pairs * sizeof(tsi_peer))); if (impl->ssl_contexts == nullptr || impl->ssl_context_x509_subject_names == nullptr) { tsi_ssl_handshaker_factory_unref(&impl->base); return TSI_OUT_OF_RESOURCES; } - impl->ssl_context_count = num_key_cert_pairs; + impl->ssl_context_count = options->num_key_cert_pairs; - if (num_alpn_protocols > 0) { - result = build_alpn_protocol_name_list(alpn_protocols, num_alpn_protocols, - &impl->alpn_protocol_list, - &impl->alpn_protocol_list_length); + if (options->num_alpn_protocols > 0) { + result = build_alpn_protocol_name_list( + options->alpn_protocols, options->num_alpn_protocols, + &impl->alpn_protocol_list, &impl->alpn_protocol_list_length); if (result != TSI_OK) { tsi_ssl_handshaker_factory_unref(&impl->base); return result; } } - for (i = 0; i < num_key_cert_pairs; i++) { + for (i = 0; i < options->num_key_cert_pairs; i++) { do { impl->ssl_contexts[i] = SSL_CTX_new(TLSv1_2_method()); if (impl->ssl_contexts[i] == nullptr) { @@ -1504,20 +1626,44 @@ tsi_result tsi_create_ssl_server_handshaker_factory_ex( break; } result = populate_ssl_context(impl->ssl_contexts[i], - &pem_key_cert_pairs[i], cipher_suites); + &options->pem_key_cert_pairs[i], + options->cipher_suites); if (result != TSI_OK) break; - if (pem_client_root_certs != nullptr) { + // TODO(elessar): Provide ability to disable session ticket keys. + + // Allow client cache sessions (it's needed for OpenSSL only). + int set_sid_ctx_result = SSL_CTX_set_session_id_context( + impl->ssl_contexts[i], kSslSessionIdContext, + GPR_ARRAY_SIZE(kSslSessionIdContext)); + if (set_sid_ctx_result == 0) { + gpr_log(GPR_ERROR, "Failed to set session id context."); + result = TSI_INTERNAL_ERROR; + break; + } + + if (options->session_ticket_key != nullptr) { + if (SSL_CTX_set_tlsext_ticket_keys( + impl->ssl_contexts[i], + const_cast<char*>(options->session_ticket_key), + options->session_ticket_key_size) == 0) { + gpr_log(GPR_ERROR, "Invalid STEK size."); + result = TSI_INVALID_ARGUMENT; + break; + } + } + + if (options->pem_client_root_certs != nullptr) { STACK_OF(X509_NAME)* root_names = nullptr; result = ssl_ctx_load_verification_certs( - impl->ssl_contexts[i], pem_client_root_certs, - strlen(pem_client_root_certs), &root_names); + impl->ssl_contexts[i], options->pem_client_root_certs, + strlen(options->pem_client_root_certs), &root_names); if (result != TSI_OK) { gpr_log(GPR_ERROR, "Invalid verification certs."); break; } SSL_CTX_set_client_CA_list(impl->ssl_contexts[i], root_names); - switch (client_certificate_request) { + switch (options->client_certificate_request) { case TSI_DONT_REQUEST_CLIENT_CERTIFICATE: SSL_CTX_set_verify(impl->ssl_contexts[i], SSL_VERIFY_NONE, nullptr); break; @@ -1544,7 +1690,7 @@ tsi_result tsi_create_ssl_server_handshaker_factory_ex( } result = extract_x509_subject_names_from_pem_cert( - pem_key_cert_pairs[i].cert_chain, + options->pem_key_cert_pairs[i].cert_chain, &impl->ssl_context_x509_subject_names[i]); if (result != TSI_OK) break; diff --git a/src/core/tsi/ssl_transport_security.h b/src/core/tsi/ssl_transport_security.h index edebadc1be..29d209b8f5 100644 --- a/src/core/tsi/ssl_transport_security.h +++ b/src/core/tsi/ssl_transport_security.h @@ -30,11 +30,27 @@ #define TSI_X509_SUBJECT_COMMON_NAME_PEER_PROPERTY "x509_subject_common_name" #define TSI_X509_SUBJECT_ALTERNATIVE_NAME_PEER_PROPERTY \ "x509_subject_alternative_name" +#define TSI_SSL_SESSION_REUSED_PEER_PROPERTY "ssl_session_reused" #define TSI_X509_PEM_CERT_PROPERTY "x509_pem_cert" #define TSI_SSL_ALPN_SELECTED_PROTOCOL "ssl_alpn_selected_protocol" +/* --- tsi_ssl_session_cache object --- + + Cache for SSL sessions for sessions resumption. */ + +typedef struct tsi_ssl_session_cache tsi_ssl_session_cache; + +/* Create LRU cache for SSL sessions with \a capacity. */ +tsi_ssl_session_cache* tsi_ssl_session_cache_create_lru(size_t capacity); + +/* Increment reference counter of \a cache. */ +void tsi_ssl_session_cache_ref(tsi_ssl_session_cache* cache); + +/* Decrement reference counter of \a cache. */ +void tsi_ssl_session_cache_unref(tsi_ssl_session_cache* cache); + /* --- tsi_ssl_client_handshaker_factory object --- This object creates a client tsi_handshaker objects implemented in terms of @@ -81,6 +97,43 @@ tsi_result tsi_create_ssl_client_handshaker_factory( const char** alpn_protocols, uint16_t num_alpn_protocols, tsi_ssl_client_handshaker_factory** factory); +typedef struct { + /* pem_key_cert_pair is a pointer to the object containing client's private + key and certificate chain. This parameter can be NULL if the client does + not have such a key/cert pair. */ + const tsi_ssl_pem_key_cert_pair* pem_key_cert_pair; + /* pem_roots_cert is the NULL-terminated string containing the PEM encoding of + the client root certificates. This parameter may be NULL if the server does + not want the client to be authenticated with SSL. */ + const char* pem_root_certs; + /* cipher_suites contains an optional list of the ciphers that the client + supports. The format of this string is described in: + https://www.openssl.org/docs/apps/ciphers.html. + This parameter can be set to NULL to use the default set of ciphers. + TODO(jboeuf): Revisit the format of this parameter. */ + const char* cipher_suites; + /* alpn_protocols is an array containing the NULL terminated protocol names + that the handshakers created with this factory support. This parameter can + be NULL. */ + const char** alpn_protocols; + /* num_alpn_protocols is the number of alpn protocols and associated lengths + specified. If this parameter is 0, the other alpn parameters must be + NULL. */ + size_t num_alpn_protocols; + /* ssl_session_cache is a cache for reusable client-side sessions. */ + tsi_ssl_session_cache* session_cache; +} tsi_ssl_client_handshaker_options; + +/* Creates a client handshaker factory. + - options is the options used to create a factory. + - factory is the address of the factory pointer to be created. + + - This method returns TSI_OK on success or TSI_INVALID_PARAMETER in the case + where a parameter is invalid. */ +tsi_result tsi_create_ssl_client_handshaker_factory_with_options( + const tsi_ssl_client_handshaker_options* options, + tsi_ssl_client_handshaker_factory** factory); + /* Creates a client handshaker. - self is the factory from which the handshaker will be created. - server_name_indication indicates the name of the server the client is @@ -147,6 +200,51 @@ tsi_result tsi_create_ssl_server_handshaker_factory_ex( const char* cipher_suites, const char** alpn_protocols, uint16_t num_alpn_protocols, tsi_ssl_server_handshaker_factory** factory); +typedef struct { + /* pem_key_cert_pairs is an array private key / certificate chains of the + server. */ + const tsi_ssl_pem_key_cert_pair* pem_key_cert_pairs; + /* num_key_cert_pairs is the number of items in the pem_key_cert_pairs + array. */ + size_t num_key_cert_pairs; + /* pem_root_certs is the NULL-terminated string containing the PEM encoding + of the server root certificates. */ + const char* pem_client_root_certs; + /* client_certificate_request, if set to non-zero will force the client to + authenticate with an SSL cert. Note that this option is ignored if + pem_client_root_certs is NULL or pem_client_roots_certs_size is 0. */ + tsi_client_certificate_request_type client_certificate_request; + /* cipher_suites contains an optional list of the ciphers that the server + supports. The format of this string is described in: + https://www.openssl.org/docs/apps/ciphers.html. + This parameter can be set to NULL to use the default set of ciphers. + TODO(jboeuf): Revisit the format of this parameter. */ + const char* cipher_suites; + /* alpn_protocols is an array containing the NULL terminated protocol names + that the handshakers created with this factory support. This parameter can + be NULL. */ + const char** alpn_protocols; + /* num_alpn_protocols is the number of alpn protocols and associated lengths + specified. If this parameter is 0, the other alpn parameters must be + NULL. */ + uint16_t num_alpn_protocols; + /* session_ticket_key is optional key for encrypting session keys. If paramter + is not specified it must be NULL. */ + const char* session_ticket_key; + /* session_ticket_key_size is a size of session ticket encryption key. */ + size_t session_ticket_key_size; +} tsi_ssl_server_handshaker_options; + +/* Creates a server handshaker factory. + - options is the options used to create a factory. + - factory is the address of the factory pointer to be created. + + - This method returns TSI_OK on success or TSI_INVALID_PARAMETER in the case + where a parameter is invalid. */ +tsi_result tsi_create_ssl_server_handshaker_factory_with_options( + const tsi_ssl_server_handshaker_options* options, + tsi_ssl_server_handshaker_factory** factory); + /* Creates a server handshaker. - self is the factory from which the handshaker will be created. - handshaker is the address of the handshaker pointer to be created. diff --git a/src/python/grpcio/grpc_core_dependencies.py b/src/python/grpcio/grpc_core_dependencies.py index d96cbec292..62e54a388a 100644 --- a/src/python/grpcio/grpc_core_dependencies.py +++ b/src/python/grpcio/grpc_core_dependencies.py @@ -319,6 +319,9 @@ CORE_SOURCE_FILES = [ 'src/core/ext/filters/deadline/deadline_filter.cc', 'src/core/tsi/alts_transport_security.cc', 'src/core/tsi/fake_transport_security.cc', + 'src/core/tsi/ssl/session_cache/ssl_session_boringssl.cc', + 'src/core/tsi/ssl/session_cache/ssl_session_cache.cc', + 'src/core/tsi/ssl/session_cache/ssl_session_openssl.cc', 'src/core/tsi/ssl_transport_security.cc', 'src/core/tsi/transport_security_grpc.cc', 'src/core/ext/transport/chttp2/server/chttp2_server.cc', diff --git a/src/python/grpcio_tests/tests/unit/_auth_context_test.py b/src/python/grpcio_tests/tests/unit/_auth_context_test.py index 468869a03e..8c1a30e032 100644 --- a/src/python/grpcio_tests/tests/unit/_auth_context_test.py +++ b/src/python/grpcio_tests/tests/unit/_auth_context_test.py @@ -102,7 +102,8 @@ class AuthContextTest(unittest.TestCase): self.assertIsNone(auth_data[_ID]) self.assertIsNone(auth_data[_ID_KEY]) self.assertDictEqual({ - 'transport_security_type': [b'ssl'] + 'transport_security_type': [b'ssl'], + 'ssl_session_reused': [b'false'], }, auth_data[_AUTH_CTX]) def testSecureClientCert(self): diff --git a/src/ruby/ext/grpc/rb_grpc_imports.generated.c b/src/ruby/ext/grpc/rb_grpc_imports.generated.c index c045480ff4..7009bd80f6 100644 --- a/src/ruby/ext/grpc/rb_grpc_imports.generated.c +++ b/src/ruby/ext/grpc/rb_grpc_imports.generated.c @@ -106,6 +106,9 @@ grpc_auth_context_release_type grpc_auth_context_release_import; grpc_auth_context_add_property_type grpc_auth_context_add_property_import; grpc_auth_context_add_cstring_property_type grpc_auth_context_add_cstring_property_import; grpc_auth_context_set_peer_identity_property_name_type grpc_auth_context_set_peer_identity_property_name_import; +grpc_ssl_session_cache_create_lru_type grpc_ssl_session_cache_create_lru_import; +grpc_ssl_session_cache_destroy_type grpc_ssl_session_cache_destroy_import; +grpc_ssl_session_cache_create_channel_arg_type grpc_ssl_session_cache_create_channel_arg_import; grpc_channel_credentials_release_type grpc_channel_credentials_release_import; grpc_google_default_credentials_create_type grpc_google_default_credentials_create_import; grpc_set_ssl_roots_override_callback_type grpc_set_ssl_roots_override_callback_import; @@ -342,6 +345,9 @@ void grpc_rb_load_imports(HMODULE library) { grpc_auth_context_add_property_import = (grpc_auth_context_add_property_type) GetProcAddress(library, "grpc_auth_context_add_property"); grpc_auth_context_add_cstring_property_import = (grpc_auth_context_add_cstring_property_type) GetProcAddress(library, "grpc_auth_context_add_cstring_property"); grpc_auth_context_set_peer_identity_property_name_import = (grpc_auth_context_set_peer_identity_property_name_type) GetProcAddress(library, "grpc_auth_context_set_peer_identity_property_name"); + grpc_ssl_session_cache_create_lru_import = (grpc_ssl_session_cache_create_lru_type) GetProcAddress(library, "grpc_ssl_session_cache_create_lru"); + grpc_ssl_session_cache_destroy_import = (grpc_ssl_session_cache_destroy_type) GetProcAddress(library, "grpc_ssl_session_cache_destroy"); + grpc_ssl_session_cache_create_channel_arg_import = (grpc_ssl_session_cache_create_channel_arg_type) GetProcAddress(library, "grpc_ssl_session_cache_create_channel_arg"); grpc_channel_credentials_release_import = (grpc_channel_credentials_release_type) GetProcAddress(library, "grpc_channel_credentials_release"); grpc_google_default_credentials_create_import = (grpc_google_default_credentials_create_type) GetProcAddress(library, "grpc_google_default_credentials_create"); grpc_set_ssl_roots_override_callback_import = (grpc_set_ssl_roots_override_callback_type) GetProcAddress(library, "grpc_set_ssl_roots_override_callback"); diff --git a/src/ruby/ext/grpc/rb_grpc_imports.generated.h b/src/ruby/ext/grpc/rb_grpc_imports.generated.h index 4f07452c68..244976c9c5 100644 --- a/src/ruby/ext/grpc/rb_grpc_imports.generated.h +++ b/src/ruby/ext/grpc/rb_grpc_imports.generated.h @@ -293,6 +293,15 @@ extern grpc_auth_context_add_cstring_property_type grpc_auth_context_add_cstring typedef int(*grpc_auth_context_set_peer_identity_property_name_type)(grpc_auth_context* ctx, const char* name); extern grpc_auth_context_set_peer_identity_property_name_type grpc_auth_context_set_peer_identity_property_name_import; #define grpc_auth_context_set_peer_identity_property_name grpc_auth_context_set_peer_identity_property_name_import +typedef grpc_ssl_session_cache*(*grpc_ssl_session_cache_create_lru_type)(size_t capacity); +extern grpc_ssl_session_cache_create_lru_type grpc_ssl_session_cache_create_lru_import; +#define grpc_ssl_session_cache_create_lru grpc_ssl_session_cache_create_lru_import +typedef void(*grpc_ssl_session_cache_destroy_type)(grpc_ssl_session_cache* cache); +extern grpc_ssl_session_cache_destroy_type grpc_ssl_session_cache_destroy_import; +#define grpc_ssl_session_cache_destroy grpc_ssl_session_cache_destroy_import +typedef grpc_arg(*grpc_ssl_session_cache_create_channel_arg_type)(grpc_ssl_session_cache* cache); +extern grpc_ssl_session_cache_create_channel_arg_type grpc_ssl_session_cache_create_channel_arg_import; +#define grpc_ssl_session_cache_create_channel_arg grpc_ssl_session_cache_create_channel_arg_import typedef void(*grpc_channel_credentials_release_type)(grpc_channel_credentials* creds); extern grpc_channel_credentials_release_type grpc_channel_credentials_release_import; #define grpc_channel_credentials_release grpc_channel_credentials_release_import diff --git a/test/core/end2end/BUILD b/test/core/end2end/BUILD index 952f3505fb..dd16694204 100644 --- a/test/core/end2end/BUILD +++ b/test/core/end2end/BUILD @@ -163,3 +163,20 @@ grpc_cc_test( ) grpc_end2end_tests() + +grpc_cc_test( + name = "h2_ssl_session_reuse_test", + srcs = ["h2_ssl_session_reuse_test.cc"], + external_deps = [ + "gtest", + ], + language = "C++", + deps = [ + ':end2end_tests', + '//:gpr', + '//:grpc', + '//:tsi', + '//test/core/util:gpr_test_util', + '//test/core/util:grpc_test_util', + ], +) diff --git a/test/core/end2end/h2_ssl_session_reuse_test.cc b/test/core/end2end/h2_ssl_session_reuse_test.cc new file mode 100644 index 0000000000..d5984be93f --- /dev/null +++ b/test/core/end2end/h2_ssl_session_reuse_test.cc @@ -0,0 +1,280 @@ +/* + * + * 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 "test/core/end2end/end2end_tests.h" + +#include <stdio.h> +#include <string.h> + +#include <grpc/support/alloc.h> +#include <grpc/support/log.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/gpr/tmpfile.h" +#include "src/core/lib/security/credentials/credentials.h" +#include "test/core/end2end/cq_verifier.h" +#include "test/core/end2end/data/ssl_test_data.h" +#include "test/core/util/port.h" +#include "test/core/util/test_config.h" + +#include <gtest/gtest.h> + +namespace grpc { +namespace testing { +namespace { + +void* tag(intptr_t t) { return (void*)t; } + +gpr_timespec five_seconds_time() { return grpc_timeout_seconds_to_deadline(5); } + +grpc_server* server_create(grpc_completion_queue* cq, char* server_addr) { + grpc_ssl_pem_key_cert_pair pem_cert_key_pair = {test_server1_key, + test_server1_cert}; + grpc_server_credentials* server_creds = grpc_ssl_server_credentials_create_ex( + test_root_cert, &pem_cert_key_pair, 1, + GRPC_SSL_REQUEST_CLIENT_CERTIFICATE_AND_VERIFY, nullptr); + + grpc_server* server = grpc_server_create(nullptr, nullptr); + grpc_server_register_completion_queue(server, cq, nullptr); + GPR_ASSERT( + grpc_server_add_secure_http2_port(server, server_addr, server_creds)); + grpc_server_credentials_release(server_creds); + grpc_server_start(server); + + return server; +} + +grpc_channel* client_create(char* server_addr, grpc_ssl_session_cache* cache) { + grpc_ssl_pem_key_cert_pair signed_client_key_cert_pair = { + test_signed_client_key, test_signed_client_cert}; + grpc_channel_credentials* client_creds = grpc_ssl_credentials_create( + test_root_cert, &signed_client_key_cert_pair, nullptr); + + grpc_arg args[] = { + grpc_channel_arg_string_create( + const_cast<char*>(GRPC_SSL_TARGET_NAME_OVERRIDE_ARG), + const_cast<char*>("waterzooi.test.google.be")), + grpc_ssl_session_cache_create_channel_arg(cache), + }; + + grpc_channel_args* client_args = + grpc_channel_args_copy_and_add(nullptr, args, GPR_ARRAY_SIZE(args)); + + grpc_channel* client = grpc_secure_channel_create(client_creds, server_addr, + client_args, nullptr); + GPR_ASSERT(client != nullptr); + grpc_channel_credentials_release(client_creds); + + { + grpc_core::ExecCtx exec_ctx; + grpc_channel_args_destroy(client_args); + } + + return client; +} + +void do_round_trip(grpc_completion_queue* cq, grpc_server* server, + char* server_addr, grpc_ssl_session_cache* cache, + bool expect_session_reuse) { + grpc_channel* client = client_create(server_addr, cache); + + cq_verifier* cqv = cq_verifier_create(cq); + grpc_op ops[6]; + grpc_op* op; + grpc_metadata_array initial_metadata_recv; + grpc_metadata_array trailing_metadata_recv; + grpc_metadata_array request_metadata_recv; + grpc_call_details call_details; + grpc_status_code status; + grpc_call_error error; + grpc_slice details; + int was_cancelled = 2; + + gpr_timespec deadline = grpc_timeout_seconds_to_deadline(60); + grpc_call* c = grpc_channel_create_call( + client, nullptr, GRPC_PROPAGATE_DEFAULTS, cq, + grpc_slice_from_static_string("/foo"), nullptr, deadline, nullptr); + GPR_ASSERT(c); + + grpc_metadata_array_init(&initial_metadata_recv); + grpc_metadata_array_init(&trailing_metadata_recv); + grpc_metadata_array_init(&request_metadata_recv); + grpc_call_details_init(&call_details); + + memset(ops, 0, sizeof(ops)); + op = ops; + op->op = GRPC_OP_SEND_INITIAL_METADATA; + op->data.send_initial_metadata.count = 0; + op->flags = 0; + op->reserved = nullptr; + op++; + op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; + op->flags = 0; + op->reserved = nullptr; + op++; + op->op = GRPC_OP_RECV_INITIAL_METADATA; + op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv; + op->flags = 0; + op->reserved = nullptr; + op++; + op->op = GRPC_OP_RECV_STATUS_ON_CLIENT; + op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv; + op->data.recv_status_on_client.status = &status; + op->data.recv_status_on_client.status_details = &details; + op->flags = 0; + op->reserved = nullptr; + op++; + error = grpc_call_start_batch(c, ops, static_cast<size_t>(op - ops), tag(1), + nullptr); + GPR_ASSERT(GRPC_CALL_OK == error); + + grpc_call* s; + error = grpc_server_request_call(server, &s, &call_details, + &request_metadata_recv, cq, cq, tag(101)); + GPR_ASSERT(GRPC_CALL_OK == error); + CQ_EXPECT_COMPLETION(cqv, tag(101), 1); + cq_verify(cqv); + + grpc_auth_context* auth = grpc_call_auth_context(s); + grpc_auth_property_iterator it = grpc_auth_context_find_properties_by_name( + auth, GRPC_SSL_SESSION_REUSED_PROPERTY); + const grpc_auth_property* property = grpc_auth_property_iterator_next(&it); + GPR_ASSERT(property != nullptr); + + if (expect_session_reuse) { + GPR_ASSERT(strcmp(property->value, "true") == 0); + } else { + GPR_ASSERT(strcmp(property->value, "false") == 0); + } + grpc_auth_context_release(auth); + + memset(ops, 0, sizeof(ops)); + op = ops; + op->op = GRPC_OP_SEND_INITIAL_METADATA; + op->data.send_initial_metadata.count = 0; + op->flags = 0; + op->reserved = nullptr; + op++; + op->op = GRPC_OP_RECV_CLOSE_ON_SERVER; + op->data.recv_close_on_server.cancelled = &was_cancelled; + op->flags = 0; + op->reserved = nullptr; + op++; + op->op = GRPC_OP_SEND_STATUS_FROM_SERVER; + op->data.send_status_from_server.trailing_metadata_count = 0; + op->data.send_status_from_server.status = GRPC_STATUS_OK; + op->flags = 0; + op->reserved = nullptr; + op++; + error = grpc_call_start_batch(s, ops, static_cast<size_t>(op - ops), tag(103), + nullptr); + GPR_ASSERT(GRPC_CALL_OK == error); + + CQ_EXPECT_COMPLETION(cqv, tag(103), 1); + CQ_EXPECT_COMPLETION(cqv, tag(1), 1); + cq_verify(cqv); + + grpc_metadata_array_destroy(&initial_metadata_recv); + grpc_metadata_array_destroy(&trailing_metadata_recv); + grpc_metadata_array_destroy(&request_metadata_recv); + grpc_call_details_destroy(&call_details); + + grpc_call_unref(c); + grpc_call_unref(s); + + cq_verifier_destroy(cqv); + + grpc_channel_destroy(client); +} + +void drain_cq(grpc_completion_queue* cq) { + grpc_event ev; + do { + ev = grpc_completion_queue_next(cq, five_seconds_time(), nullptr); + } while (ev.type != GRPC_QUEUE_SHUTDOWN); +} + +TEST(H2SessionReuseTest, SingleReuse) { + int port = grpc_pick_unused_port_or_die(); + + char* server_addr; + gpr_join_host_port(&server_addr, "localhost", port); + + grpc_completion_queue* cq = grpc_completion_queue_create_for_next(nullptr); + grpc_ssl_session_cache* cache = grpc_ssl_session_cache_create_lru(16); + + grpc_server* server = server_create(cq, server_addr); + + do_round_trip(cq, server, server_addr, cache, false); + do_round_trip(cq, server, server_addr, cache, true); + do_round_trip(cq, server, server_addr, cache, true); + + gpr_free(server_addr); + grpc_ssl_session_cache_destroy(cache); + + GPR_ASSERT(grpc_completion_queue_next( + cq, grpc_timeout_milliseconds_to_deadline(100), nullptr) + .type == GRPC_QUEUE_TIMEOUT); + + grpc_completion_queue* shutdown_cq = + grpc_completion_queue_create_for_pluck(nullptr); + grpc_server_shutdown_and_notify(server, shutdown_cq, tag(1000)); + GPR_ASSERT(grpc_completion_queue_pluck(shutdown_cq, tag(1000), + grpc_timeout_seconds_to_deadline(5), + nullptr) + .type == GRPC_OP_COMPLETE); + grpc_server_destroy(server); + grpc_completion_queue_destroy(shutdown_cq); + + grpc_completion_queue_shutdown(cq); + drain_cq(cq); + grpc_completion_queue_destroy(cq); +} + +} // namespace +} // namespace testing +} // namespace grpc + +int main(int argc, char** argv) { + FILE* roots_file; + size_t roots_size = strlen(test_root_cert); + char* roots_filename; + + grpc_test_init(argc, argv); + /* Set the SSL roots env var. */ + roots_file = gpr_tmpfile("chttp2_ssl_session_reuse_test", &roots_filename); + GPR_ASSERT(roots_filename != nullptr); + GPR_ASSERT(roots_file != nullptr); + GPR_ASSERT(fwrite(test_root_cert, 1, roots_size, roots_file) == roots_size); + fclose(roots_file); + gpr_setenv(GRPC_DEFAULT_SSL_ROOTS_FILE_PATH_ENV_VAR, roots_filename); + + grpc_init(); + ::testing::InitGoogleTest(&argc, argv); + int ret = RUN_ALL_TESTS(); + grpc_shutdown(); + + /* Cleanup. */ + remove(roots_filename); + gpr_free(roots_filename); + + return ret; +} diff --git a/test/core/surface/public_headers_must_be_c89.c b/test/core/surface/public_headers_must_be_c89.c index bd4dc0b60e..38a7d7e709 100644 --- a/test/core/surface/public_headers_must_be_c89.c +++ b/test/core/surface/public_headers_must_be_c89.c @@ -141,6 +141,9 @@ int main(int argc, char **argv) { printf("%lx", (unsigned long) grpc_auth_context_add_property); printf("%lx", (unsigned long) grpc_auth_context_add_cstring_property); printf("%lx", (unsigned long) grpc_auth_context_set_peer_identity_property_name); + printf("%lx", (unsigned long) grpc_ssl_session_cache_create_lru); + printf("%lx", (unsigned long) grpc_ssl_session_cache_destroy); + printf("%lx", (unsigned long) grpc_ssl_session_cache_create_channel_arg); printf("%lx", (unsigned long) grpc_channel_credentials_release); printf("%lx", (unsigned long) grpc_google_default_credentials_create); printf("%lx", (unsigned long) grpc_set_ssl_roots_override_callback); diff --git a/test/core/tsi/BUILD b/test/core/tsi/BUILD index 8ac3e7687c..ae6e8fdc32 100644 --- a/test/core/tsi/BUILD +++ b/test/core/tsi/BUILD @@ -41,6 +41,20 @@ grpc_cc_test( ], ) +grpc_cc_test( + name = "ssl_session_cache_test", + srcs = ["ssl_session_cache_test.cc"], + language = "C++", + external_deps = [ + "gtest", + ], + deps = [ + "//:grpc", + "//:gpr", + "//:tsi", + "//test/core/util:gpr_test_util", + ], +) grpc_cc_test( name = "ssl_transport_security_test", diff --git a/test/core/tsi/ssl_session_cache_test.cc b/test/core/tsi/ssl_session_cache_test.cc new file mode 100644 index 0000000000..72df0e545c --- /dev/null +++ b/test/core/tsi/ssl_session_cache_test.cc @@ -0,0 +1,154 @@ +/* + * + * 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 <string> +#include <unordered_set> + +#include "src/core/tsi/ssl/session_cache/ssl_session_cache.h" +#include "test/core/util/test_config.h" + +#include <grpc/grpc.h> +#include <grpc/support/log.h> +#include <gtest/gtest.h> + +namespace grpc_core { + +namespace { + +class SessionTracker; + +struct SessionExDataId { + SessionTracker* tracker; + long id; +}; + +class SessionTracker { + public: + SessionTracker() { ssl_context_ = SSL_CTX_new(TLSv1_2_method()); } + + ~SessionTracker() { SSL_CTX_free(ssl_context_); } + + tsi::SslSessionPtr NewSession(long id) { + static int ex_data_id = SSL_SESSION_get_ex_new_index( + 0, nullptr, nullptr, nullptr, DestroyExData); + GPR_ASSERT(ex_data_id != -1); + // OpenSSL and different version of BoringSSL don't agree on API + // so try both. + tsi::SslSessionPtr session = NewSessionInternal(SSL_SESSION_new); + SessionExDataId* data = new SessionExDataId{this, id}; + int result = SSL_SESSION_set_ex_data(session.get(), ex_data_id, data); + EXPECT_EQ(result, 1); + alive_sessions_.insert(id); + return session; + } + + bool IsAlive(long id) const { + return alive_sessions_.find(id) != alive_sessions_.end(); + } + + size_t AliveCount() const { return alive_sessions_.size(); } + + private: + tsi::SslSessionPtr NewSessionInternal(SSL_SESSION* (*cb)()) { + return tsi::SslSessionPtr(cb()); + } + + tsi::SslSessionPtr NewSessionInternal(SSL_SESSION* (*cb)(const SSL_CTX*)) { + return tsi::SslSessionPtr(cb(ssl_context_)); + } + + static void DestroyExData(void* parent, void* ptr, CRYPTO_EX_DATA* ad, + int index, long argl, void* argp) { + SessionExDataId* data = static_cast<SessionExDataId*>(ptr); + data->tracker->alive_sessions_.erase(data->id); + delete data; + } + + SSL_CTX* ssl_context_; + std::unordered_set<long> alive_sessions_; +}; + +TEST(SslSessionCacheTest, InitialState) { + SessionTracker tracker; + // Verify session initial state. + { + tsi::SslSessionPtr tmp_sess = tracker.NewSession(1); + EXPECT_EQ(tmp_sess->references, 1); + EXPECT_TRUE(tracker.IsAlive(1)); + EXPECT_EQ(tracker.AliveCount(), 1); + } + EXPECT_FALSE(tracker.IsAlive(1)); + EXPECT_EQ(tracker.AliveCount(), 0); +} + +TEST(SslSessionCacheTest, LruCache) { + SessionTracker tracker; + { + RefCountedPtr<tsi::SslSessionLRUCache> cache = + tsi::SslSessionLRUCache::Create(3); + tsi::SslSessionPtr sess2 = tracker.NewSession(2); + SSL_SESSION* sess2_ptr = sess2.get(); + cache->Put("first.dropbox.com", std::move(sess2)); + EXPECT_EQ(cache->Get("first.dropbox.com").get(), sess2_ptr); + EXPECT_TRUE(tracker.IsAlive(2)); + EXPECT_EQ(tracker.AliveCount(), 1); + // Putting element with the same key destroys old session. + tsi::SslSessionPtr sess3 = tracker.NewSession(3); + SSL_SESSION* sess3_ptr = sess3.get(); + cache->Put("first.dropbox.com", std::move(sess3)); + EXPECT_FALSE(tracker.IsAlive(2)); + EXPECT_EQ(cache->Get("first.dropbox.com").get(), sess3_ptr); + EXPECT_TRUE(tracker.IsAlive(3)); + EXPECT_EQ(tracker.AliveCount(), 1); + // Putting three more elements discards current one. + for (long id = 4; id < 7; id++) { + EXPECT_TRUE(tracker.IsAlive(3)); + std::string domain = std::to_string(id) + ".random.domain"; + cache->Put(domain.c_str(), tracker.NewSession(id)); + } + EXPECT_EQ(cache->Size(), 3); + EXPECT_FALSE(tracker.IsAlive(3)); + EXPECT_EQ(tracker.AliveCount(), 3); + // Accessing element moves it into front of the queue. + EXPECT_TRUE(cache->Get("4.random.domain")); + EXPECT_TRUE(tracker.IsAlive(4)); + EXPECT_TRUE(tracker.IsAlive(5)); + EXPECT_TRUE(tracker.IsAlive(6)); + // One element has to be evicted from cache-> + cache->Put("7.random.domain", tracker.NewSession(7)); + EXPECT_TRUE(tracker.IsAlive(4)); + EXPECT_FALSE(tracker.IsAlive(5)); + EXPECT_TRUE(tracker.IsAlive(6)); + EXPECT_TRUE(tracker.IsAlive(7)); + EXPECT_EQ(tracker.AliveCount(), 3); + } + // Cache destructor destroys all sessions. + EXPECT_EQ(tracker.AliveCount(), 0); +} + +} // namespace +} // namespace grpc_core + +int main(int argc, char** argv) { + ::testing::InitGoogleTest(&argc, argv); + grpc_test_init(argc, argv); + grpc_init(); + int ret = RUN_ALL_TESTS(); + grpc_shutdown(); + return ret; +} diff --git a/test/core/tsi/ssl_transport_security_test.cc b/test/core/tsi/ssl_transport_security_test.cc index d9eb7470d5..0878c57931 100644 --- a/test/core/tsi/ssl_transport_security_test.cc +++ b/test/core/tsi/ssl_transport_security_test.cc @@ -52,8 +52,8 @@ typedef enum AlpnMode { typedef struct ssl_alpn_lib { AlpnMode alpn_mode; - char** server_alpn_protocols; - char** client_alpn_protocols; + const char** server_alpn_protocols; + const char** client_alpn_protocols; uint16_t num_server_alpn_protocols; uint16_t num_client_alpn_protocols; } ssl_alpn_lib; @@ -76,6 +76,10 @@ typedef struct ssl_tsi_test_fixture { ssl_alpn_lib* alpn_lib; bool force_client_auth; char* server_name_indication; + tsi_ssl_session_cache* session_cache; + bool session_reused; + const char* session_ticket_key; + size_t session_ticket_key_size; tsi_ssl_server_handshaker_factory* server_handshaker_factory; tsi_ssl_client_handshaker_factory* client_handshaker_factory; } ssl_tsi_test_fixture; @@ -89,47 +93,60 @@ static void ssl_test_setup_handshakers(tsi_test_fixture* fixture) { ssl_key_cert_lib* key_cert_lib = ssl_fixture->key_cert_lib; ssl_alpn_lib* alpn_lib = ssl_fixture->alpn_lib; /* Create client handshaker factory. */ - tsi_ssl_pem_key_cert_pair* client_key_cert_pair = nullptr; + tsi_ssl_client_handshaker_options client_options; + memset(&client_options, 0, sizeof(client_options)); + client_options.pem_root_certs = key_cert_lib->root_cert; if (ssl_fixture->force_client_auth) { - client_key_cert_pair = key_cert_lib->use_bad_client_cert - ? &key_cert_lib->bad_client_pem_key_cert_pair - : &key_cert_lib->client_pem_key_cert_pair; + client_options.pem_key_cert_pair = + key_cert_lib->use_bad_client_cert + ? &key_cert_lib->bad_client_pem_key_cert_pair + : &key_cert_lib->client_pem_key_cert_pair; } - char** client_alpn_protocols = nullptr; - uint16_t num_client_alpn_protocols = 0; if (alpn_lib->alpn_mode == ALPN_CLIENT_NO_SERVER || alpn_lib->alpn_mode == ALPN_CLIENT_SERVER_OK || alpn_lib->alpn_mode == ALPN_CLIENT_SERVER_MISMATCH) { - client_alpn_protocols = alpn_lib->client_alpn_protocols; - num_client_alpn_protocols = alpn_lib->num_client_alpn_protocols; + client_options.alpn_protocols = alpn_lib->client_alpn_protocols; + client_options.num_alpn_protocols = alpn_lib->num_client_alpn_protocols; } - GPR_ASSERT(tsi_create_ssl_client_handshaker_factory( - client_key_cert_pair, key_cert_lib->root_cert, nullptr, - (const char**)client_alpn_protocols, num_client_alpn_protocols, - &ssl_fixture->client_handshaker_factory) == TSI_OK); + if (ssl_fixture->session_cache != nullptr) { + client_options.session_cache = ssl_fixture->session_cache; + } + GPR_ASSERT(tsi_create_ssl_client_handshaker_factory_with_options( + &client_options, &ssl_fixture->client_handshaker_factory) == + TSI_OK); /* Create server handshaker factory. */ - char** server_alpn_protocols = nullptr; - uint16_t num_server_alpn_protocols = 0; + tsi_ssl_server_handshaker_options server_options; + memset(&server_options, 0, sizeof(server_options)); if (alpn_lib->alpn_mode == ALPN_SERVER_NO_CLIENT || alpn_lib->alpn_mode == ALPN_CLIENT_SERVER_OK || alpn_lib->alpn_mode == ALPN_CLIENT_SERVER_MISMATCH) { - server_alpn_protocols = alpn_lib->server_alpn_protocols; - num_server_alpn_protocols = alpn_lib->num_server_alpn_protocols; + server_options.alpn_protocols = alpn_lib->server_alpn_protocols; + server_options.num_alpn_protocols = alpn_lib->num_server_alpn_protocols; if (alpn_lib->alpn_mode == ALPN_CLIENT_SERVER_MISMATCH) { - num_server_alpn_protocols--; + server_options.num_alpn_protocols--; } } - GPR_ASSERT(tsi_create_ssl_server_handshaker_factory( - key_cert_lib->use_bad_server_cert - ? key_cert_lib->bad_server_pem_key_cert_pairs - : key_cert_lib->server_pem_key_cert_pairs, - key_cert_lib->use_bad_server_cert - ? key_cert_lib->bad_server_num_key_cert_pairs - : key_cert_lib->server_num_key_cert_pairs, - key_cert_lib->root_cert, ssl_fixture->force_client_auth, - nullptr, (const char**)server_alpn_protocols, - num_server_alpn_protocols, - &ssl_fixture->server_handshaker_factory) == TSI_OK); + server_options.pem_key_cert_pairs = + key_cert_lib->use_bad_server_cert + ? key_cert_lib->bad_server_pem_key_cert_pairs + : key_cert_lib->server_pem_key_cert_pairs; + server_options.num_key_cert_pairs = + key_cert_lib->use_bad_server_cert + ? key_cert_lib->bad_server_num_key_cert_pairs + : key_cert_lib->server_num_key_cert_pairs; + server_options.pem_client_root_certs = key_cert_lib->root_cert; + if (ssl_fixture->force_client_auth) { + server_options.client_certificate_request = + TSI_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_AND_VERIFY; + } else { + server_options.client_certificate_request = + TSI_DONT_REQUEST_CLIENT_CERTIFICATE; + } + server_options.session_ticket_key = ssl_fixture->session_ticket_key; + server_options.session_ticket_key_size = ssl_fixture->session_ticket_key_size; + GPR_ASSERT(tsi_create_ssl_server_handshaker_factory_with_options( + &server_options, &ssl_fixture->server_handshaker_factory) == + TSI_OK); /* Create server and client handshakers. */ tsi_handshaker* client_handshaker = nullptr; GPR_ASSERT(tsi_ssl_client_handshaker_factory_create_handshaker( @@ -176,6 +193,18 @@ check_basic_authenticated_peer_and_get_common_name(const tsi_peer* peer) { return property; } +static void check_session_reusage(ssl_tsi_test_fixture* ssl_fixture, + tsi_peer* peer) { + const tsi_peer_property* session_reused = + tsi_peer_get_property_by_name(peer, TSI_SSL_SESSION_REUSED_PEER_PROPERTY); + GPR_ASSERT(session_reused != nullptr); + if (ssl_fixture->session_reused) { + GPR_ASSERT(strcmp(session_reused->value.data, "true") == 0); + } else { + GPR_ASSERT(strcmp(session_reused->value.data, "false") == 0); + } +} + void check_server0_peer(tsi_peer* peer) { const tsi_peer_property* property = check_basic_authenticated_peer_and_get_common_name(peer); @@ -233,7 +262,7 @@ static void check_client_peer(ssl_tsi_test_fixture* ssl_fixture, ssl_alpn_lib* alpn_lib = ssl_fixture->alpn_lib; if (!ssl_fixture->force_client_auth) { GPR_ASSERT(peer->property_count == - (alpn_lib->alpn_mode == ALPN_CLIENT_SERVER_OK ? 1 : 0)); + (alpn_lib->alpn_mode == ALPN_CLIENT_SERVER_OK ? 2 : 1)); } else { const tsi_peer_property* property = check_basic_authenticated_peer_and_get_common_name(peer); @@ -257,8 +286,8 @@ static void ssl_test_check_handshaker_peers(tsi_test_fixture* fixture) { if (expect_success) { GPR_ASSERT(tsi_handshaker_result_extract_peer( ssl_fixture->base.client_result, &peer) == TSI_OK); + check_session_reusage(ssl_fixture, &peer); check_alpn(ssl_fixture, &peer); - if (ssl_fixture->server_name_indication != nullptr) { check_server1_peer(&peer); } else { @@ -270,6 +299,7 @@ static void ssl_test_check_handshaker_peers(tsi_test_fixture* fixture) { if (expect_success) { GPR_ASSERT(tsi_handshaker_result_extract_peer( ssl_fixture->base.server_result, &peer) == TSI_OK); + check_session_reusage(ssl_fixture, &peer); check_alpn(ssl_fixture, &peer); check_client_peer(ssl_fixture, &peer); } else { @@ -291,11 +321,11 @@ static void ssl_test_destruct(tsi_test_fixture* fixture) { /* Destroy ssl_alpn_lib. */ ssl_alpn_lib* alpn_lib = ssl_fixture->alpn_lib; for (size_t i = 0; i < alpn_lib->num_server_alpn_protocols; i++) { - gpr_free(alpn_lib->server_alpn_protocols[i]); + gpr_free(const_cast<char*>(alpn_lib->server_alpn_protocols[i])); } gpr_free(alpn_lib->server_alpn_protocols); for (size_t i = 0; i < alpn_lib->num_client_alpn_protocols; i++) { - gpr_free(alpn_lib->client_alpn_protocols[i]); + gpr_free(const_cast<char*>(alpn_lib->client_alpn_protocols[i])); } gpr_free(alpn_lib->client_alpn_protocols); gpr_free(alpn_lib); @@ -316,6 +346,9 @@ static void ssl_test_destruct(tsi_test_fixture* fixture) { key_cert_lib->bad_client_pem_key_cert_pair); gpr_free(key_cert_lib->root_cert); gpr_free(key_cert_lib); + if (ssl_fixture->session_cache != nullptr) { + tsi_ssl_session_cache_unref(ssl_fixture->session_cache); + } /* Unreference others. */ tsi_ssl_server_handshaker_factory_unref( ssl_fixture->server_handshaker_factory); @@ -388,10 +421,10 @@ static tsi_test_fixture* ssl_tsi_test_fixture_create() { /* Create ssl_alpn_lib. */ ssl_alpn_lib* alpn_lib = static_cast<ssl_alpn_lib*>(gpr_zalloc(sizeof(*alpn_lib))); - alpn_lib->server_alpn_protocols = - static_cast<char**>(gpr_zalloc(sizeof(char*) * SSL_TSI_TEST_ALPN_NUM)); - alpn_lib->client_alpn_protocols = - static_cast<char**>(gpr_zalloc(sizeof(char*) * SSL_TSI_TEST_ALPN_NUM)); + alpn_lib->server_alpn_protocols = static_cast<const char**>( + gpr_zalloc(sizeof(char*) * SSL_TSI_TEST_ALPN_NUM)); + alpn_lib->client_alpn_protocols = static_cast<const char**>( + gpr_zalloc(sizeof(char*) * SSL_TSI_TEST_ALPN_NUM)); alpn_lib->server_alpn_protocols[0] = gpr_strdup(SSL_TSI_TEST_ALPN1); alpn_lib->server_alpn_protocols[1] = gpr_strdup(SSL_TSI_TEST_ALPN3); alpn_lib->client_alpn_protocols[0] = gpr_strdup(SSL_TSI_TEST_ALPN2); @@ -402,6 +435,9 @@ static tsi_test_fixture* ssl_tsi_test_fixture_create() { ssl_fixture->alpn_lib = alpn_lib; ssl_fixture->base.vtable = &vtable; ssl_fixture->server_name_indication = nullptr; + ssl_fixture->session_reused = false; + ssl_fixture->session_ticket_key = nullptr; + ssl_fixture->session_ticket_key_size = 0; ssl_fixture->force_client_auth = false; return &ssl_fixture->base; } @@ -558,6 +594,38 @@ void ssl_tsi_test_do_round_trip_odd_buffer_size() { } } +void ssl_tsi_test_do_handshake_session_cache() { + tsi_ssl_session_cache* session_cache = tsi_ssl_session_cache_create_lru(16); + char session_ticket_key[48]; + auto do_handshake = [&session_ticket_key, + &session_cache](bool session_reused) { + tsi_test_fixture* fixture = ssl_tsi_test_fixture_create(); + ssl_tsi_test_fixture* ssl_fixture = + reinterpret_cast<ssl_tsi_test_fixture*>(fixture); + ssl_fixture->server_name_indication = + const_cast<char*>("waterzooi.test.google.be"); + ssl_fixture->session_ticket_key = session_ticket_key; + ssl_fixture->session_ticket_key_size = 48; + tsi_ssl_session_cache_ref(session_cache); + ssl_fixture->session_cache = session_cache; + ssl_fixture->session_reused = session_reused; + tsi_test_do_round_trip(&ssl_fixture->base); + tsi_test_fixture_destroy(fixture); + }; + memset(session_ticket_key, 'a', 48); + do_handshake(false); + do_handshake(true); + do_handshake(true); + // Changing session_ticket_key on server invalidates ticket. + memset(session_ticket_key, 'b', 48); + do_handshake(false); + do_handshake(true); + memset(session_ticket_key, 'c', 48); + do_handshake(false); + do_handshake(true); + tsi_ssl_session_cache_unref(session_cache); +} + static const tsi_ssl_handshaker_factory_vtable* original_vtable; static bool handshaker_factory_destructor_called; @@ -575,13 +643,14 @@ static tsi_ssl_handshaker_factory_vtable test_handshaker_factory_vtable = { void test_tsi_ssl_client_handshaker_factory_refcounting() { int i; - const char* cert_chain = - load_file(SSL_TSI_TEST_CREDENTIALS_DIR, "client.pem"); + char* cert_chain = load_file(SSL_TSI_TEST_CREDENTIALS_DIR, "client.pem"); + tsi_ssl_client_handshaker_options options; + memset(&options, 0, sizeof(options)); + options.pem_root_certs = cert_chain; tsi_ssl_client_handshaker_factory* client_handshaker_factory; - GPR_ASSERT(tsi_create_ssl_client_handshaker_factory( - nullptr, cert_chain, nullptr, nullptr, 0, - &client_handshaker_factory) == TSI_OK); + GPR_ASSERT(tsi_create_ssl_client_handshaker_factory_with_options( + &options, &client_handshaker_factory) == TSI_OK); handshaker_factory_destructor_called = false; original_vtable = tsi_ssl_handshaker_factory_swap_vtable( @@ -608,7 +677,7 @@ void test_tsi_ssl_client_handshaker_factory_refcounting() { tsi_handshaker_destroy(handshaker[2]); GPR_ASSERT(handshaker_factory_destructor_called); - gpr_free((void*)cert_chain); + gpr_free(cert_chain); } void test_tsi_ssl_server_handshaker_factory_refcounting() { @@ -673,6 +742,7 @@ void ssl_tsi_test_handshaker_factory_internals() { int main(int argc, char** argv) { grpc_test_init(argc, argv); grpc_init(); + ssl_tsi_test_do_handshake_tiny_handshake_buffer(); ssl_tsi_test_do_handshake_small_handshake_buffer(); ssl_tsi_test_do_handshake(); @@ -688,6 +758,7 @@ int main(int argc, char** argv) { #endif ssl_tsi_test_do_handshake_alpn_server_no_client(); ssl_tsi_test_do_handshake_alpn_client_server_ok(); + ssl_tsi_test_do_handshake_session_cache(); ssl_tsi_test_do_round_trip_for_all_configs(); ssl_tsi_test_do_round_trip_odd_buffer_size(); ssl_tsi_test_handshaker_factory_internals(); diff --git a/tools/doxygen/Doxyfile.core.internal b/tools/doxygen/Doxyfile.core.internal index 212e1d53dd..af0c875b99 100644 --- a/tools/doxygen/Doxyfile.core.internal +++ b/tools/doxygen/Doxyfile.core.internal @@ -1478,6 +1478,11 @@ src/core/tsi/alts_transport_security.cc \ src/core/tsi/alts_transport_security.h \ src/core/tsi/fake_transport_security.cc \ src/core/tsi/fake_transport_security.h \ +src/core/tsi/ssl/session_cache/ssl_session.h \ +src/core/tsi/ssl/session_cache/ssl_session_boringssl.cc \ +src/core/tsi/ssl/session_cache/ssl_session_cache.cc \ +src/core/tsi/ssl/session_cache/ssl_session_cache.h \ +src/core/tsi/ssl/session_cache/ssl_session_openssl.cc \ src/core/tsi/ssl_transport_security.cc \ src/core/tsi/ssl_transport_security.h \ src/core/tsi/ssl_types.h \ diff --git a/tools/internal_ci/linux/grpc_build_artifacts_extra.cfg b/tools/internal_ci/linux/grpc_build_artifacts_extra.cfg new file mode 100644 index 0000000000..619e3ea3a9 --- /dev/null +++ b/tools/internal_ci/linux/grpc_build_artifacts_extra.cfg @@ -0,0 +1,26 @@ +# Copyright 2017 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. + +# Config file for the internal CI (in protobuf text format) + +# Location of the continuous shell script in repository. +build_file: "grpc/tools/internal_ci/linux/grpc_build_artifacts_extra.sh" +timeout_mins: 240 +action { + define_artifacts { + regex: "**/*sponge_log.xml" + regex: "github/grpc/reports/**" + regex: "github/grpc/artifacts/**" + } +} diff --git a/tools/internal_ci/linux/grpc_build_artifacts_extra.sh b/tools/internal_ci/linux/grpc_build_artifacts_extra.sh new file mode 100755 index 0000000000..718123d7da --- /dev/null +++ b/tools/internal_ci/linux/grpc_build_artifacts_extra.sh @@ -0,0 +1,29 @@ +#!/bin/bash +# Copyright 2017 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. + +set -ex + +# change to grpc repo root +cd $(dirname $0)/../../.. + +source tools/internal_ci/helper_scripts/prepare_build_linux_rc + +set +ex +[[ -s /etc/profile.d/rvm.sh ]] && . /etc/profile.d/rvm.sh +set -e # rvm commands are very verbose +rvm --default use ruby-2.4.1 +set -ex + +tools/run_tests/task_runner.py -f artifact linux_extra armv7 -j 6 diff --git a/tools/interop_matrix/client_matrix.py b/tools/interop_matrix/client_matrix.py index 83d75ec0eb..836a8ac745 100644 --- a/tools/interop_matrix/client_matrix.py +++ b/tools/interop_matrix/client_matrix.py @@ -185,22 +185,20 @@ LANG_RELEASE_MATRIX = { { 'v1.0.1': None }, + # {'v1.1.4': None}, + # {'v1.2.5': None}, + # {'v1.3.9': None}, + # {'v1.4.2': None}, + # {'v1.6.6': None}, { - 'v1.1.4': None - }, - { - 'v1.2.5': None - }, - { - 'v1.3.9': None + 'v1.7.2': None }, { - 'v1.4.2': None + 'v1.8.4': None }, { - 'v1.6.6': None - }, - #{'v1.7.1': None}, Failing tests + 'v1.9.1': None + } ], 'ruby': [ { diff --git a/tools/interop_matrix/testcases/node__master b/tools/interop_matrix/testcases/node__master index 9e31fbf97d..588ca95895 100755 --- a/tools/interop_matrix/testcases/node__master +++ b/tools/interop_matrix/testcases/node__master @@ -1,20 +1,20 @@ #!/bin/bash echo "Testing ${docker_image:=grpc_interop_node:1415ecbf-5d0f-423e-8c2c-e0cb6d154e73}" -docker run -i --rm=true -w /var/local/git/grpc --net=host $docker_image bash -c "tools/run_tests/interop/with_nvm.sh node src/node/interop/interop_client.js --server_host=grpc-test.sandbox.googleapis.com --server_host_override=grpc-test.sandbox.googleapis.com --server_port=443 --use_tls=true --test_case=large_unary" -docker run -i --rm=true -w /var/local/git/grpc --net=host $docker_image bash -c "tools/run_tests/interop/with_nvm.sh node src/node/interop/interop_client.js --server_host=grpc-test.sandbox.googleapis.com --server_host_override=grpc-test.sandbox.googleapis.com --server_port=443 --use_tls=true --test_case=empty_unary" -docker run -i --rm=true -w /var/local/git/grpc --net=host $docker_image bash -c "tools/run_tests/interop/with_nvm.sh node src/node/interop/interop_client.js --server_host=grpc-test.sandbox.googleapis.com --server_host_override=grpc-test.sandbox.googleapis.com --server_port=443 --use_tls=true --test_case=ping_pong" -docker run -i --rm=true -w /var/local/git/grpc --net=host $docker_image bash -c "tools/run_tests/interop/with_nvm.sh node src/node/interop/interop_client.js --server_host=grpc-test.sandbox.googleapis.com --server_host_override=grpc-test.sandbox.googleapis.com --server_port=443 --use_tls=true --test_case=empty_stream" -docker run -i --rm=true -w /var/local/git/grpc --net=host $docker_image bash -c "tools/run_tests/interop/with_nvm.sh node src/node/interop/interop_client.js --server_host=grpc-test.sandbox.googleapis.com --server_host_override=grpc-test.sandbox.googleapis.com --server_port=443 --use_tls=true --test_case=client_streaming" -docker run -i --rm=true -w /var/local/git/grpc --net=host $docker_image bash -c "tools/run_tests/interop/with_nvm.sh node src/node/interop/interop_client.js --server_host=grpc-test.sandbox.googleapis.com --server_host_override=grpc-test.sandbox.googleapis.com --server_port=443 --use_tls=true --test_case=server_streaming" -docker run -i --rm=true -w /var/local/git/grpc --net=host $docker_image bash -c "tools/run_tests/interop/with_nvm.sh node src/node/interop/interop_client.js --server_host=grpc-test.sandbox.googleapis.com --server_host_override=grpc-test.sandbox.googleapis.com --server_port=443 --use_tls=true --test_case=cancel_after_begin" -docker run -i --rm=true -w /var/local/git/grpc --net=host $docker_image bash -c "tools/run_tests/interop/with_nvm.sh node src/node/interop/interop_client.js --server_host=grpc-test.sandbox.googleapis.com --server_host_override=grpc-test.sandbox.googleapis.com --server_port=443 --use_tls=true --test_case=cancel_after_first_response" -docker run -i --rm=true -w /var/local/git/grpc --net=host $docker_image bash -c "tools/run_tests/interop/with_nvm.sh node src/node/interop/interop_client.js --server_host=grpc-test.sandbox.googleapis.com --server_host_override=grpc-test.sandbox.googleapis.com --server_port=443 --use_tls=true --test_case=timeout_on_sleeping_server" -docker run -i --rm=true -w /var/local/git/grpc --net=host $docker_image bash -c "tools/run_tests/interop/with_nvm.sh node src/node/interop/interop_client.js --server_host=grpc-test4.sandbox.googleapis.com --server_host_override=grpc-test4.sandbox.googleapis.com --server_port=443 --use_tls=true --test_case=large_unary" -docker run -i --rm=true -w /var/local/git/grpc --net=host $docker_image bash -c "tools/run_tests/interop/with_nvm.sh node src/node/interop/interop_client.js --server_host=grpc-test4.sandbox.googleapis.com --server_host_override=grpc-test4.sandbox.googleapis.com --server_port=443 --use_tls=true --test_case=empty_unary" -docker run -i --rm=true -w /var/local/git/grpc --net=host $docker_image bash -c "tools/run_tests/interop/with_nvm.sh node src/node/interop/interop_client.js --server_host=grpc-test4.sandbox.googleapis.com --server_host_override=grpc-test4.sandbox.googleapis.com --server_port=443 --use_tls=true --test_case=ping_pong" -docker run -i --rm=true -w /var/local/git/grpc --net=host $docker_image bash -c "tools/run_tests/interop/with_nvm.sh node src/node/interop/interop_client.js --server_host=grpc-test4.sandbox.googleapis.com --server_host_override=grpc-test4.sandbox.googleapis.com --server_port=443 --use_tls=true --test_case=empty_stream" -docker run -i --rm=true -w /var/local/git/grpc --net=host $docker_image bash -c "tools/run_tests/interop/with_nvm.sh node src/node/interop/interop_client.js --server_host=grpc-test4.sandbox.googleapis.com --server_host_override=grpc-test4.sandbox.googleapis.com --server_port=443 --use_tls=true --test_case=client_streaming" -docker run -i --rm=true -w /var/local/git/grpc --net=host $docker_image bash -c "tools/run_tests/interop/with_nvm.sh node src/node/interop/interop_client.js --server_host=grpc-test4.sandbox.googleapis.com --server_host_override=grpc-test4.sandbox.googleapis.com --server_port=443 --use_tls=true --test_case=server_streaming" -docker run -i --rm=true -w /var/local/git/grpc --net=host $docker_image bash -c "tools/run_tests/interop/with_nvm.sh node src/node/interop/interop_client.js --server_host=grpc-test4.sandbox.googleapis.com --server_host_override=grpc-test4.sandbox.googleapis.com --server_port=443 --use_tls=true --test_case=cancel_after_begin" -docker run -i --rm=true -w /var/local/git/grpc --net=host $docker_image bash -c "tools/run_tests/interop/with_nvm.sh node src/node/interop/interop_client.js --server_host=grpc-test4.sandbox.googleapis.com --server_host_override=grpc-test4.sandbox.googleapis.com --server_port=443 --use_tls=true --test_case=cancel_after_first_response" -docker run -i --rm=true -w /var/local/git/grpc --net=host $docker_image bash -c "tools/run_tests/interop/with_nvm.sh node src/node/interop/interop_client.js --server_host=grpc-test4.sandbox.googleapis.com --server_host_override=grpc-test4.sandbox.googleapis.com --server_port=443 --use_tls=true --test_case=timeout_on_sleeping_server" +docker run -i --rm=true -w /var/local/git/grpc-node --net=host $docker_image bash -c "packages/grpc-native-core/deps/grpc/tools/run_tests/interop/with_nvm.sh node --require ./test/fixtures/native_native test/interop/interop_client.js --server_host=grpc-test.sandbox.googleapis.com --server_host_override=grpc-test.sandbox.googleapis.com --server_port=443 --use_tls=true --test_case=large_unary" +docker run -i --rm=true -w /var/local/git/grpc-node --net=host $docker_image bash -c "packages/grpc-native-core/deps/grpc/tools/run_tests/interop/with_nvm.sh node --require ./test/fixtures/native_native test/interop/interop_client.js --server_host=grpc-test.sandbox.googleapis.com --server_host_override=grpc-test.sandbox.googleapis.com --server_port=443 --use_tls=true --test_case=empty_unary" +docker run -i --rm=true -w /var/local/git/grpc-node --net=host $docker_image bash -c "packages/grpc-native-core/deps/grpc/tools/run_tests/interop/with_nvm.sh node --require ./test/fixtures/native_native test/interop/interop_client.js --server_host=grpc-test.sandbox.googleapis.com --server_host_override=grpc-test.sandbox.googleapis.com --server_port=443 --use_tls=true --test_case=ping_pong" +docker run -i --rm=true -w /var/local/git/grpc-node --net=host $docker_image bash -c "packages/grpc-native-core/deps/grpc/tools/run_tests/interop/with_nvm.sh node --require ./test/fixtures/native_native test/interop/interop_client.js --server_host=grpc-test.sandbox.googleapis.com --server_host_override=grpc-test.sandbox.googleapis.com --server_port=443 --use_tls=true --test_case=empty_stream" +docker run -i --rm=true -w /var/local/git/grpc-node --net=host $docker_image bash -c "packages/grpc-native-core/deps/grpc/tools/run_tests/interop/with_nvm.sh node --require ./test/fixtures/native_native test/interop/interop_client.js --server_host=grpc-test.sandbox.googleapis.com --server_host_override=grpc-test.sandbox.googleapis.com --server_port=443 --use_tls=true --test_case=client_streaming" +docker run -i --rm=true -w /var/local/git/grpc-node --net=host $docker_image bash -c "packages/grpc-native-core/deps/grpc/tools/run_tests/interop/with_nvm.sh node --require ./test/fixtures/native_native test/interop/interop_client.js --server_host=grpc-test.sandbox.googleapis.com --server_host_override=grpc-test.sandbox.googleapis.com --server_port=443 --use_tls=true --test_case=server_streaming" +docker run -i --rm=true -w /var/local/git/grpc-node --net=host $docker_image bash -c "packages/grpc-native-core/deps/grpc/tools/run_tests/interop/with_nvm.sh node --require ./test/fixtures/native_native test/interop/interop_client.js --server_host=grpc-test.sandbox.googleapis.com --server_host_override=grpc-test.sandbox.googleapis.com --server_port=443 --use_tls=true --test_case=cancel_after_begin" +docker run -i --rm=true -w /var/local/git/grpc-node --net=host $docker_image bash -c "packages/grpc-native-core/deps/grpc/tools/run_tests/interop/with_nvm.sh node --require ./test/fixtures/native_native test/interop/interop_client.js --server_host=grpc-test.sandbox.googleapis.com --server_host_override=grpc-test.sandbox.googleapis.com --server_port=443 --use_tls=true --test_case=cancel_after_first_response" +docker run -i --rm=true -w /var/local/git/grpc-node --net=host $docker_image bash -c "packages/grpc-native-core/deps/grpc/tools/run_tests/interop/with_nvm.sh node --require ./test/fixtures/native_native test/interop/interop_client.js --server_host=grpc-test.sandbox.googleapis.com --server_host_override=grpc-test.sandbox.googleapis.com --server_port=443 --use_tls=true --test_case=timeout_on_sleeping_server" +docker run -i --rm=true -w /var/local/git/grpc-node --net=host $docker_image bash -c "packages/grpc-native-core/deps/grpc/tools/run_tests/interop/with_nvm.sh node --require ./test/fixtures/native_native test/interop/interop_client.js --server_host=grpc-test4.sandbox.googleapis.com --server_host_override=grpc-test4.sandbox.googleapis.com --server_port=443 --use_tls=true --test_case=large_unary" +docker run -i --rm=true -w /var/local/git/grpc-node --net=host $docker_image bash -c "packages/grpc-native-core/deps/grpc/tools/run_tests/interop/with_nvm.sh node --require ./test/fixtures/native_native test/interop/interop_client.js --server_host=grpc-test4.sandbox.googleapis.com --server_host_override=grpc-test4.sandbox.googleapis.com --server_port=443 --use_tls=true --test_case=empty_unary" +docker run -i --rm=true -w /var/local/git/grpc-node --net=host $docker_image bash -c "packages/grpc-native-core/deps/grpc/tools/run_tests/interop/with_nvm.sh node --require ./test/fixtures/native_native test/interop/interop_client.js --server_host=grpc-test4.sandbox.googleapis.com --server_host_override=grpc-test4.sandbox.googleapis.com --server_port=443 --use_tls=true --test_case=ping_pong" +docker run -i --rm=true -w /var/local/git/grpc-node --net=host $docker_image bash -c "packages/grpc-native-core/deps/grpc/tools/run_tests/interop/with_nvm.sh node --require ./test/fixtures/native_native test/interop/interop_client.js --server_host=grpc-test4.sandbox.googleapis.com --server_host_override=grpc-test4.sandbox.googleapis.com --server_port=443 --use_tls=true --test_case=empty_stream" +docker run -i --rm=true -w /var/local/git/grpc-node --net=host $docker_image bash -c "packages/grpc-native-core/deps/grpc/tools/run_tests/interop/with_nvm.sh node --require ./test/fixtures/native_native test/interop/interop_client.js --server_host=grpc-test4.sandbox.googleapis.com --server_host_override=grpc-test4.sandbox.googleapis.com --server_port=443 --use_tls=true --test_case=client_streaming" +docker run -i --rm=true -w /var/local/git/grpc-node --net=host $docker_image bash -c "packages/grpc-native-core/deps/grpc/tools/run_tests/interop/with_nvm.sh node --require ./test/fixtures/native_native test/interop/interop_client.js --server_host=grpc-test4.sandbox.googleapis.com --server_host_override=grpc-test4.sandbox.googleapis.com --server_port=443 --use_tls=true --test_case=server_streaming" +docker run -i --rm=true -w /var/local/git/grpc-node --net=host $docker_image bash -c "packages/grpc-native-core/deps/grpc/tools/run_tests/interop/with_nvm.sh node --require ./test/fixtures/native_native test/interop/interop_client.js --server_host=grpc-test4.sandbox.googleapis.com --server_host_override=grpc-test4.sandbox.googleapis.com --server_port=443 --use_tls=true --test_case=cancel_after_begin" +docker run -i --rm=true -w /var/local/git/grpc-node --net=host $docker_image bash -c "packages/grpc-native-core/deps/grpc/tools/run_tests/interop/with_nvm.sh node --require ./test/fixtures/native_native test/interop/interop_client.js --server_host=grpc-test4.sandbox.googleapis.com --server_host_override=grpc-test4.sandbox.googleapis.com --server_port=443 --use_tls=true --test_case=cancel_after_first_response" +docker run -i --rm=true -w /var/local/git/grpc-node --net=host $docker_image bash -c "packages/grpc-native-core/deps/grpc/tools/run_tests/interop/with_nvm.sh node --require ./test/fixtures/native_native test/interop/interop_client.js --server_host=grpc-test4.sandbox.googleapis.com --server_host_override=grpc-test4.sandbox.googleapis.com --server_port=443 --use_tls=true --test_case=timeout_on_sleeping_server" diff --git a/tools/run_tests/generated/sources_and_headers.json b/tools/run_tests/generated/sources_and_headers.json index f80cd9f2b5..1b11fd13cc 100644 --- a/tools/run_tests/generated/sources_and_headers.json +++ b/tools/run_tests/generated/sources_and_headers.json @@ -3683,6 +3683,28 @@ "gpr_test_util", "grpc", "grpc++", + "grpc++_test", + "grpc_test_util" + ], + "headers": [ + "test/core/end2end/end2end_tests.h" + ], + "is_filegroup": false, + "language": "c++", + "name": "h2_ssl_session_reuse_test", + "src": [ + "test/core/end2end/end2end_tests.h", + "test/core/end2end/h2_ssl_session_reuse_test.cc" + ], + "third_party": false, + "type": "target" + }, + { + "deps": [ + "gpr", + "gpr_test_util", + "grpc", + "grpc++", "grpc++_test_util", "grpc_test_util" ], @@ -10393,6 +10415,8 @@ "headers": [ "src/core/tsi/alts_transport_security.h", "src/core/tsi/fake_transport_security.h", + "src/core/tsi/ssl/session_cache/ssl_session.h", + "src/core/tsi/ssl/session_cache/ssl_session_cache.h", "src/core/tsi/ssl_transport_security.h", "src/core/tsi/ssl_types.h", "src/core/tsi/transport_security_grpc.h" @@ -10405,6 +10429,11 @@ "src/core/tsi/alts_transport_security.h", "src/core/tsi/fake_transport_security.cc", "src/core/tsi/fake_transport_security.h", + "src/core/tsi/ssl/session_cache/ssl_session.h", + "src/core/tsi/ssl/session_cache/ssl_session_boringssl.cc", + "src/core/tsi/ssl/session_cache/ssl_session_cache.cc", + "src/core/tsi/ssl/session_cache/ssl_session_cache.h", + "src/core/tsi/ssl/session_cache/ssl_session_openssl.cc", "src/core/tsi/ssl_transport_security.cc", "src/core/tsi/ssl_transport_security.h", "src/core/tsi/ssl_types.h", diff --git a/tools/run_tests/generated/tests.json b/tools/run_tests/generated/tests.json index 3d6b6caa2a..11f2b9a0dd 100644 --- a/tools/run_tests/generated/tests.json +++ b/tools/run_tests/generated/tests.json @@ -4264,6 +4264,30 @@ "flaky": false, "gtest": true, "language": "c++", + "name": "h2_ssl_session_reuse_test", + "platforms": [ + "linux", + "mac", + "posix", + "windows" + ], + "uses_polling": true + }, + { + "args": [], + "benchmark": false, + "ci_platforms": [ + "linux", + "mac", + "posix", + "windows" + ], + "cpu_cost": 1.0, + "exclude_configs": [], + "exclude_iomgrs": [], + "flaky": false, + "gtest": true, + "language": "c++", "name": "health_service_end2end_test", "platforms": [ "linux", |