diff options
author | Håvard Pettersen <havardpe@oath.com> | 2018-09-27 15:39:34 +0000 |
---|---|---|
committer | Håvard Pettersen <havardpe@oath.com> | 2018-09-27 16:11:07 +0000 |
commit | 763f8e2f35a76bbc15df66843162400fbe136a56 (patch) | |
tree | d9840b55b8bfcee4ed76a39d7b933e9d78e08064 /vespalib | |
parent | f905c1d6993b22ca5132e10bacd21761c35b5fc5 (diff) |
mixed mode tls support in fnet
Diffstat (limited to 'vespalib')
10 files changed, 237 insertions, 4 deletions
diff --git a/vespalib/src/vespa/vespalib/net/crypto_engine.cpp b/vespalib/src/vespa/vespalib/net/crypto_engine.cpp index 3dd800dadf0..d45b0cea1c5 100644 --- a/vespalib/src/vespa/vespalib/net/crypto_engine.cpp +++ b/vespalib/src/vespa/vespalib/net/crypto_engine.cpp @@ -9,6 +9,7 @@ #include <vespa/vespalib/net/tls/transport_security_options.h> #include <vespa/vespalib/net/tls/transport_security_options_reading.h> #include <vespa/vespalib/net/tls/tls_crypto_engine.h> +#include <vespa/vespalib/net/tls/maybe_tls_crypto_engine.h> #include <vespa/vespalib/data/smart_buffer.h> #include <assert.h> @@ -175,10 +176,22 @@ CryptoEngine::SP create_default_crypto_engine() { if (cfg_file.empty()) { return std::make_shared<NullCryptoEngine>(); } - LOG(debug, "Using TLS crypto engine with config file '%s'", cfg_file.c_str()); auto tls_opts = net::tls::read_options_from_json_file(cfg_file); - return std::make_shared<TlsCryptoEngine>(*tls_opts); + auto tls = std::make_shared<TlsCryptoEngine>(*tls_opts); + env = getenv("VESPA_TLS_INSECURE_MIXED_MODE"); + vespalib::string mixed_mode = env ? env : ""; + if (mixed_mode == "plaintext_client_mixed_server") { + LOG(debug, "tls insecure mixed-mode activated: plaintext client, mixed server"); + return std::make_shared<MaybeTlsCryptoEngine>(std::move(tls), false); + } else if (mixed_mode == "tls_client_mixed_server") { + LOG(debug, "tls insecure mixed-mode activated: tls client, mixed server"); + return std::make_shared<MaybeTlsCryptoEngine>(std::move(tls), true); + } else if (!mixed_mode.empty()) { + LOG(warning, "bad tls insecure mixed-mode specified: '%s' (ignoring)", + mixed_mode.c_str()); + } + return tls; } } // namespace vespalib::<unnamed> diff --git a/vespalib/src/vespa/vespalib/net/tls/CMakeLists.txt b/vespalib/src/vespa/vespalib/net/tls/CMakeLists.txt index 17abd82366d..87b7cff1c13 100644 --- a/vespalib/src/vespa/vespalib/net/tls/CMakeLists.txt +++ b/vespalib/src/vespa/vespalib/net/tls/CMakeLists.txt @@ -4,9 +4,12 @@ vespa_add_library(vespalib_vespalib_net_tls OBJECT crypto_codec.cpp crypto_codec_adapter.cpp crypto_exception.cpp + maybe_tls_crypto_engine.cpp + maybe_tls_crypto_socket.cpp protocol_snooping.cpp tls_context.cpp tls_crypto_engine.cpp + tls_crypto_socket.cpp transport_security_options.cpp transport_security_options_reading.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 index 494919f449f..28aede69d22 100644 --- a/vespalib/src/vespa/vespalib/net/tls/crypto_codec_adapter.cpp +++ b/vespalib/src/vespa/vespalib/net/tls/crypto_codec_adapter.cpp @@ -56,6 +56,16 @@ CryptoCodecAdapter::flush_all() return res; } +void +CryptoCodecAdapter::inject_read_data(const char *buf, size_t len) +{ + if (len > 0) { + auto dst = _input.reserve(len); + memcpy(dst.data, buf, len); + _input.commit(len); + } +} + CryptoSocket::HandshakeResult CryptoCodecAdapter::handshake() { diff --git a/vespalib/src/vespa/vespalib/net/tls/crypto_codec_adapter.h b/vespalib/src/vespa/vespalib/net/tls/crypto_codec_adapter.h index 29996a9abbb..af2db3525d3 100644 --- a/vespalib/src/vespa/vespalib/net/tls/crypto_codec_adapter.h +++ b/vespalib/src/vespa/vespalib/net/tls/crypto_codec_adapter.h @@ -2,7 +2,7 @@ #pragma once -#include <vespa/vespalib/net/crypto_socket.h> +#include "tls_crypto_socket.h" #include <vespa/vespalib/net/socket_handle.h> #include <vespa/vespalib/data/smart_buffer.h> #include "crypto_codec.h" @@ -13,7 +13,7 @@ namespace vespalib::net::tls { * Component adapting an underlying CryptoCodec to the CryptoSocket * interface by performing buffer and socket management. **/ -class CryptoCodecAdapter : public CryptoSocket +class CryptoCodecAdapter : public TlsCryptoSocket { private: SmartBuffer _input; @@ -31,6 +31,7 @@ private: public: CryptoCodecAdapter(SocketHandle socket, std::unique_ptr<CryptoCodec> codec) : _input(64 * 1024), _output(64 * 1024), _socket(std::move(socket)), _codec(std::move(codec)) {} + void inject_read_data(const char *buf, size_t len) override; 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(); } diff --git a/vespalib/src/vespa/vespalib/net/tls/maybe_tls_crypto_engine.cpp b/vespalib/src/vespa/vespalib/net/tls/maybe_tls_crypto_engine.cpp new file mode 100644 index 00000000000..891f8cdab23 --- /dev/null +++ b/vespalib/src/vespa/vespalib/net/tls/maybe_tls_crypto_engine.cpp @@ -0,0 +1,20 @@ +// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + +#include "maybe_tls_crypto_engine.h" +#include "maybe_tls_crypto_socket.h" + +namespace vespalib { + +CryptoSocket::UP +MaybeTlsCryptoEngine::create_crypto_socket(SocketHandle socket, bool is_server) +{ + if (is_server) { + return std::make_unique<MaybeTlsCryptoSocket>(std::move(socket), _tls_engine); + } else if (_use_tls_when_client) { + return _tls_engine->create_crypto_socket(std::move(socket), false); + } else { + return _null_engine->create_crypto_socket(std::move(socket), false); + } +} + +} // namespace vespalib diff --git a/vespalib/src/vespa/vespalib/net/tls/maybe_tls_crypto_engine.h b/vespalib/src/vespa/vespalib/net/tls/maybe_tls_crypto_engine.h new file mode 100644 index 00000000000..8e76460231c --- /dev/null +++ b/vespalib/src/vespa/vespalib/net/tls/maybe_tls_crypto_engine.h @@ -0,0 +1,34 @@ +// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + +#pragma once + +#include <memory> +#include "tls_crypto_engine.h" +#include <vespa/vespalib/net/crypto_engine.h> + +namespace vespalib { + +/** + * A crypto engine that supports both tls encrypted connections and + * unencrypted connections. The use of tls for incoming connections is + * auto-detected using clever heuristics. The use of tls for outgoing + * connections is controlled by the use_tls_when_client flag given to + * the constructor. + **/ +class MaybeTlsCryptoEngine : public CryptoEngine +{ +private: + std::shared_ptr<NullCryptoEngine> _null_engine; + std::shared_ptr<TlsCryptoEngine> _tls_engine; + bool _use_tls_when_client; + +public: + MaybeTlsCryptoEngine(std::shared_ptr<TlsCryptoEngine> tls_engine, + bool use_tls_when_client) + : _null_engine(std::make_shared<NullCryptoEngine>()), + _tls_engine(std::move(tls_engine)), + _use_tls_when_client(use_tls_when_client) {} + CryptoSocket::UP create_crypto_socket(SocketHandle socket, bool is_server) override; +}; + +} // namespace vespalib diff --git a/vespalib/src/vespa/vespalib/net/tls/maybe_tls_crypto_socket.cpp b/vespalib/src/vespa/vespalib/net/tls/maybe_tls_crypto_socket.cpp new file mode 100644 index 00000000000..2c3b2e0bdcc --- /dev/null +++ b/vespalib/src/vespa/vespalib/net/tls/maybe_tls_crypto_socket.cpp @@ -0,0 +1,94 @@ +// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + +#include "maybe_tls_crypto_socket.h" +#include "tls_crypto_socket.h" +#include "protocol_snooping.h" +#include <vespa/vespalib/data/smart_buffer.h> +#include <cassert> + +namespace vespalib { + +namespace { + +class MyCryptoSocket : public CryptoSocket +{ +private: + static constexpr size_t SNOOP_SIZE = net::tls::snooping::min_header_bytes_to_observe(); + + CryptoSocket::UP &_self; + SocketHandle _socket; + std::shared_ptr<TlsCryptoEngine> _factory; + SmartBuffer _buffer; + + bool is_blocked(ssize_t res, int error) const { + return ((res < 0) && ((error == EWOULDBLOCK) || (error == EAGAIN))); + } + + bool looksLikeTlsToMe(const char *buf) { + return (net::tls::snooping::snoop_client_hello_header(buf) == net::tls::snooping::TlsSnoopingResult::ProbablyTls); + } + +public: + MyCryptoSocket(CryptoSocket::UP &self, SocketHandle socket, std::shared_ptr<TlsCryptoEngine> tls_engine) + : _self(self), _socket(std::move(socket)), _factory(std::move(tls_engine)), _buffer(4096) + { + assert(SNOOP_SIZE > 0); // we read before checking this + assert(SNOOP_SIZE <= 8); // we promise this in our comment + } + int get_fd() const override { return _socket.get(); } + HandshakeResult handshake() override { + if (_factory) { + auto dst = _buffer.reserve(SNOOP_SIZE); + ssize_t res = _socket.read(dst.data, dst.size); + if (res > 0) { + _buffer.commit(res); + } else if (!is_blocked(res, errno)) { + return HandshakeResult::FAIL; + } + auto src = _buffer.obtain(); + if (src.size < SNOOP_SIZE) { + return HandshakeResult::NEED_READ; + } + if (looksLikeTlsToMe(src.data)) { + CryptoSocket::UP &self = _self; // need copy due to self destruction + auto tls_socket = _factory->create_crypto_socket(std::move(_socket), true); + TlsCryptoSocket *covariant = dynamic_cast<TlsCryptoSocket*>(tls_socket.get()); + assert(covariant != nullptr); + covariant->inject_read_data(src.data, src.size); + self = std::move(tls_socket); + return self->handshake(); + } else { + _factory.reset(); + } + } + return HandshakeResult::DONE; + } + size_t min_read_buffer_size() const override { return 1; } + ssize_t read(char *buf, size_t len) override { + int drain_result = drain(buf, len); + if (drain_result != 0) { + return drain_result; + } + return _socket.read(buf, len); + } + ssize_t drain(char *buf, size_t len) override { + auto src = _buffer.obtain(); + size_t frame = std::min(len, src.size); + if (frame > 0) { + memcpy(buf, src.data, frame); + _buffer.evict(frame); + } + return frame; + } + ssize_t write(const char *buf, size_t len) override { return _socket.write(buf, len); } + ssize_t flush() override { return 0; } +}; + +} // namespace vespalib::<unnamed> + +MaybeTlsCryptoSocket::MaybeTlsCryptoSocket(SocketHandle socket, std::shared_ptr<TlsCryptoEngine> tls_engine) + : _socket(std::make_unique<MyCryptoSocket>(_socket, std::move(socket), std::move(tls_engine))) +{ +} + +} // namespace vespalib diff --git a/vespalib/src/vespa/vespalib/net/tls/maybe_tls_crypto_socket.h b/vespalib/src/vespa/vespalib/net/tls/maybe_tls_crypto_socket.h new file mode 100644 index 00000000000..3c7963cc948 --- /dev/null +++ b/vespalib/src/vespa/vespalib/net/tls/maybe_tls_crypto_socket.h @@ -0,0 +1,35 @@ +// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + +#pragma once + +#include <memory> +#include <vespa/vespalib/net/crypto_socket.h> +#include <vespa/vespalib/net/socket_handle.h> +#include "tls_crypto_engine.h" + +namespace vespalib { + +/** + * A crypto socket for the server side of a connection that + * auto-detects whether the connection is tls encrypted or unencrypted + * using clever heuristics. The assumption is that the client side + * will send at least 8 bytes of data before expecting anything from + * the server. These 8 bytes are inspected to see if they look like + * part of a tls handshake or not. + **/ +class MaybeTlsCryptoSocket : public CryptoSocket +{ +private: + CryptoSocket::UP _socket; +public: + MaybeTlsCryptoSocket(SocketHandle socket, std::shared_ptr<TlsCryptoEngine> tls_engine); + int get_fd() const override { return _socket->get_fd(); } + HandshakeResult handshake() override { return _socket->handshake(); } + size_t min_read_buffer_size() const override { return _socket->min_read_buffer_size(); } + ssize_t read(char *buf, size_t len) override { return _socket->read(buf, len); } + ssize_t drain(char *buf, size_t len) override { return _socket->drain(buf, len); } + ssize_t write(const char *buf, size_t len) override { return _socket->write(buf, len); } + ssize_t flush() override { return _socket->flush(); } +}; + +} // namespace vespalib diff --git a/vespalib/src/vespa/vespalib/net/tls/tls_crypto_socket.cpp b/vespalib/src/vespa/vespalib/net/tls/tls_crypto_socket.cpp new file mode 100644 index 00000000000..ee93827898e --- /dev/null +++ b/vespalib/src/vespa/vespalib/net/tls/tls_crypto_socket.cpp @@ -0,0 +1,9 @@ +// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + +#include "tls_crypto_socket.h" + +namespace vespalib { + +TlsCryptoSocket::~TlsCryptoSocket() = default; + +} // namespace vespalib diff --git a/vespalib/src/vespa/vespalib/net/tls/tls_crypto_socket.h b/vespalib/src/vespa/vespalib/net/tls/tls_crypto_socket.h new file mode 100644 index 00000000000..c98af6ee651 --- /dev/null +++ b/vespalib/src/vespa/vespalib/net/tls/tls_crypto_socket.h @@ -0,0 +1,14 @@ +// 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> + +namespace vespalib { + +struct TlsCryptoSocket : public CryptoSocket { + ~TlsCryptoSocket(); + virtual void inject_read_data(const char *buf, size_t len) = 0; +}; + +} // namespace vespalib |