From b2ac05ad7df7bfc54b66345cd7feeca11af904f8 Mon Sep 17 00:00:00 2001 From: Uwe Ebel Date: Mon, 1 Aug 2016 05:32:47 +0200 Subject: [PATCH] Make the minimum TLS protocol version configurable The ApiListener accepts all TLS versions that the underlying OpenSSL library supports. This patch give the ability to restrict the connection to a minimum TLS version. fixes #11292 Signed-off-by: Gunnar Beutner --- doc/6-object-types.md | 1 + lib/base/tlsutility.cpp | 22 ++++++++++++++++++++++ lib/base/tlsutility.hpp | 1 + lib/remote/apilistener.cpp | 19 +++++++++++++++++++ lib/remote/apilistener.hpp | 2 ++ lib/remote/apilistener.ti | 3 +++ 6 files changed, 48 insertions(+) diff --git a/doc/6-object-types.md b/doc/6-object-types.md index 3b87ff8a1..049fc5e6a 100644 --- a/doc/6-object-types.md +++ b/doc/6-object-types.md @@ -51,6 +51,7 @@ Configuration Attributes: accept\_config |**Optional.** Accept zone configuration. Defaults to `false`. accept\_commands |**Optional.** Accept remote commands. Defaults to `false`. cipher\_list |**Optional.** Cipher list that is allowed. + tls\_protocolmin |**Optional.** Minimum TLS protocol version. Must be one of `TLSv1`, `TLSv1.1` or `TLSv1.2`. Defaults to `TLSv1`. ## ApiUser diff --git a/lib/base/tlsutility.cpp b/lib/base/tlsutility.cpp index 0315a1dbd..057fc1840 100644 --- a/lib/base/tlsutility.cpp +++ b/lib/base/tlsutility.cpp @@ -181,6 +181,28 @@ void SetCipherListToSSLContext(const boost::shared_ptr& context, const } } +/** + * Set the minimum TLS protocol version to the specified SSL context. + * + * @param context The ssl context. + * @param tlsProtocolmin The minimum TLS protocol version. + */ +void SetTlsProtocolminToSSLContext(const boost::shared_ptr& context, const String& tlsProtocolmin) +{ + long flags = SSL_CTX_get_options(context.get()); + + flags |= SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3; + + if (tlsProtocolmin == SSL_TXT_TLSV1_1) + flags |= SSL_OP_NO_TLSv1; + else if (tlsProtocolmin == SSL_TXT_TLSV1_2) + flags |= SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1; + else if (tlsProtocolmin != SSL_TXT_TLSV1) + BOOST_THROW_EXCEPTION(std::invalid_argument("Invalid TLS protocol version specified.")); + + SSL_CTX_set_options(context.get(), flags); +} + /** * Loads a CRL and appends its certificates to the specified SSL context. * diff --git a/lib/base/tlsutility.hpp b/lib/base/tlsutility.hpp index 6a41d4818..aef59af44 100644 --- a/lib/base/tlsutility.hpp +++ b/lib/base/tlsutility.hpp @@ -41,6 +41,7 @@ void I2_BASE_API InitializeOpenSSL(void); boost::shared_ptr I2_BASE_API MakeSSLContext(const String& pubkey = String(), const String& privkey = String(), const String& cakey = String()); void I2_BASE_API AddCRLToSSLContext(const boost::shared_ptr& context, const String& crlPath); void I2_BASE_API SetCipherListToSSLContext(const boost::shared_ptr& context, const String& cipherList); +void I2_BASE_API SetTlsProtocolminToSSLContext(const boost::shared_ptr& context, const String& tlsProtocolmin); String I2_BASE_API GetCertificateCN(const boost::shared_ptr& certificate); boost::shared_ptr I2_BASE_API GetX509Certificate(const String& pemfile); int I2_BASE_API MakeX509CSR(const String& cn, const String& keyfile, const String& csrfile = String(), const String& certfile = String(), const String& serialFile = String(), bool ca = false); diff --git a/lib/remote/apilistener.cpp b/lib/remote/apilistener.cpp index e1b002528..370c47a1a 100644 --- a/lib/remote/apilistener.cpp +++ b/lib/remote/apilistener.cpp @@ -104,6 +104,14 @@ void ApiListener::OnConfigLoaded(void) + GetCipherList() + "'.", GetDebugInfo())); } } + + if (!GetTlsProtocolmin().IsEmpty()){ + try { + SetTlsProtocolminToSSLContext(m_SSLContext, GetTlsProtocolmin()); + } catch (const std::exception&) { + BOOST_THROW_EXCEPTION(ScriptError("Cannot set minimum TLS protocol version to SSL context with tls_protocolmin: '" + GetTlsProtocolmin() + "'.", GetDebugInfo())); + } + } } void ApiListener::OnAllConfigLoaded(void) @@ -1171,3 +1179,14 @@ Endpoint::Ptr ApiListener::GetLocalEndpoint(void) const { return m_LocalEndpoint; } + +void ApiListener::ValidateTlsProtocolmin(const String& value, const ValidationUtils& utils) override +{ + ObjectImpl::ValidateTlsProtocolmin(value, utils); + + if (value != SSL_TXT_TLSV1 && value != SSL_TXT_TLSV1_1 && + value != SSL_TXT_TLSV1_2) { + BOOST_THROW_EXCEPTION(ValidationError(this, boost::assign::list_of("tls_protocolmin"), "Invalid TLS version. " + "Must be one of '" SSL_TXT_TLSV1 "', '" SSL_TXT_TLSV1_1 "' or '" SSL_TXT_TLSV1_2 "'")); + } +} diff --git a/lib/remote/apilistener.hpp b/lib/remote/apilistener.hpp index 3f9a325a8..4c1771361 100644 --- a/lib/remote/apilistener.hpp +++ b/lib/remote/apilistener.hpp @@ -105,6 +105,8 @@ protected: virtual void OnAllConfigLoaded(void) override; virtual void Start(bool runtimeCreated) override; + virtual void ValidateTlsProtocolmin(const String& value, const ValidationUtils& utils) override; + private: boost::shared_ptr m_SSLContext; std::set m_Servers; diff --git a/lib/remote/apilistener.ti b/lib/remote/apilistener.ti index 91e4b0e51..fee11f2c8 100644 --- a/lib/remote/apilistener.ti +++ b/lib/remote/apilistener.ti @@ -35,6 +35,9 @@ class ApiListener : ConfigObject [config] String cipher_list { default {{{ return "ALL:!LOW:!WEAK:!MEDIUM:!EXP:!NULL"; }}} }; + [config] String tls_protocolmin { + default {{{ return "TLSv1"; }}} + }; [config] String bind_host; [config] String bind_port { -- 2.50.1