From a7028dde8589a8119389d66e5dc55760b3af0019 Mon Sep 17 00:00:00 2001 From: Vladimir Davydov <vdavydov@tarantool.org> Date: Thu, 23 Dec 2021 18:52:02 +0300 Subject: [PATCH] Add SSL iostream stub This commit adds SSL context to iostream_ctx and a new kind of error for SSL connections. The SSL context is created if the target URI has transport=ssl parameter. If transport=plain or absent, it will be set to NULL and plain streams will be created as before. For other values of the transport parameter, an error is raised. Note, this is just a stub - an attempt to create an SSL context always currently fails with "SSL not available" error. It is supposed to be implemented in EE build. The new kind of error is currently only used for decoding errors from MsgPack and never raised. It will be raised by the actual implementation of SSL stream. --- src/box/CMakeLists.txt | 1 + src/box/mp_error.cc | 3 ++ src/lib/core/CMakeLists.txt | 16 ++++++ src/lib/core/iostream.c | 36 ++++++++++++-- src/lib/core/iostream.h | 10 +++- src/lib/core/ssl.c | 25 ++++++++++ src/lib/core/ssl.h | 50 +++++++++++++++++++ src/lib/core/ssl_error.cc | 17 +++++++ src/lib/core/ssl_error.h | 36 ++++++++++++++ src/trivia/config.h.cmake | 1 + test/box-luatest/transport_test.lua | 77 +++++++++++++++++++++++++++++ test/unit/mp_error.cc | 1 + test/unit/mp_error.result | 5 +- 13 files changed, 271 insertions(+), 7 deletions(-) create mode 100644 src/lib/core/ssl.c create mode 100644 src/lib/core/ssl.h create mode 100644 src/lib/core/ssl_error.cc create mode 100644 src/lib/core/ssl_error.h create mode 100644 test/box-luatest/transport_test.lua diff --git a/src/box/CMakeLists.txt b/src/box/CMakeLists.txt index cc1db34c09..8156fd56d6 100644 --- a/src/box/CMakeLists.txt +++ b/src/box/CMakeLists.txt @@ -77,6 +77,7 @@ set_property(DIRECTORY PROPERTY ADDITIONAL_MAKE_CLEAN_FILES ${lua_sources}) include_directories(${ZSTD_INCLUDE_DIRS}) include_directories(${PROJECT_BINARY_DIR}/src/box/sql) include_directories(${PROJECT_BINARY_DIR}/src/box) +include_directories(${EXTRA_CORE_INCLUDE_DIRS}) include_directories(${EXTRA_BOX_INCLUDE_DIRS}) add_library(box_error STATIC error.cc errcode.c mp_error.cc) diff --git a/src/box/mp_error.cc b/src/box/mp_error.cc index fba562a84a..3c4176e591 100644 --- a/src/box/mp_error.cc +++ b/src/box/mp_error.cc @@ -41,6 +41,7 @@ #include "msgpuck.h" #include "mp_extension_types.h" #include "fiber.h" +#include "ssl_error.h" /** * MP_ERROR format: @@ -257,6 +258,8 @@ error_build_xc(struct mp_error *mp_error) err = new SwimError(); } else if (strcmp(mp_error->type, "CryptoError") == 0) { err = new CryptoError(); + } else if (strcmp(mp_error->type, "SSLError") == 0) { + err = new SSLError(); } else { err = new ClientError(); } diff --git a/src/lib/core/CMakeLists.txt b/src/lib/core/CMakeLists.txt index 220a39e683..0f76833c4b 100644 --- a/src/lib/core/CMakeLists.txt +++ b/src/lib/core/CMakeLists.txt @@ -37,6 +37,18 @@ set(core_sources mp_datetime.c ) +if(ENABLE_SSL) + list(APPEND core_sources ${SSL_SOURCES}) +else() + list(APPEND core_sources ssl.c ssl_error.cc) +endif() + +include_directories(${EXTRA_CORE_INCLUDE_DIRS}) + +if(ENABLE_SSL) + include_directories(${OPENSSL_INCLUDE_DIR}) +endif() + if (TARGET_OS_NETBSD) # A workaround for "undefined reference to `__gcc_personality_v0'" # on x86_64-rumprun-netbsd-gcc @@ -63,3 +75,7 @@ endif() if ("${HAVE_CLOCK_GETTIME}" AND NOT "${HAVE_CLOCK_GETTIME_WITHOUT_RT}") target_link_libraries(core rt) endif() + +if(ENABLE_SSL) + target_link_libraries(core ${OPENSSL_LIBRARIES}) +endif() diff --git a/src/lib/core/iostream.c b/src/lib/core/iostream.c index 17b7c85eb4..474fd7ec0f 100644 --- a/src/lib/core/iostream.c +++ b/src/lib/core/iostream.c @@ -12,7 +12,10 @@ #include <sys/types.h> #include <unistd.h> +#include "diag.h" #include "sio.h" +#include "ssl.h" +#include "uri/uri.h" static const struct iostream_vtab plain_iostream_vtab; @@ -86,23 +89,48 @@ int iostream_ctx_create(struct iostream_ctx *ctx, enum iostream_mode mode, const struct uri *uri) { - (void)uri; assert(mode == IOSTREAM_SERVER || mode == IOSTREAM_CLIENT); ctx->mode = mode; + const char *transport = uri_param(uri, "transport", 0); + if (transport != NULL) { + if (strcmp(transport, "ssl") == 0) { + ctx->ssl = ssl_iostream_ctx_new(mode, uri); + if (ctx->ssl == NULL) + goto err; + } else if (strcmp(transport, "plain") == 0) { + ctx->ssl = NULL; + } else { + diag_set(IllegalParams, "Invalid transport: %s", + transport); + goto err; + } + } return 0; +err: + iostream_ctx_clear(ctx); + return -1; } void iostream_ctx_destroy(struct iostream_ctx *ctx) { + if (ctx->ssl != NULL) + ssl_iostream_ctx_delete(ctx->ssl); iostream_ctx_clear(ctx); } int -iostream_create(struct iostream *io, int fd, struct iostream_ctx *ctx) +iostream_create(struct iostream *io, int fd, const struct iostream_ctx *ctx) { assert(ctx->mode == IOSTREAM_SERVER || ctx->mode == IOSTREAM_CLIENT); - (void)ctx; - plain_iostream_create(io, fd); + if (ctx->ssl != NULL) { + if (ssl_iostream_create(io, fd, ctx->mode, ctx->ssl) != 0) + goto err; + } else { + plain_iostream_create(io, fd); + } return 0; +err: + iostream_clear(io); + return -1; } diff --git a/src/lib/core/iostream.h b/src/lib/core/iostream.h index 9e25fd1386..ec4780047e 100644 --- a/src/lib/core/iostream.h +++ b/src/lib/core/iostream.h @@ -191,12 +191,19 @@ enum iostream_mode { IOSTREAM_CLIENT, }; +struct ssl_iostream_ctx; + /** * Context used for creating IO stream objects of a particular type. */ struct iostream_ctx { /** IO stream mode: server or client. */ enum iostream_mode mode; + /** + * Context used for creating encrypted streams. If it's NULL, then + * streams created with this context will be unencrypted. + */ + struct ssl_iostream_ctx *ssl; }; /** @@ -208,6 +215,7 @@ static inline void iostream_ctx_clear(struct iostream_ctx *ctx) { ctx->mode = IOSTREAM_MODE_UNINITIALIZED; + ctx->ssl = NULL; } /** @@ -232,7 +240,7 @@ iostream_ctx_destroy(struct iostream_ctx *ctx); * and clears the iostream struct (see iostream_clear). */ int -iostream_create(struct iostream *io, int fd, struct iostream_ctx *ctx); +iostream_create(struct iostream *io, int fd, const struct iostream_ctx *ctx); #if defined(__cplusplus) } /* extern "C" */ diff --git a/src/lib/core/ssl.c b/src/lib/core/ssl.c new file mode 100644 index 0000000000..ae456ade9f --- /dev/null +++ b/src/lib/core/ssl.c @@ -0,0 +1,25 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright 2010-2021, Tarantool AUTHORS, please see AUTHORS file. + */ +#include "ssl.h" + +#include <stddef.h> + +#include "diag.h" +#include "iostream.h" +#include "trivia/config.h" + +#if defined(ENABLE_SSL) +# error unimplemented +#endif + +struct ssl_iostream_ctx * +ssl_iostream_ctx_new(enum iostream_mode mode, const struct uri *uri) +{ + (void)mode; + (void)uri; + diag_set(IllegalParams, "SSL is not available in this build"); + return NULL; +} diff --git a/src/lib/core/ssl.h b/src/lib/core/ssl.h new file mode 100644 index 0000000000..6c0dabba22 --- /dev/null +++ b/src/lib/core/ssl.h @@ -0,0 +1,50 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright 2010-2021, Tarantool AUTHORS, please see AUTHORS file. + */ +#pragma once + +#include "trivia/config.h" + +#if defined(ENABLE_SSL) +# include "ssl_impl.h" +#else /* !defined(ENABLE_SSL) */ + +#include "iostream.h" +#include "trivia/util.h" + +#if defined(__cplusplus) +extern "C" { +#endif /* defined(__cplusplus) */ + +struct uri; +struct ssl_iostream_ctx; + +struct ssl_iostream_ctx * +ssl_iostream_ctx_new(enum iostream_mode mode, const struct uri *uri); + +static inline void +ssl_iostream_ctx_delete(struct ssl_iostream_ctx *ctx) +{ + (void)ctx; + unreachable(); +} + +static inline int +ssl_iostream_create(struct iostream *io, int fd, enum iostream_mode mode, + const struct ssl_iostream_ctx *ctx) +{ + (void)io; + (void)fd; + (void)mode; + (void)ctx; + unreachable(); + return 0; +} + +#if defined(__cplusplus) +} /* extern "C" */ +#endif /* defined(__cplusplus) */ + +#endif /* !defined(ENABLE_SSL) */ diff --git a/src/lib/core/ssl_error.cc b/src/lib/core/ssl_error.cc new file mode 100644 index 0000000000..00cdfe9e9e --- /dev/null +++ b/src/lib/core/ssl_error.cc @@ -0,0 +1,17 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright 2010-2021, Tarantool AUTHORS, please see AUTHORS file. + */ +#include "ssl_error.h" + +#include <stddef.h> + +#include "reflection.h" +#include "trivia/config.h" + +#if defined(ENABLE_SSL) +# error unimplemented +#endif + +const struct type_info type_SSLError = make_type("SSLError", NULL); diff --git a/src/lib/core/ssl_error.h b/src/lib/core/ssl_error.h new file mode 100644 index 0000000000..a684cc066e --- /dev/null +++ b/src/lib/core/ssl_error.h @@ -0,0 +1,36 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright 2010-2021, Tarantool AUTHORS, please see AUTHORS file. + */ +#pragma once + +#include "trivia/config.h" + +#if defined(ENABLE_SSL) +# include "ssl_error_impl.h" +#else /* !defined(ENABLE_SSL) */ + +#include <stddef.h> + +#include "exception.h" +#include "reflection.h" + +#if defined(__cplusplus) +extern "C" { +#endif /* defined(__cplusplus) */ + +extern const struct type_info type_SSLError; + +#if defined(__cplusplus) +} /* extern "C" */ + +class SSLError: public Exception { +public: + SSLError(): Exception(&type_SSLError, NULL, 0) {} + virtual void raise() { throw this; } +}; + +#endif /* defined(__cplusplus) */ + +#endif /* !defined(ENABLE_SSL) */ diff --git a/src/trivia/config.h.cmake b/src/trivia/config.h.cmake index 2fafc46da5..e299f59416 100644 --- a/src/trivia/config.h.cmake +++ b/src/trivia/config.h.cmake @@ -256,6 +256,7 @@ /* Cacheline size to calculate alignments */ #define CACHELINE_SIZE 64 +#cmakedefine ENABLE_SSL 1 #cmakedefine ENABLE_AUDIT_LOG 1 #cmakedefine ENABLE_FEEDBACK_DAEMON 1 diff --git a/test/box-luatest/transport_test.lua b/test/box-luatest/transport_test.lua new file mode 100644 index 0000000000..9664644a4c --- /dev/null +++ b/test/box-luatest/transport_test.lua @@ -0,0 +1,77 @@ +local net = require('net.box') +local server = require('test.luatest_helpers.server') +local tarantool = require('tarantool') +local t = require('luatest') +local g = t.group() + +g.before_all = function() + g.server = server:new({alias = 'master'}) + g.server:start() +end + +g.after_all = function() + g.server:stop() +end + +g.test_listen = function() + g.server:exec(function() + local t = require('luatest') + local listen = box.cfg.listen + box.cfg({listen = {listen, params = {transport = 'plain'}}}) + t.assert_error_msg_equals( + 'Invalid transport: foo', + box.cfg, {listen = {listen, params = {transport = 'foo'}}}) + box.cfg({listen = listen}) + end) +end + +g.test_replication = function() + g.server:exec(function() + local t = require('luatest') + local listen = box.cfg.listen + box.cfg({replication = {listen, params = {transport = 'plain'}}}) + t.assert_error_msg_equals( + 'Invalid transport: foo', + box.cfg, {replication = {listen, params = {transport = 'foo'}}}) + box.cfg({replication = {}}) + end) +end + +g.test_net_box = function() + local c = net.connect({g.server.net_box_uri, + params = {transport = 'plain'}}) + t.assert_equals(c.state, 'active') + c:close() + t.assert_error_msg_equals( + 'Invalid transport: foo', + net.connect, {g.server.net_box_uri, params = {transport = 'foo'}}) +end + +-- Check that SSL isn't available in open-source builds. +if tarantool.package ~= 'Tarantool Enterprise' then + +g.test_listen_ssl = function() + g.server:exec(function() + local t = require('luatest') + t.assert_error_msg_equals( + 'SSL is not available in this build', + box.cfg, {listen = 'localhost:0?transport=ssl'}) + end) +end + +g.test_replication_ssl = function() + g.server:exec(function() + local t = require('luatest') + t.assert_error_msg_equals( + 'SSL is not available in this build', + box.cfg, {replication = 'localhost:0?transport=ssl'}) + end) +end + +g.test_net_box_ssl = function() + t.assert_error_msg_equals( + 'SSL is not available in this build', + net.connect, {g.server.net_box_uri, params = {transport = 'ssl'}}) +end + +end -- tarantool.package ~= 'Tarantool Enterprise' diff --git a/test/unit/mp_error.cc b/test/unit/mp_error.cc index 5e68b34d29..777c68dff1 100644 --- a/test/unit/mp_error.cc +++ b/test/unit/mp_error.cc @@ -86,6 +86,7 @@ const char *standard_errors[] = { "CollationError", "SwimError", "CryptoError", + "SSLError", }; enum { diff --git a/test/unit/mp_error.result b/test/unit/mp_error.result index 356d2dc970..3cb18996f3 100644 --- a/test/unit/mp_error.result +++ b/test/unit/mp_error.result @@ -1,7 +1,7 @@ *** main *** 1..7 *** test_stack_error_decode *** - 1..17 + 1..18 ok 1 - check CustomError ok 2 - check AccessDeniedError ok 3 - check ClientError @@ -18,7 +18,8 @@ ok 14 - check CollationError ok 15 - check SwimError ok 16 - check CryptoError - ok 17 - stack size + ok 17 - check SSLError + ok 18 - stack size ok 1 - subtests *** test_stack_error_decode: done *** *** test_decode_unknown_type *** -- GitLab