From 2615967e7f461b502c2394d1f80ff3be0948bc5b Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Fri, 8 Feb 2019 14:23:10 +0100 Subject: [PATCH] Make ApiListener#m_SSLContext a Boost ASIO SSL context --- lib/base/tlsstream.cpp | 25 +++++++++++- lib/base/tlsstream.hpp | 4 ++ lib/base/tlsutility.cpp | 83 +++++++++++++++++++++++++------------- lib/base/tlsutility.hpp | 8 ++-- lib/remote/apilistener.cpp | 11 +++-- lib/remote/apilistener.hpp | 3 +- 6 files changed, 97 insertions(+), 37 deletions(-) diff --git a/lib/base/tlsstream.cpp b/lib/base/tlsstream.cpp index 5d7678417..129d0bc74 100644 --- a/lib/base/tlsstream.cpp +++ b/lib/base/tlsstream.cpp @@ -6,6 +6,7 @@ #include "base/logger.hpp" #include "base/configuration.hpp" #include "base/convert.hpp" +#include #include #ifndef _WIN32 @@ -26,6 +27,28 @@ bool TlsStream::m_SSLIndexInitialized = false; * @param sslContext The SSL context for the client. */ TlsStream::TlsStream(const Socket::Ptr& socket, const String& hostname, ConnectionRole role, const std::shared_ptr& sslContext) + : TlsStream(socket, hostname, role, sslContext.get()) +{ +} + +/** + * Constructor for the TlsStream class. + * + * @param role The role of the client. + * @param sslContext The SSL context for the client. + */ +TlsStream::TlsStream(const Socket::Ptr& socket, const String& hostname, ConnectionRole role, const std::shared_ptr& sslContext) + : TlsStream(socket, hostname, role, sslContext->native_handle()) +{ +} + +/** + * Constructor for the TlsStream class. + * + * @param role The role of the client. + * @param sslContext The SSL context for the client. + */ +TlsStream::TlsStream(const Socket::Ptr& socket, const String& hostname, ConnectionRole role, SSL_CTX* sslContext) : SocketEvents(socket), m_Eof(false), m_HandshakeOK(false), m_VerifyOK(true), m_ErrorCode(0), m_ErrorOccurred(false), m_Socket(socket), m_Role(role), m_SendQ(new FIFO()), m_RecvQ(new FIFO()), m_CurrentAction(TlsActionNone), m_Retry(false), m_Shutdown(false) @@ -33,7 +56,7 @@ TlsStream::TlsStream(const Socket::Ptr& socket, const String& hostname, Connecti std::ostringstream msgbuf; char errbuf[120]; - m_SSL = std::shared_ptr(SSL_new(sslContext.get()), SSL_free); + m_SSL = std::shared_ptr(SSL_new(sslContext), SSL_free); if (!m_SSL) { msgbuf << "SSL_new() failed with code " << ERR_peek_error() << ", \"" << ERR_error_string(ERR_peek_error(), errbuf) << "\""; diff --git a/lib/base/tlsstream.hpp b/lib/base/tlsstream.hpp index 8af5fb58e..cd3d0abfa 100644 --- a/lib/base/tlsstream.hpp +++ b/lib/base/tlsstream.hpp @@ -9,6 +9,7 @@ #include "base/stream.hpp" #include "base/tlsutility.hpp" #include "base/fifo.hpp" +#include namespace icinga { @@ -32,6 +33,7 @@ public: DECLARE_PTR_TYPEDEFS(TlsStream); TlsStream(const Socket::Ptr& socket, const String& hostname, ConnectionRole role, const std::shared_ptr& sslContext = MakeSSLContext()); + TlsStream(const Socket::Ptr& socket, const String& hostname, ConnectionRole role, const std::shared_ptr& sslContext); ~TlsStream() override; Socket::Ptr GetSocket() const; @@ -80,6 +82,8 @@ private: static int m_SSLIndex; static bool m_SSLIndexInitialized; + TlsStream(const Socket::Ptr& socket, const String& hostname, ConnectionRole role, SSL_CTX* sslContext); + void OnEvent(int revents) override; void HandleError() const; diff --git a/lib/base/tlsutility.cpp b/lib/base/tlsutility.cpp index 35f4d3ba5..57f8d1901 100644 --- a/lib/base/tlsutility.cpp +++ b/lib/base/tlsutility.cpp @@ -7,6 +7,7 @@ #include "base/utility.hpp" #include "base/application.hpp" #include "base/exception.hpp" +#include #include namespace icinga @@ -57,35 +58,23 @@ void InitializeOpenSSL() l_SSLInitialized = true; } -/** - * Initializes an SSL context using the specified certificates. - * - * @param pubkey The public key. - * @param privkey The matching private key. - * @param cakey CA certificate chain file. - * @returns An SSL context. - */ -std::shared_ptr MakeSSLContext(const String& pubkey, const String& privkey, const String& cakey) +static void SetupSslContext(SSL_CTX *sslContext, const String& pubkey, const String& privkey, const String& cakey) { char errbuf[120]; - InitializeOpenSSL(); - - std::shared_ptr sslContext = std::shared_ptr(SSL_CTX_new(SSLv23_method()), SSL_CTX_free); - long flags = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_CIPHER_SERVER_PREFERENCE; #ifdef SSL_OP_NO_COMPRESSION flags |= SSL_OP_NO_COMPRESSION; #endif /* SSL_OP_NO_COMPRESSION */ - SSL_CTX_set_options(sslContext.get(), flags); + SSL_CTX_set_options(sslContext, flags); - SSL_CTX_set_mode(sslContext.get(), SSL_MODE_ENABLE_PARTIAL_WRITE | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); - SSL_CTX_set_session_id_context(sslContext.get(), (const unsigned char *)"Icinga 2", 8); + SSL_CTX_set_mode(sslContext, SSL_MODE_ENABLE_PARTIAL_WRITE | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); + SSL_CTX_set_session_id_context(sslContext, (const unsigned char *)"Icinga 2", 8); if (!pubkey.IsEmpty()) { - if (!SSL_CTX_use_certificate_chain_file(sslContext.get(), pubkey.CStr())) { + if (!SSL_CTX_use_certificate_chain_file(sslContext, pubkey.CStr())) { Log(LogCritical, "SSL") << "Error with public key file '" << pubkey << "': " << ERR_peek_error() << ", \"" << ERR_error_string(ERR_peek_error(), errbuf) << "\""; BOOST_THROW_EXCEPTION(openssl_error() @@ -96,7 +85,7 @@ std::shared_ptr MakeSSLContext(const String& pubkey, const String& priv } if (!privkey.IsEmpty()) { - if (!SSL_CTX_use_PrivateKey_file(sslContext.get(), privkey.CStr(), SSL_FILETYPE_PEM)) { + if (!SSL_CTX_use_PrivateKey_file(sslContext, privkey.CStr(), SSL_FILETYPE_PEM)) { Log(LogCritical, "SSL") << "Error with private key file '" << privkey << "': " << ERR_peek_error() << ", \"" << ERR_error_string(ERR_peek_error(), errbuf) << "\""; BOOST_THROW_EXCEPTION(openssl_error() @@ -105,7 +94,7 @@ std::shared_ptr MakeSSLContext(const String& pubkey, const String& priv << boost::errinfo_file_name(privkey)); } - if (!SSL_CTX_check_private_key(sslContext.get())) { + if (!SSL_CTX_check_private_key(sslContext)) { Log(LogCritical, "SSL") << "Error checking private key '" << privkey << "': " << ERR_peek_error() << ", \"" << ERR_error_string(ERR_peek_error(), errbuf) << "\""; BOOST_THROW_EXCEPTION(openssl_error() @@ -115,7 +104,7 @@ std::shared_ptr MakeSSLContext(const String& pubkey, const String& priv } if (!cakey.IsEmpty()) { - if (!SSL_CTX_load_verify_locations(sslContext.get(), cakey.CStr(), nullptr)) { + if (!SSL_CTX_load_verify_locations(sslContext, cakey.CStr(), nullptr)) { Log(LogCritical, "SSL") << "Error loading and verifying locations in ca key file '" << cakey << "': " << ERR_peek_error() << ", \"" << ERR_error_string(ERR_peek_error(), errbuf) << "\""; BOOST_THROW_EXCEPTION(openssl_error() @@ -136,22 +125,60 @@ std::shared_ptr MakeSSLContext(const String& pubkey, const String& priv << boost::errinfo_file_name(cakey)); } - SSL_CTX_set_client_CA_list(sslContext.get(), cert_names); + SSL_CTX_set_client_CA_list(sslContext, cert_names); } +} + +/** + * Initializes an SSL context using the specified certificates. + * + * @param pubkey The public key. + * @param privkey The matching private key. + * @param cakey CA certificate chain file. + * @returns An SSL context. + */ +std::shared_ptr MakeSSLContext(const String& pubkey, const String& privkey, const String& cakey) +{ + InitializeOpenSSL(); + + std::shared_ptr sslContext = std::shared_ptr(SSL_CTX_new(SSLv23_method()), SSL_CTX_free); + + SetupSslContext(sslContext.get(), pubkey, privkey, cakey); return sslContext; } +/** + * Initializes an SSL context using the specified certificates. + * + * @param pubkey The public key. + * @param privkey The matching private key. + * @param cakey CA certificate chain file. + * @returns An SSL context. + */ +std::shared_ptr MakeAsioSslContext(const String& pubkey, const String& privkey, const String& cakey) +{ + namespace ssl = boost::asio::ssl; + + InitializeOpenSSL(); + + auto context (std::make_shared(ssl::context::sslv23)); + + SetupSslContext(context->native_handle(), pubkey, privkey, cakey); + + return context; +} + /** * Set the cipher list to the specified SSL context. * @param context The ssl context. * @param cipherList The ciper list. **/ -void SetCipherListToSSLContext(const std::shared_ptr& context, const String& cipherList) +void SetCipherListToSSLContext(const std::shared_ptr& context, const String& cipherList) { char errbuf[256]; - if (SSL_CTX_set_cipher_list(context.get(), cipherList.CStr()) == 0) { + if (SSL_CTX_set_cipher_list(context->native_handle(), cipherList.CStr()) == 0) { Log(LogCritical, "SSL") << "Cipher list '" << cipherList @@ -171,9 +198,9 @@ void SetCipherListToSSLContext(const std::shared_ptr& context, const St * @param context The ssl context. * @param tlsProtocolmin The minimum TLS protocol version. */ -void SetTlsProtocolminToSSLContext(const std::shared_ptr& context, const String& tlsProtocolmin) +void SetTlsProtocolminToSSLContext(const std::shared_ptr& context, const String& tlsProtocolmin) { - long flags = SSL_CTX_get_options(context.get()); + long flags = SSL_CTX_get_options(context->native_handle()); flags |= SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3; @@ -190,7 +217,7 @@ void SetTlsProtocolminToSSLContext(const std::shared_ptr& context, cons if (tlsProtocolmin != SSL_TXT_TLSV1) BOOST_THROW_EXCEPTION(std::invalid_argument("Invalid TLS protocol version specified.")); - SSL_CTX_set_options(context.get(), flags); + SSL_CTX_set_options(context->native_handle(), flags); } /** @@ -199,10 +226,10 @@ void SetTlsProtocolminToSSLContext(const std::shared_ptr& context, cons * @param context The SSL context. * @param crlPath The path to the CRL file. */ -void AddCRLToSSLContext(const std::shared_ptr& context, const String& crlPath) +void AddCRLToSSLContext(const std::shared_ptr& context, const String& crlPath) { char errbuf[120]; - X509_STORE *x509_store = SSL_CTX_get_cert_store(context.get()); + X509_STORE *x509_store = SSL_CTX_get_cert_store(context->native_handle()); X509_LOOKUP *lookup; lookup = X509_STORE_add_lookup(x509_store, X509_LOOKUP_file()); diff --git a/lib/base/tlsutility.hpp b/lib/base/tlsutility.hpp index afe21f2e4..69b10786c 100644 --- a/lib/base/tlsutility.hpp +++ b/lib/base/tlsutility.hpp @@ -14,6 +14,7 @@ #include #include #include +#include #include namespace icinga @@ -21,9 +22,10 @@ namespace icinga void InitializeOpenSSL(); std::shared_ptr MakeSSLContext(const String& pubkey = String(), const String& privkey = String(), const String& cakey = String()); -void AddCRLToSSLContext(const std::shared_ptr& context, const String& crlPath); -void SetCipherListToSSLContext(const std::shared_ptr& context, const String& cipherList); -void SetTlsProtocolminToSSLContext(const std::shared_ptr& context, const String& tlsProtocolmin); +std::shared_ptr MakeAsioSslContext(const String& pubkey = String(), const String& privkey = String(), const String& cakey = String()); +void AddCRLToSSLContext(const std::shared_ptr& context, const String& crlPath); +void SetCipherListToSSLContext(const std::shared_ptr& context, const String& cipherList); +void SetTlsProtocolminToSSLContext(const std::shared_ptr& context, const String& tlsProtocolmin); String GetCertificateCN(const std::shared_ptr& certificate); std::shared_ptr GetX509Certificate(const String& pemfile); int MakeX509CSR(const String& cn, const String& keyfile, const String& csrfile = String(), const String& certfile = String(), bool ca = false); diff --git a/lib/remote/apilistener.cpp b/lib/remote/apilistener.cpp index d8e1832ee..d680cc76a 100644 --- a/lib/remote/apilistener.cpp +++ b/lib/remote/apilistener.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -165,10 +166,12 @@ void ApiListener::OnConfigLoaded() void ApiListener::UpdateSSLContext() { - std::shared_ptr context; + namespace ssl = boost::asio::ssl; + + std::shared_ptr context; try { - context = MakeSSLContext(GetDefaultCertPath(), GetDefaultKeyPath(), GetDefaultCaPath()); + context = MakeAsioSslContext(GetDefaultCertPath(), GetDefaultKeyPath(), GetDefaultCaPath()); } catch (const std::exception&) { BOOST_THROW_EXCEPTION(ScriptError("Cannot make SSL context for cert path: '" + GetDefaultCertPath() + "' key path: '" + GetDefaultKeyPath() + "' ca path: '" + GetDefaultCaPath() + "'.", GetDebugInfo())); @@ -338,7 +341,7 @@ bool ApiListener::AddListener(const String& node, const String& service) ObjectLock olock(this); - std::shared_ptr sslContext = m_SSLContext; + auto sslContext (m_SSLContext); if (!sslContext) { Log(LogCritical, "ApiListener", "SSL context is required for AddListener()"); @@ -389,7 +392,7 @@ void ApiListener::AddConnection(const Endpoint::Ptr& endpoint) { ObjectLock olock(this); - std::shared_ptr sslContext = m_SSLContext; + auto sslContext (m_SSLContext); if (!sslContext) { Log(LogCritical, "ApiListener", "SSL context is required for AddConnection()"); diff --git a/lib/remote/apilistener.hpp b/lib/remote/apilistener.hpp index 96861d74b..1de66ed4f 100644 --- a/lib/remote/apilistener.hpp +++ b/lib/remote/apilistener.hpp @@ -15,6 +15,7 @@ #include "base/tlsstream.hpp" #include "base/threadpool.hpp" #include +#include #include namespace icinga @@ -106,7 +107,7 @@ protected: void ValidateTlsHandshakeTimeout(const Lazy& lvalue, const ValidationUtils& utils) override; private: - std::shared_ptr m_SSLContext; + std::shared_ptr m_SSLContext; mutable boost::mutex m_AnonymousClientsLock; mutable boost::mutex m_HttpClientsLock;