diff --git a/src/box/CMakeLists.txt b/src/box/CMakeLists.txt index cc1db34c095a5755d53e412425e52ff353985710..8156fd56d602cbf0e15706380570e18e24244385 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 fba562a84afd3843af0d92802f291ed15f48d656..3c4176e591967dea597a702763ee0a52ef44d5ec 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 220a39e683581655b9212ea08f6fb97c9612a2da..0f76833c4baa728ebf9e34b7c092f98730e36f63 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 17b7c85eb4b2baf39463747630d52db8a517148b..474fd7ec0f34aa555199652d232733ea2e967930 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 9e25fd1386ba197b5c90107f972ee9a46f371fd5..ec4780047e09fdfe70daca95edbaba65dc3dd27b 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 0000000000000000000000000000000000000000..ae456ade9f353bd595829a4f7e73cb1560c36b90 --- /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 0000000000000000000000000000000000000000..6c0dabba224245a4c7f5c8a5f2dba855c202d912 --- /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 0000000000000000000000000000000000000000..00cdfe9e9e785176df3c885d90ebc3f22a1887c6 --- /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 0000000000000000000000000000000000000000..a684cc066ea99ac47cba075d1dee990f8d7b1c35 --- /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 2fafc46da59d809e8a1abcf29178688a0ff2e8e9..e299f5941636595af5ab0ede1db36859586896b0 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 0000000000000000000000000000000000000000..9664644a4c0c3d9863c30da2783d78b06812801b --- /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 5e68b34d29f9052af4748bc11581d3b8f46c816c..777c68dff1844db220b28a290a7547b2188152f5 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 356d2dc970ad9659f9cfa96ae5385c34e57794ff..3cb18996f326dd39e29a94ce928c234b73ba005a 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 ***