summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHÃ¥vard Pettersen <havardpe@gmail.com>2018-09-04 10:25:11 +0200
committerGitHub <noreply@github.com>2018-09-04 10:25:11 +0200
commitfb7770220e568a4cdf3f904a6905132c6e90d892 (patch)
treecfd23ed6a57b70c5c1dbba28a18e95f242b96a29
parent4c14777278ac28a540fb415739b32b7b8457df31 (diff)
parent4899aaedb28a9076bc51a96643f30aed2162beb9 (diff)
Merge pull request #6777 from vespa-engine/revert-6756-vekterli/add-initial-openssl-crypto-engine-implementation
Revert "Add initial OpenSSL CryptoEngine implementation and key/cert handling"
-rw-r--r--vespalib/CMakeLists.txt3
-rw-r--r--vespalib/src/tests/net/tls/openssl_impl/CMakeLists.txt9
-rw-r--r--vespalib/src/tests/net/tls/openssl_impl/openssl_impl_test.cpp199
-rw-r--r--vespalib/src/vespa/vespalib/CMakeLists.txt4
-rw-r--r--vespalib/src/vespa/vespalib/net/tls/CMakeLists.txt11
-rw-r--r--vespalib/src/vespa/vespalib/net/tls/crypto_codec.cpp15
-rw-r--r--vespalib/src/vespa/vespalib/net/tls/crypto_codec.h124
-rw-r--r--vespalib/src/vespa/vespalib/net/tls/crypto_exception.cpp10
-rw-r--r--vespalib/src/vespa/vespalib/net/tls/crypto_exception.h10
-rw-r--r--vespalib/src/vespa/vespalib/net/tls/impl/CMakeLists.txt9
-rw-r--r--vespalib/src/vespa/vespalib/net/tls/impl/openssl_crypto_codec_impl.cpp382
-rw-r--r--vespalib/src/vespa/vespalib/net/tls/impl/openssl_crypto_codec_impl.h76
-rw-r--r--vespalib/src/vespa/vespalib/net/tls/impl/openssl_tls_context_impl.cpp241
-rw-r--r--vespalib/src/vespa/vespalib/net/tls/impl/openssl_tls_context_impl.h28
-rw-r--r--vespalib/src/vespa/vespalib/net/tls/impl/openssl_typedefs.h39
-rw-r--r--vespalib/src/vespa/vespalib/net/tls/tls_context.cpp11
-rw-r--r--vespalib/src/vespa/vespalib/net/tls/tls_context.h16
-rw-r--r--vespalib/src/vespa/vespalib/net/tls/transport_security_options.cpp12
-rw-r--r--vespalib/src/vespa/vespalib/net/tls/transport_security_options.h30
19 files changed, 0 insertions, 1229 deletions
diff --git a/vespalib/CMakeLists.txt b/vespalib/CMakeLists.txt
index a4b3f1e643c..33553da9422 100644
--- a/vespalib/CMakeLists.txt
+++ b/vespalib/CMakeLists.txt
@@ -56,7 +56,6 @@ vespa_define_module(
src/tests/net/send_fd
src/tests/net/socket
src/tests/net/socket_spec
- src/tests/net/tls/openssl_impl
src/tests/objects/nbostream
src/tests/optimized
src/tests/printable
@@ -119,8 +118,6 @@ vespa_define_module(
src/vespa/vespalib/io
src/vespa/vespalib/locale
src/vespa/vespalib/net
- src/vespa/vespalib/net/tls
- src/vespa/vespalib/net/tls/impl
src/vespa/vespalib/objects
src/vespa/vespalib/stllike
src/vespa/vespalib/test
diff --git a/vespalib/src/tests/net/tls/openssl_impl/CMakeLists.txt b/vespalib/src/tests/net/tls/openssl_impl/CMakeLists.txt
deleted file mode 100644
index 799e2291d7c..00000000000
--- a/vespalib/src/tests/net/tls/openssl_impl/CMakeLists.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-vespa_add_executable(vespalib_net_tls_openssl_impl_test_app TEST
- SOURCES
- openssl_impl_test.cpp
- DEPENDS
- vespalib
-)
-vespa_add_test(NAME vespalib_net_tls_openssl_impl_test_app COMMAND vespalib_net_tls_openssl_impl_test_app)
-
diff --git a/vespalib/src/tests/net/tls/openssl_impl/openssl_impl_test.cpp b/vespalib/src/tests/net/tls/openssl_impl/openssl_impl_test.cpp
deleted file mode 100644
index cba88f2ba56..00000000000
--- a/vespalib/src/tests/net/tls/openssl_impl/openssl_impl_test.cpp
+++ /dev/null
@@ -1,199 +0,0 @@
-// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-#include <vespa/vespalib/testkit/test_kit.h>
-#include <vespa/vespalib/net/tls/tls_context.h>
-#include <vespa/vespalib/net/tls/transport_security_options.h>
-#include <vespa/vespalib/net/tls/crypto_codec.h>
-#include <iostream>
-#include <stdlib.h>
-
-using namespace vespalib;
-using namespace vespalib::net::tls;
-
-/*
- * Generated with the following commands:
- *
- * openssl ecparam -name prime256v1 -genkey -out ca.key
- *
- * openssl req -new -x509 -nodes -key ca.key \
- * -sha256 -out ca.pem \
- * -subj '/C=US/L=LooneyVille/O=ACME/OU=ACME test CA/CN=acme.example.com' \
- * -days 10000
- *
- * openssl ecparam -name prime256v1 -genkey -out host.key
- *
- * openssl req -new -key host.key -out host.csr \
- * -subj '/C=US/L=LooneyVille/O=Wile. E. Coyote, Ltd./CN=wile.example.com' \
- * -sha256
- *
- * openssl x509 -req -in host.csr \
- * -CA ca.pem \
- * -CAkey ca.key \
- * -CAcreateserial \
- * -out host.pem \
- * -days 10000 \
- * -sha256
- *
- * TODO generate keypairs and certs at test-time to avoid any hard-coding
- * There certs are valid until 2046, so that buys us some time..!
- */
-
-// ca.pem
-constexpr const char* ca_pem = R"(-----BEGIN CERTIFICATE-----
-MIIBuDCCAV4CCQDpVjQIixTxvDAKBggqhkjOPQQDAjBkMQswCQYDVQQGEwJVUzEU
-MBIGA1UEBwwLTG9vbmV5VmlsbGUxDTALBgNVBAoMBEFDTUUxFTATBgNVBAsMDEFD
-TUUgdGVzdCBDQTEZMBcGA1UEAwwQYWNtZS5leGFtcGxlLmNvbTAeFw0xODA4MzEx
-MDU3NDVaFw00NjAxMTYxMDU3NDVaMGQxCzAJBgNVBAYTAlVTMRQwEgYDVQQHDAtM
-b29uZXlWaWxsZTENMAsGA1UECgwEQUNNRTEVMBMGA1UECwwMQUNNRSB0ZXN0IENB
-MRkwFwYDVQQDDBBhY21lLmV4YW1wbGUuY29tMFkwEwYHKoZIzj0CAQYIKoZIzj0D
-AQcDQgAE1L7IzCN5pbyVnBATIHieuxq+hf9kWyn5yfjkXMhD52T5ITz1huq4nbiN
-YtRoRP7XmipI60R/uiCHzERcsVz4rDAKBggqhkjOPQQDAgNIADBFAiEA6wmZDBca
-y0aJ6ABtjbjx/vlmVDxdkaSZSgO8h2CkvIECIFktCkbZhDFfSvbqUScPOGuwkdGQ
-L/EW2Bxp+1BPcYoZ
------END CERTIFICATE-----)";
-
-// host.pem
-constexpr const char* cert_pem = R"(-----BEGIN CERTIFICATE-----
-MIIBsTCCAVgCCQD6GfDh0ltpsjAKBggqhkjOPQQDAjBkMQswCQYDVQQGEwJVUzEU
-MBIGA1UEBwwLTG9vbmV5VmlsbGUxDTALBgNVBAoMBEFDTUUxFTATBgNVBAsMDEFD
-TUUgdGVzdCBDQTEZMBcGA1UEAwwQYWNtZS5leGFtcGxlLmNvbTAeFw0xODA4MzEx
-MDU3NDVaFw00NjAxMTYxMDU3NDVaMF4xCzAJBgNVBAYTAlVTMRQwEgYDVQQHDAtM
-b29uZXlWaWxsZTEeMBwGA1UECgwVV2lsZS4gRS4gQ295b3RlLCBMdGQuMRkwFwYD
-VQQDDBB3aWxlLmV4YW1wbGUuY29tMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE
-e+Y4hxt66em0STviGUj6ZDbxzoLoubXWRml8JDFrEc2S2433KWw2npxYSKVCyo3a
-/Vo33V8/H0WgOXioKEZJxDAKBggqhkjOPQQDAgNHADBEAiAN+87hQuGv3z0Ja2BV
-b8PHq2vp3BJHjeMuxWu4BFPn0QIgYlvIHikspgGatXRNMZ1gPC0oCccsJFcie+Cw
-zL06UPI=
------END CERTIFICATE-----)";
-
-// host.key
-constexpr const char* key_pem = R"(-----BEGIN EC PARAMETERS-----
-BggqhkjOPQMBBw==
------END EC PARAMETERS-----
------BEGIN EC PRIVATE KEY-----
-MHcCAQEEID6di2PFYn8hPrxPbkFDGkSqF+K8L520In7nx3g0jwzOoAoGCCqGSM49
-AwEHoUQDQgAEe+Y4hxt66em0STviGUj6ZDbxzoLoubXWRml8JDFrEc2S2433KWw2
-npxYSKVCyo3a/Vo33V8/H0WgOXioKEZJxA==
------END EC PRIVATE KEY-----)";
-
-const char* decode_state_to_str(DecodeResult::State state) noexcept {
- switch (state) {
- case DecodeResult::State::Failed: return "Broken";
- case DecodeResult::State::OK: return "OK";
- case DecodeResult::State::NeedsMorePeerData: return "NeedsMorePeerData";
- default:
- abort();
- }
-}
-
-const char* hs_state_to_str(HandshakeResult::State state) noexcept {
- switch (state) {
- case HandshakeResult::State::Failed: return "Broken";
- case HandshakeResult::State::Done: return "Done";
- case HandshakeResult::State::NeedsMorePeerData: return "NeedsMorePeerData";
- default:
- abort();
- }
-}
-
-void log_handshake_result(const char* mode, const HandshakeResult& res) {
- fprintf(stderr, "(handshake) %s consumed %zu peer bytes, wrote %zu peer bytes. State: %s\n",
- mode, res.bytes_consumed, res.bytes_produced,
- hs_state_to_str(res.state));
-}
-
-void log_encode_result(const char* mode, const EncodeResult& res) {
- fprintf(stderr, "(encode) %s read %zu plaintext, wrote %zu cipher. State: %s\n",
- mode, res.bytes_consumed, res.bytes_produced,
- res.failed ? "Broken! D:" : "OK");
-}
-
-void log_decode_result(const char* mode, const DecodeResult& res) {
- fprintf(stderr, "(decode) %s read %zu cipher, wrote %zu plaintext. State: %s\n",
- mode, res.bytes_consumed, res.bytes_produced,
- decode_state_to_str(res.state));
-}
-
-bool complete_handshake(CryptoCodec& client, CryptoCodec& server) {
- // Not using vespalib::string here since it doesn't have erase(iter, length) implemented.
- std::string client_to_server_buf;
- std::string server_to_client_buf;
-
- HandshakeResult cli_res;
- HandshakeResult serv_res;
- while (!(cli_res.done() && serv_res.done())) {
- client_to_server_buf.resize(client.min_encode_buffer_size());
- server_to_client_buf.resize(server.min_encode_buffer_size());
-
- cli_res = client.handshake(server_to_client_buf.data(), serv_res.bytes_produced,
- client_to_server_buf.data(), client_to_server_buf.size());
- log_handshake_result("client", cli_res);
- server_to_client_buf.erase(server_to_client_buf.begin(), server_to_client_buf.begin() + cli_res.bytes_consumed);
-
- serv_res = server.handshake(client_to_server_buf.data(), cli_res.bytes_produced,
- server_to_client_buf.data(), server_to_client_buf.size());
- log_handshake_result("server", serv_res);
- client_to_server_buf.erase(client_to_server_buf.begin(), client_to_server_buf.begin() + serv_res.bytes_consumed);
-
- if (cli_res.failed() || serv_res.failed()) {
- return false;
- }
- }
- return true;
-}
-
-TEST("client and server can complete handshake") {
- // TODO move to fixture
- auto tls_opts = TransportSecurityOptions(ca_pem, cert_pem, key_pem);
- auto tls_ctx = TlsContext::create_default_context(tls_opts);
- auto client = CryptoCodec::create_default_codec(*tls_ctx, CryptoCodec::Mode::Client);
- auto server = CryptoCodec::create_default_codec(*tls_ctx, CryptoCodec::Mode::Server);
-
- EXPECT_TRUE(complete_handshake(*client, *server));
-}
-
-TEST("client can send single data frame to server after handshake") {
- // TODO move to fixture
- auto tls_opts = TransportSecurityOptions(ca_pem, cert_pem, key_pem);
- auto tls_ctx = TlsContext::create_default_context(tls_opts);
- auto client = CryptoCodec::create_default_codec(*tls_ctx, CryptoCodec::Mode::Client);
- auto server = CryptoCodec::create_default_codec(*tls_ctx, CryptoCodec::Mode::Server);
-
- ASSERT_TRUE(complete_handshake(*client, *server));
-
- std::string client_to_server_buf;
- client_to_server_buf.resize(client->min_encode_buffer_size());
-
- std::string client_plaintext = "Hellooo world! :D";
- auto cli_res = client->encode(client_plaintext.data(), client_plaintext.size(),
- client_to_server_buf.data(), client_to_server_buf.size());
- log_encode_result("client", cli_res);
-
- std::string server_plaintext_out;
- server_plaintext_out.resize(server->min_decode_buffer_size());
- auto serv_res = server->decode(client_to_server_buf.data(), cli_res.bytes_produced,
- server_plaintext_out.data(), server_plaintext_out.size());
- log_decode_result("server", serv_res);
-
- ASSERT_FALSE(cli_res.failed);
- ASSERT_FALSE(serv_res.failed());
-
- ASSERT_TRUE(serv_res.state == DecodeResult::State::OK);
- std::string data_received(server_plaintext_out.data(), serv_res.bytes_produced);
- EXPECT_EQUAL(client_plaintext, data_received);
-}
-
-/*
- * TODO tests:
- * - full duplex read/write
- * - read and write of > frame size data
- * - handshakes with multi frame writes
- * - completed handshake with pipelined data frame
- * - short ciphertext reads on decode
- * - short plaintext writes on decode (.. if we even want to support this..)
- * - short ciphertext write on encode
- * - peer certificate validation on server
- * - peer certificate validation on client
- * - detection of peer shutdown session
- */
-
-TEST_MAIN() { TEST_RUN_ALL(); }
diff --git a/vespalib/src/vespa/vespalib/CMakeLists.txt b/vespalib/src/vespa/vespalib/CMakeLists.txt
index dadfdec49d7..480caf8f28d 100644
--- a/vespalib/src/vespa/vespalib/CMakeLists.txt
+++ b/vespalib/src/vespa/vespalib/CMakeLists.txt
@@ -9,8 +9,6 @@ vespa_add_library(vespalib
$<TARGET_OBJECTS:vespalib_vespalib_io>
$<TARGET_OBJECTS:vespalib_vespalib_locale>
$<TARGET_OBJECTS:vespalib_vespalib_net>
- $<TARGET_OBJECTS:vespalib_vespalib_net_tls>
- $<TARGET_OBJECTS:vespalib_vespalib_net_tls_impl>
$<TARGET_OBJECTS:vespalib_vespalib_objects>
$<TARGET_OBJECTS:vespalib_vespalib_stllike>
$<TARGET_OBJECTS:vespalib_vespalib_testkit>
@@ -25,5 +23,3 @@ vespa_add_library(vespalib
vespalib_vespalib_test
gcc
)
-
-vespa_add_target_package_dependency(vespalib OpenSSL)
diff --git a/vespalib/src/vespa/vespalib/net/tls/CMakeLists.txt b/vespalib/src/vespa/vespalib/net/tls/CMakeLists.txt
deleted file mode 100644
index 6f0e88d3e39..00000000000
--- a/vespalib/src/vespa/vespalib/net/tls/CMakeLists.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-# Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-vespa_add_library(vespalib_vespalib_net_tls OBJECT
- SOURCES
- crypto_codec.cpp
- crypto_exception.cpp
- tls_context.cpp
- transport_security_options.cpp
- DEPENDS
-)
-vespa_add_target_package_dependency(vespalib_vespalib_net_tls OpenSSL)
-
diff --git a/vespalib/src/vespa/vespalib/net/tls/crypto_codec.cpp b/vespalib/src/vespa/vespalib/net/tls/crypto_codec.cpp
deleted file mode 100644
index b36913d20e3..00000000000
--- a/vespalib/src/vespa/vespalib/net/tls/crypto_codec.cpp
+++ /dev/null
@@ -1,15 +0,0 @@
-// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-#include "crypto_codec.h"
-#include <vespa/vespalib/net/tls/impl/openssl_crypto_codec_impl.h>
-#include <vespa/vespalib/net/tls/impl/openssl_tls_context_impl.h>
-#include <cassert>
-
-namespace vespalib::net::tls {
-
-std::unique_ptr<CryptoCodec> CryptoCodec::create_default_codec(TlsContext& ctx, Mode mode) {
- auto* ssl_ctx = dynamic_cast<impl::OpenSslTlsContextImpl*>(&ctx);
- assert(ssl_ctx != nullptr);
- return std::make_unique<impl::OpenSslCryptoCodecImpl>(*ssl_ctx->native_context(), mode);
-}
-
-}
diff --git a/vespalib/src/vespa/vespalib/net/tls/crypto_codec.h b/vespalib/src/vespa/vespalib/net/tls/crypto_codec.h
deleted file mode 100644
index 6e690c809a5..00000000000
--- a/vespalib/src/vespa/vespalib/net/tls/crypto_codec.h
+++ /dev/null
@@ -1,124 +0,0 @@
-// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-#pragma once
-
-#include <memory>
-
-namespace vespalib::net::tls {
-
-struct HandshakeResult {
- // Handshake bytes consumed from peer.
- size_t bytes_consumed = 0;
- // Handshake bytes produced that must be sent to the peer.
- size_t bytes_produced = 0;
- enum class State {
- Failed,
- Done,
- NeedsMorePeerData
- };
- State state = State::Failed;
-
- bool failed() const noexcept { return (state == State::Failed); }
- bool done() const noexcept { return (state == State::Done); }
-};
-
-struct EncodeResult {
- // Plaintext bytes consumed
- size_t bytes_consumed = 0;
- // Ciphertext bytes produced that must be sent to the peer
- size_t bytes_produced = 0;
- bool failed = true;
-};
-
-struct DecodeResult {
- // Ciphertext bytes consumed from peer
- size_t bytes_consumed = 0;
- // Plaintext bytes produced.
- size_t bytes_produced = 0;
- enum class State {
- Failed,
- OK,
- NeedsMorePeerData
- // TODO add Closed/Shutdown as own state?
- };
- State state = State::Failed;
-
- bool failed() const noexcept { return (state == State::Failed); }
-};
-
-class TlsContext;
-
-// TODO move to different namespace, not dependent on TLS?
-
-/*
- * A CryptoCodec provides a fully transport-independent way of negotiating
- * a secure, authenticated session towards another peer. The codec requires
- * the caller to handle any and all actual data transfer
- */
-class CryptoCodec {
-public:
- enum class Mode {
- Client, Server
- };
-
- virtual ~CryptoCodec() = default;
-
- /*
- * Minimum buffer size required to represent one wire format frame
- * of encrypted (ciphertext) data, including frame overhead.
- */
- virtual size_t min_encode_buffer_size() const noexcept = 0;
- /*
- * Minimum buffer size required to represent the decoded (plaintext)
- * output of a single frame of encrypted data.
- */
- virtual size_t min_decode_buffer_size() const noexcept = 0;
-
- /*
- * Precondition: to_peer_buf_size >= min_encode_buffer_size()
- * Postcondition: if result.done(), the handshake process has completed
- * and data may be passed through encode()/decode().
- */
- virtual HandshakeResult handshake(const char* from_peer, size_t from_peer_buf_size,
- char* to_peer, size_t to_peer_buf_size) noexcept = 0;
-
- /*
- * Encodes a single ciphertext frame into `ciphertext`. If plaintext_size
- * is greater than can fit into a frame, the returned result's consumed_bytes
- * field will be < plaintext_size. The number of actual ciphertext bytes produced
- * is available in the returned result's produced_bytes field.
- *
- * Precondition: handshake must be completed
- * Precondition: ciphertext_size >= min_encode_buffer_size(), i.e. it must be
- * possible to encode at least 1 frame.
- * Postcondition: if plaintext_size > 0 and result.failed == false, a single
- * frame of ciphertext has been written into the to_peer buffer.
- * Size of written frame is given by result.bytes_produced. This
- * includes all protocol-specific frame overhead.
- */
- virtual EncodeResult encode(const char* plaintext, size_t plaintext_size,
- char* ciphertext, size_t ciphertext_size) noexcept = 0;
- /*
- * Attempt to decode ciphertext sent by the peer into plaintext. Since
- * ciphertext is sent in frames, it's possible that invoking decode()
- * may produce a CodecResult with a state of `NeedsMorePeerData` if a
- * complete frame is not present in `ciphertext`. In this case, decode()
- * must be called again once more data is available.
- *
- * Precondition: handshake must be completed
- * Precondition: plaintext_size >= min_decode_buffer_size()
- * Postcondition: if result.state == DecodeResult::State::OK, at least 1
- * complete frame has been written to the `plaintext` buffer
- */
- virtual DecodeResult decode(const char* ciphertext, size_t ciphertext_size,
- char* plaintext, size_t plaintext_size) noexcept = 0;
-
- /*
- * Creates an implementation defined CryptoCodec that provides at least TLSv1.2
- * compliant handshaking and full duplex data transfer.
- *
- * Throws CryptoException if resources cannot be allocated for the codec.
- */
- static std::unique_ptr<CryptoCodec> create_default_codec(TlsContext& ctx, Mode mode);
-};
-
-}
diff --git a/vespalib/src/vespa/vespalib/net/tls/crypto_exception.cpp b/vespalib/src/vespa/vespalib/net/tls/crypto_exception.cpp
deleted file mode 100644
index 41bb2060c04..00000000000
--- a/vespalib/src/vespa/vespalib/net/tls/crypto_exception.cpp
+++ /dev/null
@@ -1,10 +0,0 @@
-// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-
-#include "crypto_exception.h"
-
-namespace vespalib::net::tls {
-
-VESPA_IMPLEMENT_EXCEPTION(CryptoException, Exception);
-
-}
-
diff --git a/vespalib/src/vespa/vespalib/net/tls/crypto_exception.h b/vespalib/src/vespa/vespalib/net/tls/crypto_exception.h
deleted file mode 100644
index 696a158e058..00000000000
--- a/vespalib/src/vespa/vespalib/net/tls/crypto_exception.h
+++ /dev/null
@@ -1,10 +0,0 @@
-// 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/util/exception.h>
-
-namespace vespalib::net::tls {
-
-VESPA_DEFINE_EXCEPTION(CryptoException, Exception);
-
-}
diff --git a/vespalib/src/vespa/vespalib/net/tls/impl/CMakeLists.txt b/vespalib/src/vespa/vespalib/net/tls/impl/CMakeLists.txt
deleted file mode 100644
index 8b4398679db..00000000000
--- a/vespalib/src/vespa/vespalib/net/tls/impl/CMakeLists.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-vespa_add_library(vespalib_vespalib_net_tls_impl OBJECT
- SOURCES
- openssl_tls_context_impl.cpp
- openssl_crypto_codec_impl.cpp
- DEPENDS
-)
-vespa_add_target_package_dependency(vespalib_vespalib_net_tls_impl OpenSSL)
-
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
deleted file mode 100644
index 13e1be2ce34..00000000000
--- a/vespalib/src/vespa/vespalib/net/tls/impl/openssl_crypto_codec_impl.cpp
+++ /dev/null
@@ -1,382 +0,0 @@
-// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-#include "openssl_crypto_codec_impl.h"
-#include "openssl_tls_context_impl.h"
-#include <vespa/vespalib/net/tls/crypto_codec.h>
-#include <vespa/vespalib/net/tls/crypto_exception.h>
-#include <mutex>
-#include <vector>
-#include <memory>
-#include <stdexcept>
-#include <openssl/ssl.h>
-#include <openssl/crypto.h>
-#include <openssl/err.h>
-#include <openssl/pem.h>
-
-#include <vespa/log/log.h>
-LOG_SETUP(".vespalib.net.tls.openssl_crypto_codec_impl");
-
-#if (OPENSSL_VERSION_NUMBER < 0x10000000L)
-// < 1.0 requires explicit thread ID callback support.
-# error "Provided OpenSSL version is too darn old, need at least 1.0"
-#endif
-
-/*
- * Beware all ye who dare enter, for this is OpenSSL integration territory.
- * Dragons are known to roam the skies. Strange whispers are heard at night
- * in the mist-covered lands where the forest meets the lake. Rumors of a
- * tome that contains best practices and excellent documentation are heard
- * at the local inn, but no one seems to know where it exists, or even if
- * it ever existed. Be it best that people carry on with their lives and
- * pretend to not know of the beasts that lurk beyond where the torch's
- * light fades and turns to all-enveloping darkness.
- */
-
-namespace vespalib::net::tls::impl {
-
-namespace {
-
-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
- switch (ssl_error) {
- case SSL_ERROR_NONE:
- return "SSL_ERROR_NONE";
- case SSL_ERROR_ZERO_RETURN:
- return "SSL_ERROR_ZERO_RETURN";
- case SSL_ERROR_WANT_READ:
- return "SSL_ERROR_WANT_READ";
- case SSL_ERROR_WANT_WRITE:
- return "SSL_ERROR_WANT_WRITE";
- case SSL_ERROR_WANT_CONNECT:
- return "SSL_ERROR_WANT_CONNECT";
- case SSL_ERROR_WANT_ACCEPT:
- return "SSL_ERROR_WANT_ACCEPT";
- case SSL_ERROR_WANT_X509_LOOKUP:
- return "SSL_ERROR_WANT_X509_LOOKUP";
-#if (OPENSSL_VERSION_NUMBER >= 0x10100000L)
- case SSL_ERROR_WANT_ASYNC:
- return "SSL_ERROR_WANT_ASYNC";
- case SSL_ERROR_WANT_ASYNC_JOB:
- return "SSL_ERROR_WANT_ASYNC_JOB";
-#endif
-#if (OPENSSL_VERSION_NUMBER >= 0x10101000L)
- case SSL_ERROR_WANT_CLIENT_HELLO_CB:
- return "SSL_ERROR_WANT_CLIENT_HELLO_CB";
-#endif
- case SSL_ERROR_SYSCALL:
- return "SSL_ERROR_SYSCALL";
- case SSL_ERROR_SSL:
- return "SSL_ERROR_SSL";
- default:
- return "Unknown SSL error code";
- }
-}
-
-HandshakeResult handshake_consumed_bytes_and_needs_more_peer_data(size_t consumed) noexcept {
- return {consumed, 0, HandshakeResult::State::NeedsMorePeerData};
-}
-
-HandshakeResult handshake_produced_bytes_and_needs_more_peer_data(size_t produced) noexcept {
- return {0, produced, HandshakeResult::State::NeedsMorePeerData};
-}
-
-HandshakeResult handshake_consumed_bytes_and_is_complete(size_t consumed) noexcept {
- return {consumed, 0, HandshakeResult::State::Done};
-}
-
-HandshakeResult handshaked_bytes(size_t consumed, size_t produced, HandshakeResult::State state) noexcept {
- return {consumed, produced, state};
-}
-
-HandshakeResult handshake_completed() noexcept {
- return {0, 0, HandshakeResult::State::Done};
-}
-
-HandshakeResult handshake_failed() noexcept {
- return {0, 0, HandshakeResult::State::Failed};
-}
-
-EncodeResult encode_failed() noexcept {
- return {0, 0, true};
-}
-
-EncodeResult encoded_bytes(size_t consumed, size_t produced) noexcept {
- return {consumed, produced, false};
-}
-
-DecodeResult decode_failed() noexcept {
- return {0, 0, DecodeResult::State::Failed};
-}
-
-DecodeResult decoded_frames_with_plaintext_bytes(size_t produced_bytes) noexcept {
- return {0, produced_bytes, DecodeResult::State::OK};
-}
-
-DecodeResult decode_needs_more_peer_data() noexcept {
- return {0, 0, DecodeResult::State::NeedsMorePeerData};
-}
-
-DecodeResult decoded_bytes(size_t consumed, size_t produced, DecodeResult::State state) noexcept {
- return {consumed, produced, state};
-}
-
-BioPtr new_tls_frame_memory_bio() {
- BioPtr bio(::BIO_new(BIO_s_mem()));
- if (!bio) {
- throw CryptoException("IO_new(BIO_s_mem()) failed; out of memory?");
- }
- BIO_set_write_buf_size(bio.get(), 0); // 0 ==> default max frame size
- return bio;
-}
-
-} // anon ns
-
-OpenSslCryptoCodecImpl::OpenSslCryptoCodecImpl(::SSL_CTX& ctx, Mode mode)
- : _ssl(::SSL_new(&ctx)),
- _mode(mode)
-{
- if (!_ssl) {
- throw CryptoException("Failed to create new SSL from SSL_CTX");
- }
- /*
- * We use two separate memory BIOs rather than a BIO pair for writing and
- * reading ciphertext, respectively. This is because it _seems_ quite
- * a bit more straight forward to implement a full duplex API with two
- * separate BIOs, but there is little available documentation as to the
- * 'hows' and 'whys' around this.
- * There are claims from core OpenSSL devs[0] that BIO pairs are more efficient,
- * so we may reconsider the current approach (or just use the "OpenSSL controls
- * the file descriptor" yolo approach for simplicity, assuming they do optimal
- * stuff internally).
- *
- * Our BIOs are used as follows:
- *
- * Handshakes may use both BIOs opaquely:
- *
- * handshake() : SSL_do_handshake() --(_output_bio ciphertext)--> BIO_read --> [peer]
- * : SSL_do_handshake() <--(_input_bio ciphertext)-- BIO_write <-- [peer]
- *
- * Once handshaking is complete, the input BIO is only used for decodes and the output
- * BIO is only used for encodes. We explicitly disallow TLS renegotiation, both for
- * the sake of simplicity and for added security (renegotiation is a bit of a rat's nest).
- *
- * encode() : SSL_write(plaintext) --(_output_bio ciphertext)--> BIO_read --> [peer]
- * decode() : SSL_read(plaintext) <--(_input_bio ciphertext)-- BIO_write <-- [peer]
- *
- * To avoid blowing the sizes of BIOs out of the water, we do our best to encode and decode
- * on a per-TLS frame granularity (16K) maximum.
- */
- BioPtr tmp_input_bio = new_tls_frame_memory_bio();
- BioPtr tmp_output_bio = new_tls_frame_memory_bio();
- // Connect BIOs used internally by OpenSSL. This transfers ownership. No return value to check.
- // TODO replace with explicit SSL_set0_rbio/SSL_set0_wbio on OpenSSL >= v1.1
- ::SSL_set_bio(_ssl.get(), tmp_input_bio.get(), tmp_output_bio.get());
- _input_bio = tmp_input_bio.release();
- _output_bio = tmp_output_bio.release();
- if (_mode == Mode::Client) {
- ::SSL_set_connect_state(_ssl.get());
- } else {
- ::SSL_set_accept_state(_ssl.get());
- }
-}
-
-// TODO remove spammy logging once code is stable
-
-// Produces bytes previously written to _output_bio by SSL_do_handshake or SSL_write
-int OpenSslCryptoCodecImpl::drain_outgoing_network_bytes_if_any(
- char *to_peer, size_t to_peer_buf_size) noexcept {
- int out_pending = BIO_pending(_output_bio);
- if (out_pending > 0) {
- int copied = ::BIO_read(_output_bio, to_peer, static_cast<int>(to_peer_buf_size));
- // TODO BIO_should_retry here? Semantics are unclear, especially for memory BIOs.
- LOG(spam, "BIO_read copied out %d bytes of ciphertext from _output_bio", copied);
- if (copied < 0) {
- LOG(error, "Memory BIO_read() failed with BIO_pending() > 0");
- }
- return copied;
- }
- return out_pending;
-}
-
-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);
-
- if (SSL_is_init_finished(_ssl.get())) {
- return handshake_completed();
- }
- // Still ciphertext data left? If so, get rid of it before we start a new operation
- // that wants to fill the output BIO.
- int produced = drain_outgoing_network_bytes_if_any(to_peer, to_peer_buf_size);
- if (produced > 0) {
- // Handshake isn't complete yet and we've got stuff to send. Need to continue handshake
- // once more data is available from the peer.
- return handshake_produced_bytes_and_needs_more_peer_data(static_cast<size_t>(produced));
- } else if (produced < 0) {
- return handshake_failed();
- }
- const auto consume_res = do_handshake_and_consume_peer_input_bytes(from_peer, from_peer_buf_size);
- LOG_ASSERT(consume_res.bytes_produced == 0);
- if (consume_res.failed()) {
- return consume_res;
- }
- // SSL_do_handshake() might have produced more data to send. Note: handshake may
- // be complete at this point.
- produced = drain_outgoing_network_bytes_if_any(to_peer, to_peer_buf_size);
- if (produced < 0) {
- return handshake_failed();
- }
- return handshaked_bytes(consume_res.bytes_consumed, static_cast<size_t>(produced), consume_res.state);
-}
-
-HandshakeResult OpenSslCryptoCodecImpl::do_handshake_and_consume_peer_input_bytes(
- const char *from_peer, size_t from_peer_buf_size) noexcept {
- // Feed the SSL session input in frame-sized chunks between each call to SSL_do_handshake().
- // This is primarily to ensure we don't shove unbounded amounts of data into the BIO
- // in the case that someone naughty is sending us tons of garbage over the socket.
- size_t consumed_total = 0;
- while (true) {
- // Assumption: SSL_do_handshake will place all required outgoing handshake
- // data in the output memory BIO without requiring WANT_WRITE. Freestanding
- // memory BIOs are _supposedly_ auto-resizing, so this should work transparently.
- // At the very least, if this is not the case we'll auto-fail the connection
- // and quickly find out..!
- // TODO test multi-frame sized handshake
- // TODO should we invoke ::ERR_clear_error() prior?
- int ssl_result = ::SSL_do_handshake(_ssl.get());
- ssl_result = ::SSL_get_error(_ssl.get(), ssl_result);
-
- if (ssl_result == SSL_ERROR_WANT_READ) {
- LOG(spam, "SSL_do_handshake() returned SSL_ERROR_WANT_READ");
- if (from_peer_buf_size - consumed_total > 0) {
- int consumed = ::BIO_write(_input_bio, from_peer + consumed_total,
- static_cast<int>(std::min(MaximumTlsFrameSize, from_peer_buf_size - consumed_total)));
- LOG(spam, "BIO_write copied in %d bytes of ciphertext to _input_bio", consumed);
- if (consumed < 0) {
- LOG(error, "Memory BIO_write() returned %d", consumed); // TODO BIO_need_retry?
- return handshake_failed();
- }
- consumed_total += consumed; // TODO protect against consumed == 0?
- continue;
- } else {
- return handshake_consumed_bytes_and_needs_more_peer_data(consumed_total);
- }
- } else if (ssl_result == SSL_ERROR_NONE) {
- // At this point SSL_do_handshake has stated it does not need any more peer data, i.e.
- // the handshake is complete.
- if (!SSL_is_init_finished(_ssl.get())) {
- LOG(error, "SSL handshake is not completed even though no more peer data is requested");
- return handshake_failed();
- }
- return handshake_consumed_bytes_and_is_complete(consumed_total);
- } else {
- LOG(error, "SSL_do_handshake() returned unexpected error: %s", ssl_error_to_str(ssl_result));
- return handshake_failed();
- }
- };
-}
-
-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);
-
- if (!SSL_is_init_finished(_ssl.get())) {
- LOG(error, "OpenSslCryptoCodecImpl::encode() called before handshake completed");
- return encode_failed();
- }
- size_t bytes_consumed = 0;
- if (plaintext_size != 0) {
- int to_consume = static_cast<int>(std::min(plaintext_size, MaximumFramePlaintextSize));
- // SSL_write encodes plaintext to ciphertext and writes to _output_bio
- int consumed = ::SSL_write(_ssl.get(), plaintext, to_consume);
- LOG(spam, "After SSL_write() -> %d, _input_bio pending=%d, _output_bio pending=%d",
- consumed, BIO_pending(_input_bio), BIO_pending(_output_bio));
- if (consumed < 0) {
- int ssl_error = ::SSL_get_error(_ssl.get(), consumed);
- LOG(error, "SSL_write() failed to write frame, got error %s", ssl_error_to_str(ssl_error));
- // TODO explicitly detect and log TLS renegotiation error (SSL_ERROR_WANT_READ)?
- return encode_failed();
- } else if (consumed != to_consume) {
- LOG(error, "SSL_write() returned OK but did not consume all requested plaintext");
- return encode_failed();
- }
- bytes_consumed = static_cast<size_t>(consumed);
- }
-
- int produced = drain_outgoing_network_bytes_if_any(ciphertext, ciphertext_size);
- if (produced < 0) {
- return encode_failed();
- }
- if (BIO_pending(_output_bio) != 0) {
- LOG(error, "Residual data left in output BIO on encode(); provided buffer is too small");
- return encode_failed();
- }
- return encoded_bytes(bytes_consumed, static_cast<size_t>(produced));
-}
-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);
-
- if (!SSL_is_init_finished(_ssl.get())) {
- LOG(error, "OpenSslCryptoCodecImpl::decode() called before handshake completed");
- return decode_failed();
- }
- auto produce_res = drain_and_produce_plaintext_from_ssl(plaintext, static_cast<int>(plaintext_size));
- if ((produce_res.bytes_produced > 0) || produce_res.failed()) {
- return produce_res; // TODO gRPC [1] handles this differently... allows fallthrough
- }
- int consumed = consume_peer_input_bytes(ciphertext, ciphertext_size);
- if (consumed < 0) {
- return decode_failed();
- }
- produce_res = drain_and_produce_plaintext_from_ssl(plaintext, static_cast<int>(plaintext_size));
- return decoded_bytes(static_cast<size_t>(consumed), produce_res.bytes_produced, produce_res.state);
-}
-
-DecodeResult OpenSslCryptoCodecImpl::drain_and_produce_plaintext_from_ssl(
- char* plaintext, size_t plaintext_size) noexcept {
- // SSL_read() is named a bit confusingly. We read _from_ the SSL-internal state
- // via the input BIO _into_ to the receiving plaintext buffer.
- // This may consume the entire, parts of, or none of the input BIO's data,
- // depending on how much TLS frame data is available and its size relative
- // to the receiving plaintext buffer.
- int produced = ::SSL_read(_ssl.get(), plaintext, static_cast<int>(plaintext_size));
- LOG(spam, "After SSL_read() -> %d, _input_bio pending=%d, _output_bio pending=%d",
- produced, BIO_pending(_input_bio), BIO_pending(_output_bio));
- if (produced > 0) {
- // At least 1 frame decoded successfully.
- return decoded_frames_with_plaintext_bytes(static_cast<size_t>(produced));
- } else {
- int ssl_error = ::SSL_get_error(_ssl.get(), produced);
- switch (ssl_error) {
- case SSL_ERROR_WANT_READ:
- // SSL_read() was not able to decode a full frame with the ciphertext that
- // we've fed it thus far; caller must feed it some and then try again.
- LOG(spam, "SSL_read() returned SSL_ERROR_WANT_READ, must get more ciphertext");
- return decode_needs_more_peer_data();
- default:
- LOG(error, "SSL_read() returned unexpected error: %s", ssl_error_to_str(ssl_error));
- return decode_failed();
- }
- }
-}
-
-int OpenSslCryptoCodecImpl::consume_peer_input_bytes(
- const char* ciphertext, size_t ciphertext_size) noexcept {
- // TODO BIO_need_retry on failure? Can this even happen for memory BIOs?
- int consumed = ::BIO_write(_input_bio, ciphertext, static_cast<int>(std::min(MaximumTlsFrameSize, ciphertext_size)));
- LOG(spam, "BIO_write copied in %d bytes of ciphertext to _input_bio", consumed);
- if (consumed < 0) {
- LOG(error, "Memory BIO_write() returned %d", consumed);
- }
- return consumed;
-}
-
-}
-
-// External references:
-// [0] http://openssl.6102.n7.nabble.com/nonblocking-implementation-question-tp1728p1732.html
-// [1] https://github.com/grpc/grpc/blob/master/src/core/tsi/ssl_transport_security.cc
diff --git a/vespalib/src/vespa/vespalib/net/tls/impl/openssl_crypto_codec_impl.h b/vespalib/src/vespa/vespalib/net/tls/impl/openssl_crypto_codec_impl.h
deleted file mode 100644
index 44ca8859596..00000000000
--- a/vespalib/src/vespa/vespalib/net/tls/impl/openssl_crypto_codec_impl.h
+++ /dev/null
@@ -1,76 +0,0 @@
-// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-#pragma once
-
-#include "openssl_typedefs.h"
-#include <vespa/vespalib/net/tls/transport_security_options.h>
-#include <vespa/vespalib/net/tls/crypto_codec.h>
-#include <memory>
-
-namespace vespalib::net::tls { class TlsContext; }
-
-namespace vespalib::net::tls::impl {
-
-/*
- * Frame-level OpenSSL-backed TLSv1.2 crypto codec implementation.
- *
- * Currently has sub-optimal buffer management, and is mostly intended
- * as a starting point.
- *
- * NOT thread safe per instance, but independent instances may be
- * used by different threads safely.
- */
-class OpenSslCryptoCodecImpl : public CryptoCodec {
- SslPtr _ssl;
- ::BIO* _input_bio; // Owned by _ssl
- ::BIO* _output_bio; // Owned by _ssl
- Mode _mode;
-public:
- OpenSslCryptoCodecImpl(::SSL_CTX& ctx, Mode mode);
-
- /*
- * From RFC 8449 (Record Size Limit Extension for TLS), section 1:
- * "TLS versions 1.2 [RFC5246] and earlier permit senders to
- * generate records 16384 octets in size, plus any expansion
- * from compression and protection up to 2048 octets (though
- * typically this expansion is only 16 octets). TLS 1.3 reduces
- * the allowance for expansion to 256 octets."
- *
- * We're on TLSv1.2, so make room for the worst case.
- */
- static constexpr size_t MaximumTlsFrameSize = 16384 + 2048;
- static constexpr size_t MaximumFramePlaintextSize = 16384;
-
- size_t min_encode_buffer_size() const noexcept override {
- return MaximumTlsFrameSize;
- }
- size_t min_decode_buffer_size() const noexcept override {
- return MaximumFramePlaintextSize;
- }
-
- HandshakeResult handshake(const char* from_peer, size_t from_peer_buf_size,
- char* to_peer, size_t to_peer_buf_size) noexcept override;
-
- EncodeResult encode(const char* plaintext, size_t plaintext_size,
- char* ciphertext, size_t ciphertext_size) noexcept override;
- DecodeResult decode(const char* ciphertext, size_t ciphertext_size,
- char* plaintext, size_t plaintext_size) noexcept override;
-private:
- /*
- * Returns
- * n > 0 if n bytes written to `to_peer`. Always <= to_peer_buf_size
- * n == 0 if no bytes pending in output BIO
- * n < 0 on error
- */
- int drain_outgoing_network_bytes_if_any(char *to_peer, size_t to_peer_buf_size) noexcept;
- /*
- * Returns
- * n > 0 if n bytes written to `ciphertext`. Always <= ciphertext_size
- * n == 0 if no bytes pending in input BIO
- * n < 0 on error
- */
- int consume_peer_input_bytes(const char* ciphertext, size_t ciphertext_size) noexcept;
- HandshakeResult do_handshake_and_consume_peer_input_bytes(const char *from_peer, size_t from_peer_buf_size) noexcept;
- DecodeResult drain_and_produce_plaintext_from_ssl(char* plaintext, size_t plaintext_size) noexcept;
-};
-
-}
diff --git a/vespalib/src/vespa/vespalib/net/tls/impl/openssl_tls_context_impl.cpp b/vespalib/src/vespa/vespalib/net/tls/impl/openssl_tls_context_impl.cpp
deleted file mode 100644
index c868f695b98..00000000000
--- a/vespalib/src/vespa/vespalib/net/tls/impl/openssl_tls_context_impl.cpp
+++ /dev/null
@@ -1,241 +0,0 @@
-// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-#include "openssl_typedefs.h"
-#include "openssl_tls_context_impl.h"
-#include <vespa/vespalib/net/tls/crypto_exception.h>
-#include <vespa/vespalib/net/tls/transport_security_options.h>
-#include <mutex>
-#include <vector>
-#include <memory>
-#include <stdexcept>
-#include <openssl/ssl.h>
-#include <openssl/crypto.h>
-#include <openssl/err.h>
-#include <openssl/pem.h>
-
-#include <vespa/log/log.h>
-LOG_SETUP(".vespalib.net.tls.openssl_tls_context_impl");
-
-#if (OPENSSL_VERSION_NUMBER < 0x10000000L)
-// < 1.0 requires explicit thread ID callback support.
-# error "Provided OpenSSL version is too darn old, need at least 1.0"
-#endif
-
-namespace vespalib::net::tls::impl {
-
-namespace {
-
-#if (OPENSSL_VERSION_NUMBER < 0x10100000L)
-
-std::vector<std::unique_ptr<std::mutex>> _g_mutexes;
-
-// Some works on OpenSSL legacy locking: OpenSSL does not implement locking
-// itself internally, deferring to user code callbacks that Do The Needful(tm).
-// The `n` parameter refers to the nth mutex, which is always < CRYPTO_num_locks().
-void openssl_locking_cb(int mode, int n, [[maybe_unused]] const char *file, [[maybe_unused]] int line) {
- if (mode & CRYPTO_LOCK) {
- _g_mutexes[n]->lock();
- } else {
- _g_mutexes[n]->unlock();
- }
-}
-
-#endif
-
-struct OpenSslLibraryResources {
- OpenSslLibraryResources();
- ~OpenSslLibraryResources();
-};
-
-OpenSslLibraryResources::OpenSslLibraryResources() {
- // Other implementations (Asio, gRPC) disagree on whether main library init
- // itself should take place on >= v1.1. We always do it to be on the safe side..!
- ::SSL_library_init();
- ::SSL_load_error_strings();
- ::OpenSSL_add_all_algorithms();
- // Luckily, the mutex callback madness is not present on >= v1.1
-#if (OPENSSL_VERSION_NUMBER < 0x10100000L)
- // Since the init path should happen only once globally, but multiple libraries
- // may use OpenSSL, make sure we don't step on any toes if locking callbacks are
- // already set up.
- if (!::CRYPTO_get_locking_callback()) {
- const int num_locks = ::CRYPTO_num_locks();
- LOG_ASSERT(num_locks > 0);
- _g_mutexes.reserve(num_locks);
- for (int i = 0; i < num_locks; ++i) {
- _g_mutexes.emplace_back(std::make_unique<std::mutex>());
- }
- ::CRYPTO_set_locking_callback(openssl_locking_cb);
- }
-#endif
-}
-
-OpenSslLibraryResources::~OpenSslLibraryResources() {
-#if (OPENSSL_VERSION_NUMBER < 0x10100000L)
- if (::CRYPTO_get_locking_callback() == openssl_locking_cb) {
- ::CRYPTO_set_locking_callback(nullptr);
- }
-#endif
- ::ERR_free_strings();
- ::EVP_cleanup();
- ::CRYPTO_cleanup_all_ex_data();
-}
-
-// TODO make global init instead..?
-void ensure_openssl_initialized_once() {
- static OpenSslLibraryResources openssl_resources;
- (void) openssl_resources;
-}
-
-BioPtr bio_from_string(vespalib::stringref str) {
- LOG_ASSERT(str.size() <= INT_MAX);
- BioPtr bio(::BIO_new_mem_buf(str.data(), static_cast<int>(str.size())));
- if (!bio) {
- throw CryptoException("BIO_new_mem_buf");
- }
- return bio;
-}
-
-// Several OpenSSL functions take a magical user passphrase argument with
-// potentially horrible default behavior for password protected input.
-//
-// From OpenSSL docs (https://www.openssl.org/docs/man1.1.0/crypto/PEM_read_bio_PrivateKey.html):
-//
-// "If the cb parameters is set to NULL and the u parameter is not NULL
-// then the u parameter is interpreted as a null terminated string to use
-// as the passphrase. If both cb and u are NULL then the default callback
-// routine is used which will typically prompt for the passphrase on the
-// current terminal with echoing turned off."
-//
-// Neat!
-//
-// Bonus points for being non-const as well.
-constexpr inline void *empty_passphrase() {
- return const_cast<void *>(static_cast<const void *>(""));
-}
-
-// Attempt to read a PEM encoded (trusted) certificate from the given BIO.
-// BIO might contain further certificates if function returns non-nullptr.
-// Returns nullptr if no certificate could be loaded. This is usually an error,
-// as this should be the first certificate in the chain.
-X509Ptr read_trusted_x509_from_bio(::BIO& bio) {
- // "_AUX" means the certificate is trusted. Why they couldn't name this function
- // something with "trusted" instead is left as an exercise to the reader.
- return X509Ptr(::PEM_read_bio_X509_AUX(&bio, nullptr, nullptr, empty_passphrase()));
-}
-
-// Attempt to read a PEM encoded certificate from the given BIO.
-// BIO might contain further certificates if function returns non-nullptr.
-// Returns nullptr if no certificate could be loaded. This usually implies
-// that there are no more certificates left in the chain.
-X509Ptr read_untrusted_x509_from_bio(::BIO& bio) {
- return X509Ptr(::PEM_read_bio_X509(&bio, nullptr, nullptr, empty_passphrase()));
-}
-
-::SSL_CTX* new_tls1_2_ctx_with_auto_init() {
- ensure_openssl_initialized_once();
- return ::SSL_CTX_new(::TLSv1_2_method());
-}
-
-} // anon ns
-
-OpenSslTlsContextImpl::OpenSslTlsContextImpl(const TransportSecurityOptions& ts_opts)
- : _ctx(new_tls1_2_ctx_with_auto_init())
-{
- if (!_ctx) {
- throw CryptoException("Failed to create new TLSv1.2 context");
- }
- add_certificate_authorities(ts_opts.ca_certs_pem());
- add_certificate_chain(ts_opts.cert_chain_pem());
- use_private_key(ts_opts.private_key_pem());
- verify_private_key();
- enable_ephemeral_key_exchange();
- disable_compression();
- // TODO set accepted cipher suites!
- // TODO `--> If not set in options, use Modern spec from https://wiki.mozilla.org/Security/Server_Side_TLS
- // TODO set peer verification flags!
-}
-
-OpenSslTlsContextImpl::~OpenSslTlsContextImpl() {
- ::SSL_CTX_free(_ctx);
-}
-
-void OpenSslTlsContextImpl::add_certificate_authorities(vespalib::stringref ca_pem) {
- // TODO support empty CA set...? Ever useful?
- auto bio = bio_from_string(ca_pem);
- ::X509_STORE* cert_store = ::SSL_CTX_get_cert_store(_ctx); // Internal pointer, not owned by us.
- while (true) {
- auto ca_cert = read_untrusted_x509_from_bio(*bio);
- if (!ca_cert) {
- break;
- }
- if (::X509_STORE_add_cert(cert_store, ca_cert.get()) != 1) { // Does _not_ take ownership
- throw CryptoException("X509_STORE_add_cert");
- }
- }
-}
-
-void OpenSslTlsContextImpl::add_certificate_chain(vespalib::stringref chain_pem) {
- ::ERR_clear_error();
- auto bio = bio_from_string(chain_pem);
- // First certificate in the chain is the node's own (trusted) certificate.
- auto own_cert = read_trusted_x509_from_bio(*bio);
- if (!own_cert) {
- throw CryptoException("No X509 certificates could be found in provided chain");
- }
- // Ownership of certificate is _not_ transferred, OpenSSL makes internal copy.
- // This is not well documented, but is mentioned by other impls.
- if (::SSL_CTX_use_certificate(_ctx, own_cert.get()) != 1) {
- throw CryptoException("SSL_CTX_use_certificate");
- }
- // After the node's own certificate comes any intermediate CA-provided certificates.
- while (true) {
- auto ca_cert = read_untrusted_x509_from_bio(*bio);
- if (!ca_cert) {
- // No more certificates in chain, hooray!
- ::ERR_clear_error();
- break;
- }
- // Ownership of certificate _is_ transferred here!
- if (!::SSL_CTX_add_extra_chain_cert(_ctx, ca_cert.release())) {
- throw CryptoException("SSL_CTX_add_extra_chain_cert");
- }
- }
-}
-
-void OpenSslTlsContextImpl::use_private_key(vespalib::stringref key_pem) {
- auto bio = bio_from_string(key_pem);
- EvpPkeyPtr key(::PEM_read_bio_PrivateKey(bio.get(), nullptr, nullptr, empty_passphrase()));
- if (!key) {
- throw CryptoException("Failed to read PEM private key data");
- }
- // Ownership _not_ taken.
- if (::SSL_CTX_use_PrivateKey(_ctx, key.get()) != 1) {
- throw CryptoException("SSL_CTX_use_PrivateKey");
- }
-}
-
-void OpenSslTlsContextImpl::verify_private_key() {
- if (::SSL_CTX_check_private_key(_ctx) != 1) {
- throw CryptoException("SSL_CTX_check_private_key failed; mismatch between public and private key?");
- }
-}
-
-void OpenSslTlsContextImpl::enable_ephemeral_key_exchange() {
- // Always enabled by default on higher versions.
-#if (OPENSSL_VERSION_NUMBER < 0x10100000L)
- // Auto curve selection is preferred over using SSL_CTX_set_ecdh_tmp
- if (!::SSL_CTX_set_ecdh_auto(_ctx, 1)) {
- throw CryptoException("SSL_CTX_set_ecdh_auto");
- }
-#endif
- // New ECDH key per connection.
- ::SSL_CTX_set_options(_ctx, SSL_OP_SINGLE_ECDH_USE);
-}
-
-void OpenSslTlsContextImpl::disable_compression() {
- // TLS stream compression is vulnerable to a host of chosen plaintext
- // attacks (CRIME, BREACH etc), so disable it.
- ::SSL_CTX_set_options(_ctx, SSL_OP_NO_COMPRESSION);
-}
-
-}
diff --git a/vespalib/src/vespa/vespalib/net/tls/impl/openssl_tls_context_impl.h b/vespalib/src/vespa/vespalib/net/tls/impl/openssl_tls_context_impl.h
deleted file mode 100644
index 5fa982ee7ad..00000000000
--- a/vespalib/src/vespa/vespalib/net/tls/impl/openssl_tls_context_impl.h
+++ /dev/null
@@ -1,28 +0,0 @@
-// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-#pragma once
-
-#include "openssl_typedefs.h"
-#include <vespa/vespalib/net/tls/tls_context.h>
-#include <vespa/vespalib/stllike/string.h>
-
-namespace vespalib::net::tls::impl {
-
-class OpenSslTlsContextImpl : public TlsContext {
- ::SSL_CTX* _ctx;
-public:
- explicit OpenSslTlsContextImpl(const TransportSecurityOptions&);
- ~OpenSslTlsContextImpl() override;
-
- ::SSL_CTX* native_context() const noexcept { return _ctx; }
-private:
- // Note: single use per instance; does _not_ clear existing chain!
- void add_certificate_authorities(stringref ca_pem);
- void add_certificate_chain(stringref chain_pem);
- void use_private_key(stringref key_pem);
- void verify_private_key();
- // Enable use of ephemeral key exchange (ECDHE), allowing forward secrecy.
- void enable_ephemeral_key_exchange();
- void disable_compression();
-};
-
-} \ No newline at end of file
diff --git a/vespalib/src/vespa/vespalib/net/tls/impl/openssl_typedefs.h b/vespalib/src/vespa/vespalib/net/tls/impl/openssl_typedefs.h
deleted file mode 100644
index 882ffde5897..00000000000
--- a/vespalib/src/vespa/vespalib/net/tls/impl/openssl_typedefs.h
+++ /dev/null
@@ -1,39 +0,0 @@
-// 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 <openssl/ssl.h>
-#include <openssl/crypto.h>
-#include <openssl/x509.h>
-
-namespace vespalib::net::tls::impl {
-
-struct BioDeleter {
- void operator()(::BIO* bio) const noexcept {
- ::BIO_free(bio);
- }
-};
-using BioPtr = std::unique_ptr<::BIO, BioDeleter>;
-
-struct SslDeleter {
- void operator()(::SSL* ssl) const noexcept {
- ::SSL_free(ssl);
- }
-};
-using SslPtr = std::unique_ptr<::SSL, SslDeleter>;
-
-struct X509Deleter {
- void operator()(::X509* cert) const noexcept {
- ::X509_free(cert);
- }
-};
-using X509Ptr = std::unique_ptr<::X509, X509Deleter>;
-
-struct EvpPkeyDeleter {
- void operator()(::EVP_PKEY* pkey) const noexcept {
- ::EVP_PKEY_free(pkey);
- }
-};
-using EvpPkeyPtr = std::unique_ptr<::EVP_PKEY, EvpPkeyDeleter>;
-
-}
diff --git a/vespalib/src/vespa/vespalib/net/tls/tls_context.cpp b/vespalib/src/vespa/vespalib/net/tls/tls_context.cpp
deleted file mode 100644
index 467838975e7..00000000000
--- a/vespalib/src/vespa/vespalib/net/tls/tls_context.cpp
+++ /dev/null
@@ -1,11 +0,0 @@
-// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-#include "tls_context.h"
-#include <vespa/vespalib/net/tls/impl/openssl_tls_context_impl.h>
-
-namespace vespalib::net::tls {
-
-std::unique_ptr<TlsContext> TlsContext::create_default_context(const TransportSecurityOptions& opts) {
- return std::make_unique<impl::OpenSslTlsContextImpl>(opts);
-}
-
-}
diff --git a/vespalib/src/vespa/vespalib/net/tls/tls_context.h b/vespalib/src/vespa/vespalib/net/tls/tls_context.h
deleted file mode 100644
index 7292f43f88c..00000000000
--- a/vespalib/src/vespa/vespalib/net/tls/tls_context.h
+++ /dev/null
@@ -1,16 +0,0 @@
-// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-#pragma once
-
-#include <memory>
-
-namespace vespalib::net::tls {
-
-class TransportSecurityOptions;
-
-struct TlsContext {
- virtual ~TlsContext() = default;
-
- static std::unique_ptr<TlsContext> create_default_context(const TransportSecurityOptions&);
-};
-
-}
diff --git a/vespalib/src/vespa/vespalib/net/tls/transport_security_options.cpp b/vespalib/src/vespa/vespalib/net/tls/transport_security_options.cpp
deleted file mode 100644
index 4e39fe4d7fa..00000000000
--- a/vespalib/src/vespa/vespalib/net/tls/transport_security_options.cpp
+++ /dev/null
@@ -1,12 +0,0 @@
-// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-
-#include "transport_security_options.h"
-#include <openssl/crypto.h>
-
-namespace vespalib::net::tls {
-
-TransportSecurityOptions::~TransportSecurityOptions() {
- OPENSSL_cleanse(&_private_key_pem[0], _private_key_pem.size());
-}
-
-}
diff --git a/vespalib/src/vespa/vespalib/net/tls/transport_security_options.h b/vespalib/src/vespa/vespalib/net/tls/transport_security_options.h
deleted file mode 100644
index 0a228388791..00000000000
--- a/vespalib/src/vespa/vespalib/net/tls/transport_security_options.h
+++ /dev/null
@@ -1,30 +0,0 @@
-// 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/stllike/string.h>
-
-namespace vespalib::net::tls {
-
-class TransportSecurityOptions {
- vespalib::string _ca_certs_pem;
- vespalib::string _cert_chain_pem;
- vespalib::string _private_key_pem;
-public:
- TransportSecurityOptions() = default;
-
- TransportSecurityOptions(vespalib::string ca_certs_pem,
- vespalib::string cert_chain_pem,
- vespalib::string private_key_pem)
- : _ca_certs_pem(std::move(ca_certs_pem)),
- _cert_chain_pem(std::move(cert_chain_pem)),
- _private_key_pem(std::move(private_key_pem))
- {}
- ~TransportSecurityOptions();
-
- const vespalib::string& ca_certs_pem() const noexcept { return _ca_certs_pem; }
- const vespalib::string& cert_chain_pem() const noexcept { return _cert_chain_pem; }
- const vespalib::string& private_key_pem() const noexcept { return _private_key_pem; }
-};
-
-}