From 0e7a05ad7a21e435a2bf77f138d403e183bc29d6 Mon Sep 17 00:00:00 2001 From: Julian Brost Date: Wed, 14 Apr 2021 15:35:54 +0200 Subject: [PATCH] Support TLS 1.3 --- lib/base/tlsutility.cpp | 48 +++++++++++++++++++++++++++++++------- lib/base/tlsutility.hpp | 1 + lib/remote/apilistener.cpp | 8 +++---- 3 files changed, 44 insertions(+), 13 deletions(-) diff --git a/lib/base/tlsutility.cpp b/lib/base/tlsutility.cpp index 264a0872f..40e2d1767 100644 --- a/lib/base/tlsutility.cpp +++ b/lib/base/tlsutility.cpp @@ -179,7 +179,7 @@ Shared::Ptr MakeAsioSslContext(const String& pubkey, InitializeOpenSSL(); - auto context (Shared::Make(ssl::context::tlsv12)); + auto context (Shared::Make(ssl::context::tls)); SetupSslContext(context, pubkey, privkey, cakey); @@ -227,6 +227,28 @@ void SetCipherListToSSLContext(const Shared::Ptr& con #endif /* OPENSSL_VERSION_NUMBER >= 0x10100000L */ } +/** + * Resolves a string describing a TLS protocol version to the value of a TLS*_VERSION macro of OpenSSL. + * + * Throws an exception if the version is unknown or not supported. + * + * @param version String of a TLS version, for example "TLSv1.2". + * @return The value of the corresponding TLS*_VERSION macro. + */ +int ResolveTlsProtocolVersion(const std::string& version) { + if (version == "TLSv1.2") { + return TLS1_2_VERSION; + } else if (version == "TLSv1.3") { +#if OPENSSL_VERSION_NUMBER >= 0x10101000L + return TLS1_3_VERSION; +#else /* OPENSSL_VERSION_NUMBER >= 0x10101000L */ + throw std::runtime_error("'" + version + "' is only supported with OpenSSL 1.1.1 or newer"); +#endif /* OPENSSL_VERSION_NUMBER >= 0x10101000L */ + } else { + throw std::runtime_error("Unknown TLS protocol version '" + version + "'"); + } +} + /** * Set the minimum TLS protocol version to the specified SSL context. * @@ -235,16 +257,24 @@ void SetCipherListToSSLContext(const Shared::Ptr& con */ void SetTlsProtocolminToSSLContext(const Shared::Ptr& context, const String& tlsProtocolmin) { - // tlsProtocolmin has no effect since we enforce TLS 1.2 since 2.11. - /* - std::shared_ptr sslContext = std::shared_ptr(context->native_handle()); +#if OPENSSL_VERSION_NUMBER >= 0x10100000L + int ret = SSL_CTX_set_min_proto_version(context->native_handle(), ResolveTlsProtocolVersion(tlsProtocolmin)); - long flags = SSL_CTX_get_options(sslContext.get()); + if (ret != 1) { + char errbuf[256]; - flags |= ...; - - SSL_CTX_set_options(sslContext.get(), flags); - */ + ERR_error_string_n(ERR_peek_error(), errbuf, sizeof errbuf); + Log(LogCritical, "SSL") + << "Error setting minimum TLS protocol version: " << ERR_peek_error() << ", \"" << errbuf << "\""; + BOOST_THROW_EXCEPTION(openssl_error() + << boost::errinfo_api_function("SSL_CTX_set_min_proto_version") + << errinfo_openssl_error(ERR_peek_error())); + } +#else /* OPENSSL_VERSION_NUMBER >= 0x10100000L */ + // This should never happen. On this OpenSSL version, ResolveTlsProtocolVersion() should either return TLS 1.2 + // or throw an exception, as that's the only TLS version supported by both Icinga and ancient OpenSSL. + VERIFY(ResolveTlsProtocolVersion(tlsProtocolmin) == TLS1_2_VERSION); +#endif /* OPENSSL_VERSION_NUMBER >= 0x10100000L */ } /** diff --git a/lib/base/tlsutility.hpp b/lib/base/tlsutility.hpp index 73d032ed9..2493ff279 100644 --- a/lib/base/tlsutility.hpp +++ b/lib/base/tlsutility.hpp @@ -33,6 +33,7 @@ void AddCRLToSSLContext(const Shared::Ptr& context, c void AddCRLToSSLContext(X509_STORE *x509_store, const String& crlPath); void SetCipherListToSSLContext(const Shared::Ptr& context, const String& cipherList); void SetTlsProtocolminToSSLContext(const Shared::Ptr& context, const String& tlsProtocolmin); +int ResolveTlsProtocolVersion(const std::string& version); String GetCertificateCN(const std::shared_ptr& certificate); std::shared_ptr GetX509Certificate(const String& pemfile); diff --git a/lib/remote/apilistener.cpp b/lib/remote/apilistener.cpp index 7097fa135..fd13596f8 100644 --- a/lib/remote/apilistener.cpp +++ b/lib/remote/apilistener.cpp @@ -1802,10 +1802,10 @@ void ApiListener::ValidateTlsProtocolmin(const Lazy& lvalue, const Valid { ObjectImpl::ValidateTlsProtocolmin(lvalue, utils); - if (lvalue() != SSL_TXT_TLSV1_2) { - String message = "Invalid TLS version. Must be '" SSL_TXT_TLSV1_2 "'"; - - BOOST_THROW_EXCEPTION(ValidationError(this, { "tls_protocolmin" }, message)); + try { + ResolveTlsProtocolVersion(lvalue()); + } catch (const std::exception& ex) { + BOOST_THROW_EXCEPTION(ValidationError(this, { "tls_protocolmin" }, ex.what())); } }