summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--fnet/src/tests/frt/rpc/CMakeLists.txt2
-rw-r--r--fnet/src/tests/frt/rpc/my_crypto_engine.hpp6
-rw-r--r--vespalib/src/vespa/vespalib/net/crypto_socket.h7
-rw-r--r--vespalib/src/vespa/vespalib/net/tls/CMakeLists.txt2
-rw-r--r--vespalib/src/vespa/vespalib/net/tls/crypto_codec_adapter.cpp146
-rw-r--r--vespalib/src/vespa/vespalib/net/tls/crypto_codec_adapter.h46
-rw-r--r--vespalib/src/vespa/vespalib/net/tls/impl/openssl_crypto_codec_impl.cpp13
-rw-r--r--vespalib/src/vespa/vespalib/net/tls/tls_crypto_engine.cpp22
-rw-r--r--vespalib/src/vespa/vespalib/net/tls/tls_crypto_engine.h23
9 files changed, 259 insertions, 8 deletions
diff --git a/fnet/src/tests/frt/rpc/CMakeLists.txt b/fnet/src/tests/frt/rpc/CMakeLists.txt
index f935590ee77..2bacd37686a 100644
--- a/fnet/src/tests/frt/rpc/CMakeLists.txt
+++ b/fnet/src/tests/frt/rpc/CMakeLists.txt
@@ -7,6 +7,7 @@ vespa_add_executable(fnet_invoke_test_app TEST
)
vespa_add_test(NAME fnet_invoke_test_app COMMAND fnet_invoke_test_app)
vespa_add_test(NAME fnet_invoke_test_app_xor COMMAND fnet_invoke_test_app ENVIRONMENT "CRYPTOENGINE=xor")
+vespa_add_test(NAME fnet_invoke_test_app_tls COMMAND fnet_invoke_test_app ENVIRONMENT "CRYPTOENGINE=tls")
vespa_add_executable(fnet_detach_return_invoke_test_app TEST
SOURCES
detach_return_invoke.cpp
@@ -22,6 +23,7 @@ vespa_add_executable(fnet_session_test_app TEST
)
vespa_add_test(NAME fnet_session_test_app COMMAND fnet_session_test_app)
vespa_add_test(NAME fnet_session_test_app_xor COMMAND fnet_session_test_app ENVIRONMENT "CRYPTOENGINE=xor")
+vespa_add_test(NAME fnet_session_test_app_tls COMMAND fnet_session_test_app ENVIRONMENT "CRYPTOENGINE=tls")
vespa_add_executable(fnet_sharedblob_test_app TEST
SOURCES
sharedblob.cpp
diff --git a/fnet/src/tests/frt/rpc/my_crypto_engine.hpp b/fnet/src/tests/frt/rpc/my_crypto_engine.hpp
index 6f573e5695a..f460a3c9283 100644
--- a/fnet/src/tests/frt/rpc/my_crypto_engine.hpp
+++ b/fnet/src/tests/frt/rpc/my_crypto_engine.hpp
@@ -1,5 +1,8 @@
// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include <vespa/vespalib/net/tls/tls_crypto_engine.h>
+#include <vespa/vespalib/test/make_tls_options_for_testing.h>
+
vespalib::CryptoEngine::SP my_crypto_engine() {
const char *env_str = getenv("CRYPTOENGINE");
if (!env_str) {
@@ -10,6 +13,9 @@ vespalib::CryptoEngine::SP my_crypto_engine() {
if (engine == "xor") {
fprintf(stderr, "crypto engine: xor\n");
return std::make_shared<vespalib::XorCryptoEngine>();
+ } else if (engine == "tls") {
+ fprintf(stderr, "crypto engine: tls\n");
+ return std::make_shared<vespalib::TlsCryptoEngine>(vespalib::test::make_tls_options_for_testing());
}
TEST_FATAL(("invalid crypto engine: " + engine).c_str());
abort();
diff --git a/vespalib/src/vespa/vespalib/net/crypto_socket.h b/vespalib/src/vespa/vespalib/net/crypto_socket.h
index 7fe7871960f..f78f7fc0ce7 100644
--- a/vespalib/src/vespa/vespalib/net/crypto_socket.h
+++ b/vespalib/src/vespa/vespalib/net/crypto_socket.h
@@ -74,13 +74,16 @@ struct CryptoSocket {
virtual ssize_t write(const char *buf, size_t len) = 0;
/**
- * Try to flush data in the write pipeline that is not depenedent
+ * Try to flush data in the write pipeline that is not dependent
* on data not yet written by the application into the underlying
* socket. This is to enable the application to identify pending
* work that may not be completed until the underlying socket is
* ready for writing more data. The semantics are the same as with
* a normal socket write (errno, etc.) with the exception that 0
- * will be returned when there is no more data to flush.
+ * will be returned when there is no more data to flush and any
+ * positive number indicates that we were able to flush something
+ * (it does not need to reflect the actual number of bytes written
+ * to the underlying socket).
**/
virtual ssize_t flush() = 0;
diff --git a/vespalib/src/vespa/vespalib/net/tls/CMakeLists.txt b/vespalib/src/vespa/vespalib/net/tls/CMakeLists.txt
index 938ae0896a2..02941460e40 100644
--- a/vespalib/src/vespa/vespalib/net/tls/CMakeLists.txt
+++ b/vespalib/src/vespa/vespalib/net/tls/CMakeLists.txt
@@ -2,8 +2,10 @@
vespa_add_library(vespalib_vespalib_net_tls OBJECT
SOURCES
crypto_codec.cpp
+ crypto_codec_adapter.cpp
crypto_exception.cpp
tls_context.cpp
+ tls_crypto_engine.cpp
transport_security_options.cpp
DEPENDS
)
diff --git a/vespalib/src/vespa/vespalib/net/tls/crypto_codec_adapter.cpp b/vespalib/src/vespa/vespalib/net/tls/crypto_codec_adapter.cpp
new file mode 100644
index 00000000000..435f16cc340
--- /dev/null
+++ b/vespalib/src/vespa/vespalib/net/tls/crypto_codec_adapter.cpp
@@ -0,0 +1,146 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#include "crypto_codec_adapter.h"
+#include <assert.h>
+
+namespace vespalib::net::tls {
+
+CryptoSocket::HandshakeResult
+CryptoCodecAdapter::hs_try_flush()
+{
+ auto flush_res = flush_all();
+ if (flush_res == 0) {
+ return HandshakeResult::DONE;
+ } else if (is_blocked(flush_res, errno)) {
+ return HandshakeResult::NEED_WRITE;
+ } else {
+ return HandshakeResult::FAIL;
+ }
+}
+
+CryptoSocket::HandshakeResult
+CryptoCodecAdapter::hs_try_fill()
+{
+ auto fill_res = fill_input();
+ if (fill_res > 0) {
+ return HandshakeResult::DONE;
+ } else if (is_blocked(fill_res, errno)) {
+ return HandshakeResult::NEED_READ;
+ } else { // eof included here
+ return HandshakeResult::FAIL;
+ }
+}
+
+ssize_t
+CryptoCodecAdapter::fill_input()
+{
+ if (_input.get().size < _codec->min_encode_buffer_size()) {
+ auto dst = _input.reserve(_codec->min_encode_buffer_size());
+ ssize_t res = _socket.read(dst.data, dst.size);
+ if (res > 0) {
+ _input.commit(res);
+ } else {
+ return res; // eof/error
+ }
+ }
+ return 1; // progress
+}
+
+ssize_t
+CryptoCodecAdapter::flush_all()
+{
+ ssize_t res = flush();
+ while (res > 0) {
+ res = flush();
+ }
+ return res;
+}
+
+CryptoSocket::HandshakeResult
+CryptoCodecAdapter::handshake()
+{
+ for (;;) {
+ auto in = _input.obtain();
+ auto out = _output.reserve(_codec->min_encode_buffer_size());
+ auto hs_res = _codec->handshake(in.data, in.size, out.data, out.size);
+ _input.evict(hs_res.bytes_consumed);
+ _output.commit(hs_res.bytes_produced);
+ switch (hs_res.state) {
+ case ::vespalib::net::tls::HandshakeResult::State::Failed: return HandshakeResult::FAIL;
+ case ::vespalib::net::tls::HandshakeResult::State::Done: return hs_try_flush();
+ case ::vespalib::net::tls::HandshakeResult::State::NeedsMorePeerData:
+ auto flush_res = hs_try_flush();
+ if (flush_res != HandshakeResult::DONE) {
+ return flush_res;
+ }
+ auto fill_res = hs_try_fill();
+ if (fill_res != HandshakeResult::DONE) {
+ return fill_res;
+ }
+ }
+ }
+ return HandshakeResult::DONE;
+}
+
+ssize_t
+CryptoCodecAdapter::read(char *buf, size_t len)
+{
+ auto fill_res = fill_input();
+ if (fill_res <= 0) {
+ return fill_res;
+ }
+ ssize_t res = drain(buf, len);
+ if (res != 0) {
+ return res;
+ }
+ errno = EWOULDBLOCK;
+ return -1;
+}
+
+ssize_t
+CryptoCodecAdapter::drain(char *buf, size_t len)
+{
+ auto src = _input.obtain();
+ auto res = _codec->decode(src.data, src.size, buf, len);
+ if (res.failed()) {
+ errno = EIO;
+ return -1;
+ }
+ _input.evict(res.bytes_consumed);
+ return res.bytes_produced;
+}
+
+ssize_t
+CryptoCodecAdapter::write(const char *buf, size_t len)
+{
+ if (flush_all() < 0) {
+ return -1;
+ }
+ auto dst = _output.reserve(_codec->min_encode_buffer_size());
+ auto res = _codec->encode(buf, len, dst.data, dst.size);
+ if (res.failed) {
+ errno = EIO;
+ return -1;
+ }
+ _output.commit(res.bytes_produced);
+ return res.bytes_consumed;
+}
+
+ssize_t
+CryptoCodecAdapter::flush()
+{
+ auto pending = _output.obtain();
+ if (pending.size > 0) {
+ ssize_t res = _socket.write(pending.data, pending.size);
+ if (res > 0) {
+ _output.evict(res);
+ return 1; // progress
+ } else {
+ assert(res < 0);
+ return -1; // error
+ }
+ }
+ return 0; // done
+}
+
+} // namespace vespalib::net::tls
diff --git a/vespalib/src/vespa/vespalib/net/tls/crypto_codec_adapter.h b/vespalib/src/vespa/vespalib/net/tls/crypto_codec_adapter.h
new file mode 100644
index 00000000000..6a624ca44f7
--- /dev/null
+++ b/vespalib/src/vespa/vespalib/net/tls/crypto_codec_adapter.h
@@ -0,0 +1,46 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#pragma once
+
+#include <vespa/vespalib/net/crypto_socket.h>
+#include <vespa/vespalib/net/socket_handle.h>
+#include <vespa/vespalib/data/simple_buffer.h>
+#include "crypto_codec.h"
+
+namespace vespalib::net::tls {
+
+/**
+ * Component adapting an underlying CryptoCodec to the CryptoSocket
+ * interface by performing buffer and socket management.
+ *
+ * NOTE: initial implementation is for functionality/proof-of-concept
+ * purposes, not performance.
+ **/
+class CryptoCodecAdapter : public CryptoSocket
+{
+private:
+ SimpleBuffer _input;
+ SimpleBuffer _output;
+ SocketHandle _socket;
+ std::unique_ptr<CryptoCodec> _codec;
+
+ bool is_blocked(ssize_t res, int error) const {
+ return ((res < 0) && ((error == EWOULDBLOCK) || (error == EAGAIN)));
+ }
+ HandshakeResult hs_try_flush();
+ HandshakeResult hs_try_fill();
+ ssize_t fill_input(); // -1/0/1 -> error/eof/ok
+ ssize_t flush_all(); // -1/0 -> error/ok
+public:
+ CryptoCodecAdapter(SocketHandle socket, std::unique_ptr<CryptoCodec> codec)
+ : _socket(std::move(socket)), _codec(std::move(codec)) {}
+ int get_fd() const override { return _socket.get(); }
+ HandshakeResult handshake() override;
+ size_t min_read_buffer_size() const override { return _codec->min_decode_buffer_size(); }
+ ssize_t read(char *buf, size_t len) override;
+ ssize_t drain(char *, size_t) override;
+ ssize_t write(const char *buf, size_t len) override;
+ ssize_t flush() override;
+};
+
+} // namespace vespalib::net::tls
diff --git a/vespalib/src/vespa/vespalib/net/tls/impl/openssl_crypto_codec_impl.cpp b/vespalib/src/vespa/vespalib/net/tls/impl/openssl_crypto_codec_impl.cpp
index 13e1be2ce34..a563a43baac 100644
--- a/vespalib/src/vespa/vespalib/net/tls/impl/openssl_crypto_codec_impl.cpp
+++ b/vespalib/src/vespa/vespalib/net/tls/impl/openssl_crypto_codec_impl.cpp
@@ -35,6 +35,10 @@ namespace vespalib::net::tls::impl {
namespace {
+bool verify_buf(const char *buf, size_t len) {
+ return ((len < INT32_MAX) && ((len == 0) || (buf != nullptr)));
+}
+
const char* ssl_error_to_str(int ssl_error) noexcept {
// From https://www.openssl.org/docs/manmaster/man3/SSL_get_error.html
// Our code paths shouldn't trigger most of these, but included for completeness
@@ -200,8 +204,7 @@ int OpenSslCryptoCodecImpl::drain_outgoing_network_bytes_if_any(
HandshakeResult OpenSslCryptoCodecImpl::handshake(const char* from_peer, size_t from_peer_buf_size,
char* to_peer, size_t to_peer_buf_size) noexcept {
- LOG_ASSERT(from_peer != nullptr && to_peer != nullptr
- && from_peer_buf_size < INT32_MAX && to_peer_buf_size < INT32_MAX);
+ LOG_ASSERT(verify_buf(from_peer, from_peer_buf_size) && verify_buf(to_peer, to_peer_buf_size));
if (SSL_is_init_finished(_ssl.get())) {
return handshake_completed();
@@ -279,8 +282,7 @@ HandshakeResult OpenSslCryptoCodecImpl::do_handshake_and_consume_peer_input_byte
EncodeResult OpenSslCryptoCodecImpl::encode(const char* plaintext, size_t plaintext_size,
char* ciphertext, size_t ciphertext_size) noexcept {
- LOG_ASSERT(plaintext != nullptr && ciphertext != nullptr
- && plaintext_size < INT32_MAX && ciphertext_size < INT32_MAX);
+ LOG_ASSERT(verify_buf(plaintext, plaintext_size) && verify_buf(ciphertext, ciphertext_size));
if (!SSL_is_init_finished(_ssl.get())) {
LOG(error, "OpenSslCryptoCodecImpl::encode() called before handshake completed");
@@ -317,8 +319,7 @@ EncodeResult OpenSslCryptoCodecImpl::encode(const char* plaintext, size_t plaint
}
DecodeResult OpenSslCryptoCodecImpl::decode(const char* ciphertext, size_t ciphertext_size,
char* plaintext, size_t plaintext_size) noexcept {
- LOG_ASSERT(ciphertext != nullptr && plaintext != nullptr
- && ciphertext_size < INT32_MAX && plaintext_size < INT32_MAX);
+ LOG_ASSERT(verify_buf(ciphertext, ciphertext_size) && verify_buf(plaintext, plaintext_size));
if (!SSL_is_init_finished(_ssl.get())) {
LOG(error, "OpenSslCryptoCodecImpl::decode() called before handshake completed");
diff --git a/vespalib/src/vespa/vespalib/net/tls/tls_crypto_engine.cpp b/vespalib/src/vespa/vespalib/net/tls/tls_crypto_engine.cpp
new file mode 100644
index 00000000000..72d9eacf37c
--- /dev/null
+++ b/vespalib/src/vespa/vespalib/net/tls/tls_crypto_engine.cpp
@@ -0,0 +1,22 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#include "tls_crypto_engine.h"
+#include "crypto_codec.h"
+#include "crypto_codec_adapter.h"
+
+namespace vespalib {
+
+TlsCryptoEngine::TlsCryptoEngine(net::tls::TransportSecurityOptions tls_opts)
+ : _tls_ctx(net::tls::TlsContext::create_default_context(tls_opts))
+{
+}
+
+CryptoSocket::UP
+TlsCryptoEngine::create_crypto_socket(SocketHandle socket, bool is_server)
+{
+ auto mode = is_server ? net::tls::CryptoCodec::Mode::Server : net::tls::CryptoCodec::Mode::Client;
+ auto codec = net::tls::CryptoCodec::create_default_codec(*_tls_ctx, mode);
+ return std::make_unique<net::tls::CryptoCodecAdapter>(std::move(socket), std::move(codec));
+}
+
+} // namespace vespalib
diff --git a/vespalib/src/vespa/vespalib/net/tls/tls_crypto_engine.h b/vespalib/src/vespa/vespalib/net/tls/tls_crypto_engine.h
new file mode 100644
index 00000000000..58fda2b3b21
--- /dev/null
+++ b/vespalib/src/vespa/vespalib/net/tls/tls_crypto_engine.h
@@ -0,0 +1,23 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#pragma once
+
+#include <vespa/vespalib/net/crypto_engine.h>
+#include "transport_security_options.h"
+#include "tls_context.h"
+
+namespace vespalib {
+
+/**
+ * Crypto engine implementing TLS.
+ **/
+class TlsCryptoEngine : public CryptoEngine
+{
+private:
+ std::unique_ptr<net::tls::TlsContext> _tls_ctx;
+public:
+ TlsCryptoEngine(net::tls::TransportSecurityOptions tls_opts);
+ CryptoSocket::UP create_crypto_socket(SocketHandle socket, bool is_server) override;
+};
+
+} // namespace vespalib