[pkg-nagios-changes] [Git][nagios-team/pkg-icinga2][upstream] New upstream version 2.12.3

Bas Couwenberg gitlab at salsa.debian.org
Wed Dec 16 05:03:36 GMT 2020



Bas Couwenberg pushed to branch upstream at Debian Nagios Maintainer Group / pkg-icinga2


Commits:
1684f2b5 by Bas Couwenberg at 2020-12-16T05:31:29+01:00
New upstream version 2.12.3
- - - - -


18 changed files:

- .github/workflows/packages.yml
- CHANGELOG.md
- VERSION
- lib/base/CMakeLists.txt
- lib/base/process.cpp
- lib/base/process.hpp
- − lib/base/spinlock.cpp
- − lib/base/spinlock.hpp
- lib/base/tlsutility.cpp
- lib/base/tlsutility.hpp
- lib/cli/pkiverifycommand.cpp
- lib/icinga/checkable-check.cpp
- lib/icinga/checkable-notification.cpp
- lib/remote/apilistener-configsync.cpp
- lib/remote/apilistener-filesync.cpp
- lib/remote/apilistener.hpp
- lib/remote/jsonrpcconnection-pki.cpp
- lib/remote/pkiutility.cpp


Changes:

=====================================
.github/workflows/packages.yml
=====================================
@@ -132,28 +132,16 @@ jobs:
             release: 8
           - name: centos
             release: 7
-          - name: centos
-            release: 6
           - name: fedora
             release: 32
           - name: fedora
             release: 31
-          - name: fedora
-            release: 30
-          - name: fedora
-            release: 29
           - name: sles
             release: '15.1'
-          - name: sles
-            release: '15.0'
           - name: sles
             release: '12.5'
-          - name: sles
-            release: '12.4'
           - name: opensuse
             release: '15.1'
-          - name: opensuse
-            release: '15.0'
 
     runs-on: ubuntu-latest
 


=====================================
CHANGELOG.md
=====================================
@@ -7,6 +7,37 @@ documentation before upgrading to a new release.
 
 Released closed milestones can be found on [GitHub](https://github.com/Icinga/icinga2/milestones?state=closed).
 
+## 2.12.3 (2020-12-15)
+
+Version 2.12.3 resolves a security vulnerability with revoked certificates being
+renewed automatically ignoring the CRL.
+
+This version also resolves issues with high load on Windows regarding the config sync
+and not being able to disable/enable Icinga 2 features over the API.
+
+### Security
+
+* Fix that revoked certificates due for renewal will automatically be renewed ignoring the CRL (CVE-2020-29663)
+
+When a CRL is specified in the ApiListener configuration, Icinga 2 only used it
+when connections were established so far, but not when a certificate is requested.
+This allows a node to automatically renew a revoked certificate if it meets the
+other conditions for auto renewal (issued before 2017 or expires in less than 30 days).
+
+Because Icinga 2 currently (v2.12.3 and earlier) uses a validity duration of 15 years,
+this only affects setups with external certificate signing and revoked certificates
+that expire in less then 30 days.
+
+### Bugfixes
+
+* Improve config sync locking - resolves high load issues on Windows #8511
+* Fix runtime config updates being ignored for objects without zone #8549
+* Use proper buffer size for OpenSSL error messages #8542
+
+### Enhancements
+
+* On checkable recovery: re-check children that have a problem #8506
+
 ## 2.12.2 (2020-12-01)
 
 Version 2.12.2 fixes several issues to improve the reliability of the cluster functionality.


=====================================
VERSION
=====================================
@@ -1,2 +1,2 @@
-Version: 2.12.2
+Version: 2.12.3
 Revision: 1


=====================================
lib/base/CMakeLists.txt
=====================================
@@ -64,7 +64,6 @@ set(base_SOURCES
   shared-object.hpp
   singleton.hpp
   socket.cpp socket.hpp
-  spinlock.cpp spinlock.hpp
   stacktrace.cpp stacktrace.hpp
   statsfunction.hpp
   stdiostream.cpp stdiostream.hpp


=====================================
lib/base/process.cpp
=====================================
@@ -49,7 +49,8 @@ static boost::once_flag l_ProcessOnceFlag = BOOST_ONCE_INIT;
 static boost::once_flag l_SpawnHelperOnceFlag = BOOST_ONCE_INIT;
 
 Process::Process(Process::Arguments arguments, Dictionary::Ptr extraEnvironment)
-	: m_Arguments(std::move(arguments)), m_ExtraEnvironment(std::move(extraEnvironment)), m_Timeout(600), m_AdjustPriority(false)
+	: m_Arguments(std::move(arguments)), m_ExtraEnvironment(std::move(extraEnvironment)),
+	  m_Timeout(600), m_AdjustPriority(false), m_ResultAvailable(false)
 #ifdef _WIN32
 	, m_ReadPending(false), m_ReadFailed(false), m_Overlapped()
 #endif /* _WIN32 */
@@ -1007,6 +1008,12 @@ void Process::Run(const std::function<void(const ProcessResult&)>& callback)
 #endif /* _WIN32 */
 }
 
+const ProcessResult& Process::WaitForResult() {
+	std::unique_lock<std::mutex> lock(m_ResultMutex);
+	m_ResultCondition.wait(lock, [this]{ return m_ResultAvailable; });
+	return m_Result;
+}
+
 bool Process::DoEvents()
 {
 	bool is_timeout = false;
@@ -1114,10 +1121,15 @@ bool Process::DoEvents()
 	}
 #endif /* _WIN32 */
 
-	m_Result.PID = m_PID;
-	m_Result.ExecutionEnd = Utility::GetTime();
-	m_Result.ExitStatus = exitcode;
-	m_Result.Output = output;
+	{
+		std::lock_guard<std::mutex> lock(m_ResultMutex);
+		m_Result.PID = m_PID;
+		m_Result.ExecutionEnd = Utility::GetTime();
+		m_Result.ExitStatus = exitcode;
+		m_Result.Output = output;
+		m_ResultAvailable = true;
+	}
+	m_ResultCondition.notify_all();
 
 	if (m_Callback)
 		Utility::QueueAsyncCallback(std::bind(m_Callback, m_Result));


=====================================
lib/base/process.hpp
=====================================
@@ -9,6 +9,8 @@
 #include <deque>
 #include <vector>
 #include <sstream>
+#include <mutex>
+#include <condition_variable>
 
 namespace icinga
 {
@@ -61,6 +63,8 @@ public:
 
 	void Run(const std::function<void (const ProcessResult&)>& callback = std::function<void (const ProcessResult&)>());
 
+	const ProcessResult& WaitForResult();
+
 	pid_t GetPID() const;
 
 	static Arguments PrepareCommand(const Value& command);
@@ -94,6 +98,9 @@ private:
 	std::ostringstream m_OutputStream;
 	std::function<void (const ProcessResult&)> m_Callback;
 	ProcessResult m_Result;
+	bool m_ResultAvailable;
+	std::mutex m_ResultMutex;
+	std::condition_variable m_ResultCondition;
 
 	static void IOThreadProc(int tid);
 	bool DoEvents();


=====================================
lib/base/spinlock.cpp deleted
=====================================
@@ -1,22 +0,0 @@
-/* Icinga 2 | (c) 2020 Icinga GmbH | GPLv2+ */
-
-#include "base/spinlock.hpp"
-#include <atomic>
-
-using namespace icinga;
-
-void SpinLock::lock()
-{
-	while (m_Locked.test_and_set(std::memory_order_acquire)) {
-	}
-}
-
-bool SpinLock::try_lock()
-{
-	return !m_Locked.test_and_set(std::memory_order_acquire);
-}
-
-void SpinLock::unlock()
-{
-	m_Locked.clear(std::memory_order_release);
-}


=====================================
lib/base/spinlock.hpp deleted
=====================================
@@ -1,35 +0,0 @@
-/* Icinga 2 | (c) 2020 Icinga GmbH | GPLv2+ */
-
-#ifndef SPINLOCK_H
-#define SPINLOCK_H
-
-#include <atomic>
-
-namespace icinga
-{
-
-/**
- * A spin lock.
- *
- * @ingroup base
- */
-class SpinLock
-{
-public:
-	SpinLock() = default;
-	SpinLock(const SpinLock&) = delete;
-	SpinLock& operator=(const SpinLock&) = delete;
-	SpinLock(SpinLock&&) = delete;
-	SpinLock& operator=(SpinLock&&) = delete;
-
-	void lock();
-	bool try_lock();
-	void unlock();
-
-private:
-	std::atomic_flag m_Locked = ATOMIC_FLAG_INIT;
-};
-
-}
-
-#endif /* SPINLOCK_H */


=====================================
lib/base/tlsutility.cpp
=====================================
@@ -106,8 +106,9 @@ static void SetupSslContext(const Shared<boost::asio::ssl::context>::Ptr& contex
 
 	if (!pubkey.IsEmpty()) {
 		if (!SSL_CTX_use_certificate_chain_file(sslContext, pubkey.CStr())) {
+			ERR_error_string_n(ERR_peek_error(), errbuf, sizeof errbuf);
 			Log(LogCritical, "SSL")
-				<< "Error with public key file '" << pubkey << "': " << ERR_peek_error() << ", \"" << ERR_error_string(ERR_peek_error(), errbuf) << "\"";
+				<< "Error with public key file '" << pubkey << "': " << ERR_peek_error() << ", \"" << errbuf << "\"";
 			BOOST_THROW_EXCEPTION(openssl_error()
 				<< boost::errinfo_api_function("SSL_CTX_use_certificate_chain_file")
 				<< errinfo_openssl_error(ERR_peek_error())
@@ -117,8 +118,9 @@ static void SetupSslContext(const Shared<boost::asio::ssl::context>::Ptr& contex
 
 	if (!privkey.IsEmpty()) {
 		if (!SSL_CTX_use_PrivateKey_file(sslContext, privkey.CStr(), SSL_FILETYPE_PEM)) {
+			ERR_error_string_n(ERR_peek_error(), errbuf, sizeof errbuf);
 			Log(LogCritical, "SSL")
-				<< "Error with private key file '" << privkey << "': " << ERR_peek_error() << ", \"" << ERR_error_string(ERR_peek_error(), errbuf) << "\"";
+				<< "Error with private key file '" << privkey << "': " << ERR_peek_error() << ", \"" << errbuf << "\"";
 			BOOST_THROW_EXCEPTION(openssl_error()
 				<< boost::errinfo_api_function("SSL_CTX_use_PrivateKey_file")
 				<< errinfo_openssl_error(ERR_peek_error())
@@ -126,8 +128,9 @@ static void SetupSslContext(const Shared<boost::asio::ssl::context>::Ptr& contex
 		}
 
 		if (!SSL_CTX_check_private_key(sslContext)) {
+			ERR_error_string_n(ERR_peek_error(), errbuf, sizeof errbuf);
 			Log(LogCritical, "SSL")
-				<< "Error checking private key '" << privkey << "': " << ERR_peek_error() << ", \"" << ERR_error_string(ERR_peek_error(), errbuf) << "\"";
+				<< "Error checking private key '" << privkey << "': " << ERR_peek_error() << ", \"" << errbuf << "\"";
 			BOOST_THROW_EXCEPTION(openssl_error()
 				<< boost::errinfo_api_function("SSL_CTX_check_private_key")
 				<< errinfo_openssl_error(ERR_peek_error()));
@@ -136,8 +139,9 @@ static void SetupSslContext(const Shared<boost::asio::ssl::context>::Ptr& contex
 
 	if (!cakey.IsEmpty()) {
 		if (!SSL_CTX_load_verify_locations(sslContext, cakey.CStr(), nullptr)) {
+			ERR_error_string_n(ERR_peek_error(), errbuf, sizeof errbuf);
 			Log(LogCritical, "SSL")
-				<< "Error loading and verifying locations in ca key file '" << cakey << "': " << ERR_peek_error() << ", \"" << ERR_error_string(ERR_peek_error(), errbuf) << "\"";
+				<< "Error loading and verifying locations in ca key file '" << cakey << "': " << ERR_peek_error() << ", \"" << errbuf << "\"";
 			BOOST_THROW_EXCEPTION(openssl_error()
 				<< boost::errinfo_api_function("SSL_CTX_load_verify_locations")
 				<< errinfo_openssl_error(ERR_peek_error())
@@ -148,8 +152,9 @@ static void SetupSslContext(const Shared<boost::asio::ssl::context>::Ptr& contex
 
 		cert_names = SSL_load_client_CA_file(cakey.CStr());
 		if (!cert_names) {
+			ERR_error_string_n(ERR_peek_error(), errbuf, sizeof errbuf);
 			Log(LogCritical, "SSL")
-				<< "Error loading client ca key file '" << cakey << "': " << ERR_peek_error() << ", \"" << ERR_error_string(ERR_peek_error(), errbuf) << "\"";
+				<< "Error loading client ca key file '" << cakey << "': " << ERR_peek_error() << ", \"" << errbuf << "\"";
 			BOOST_THROW_EXCEPTION(openssl_error()
 				<< boost::errinfo_api_function("SSL_load_client_CA_file")
 				<< errinfo_openssl_error(ERR_peek_error())
@@ -191,12 +196,13 @@ void SetCipherListToSSLContext(const Shared<boost::asio::ssl::context>::Ptr& con
 	char errbuf[256];
 
 	if (SSL_CTX_set_cipher_list(context->native_handle(), cipherList.CStr()) == 0) {
+		ERR_error_string_n(ERR_peek_error(), errbuf, sizeof errbuf);
 		Log(LogCritical, "SSL")
 			<< "Cipher list '"
 			<< cipherList
 			<< "' does not specify any usable ciphers: "
 			<< ERR_peek_error() << ", \""
-			<< ERR_error_string(ERR_peek_error(), errbuf) << "\"";
+			<< errbuf << "\"";
 
 		BOOST_THROW_EXCEPTION(openssl_error()
 			<< boost::errinfo_api_function("SSL_CTX_set_cipher_list")
@@ -242,30 +248,43 @@ void SetTlsProtocolminToSSLContext(const Shared<boost::asio::ssl::context>::Ptr&
 }
 
 /**
- * Loads a CRL and appends its certificates to the specified SSL context..
+ * Loads a CRL and appends its certificates to the specified Boost SSL context.
  *
  * @param context The SSL context.
  * @param crlPath The path to the CRL file.
  */
 void AddCRLToSSLContext(const Shared<boost::asio::ssl::context>::Ptr& context, const String& crlPath)
 {
-	char errbuf[256];
 	X509_STORE *x509_store = SSL_CTX_get_cert_store(context->native_handle());
+	AddCRLToSSLContext(x509_store, crlPath);
+}
+
+/**
+ * Loads a CRL and appends its certificates to the specified OpenSSL X509 store.
+ *
+ * @param context The SSL context.
+ * @param crlPath The path to the CRL file.
+ */
+void AddCRLToSSLContext(X509_STORE *x509_store, const String& crlPath)
+{
+	char errbuf[256];
 
 	X509_LOOKUP *lookup;
 	lookup = X509_STORE_add_lookup(x509_store, X509_LOOKUP_file());
 
 	if (!lookup) {
+		ERR_error_string_n(ERR_peek_error(), errbuf, sizeof errbuf);
 		Log(LogCritical, "SSL")
-			<< "Error adding X509 store lookup: " << ERR_peek_error() << ", \"" << ERR_error_string(ERR_peek_error(), errbuf) << "\"";
+			<< "Error adding X509 store lookup: " << ERR_peek_error() << ", \"" << errbuf << "\"";
 		BOOST_THROW_EXCEPTION(openssl_error()
 			<< boost::errinfo_api_function("X509_STORE_add_lookup")
 			<< errinfo_openssl_error(ERR_peek_error()));
 	}
 
 	if (X509_LOOKUP_load_file(lookup, crlPath.CStr(), X509_FILETYPE_PEM) != 1) {
+		ERR_error_string_n(ERR_peek_error(), errbuf, sizeof errbuf);
 		Log(LogCritical, "SSL")
-			<< "Error loading crl file '" << crlPath << "': " << ERR_peek_error() << ", \"" << ERR_error_string(ERR_peek_error(), errbuf) << "\"";
+			<< "Error loading crl file '" << crlPath << "': " << ERR_peek_error() << ", \"" << errbuf << "\"";
 		BOOST_THROW_EXCEPTION(openssl_error()
 			<< boost::errinfo_api_function("X509_LOOKUP_load_file")
 			<< errinfo_openssl_error(ERR_peek_error())
@@ -286,8 +305,9 @@ static String GetX509NameCN(X509_NAME *name)
 	int rc = X509_NAME_get_text_by_NID(name, NID_commonName, buffer, sizeof(buffer));
 
 	if (rc == -1) {
+		ERR_error_string_n(ERR_peek_error(), errbuf, sizeof errbuf);
 		Log(LogCritical, "SSL")
-			<< "Error with x509 NAME getting text by NID: " << ERR_peek_error() << ", \"" << ERR_error_string(ERR_peek_error(), errbuf) << "\"";
+			<< "Error with x509 NAME getting text by NID: " << ERR_peek_error() << ", \"" << errbuf << "\"";
 		BOOST_THROW_EXCEPTION(openssl_error()
 			<< boost::errinfo_api_function("X509_NAME_get_text_by_NID")
 			<< errinfo_openssl_error(ERR_peek_error()));
@@ -320,16 +340,18 @@ std::shared_ptr<X509> GetX509Certificate(const String& pemfile)
 	BIO *fpcert = BIO_new(BIO_s_file());
 
 	if (!fpcert) {
+		ERR_error_string_n(ERR_peek_error(), errbuf, sizeof errbuf);
 		Log(LogCritical, "SSL")
-			<< "Error creating new BIO: " << ERR_peek_error() << ", \"" << ERR_error_string(ERR_peek_error(), errbuf) << "\"";
+			<< "Error creating new BIO: " << ERR_peek_error() << ", \"" << errbuf << "\"";
 		BOOST_THROW_EXCEPTION(openssl_error()
 			<< boost::errinfo_api_function("BIO_new")
 			<< errinfo_openssl_error(ERR_peek_error()));
 	}
 
 	if (BIO_read_filename(fpcert, pemfile.CStr()) < 0) {
+		ERR_error_string_n(ERR_peek_error(), errbuf, sizeof errbuf);
 		Log(LogCritical, "SSL")
-			<< "Error reading pem file '" << pemfile << "': " << ERR_peek_error() << ", \"" << ERR_error_string(ERR_peek_error(), errbuf) << "\"";
+			<< "Error reading pem file '" << pemfile << "': " << ERR_peek_error() << ", \"" << errbuf << "\"";
 		BOOST_THROW_EXCEPTION(openssl_error()
 			<< boost::errinfo_api_function("BIO_read_filename")
 			<< errinfo_openssl_error(ERR_peek_error())
@@ -338,8 +360,9 @@ std::shared_ptr<X509> GetX509Certificate(const String& pemfile)
 
 	cert = PEM_read_bio_X509_AUX(fpcert, nullptr, nullptr, nullptr);
 	if (!cert) {
+		ERR_error_string_n(ERR_peek_error(), errbuf, sizeof errbuf);
 		Log(LogCritical, "SSL")
-			<< "Error on bio X509 AUX reading pem file '" << pemfile << "': " << ERR_peek_error() << ", \"" << ERR_error_string(ERR_peek_error(), errbuf) << "\"";
+			<< "Error on bio X509 AUX reading pem file '" << pemfile << "': " << ERR_peek_error() << ", \"" << errbuf << "\"";
 		BOOST_THROW_EXCEPTION(openssl_error()
 			<< boost::errinfo_api_function("PEM_read_bio_X509_AUX")
 			<< errinfo_openssl_error(ERR_peek_error())
@@ -361,8 +384,9 @@ int MakeX509CSR(const String& cn, const String& keyfile, const String& csrfile,
 	BIGNUM *e = BN_new();
 
 	if (!rsa || !e) {
+		ERR_error_string_n(ERR_peek_error(), errbuf, sizeof errbuf);
 		Log(LogCritical, "SSL")
-			<< "Error while creating RSA key: " << ERR_peek_error() << ", \"" << ERR_error_string(ERR_peek_error(), errbuf) << "\"";
+			<< "Error while creating RSA key: " << ERR_peek_error() << ", \"" << errbuf << "\"";
 		BOOST_THROW_EXCEPTION(openssl_error()
 			<< boost::errinfo_api_function("RSA_generate_key")
 			<< errinfo_openssl_error(ERR_peek_error()));
@@ -371,8 +395,9 @@ int MakeX509CSR(const String& cn, const String& keyfile, const String& csrfile,
 	BN_set_word(e, RSA_F4);
 
 	if (!RSA_generate_key_ex(rsa, 4096, e, nullptr)) {
+		ERR_error_string_n(ERR_peek_error(), errbuf, sizeof errbuf);
 		Log(LogCritical, "SSL")
-			<< "Error while creating RSA key: " << ERR_peek_error() << ", \"" << ERR_error_string(ERR_peek_error(), errbuf) << "\"";
+			<< "Error while creating RSA key: " << ERR_peek_error() << ", \"" << errbuf << "\"";
 		BOOST_THROW_EXCEPTION(openssl_error()
 			<< boost::errinfo_api_function("RSA_generate_key")
 			<< errinfo_openssl_error(ERR_peek_error()));
@@ -386,8 +411,9 @@ int MakeX509CSR(const String& cn, const String& keyfile, const String& csrfile,
 	BIO *bio = BIO_new_file(const_cast<char *>(keyfile.CStr()), "w");
 
 	if (!bio) {
+		ERR_error_string_n(ERR_peek_error(), errbuf, sizeof errbuf);
 		Log(LogCritical, "SSL")
-			<< "Error while opening private RSA key file '" << keyfile << "': " << ERR_peek_error() << ", \"" << ERR_error_string(ERR_peek_error(), errbuf) << "\"";
+			<< "Error while opening private RSA key file '" << keyfile << "': " << ERR_peek_error() << ", \"" << errbuf << "\"";
 		BOOST_THROW_EXCEPTION(openssl_error()
 			<< boost::errinfo_api_function("BIO_new_file")
 			<< errinfo_openssl_error(ERR_peek_error())
@@ -395,8 +421,9 @@ int MakeX509CSR(const String& cn, const String& keyfile, const String& csrfile,
 	}
 
 	if (!PEM_write_bio_RSAPrivateKey(bio, rsa, nullptr, nullptr, 0, nullptr, nullptr)) {
+		ERR_error_string_n(ERR_peek_error(), errbuf, sizeof errbuf);
 		Log(LogCritical, "SSL")
-			<< "Error while writing private RSA key to file '" << keyfile << "': " << ERR_peek_error() << ", \"" << ERR_error_string(ERR_peek_error(), errbuf) << "\"";
+			<< "Error while writing private RSA key to file '" << keyfile << "': " << ERR_peek_error() << ", \"" << errbuf << "\"";
 		BOOST_THROW_EXCEPTION(openssl_error()
 			<< boost::errinfo_api_function("PEM_write_bio_RSAPrivateKey")
 			<< errinfo_openssl_error(ERR_peek_error())
@@ -426,8 +453,9 @@ int MakeX509CSR(const String& cn, const String& keyfile, const String& csrfile,
 		bio = BIO_new_file(const_cast<char *>(certfile.CStr()), "w");
 
 		if (!bio) {
+			ERR_error_string_n(ERR_peek_error(), errbuf, sizeof errbuf);
 			Log(LogCritical, "SSL")
-				<< "Error while opening certificate file '" << certfile << "': " << ERR_peek_error() << ", \"" << ERR_error_string(ERR_peek_error(), errbuf) << "\"";
+				<< "Error while opening certificate file '" << certfile << "': " << ERR_peek_error() << ", \"" << errbuf << "\"";
 			BOOST_THROW_EXCEPTION(openssl_error()
 				<< boost::errinfo_api_function("BIO_new_file")
 				<< errinfo_openssl_error(ERR_peek_error())
@@ -435,8 +463,9 @@ int MakeX509CSR(const String& cn, const String& keyfile, const String& csrfile,
 		}
 
 		if (!PEM_write_bio_X509(bio, cert.get())) {
+			ERR_error_string_n(ERR_peek_error(), errbuf, sizeof errbuf);
 			Log(LogCritical, "SSL")
-				<< "Error while writing certificate to file '" << certfile << "': " << ERR_peek_error() << ", \"" << ERR_error_string(ERR_peek_error(), errbuf) << "\"";
+				<< "Error while writing certificate to file '" << certfile << "': " << ERR_peek_error() << ", \"" << errbuf << "\"";
 			BOOST_THROW_EXCEPTION(openssl_error()
 				<< boost::errinfo_api_function("PEM_write_bio_X509")
 				<< errinfo_openssl_error(ERR_peek_error())
@@ -478,8 +507,9 @@ int MakeX509CSR(const String& cn, const String& keyfile, const String& csrfile,
 		bio = BIO_new_file(const_cast<char *>(csrfile.CStr()), "w");
 
 		if (!bio) {
+			ERR_error_string_n(ERR_peek_error(), errbuf, sizeof errbuf);
 			Log(LogCritical, "SSL")
-				<< "Error while opening CSR file '" << csrfile << "': " << ERR_peek_error() << ", \"" << ERR_error_string(ERR_peek_error(), errbuf) << "\"";
+				<< "Error while opening CSR file '" << csrfile << "': " << ERR_peek_error() << ", \"" << errbuf << "\"";
 			BOOST_THROW_EXCEPTION(openssl_error()
 				<< boost::errinfo_api_function("BIO_new_file")
 				<< errinfo_openssl_error(ERR_peek_error())
@@ -487,8 +517,9 @@ int MakeX509CSR(const String& cn, const String& keyfile, const String& csrfile,
 		}
 
 		if (!PEM_write_bio_X509_REQ(bio, req)) {
+			ERR_error_string_n(ERR_peek_error(), errbuf, sizeof errbuf);
 			Log(LogCritical, "SSL")
-				<< "Error while writing CSR to file '" << csrfile << "': " << ERR_peek_error() << ", \"" << ERR_error_string(ERR_peek_error(), errbuf) << "\"";
+				<< "Error while writing CSR to file '" << csrfile << "': " << ERR_peek_error() << ", \"" << errbuf << "\"";
 			BOOST_THROW_EXCEPTION(openssl_error()
 				<< boost::errinfo_api_function("PEM_write_bio_X509")
 				<< errinfo_openssl_error(ERR_peek_error())
@@ -518,29 +549,32 @@ std::shared_ptr<X509> CreateCert(EVP_PKEY *pubkey, X509_NAME *subject, X509_NAME
 
 	String id = Utility::NewUniqueID();
 
-	char errbuf[120];
+	char errbuf[256];
 	SHA_CTX context;
 	unsigned char digest[SHA_DIGEST_LENGTH];
 
 	if (!SHA1_Init(&context)) {
+		ERR_error_string_n(ERR_peek_error(), errbuf, sizeof errbuf);
 		Log(LogCritical, "SSL")
-			<< "Error on SHA1 Init: " << ERR_peek_error() << ", \"" << ERR_error_string(ERR_peek_error(), errbuf) << "\"";
+			<< "Error on SHA1 Init: " << ERR_peek_error() << ", \"" << errbuf << "\"";
 		BOOST_THROW_EXCEPTION(openssl_error()
 			<< boost::errinfo_api_function("SHA1_Init")
 			<< errinfo_openssl_error(ERR_peek_error()));
 	}
 
 	if (!SHA1_Update(&context, (unsigned char*)id.CStr(), id.GetLength())) {
+		ERR_error_string_n(ERR_peek_error(), errbuf, sizeof errbuf);
 		Log(LogCritical, "SSL")
-			<< "Error on SHA1 Update: " << ERR_peek_error() << ", \"" << ERR_error_string(ERR_peek_error(), errbuf) << "\"";
+			<< "Error on SHA1 Update: " << ERR_peek_error() << ", \"" << errbuf << "\"";
 		BOOST_THROW_EXCEPTION(openssl_error()
 			<< boost::errinfo_api_function("SHA1_Update")
 			<< errinfo_openssl_error(ERR_peek_error()));
 	}
 
 	if (!SHA1_Final(digest, &context)) {
+		ERR_error_string_n(ERR_peek_error(), errbuf, sizeof errbuf);
 		Log(LogCritical, "SSL")
-			<< "Error on SHA1 Final: " << ERR_peek_error() << ", \"" << ERR_error_string(ERR_peek_error(), errbuf) << "\"";
+			<< "Error on SHA1 Final: " << ERR_peek_error() << ", \"" << errbuf << "\"";
 		BOOST_THROW_EXCEPTION(openssl_error()
 			<< boost::errinfo_api_function("SHA1_Final")
 			<< errinfo_openssl_error(ERR_peek_error()));
@@ -592,7 +626,7 @@ String GetIcingaCADir()
 
 std::shared_ptr<X509> CreateCertIcingaCA(EVP_PKEY *pubkey, X509_NAME *subject)
 {
-	char errbuf[120];
+	char errbuf[256];
 
 	String cadir = GetIcingaCADir();
 
@@ -603,16 +637,18 @@ std::shared_ptr<X509> CreateCertIcingaCA(EVP_PKEY *pubkey, X509_NAME *subject)
 	BIO *cakeybio = BIO_new_file(const_cast<char *>(cakeyfile.CStr()), "r");
 
 	if (!cakeybio) {
+		ERR_error_string_n(ERR_peek_error(), errbuf, sizeof errbuf);
 		Log(LogCritical, "SSL")
-			<< "Could not open CA key file '" << cakeyfile << "': " << ERR_peek_error() << ", \"" << ERR_error_string(ERR_peek_error(), errbuf) << "\"";
+			<< "Could not open CA key file '" << cakeyfile << "': " << ERR_peek_error() << ", \"" << errbuf << "\"";
 		return std::shared_ptr<X509>();
 	}
 
 	rsa = PEM_read_bio_RSAPrivateKey(cakeybio, nullptr, nullptr, nullptr);
 
 	if (!rsa) {
+		ERR_error_string_n(ERR_peek_error(), errbuf, sizeof errbuf);
 		Log(LogCritical, "SSL")
-			<< "Could not read RSA key from CA key file '" << cakeyfile << "': " << ERR_peek_error() << ", \"" << ERR_error_string(ERR_peek_error(), errbuf) << "\"";
+			<< "Could not read RSA key from CA key file '" << cakeyfile << "': " << ERR_peek_error() << ", \"" << errbuf << "\"";
 		return std::shared_ptr<X509>();
 	}
 
@@ -692,29 +728,32 @@ String PBKDF2_SHA256(const String& password, const String& salt, int iterations)
 
 String SHA1(const String& s, bool binary)
 {
-	char errbuf[120];
+	char errbuf[256];
 	SHA_CTX context;
 	unsigned char digest[SHA_DIGEST_LENGTH];
 
 	if (!SHA1_Init(&context)) {
+		ERR_error_string_n(ERR_peek_error(), errbuf, sizeof errbuf);
 		Log(LogCritical, "SSL")
-			<< "Error on SHA Init: " << ERR_peek_error() << ", \"" << ERR_error_string(ERR_peek_error(), errbuf) << "\"";
+			<< "Error on SHA Init: " << ERR_peek_error() << ", \"" << errbuf << "\"";
 		BOOST_THROW_EXCEPTION(openssl_error()
 			<< boost::errinfo_api_function("SHA1_Init")
 			<< errinfo_openssl_error(ERR_peek_error()));
 	}
 
 	if (!SHA1_Update(&context, (unsigned char*)s.CStr(), s.GetLength())) {
+		ERR_error_string_n(ERR_peek_error(), errbuf, sizeof errbuf);
 		Log(LogCritical, "SSL")
-			<< "Error on SHA Update: " << ERR_peek_error() << ", \"" << ERR_error_string(ERR_peek_error(), errbuf) << "\"";
+			<< "Error on SHA Update: " << ERR_peek_error() << ", \"" << errbuf << "\"";
 		BOOST_THROW_EXCEPTION(openssl_error()
 			<< boost::errinfo_api_function("SHA1_Update")
 			<< errinfo_openssl_error(ERR_peek_error()));
 	}
 
 	if (!SHA1_Final(digest, &context)) {
+		ERR_error_string_n(ERR_peek_error(), errbuf, sizeof errbuf);
 		Log(LogCritical, "SSL")
-			<< "Error on SHA Final: " << ERR_peek_error() << ", \"" << ERR_error_string(ERR_peek_error(), errbuf) << "\"";
+			<< "Error on SHA Final: " << ERR_peek_error() << ", \"" << errbuf << "\"";
 		BOOST_THROW_EXCEPTION(openssl_error()
 			<< boost::errinfo_api_function("SHA1_Final")
 			<< errinfo_openssl_error(ERR_peek_error()));
@@ -732,29 +771,32 @@ String SHA1(const String& s, bool binary)
 
 String SHA256(const String& s)
 {
-	char errbuf[120];
+	char errbuf[256];
 	SHA256_CTX context;
 	unsigned char digest[SHA256_DIGEST_LENGTH];
 
 	if (!SHA256_Init(&context)) {
+		ERR_error_string_n(ERR_peek_error(), errbuf, sizeof errbuf);
 		Log(LogCritical, "SSL")
-			<< "Error on SHA256 Init: " << ERR_peek_error() << ", \"" << ERR_error_string(ERR_peek_error(), errbuf) << "\"";
+			<< "Error on SHA256 Init: " << ERR_peek_error() << ", \"" << errbuf << "\"";
 		BOOST_THROW_EXCEPTION(openssl_error()
 			<< boost::errinfo_api_function("SHA256_Init")
 			<< errinfo_openssl_error(ERR_peek_error()));
 	}
 
 	if (!SHA256_Update(&context, (unsigned char*)s.CStr(), s.GetLength())) {
+		ERR_error_string_n(ERR_peek_error(), errbuf, sizeof errbuf);
 		Log(LogCritical, "SSL")
-			<< "Error on SHA256 Update: " << ERR_peek_error() << ", \"" << ERR_error_string(ERR_peek_error(), errbuf) << "\"";
+			<< "Error on SHA256 Update: " << ERR_peek_error() << ", \"" << errbuf << "\"";
 		BOOST_THROW_EXCEPTION(openssl_error()
 			<< boost::errinfo_api_function("SHA256_Update")
 			<< errinfo_openssl_error(ERR_peek_error()));
 	}
 
 	if (!SHA256_Final(digest, &context)) {
+		ERR_error_string_n(ERR_peek_error(), errbuf, sizeof errbuf);
 		Log(LogCritical, "SSL")
-			<< "Error on SHA256 Final: " << ERR_peek_error() << ", \"" << ERR_error_string(ERR_peek_error(), errbuf) << "\"";
+			<< "Error on SHA256 Final: " << ERR_peek_error() << ", \"" << errbuf << "\"";
 		BOOST_THROW_EXCEPTION(openssl_error()
 			<< boost::errinfo_api_function("SHA256_Final")
 			<< errinfo_openssl_error(ERR_peek_error()));
@@ -779,10 +821,11 @@ String RandomString(int length)
 	if (!RAND_bytes(bytes, length)) {
 		delete [] bytes;
 
-		char errbuf[120];
+		char errbuf[256];
+		ERR_error_string_n(ERR_peek_error(), errbuf, sizeof errbuf);
 
 		Log(LogCritical, "SSL")
-			<< "Error for RAND_bytes: " << ERR_peek_error() << ", \"" << ERR_error_string(ERR_peek_error(), errbuf) << "\"";
+			<< "Error for RAND_bytes: " << ERR_peek_error() << ", \"" << errbuf << "\"";
 		BOOST_THROW_EXCEPTION(openssl_error()
 			<< boost::errinfo_api_function("RAND_bytes")
 			<< errinfo_openssl_error(ERR_peek_error()));
@@ -801,7 +844,7 @@ String RandomString(int length)
 	return result;
 }
 
-bool VerifyCertificate(const std::shared_ptr<X509>& caCertificate, const std::shared_ptr<X509>& certificate)
+bool VerifyCertificate(const std::shared_ptr<X509> &caCertificate, const std::shared_ptr<X509> &certificate, const String& crlFile)
 {
 	X509_STORE *store = X509_STORE_new();
 
@@ -810,6 +853,10 @@ bool VerifyCertificate(const std::shared_ptr<X509>& caCertificate, const std::sh
 
 	X509_STORE_add_cert(store, caCertificate.get());
 
+	if (!crlFile.IsEmpty()) {
+		AddCRLToSSLContext(store, crlFile);
+	}
+
 	X509_STORE_CTX *csc = X509_STORE_CTX_new();
 	X509_STORE_CTX_init(csc, store, certificate.get(), nullptr);
 
@@ -899,19 +946,4 @@ Array::Ptr GetSubjectAltNames(const std::shared_ptr<X509>& cert)
 	return sans;
 }
 
-std::string to_string(const errinfo_openssl_error& e)
-{
-	std::ostringstream tmp;
-	int code = e.value();
-	char errbuf[120];
-
-	const char *message = ERR_error_string(code, errbuf);
-
-	if (!message)
-		message = "Unknown error.";
-
-	tmp << code << ", \"" << message << "\"";
-	return "[errinfo_openssl_error]" + tmp.str() + "\n";
-}
-
 }


=====================================
lib/base/tlsutility.hpp
=====================================
@@ -30,6 +30,7 @@ String GetOpenSSLVersion();
 
 Shared<boost::asio::ssl::context>::Ptr MakeAsioSslContext(const String& pubkey = String(), const String& privkey = String(), const String& cakey = String());
 void AddCRLToSSLContext(const Shared<boost::asio::ssl::context>::Ptr& context, const String& crlPath);
+void AddCRLToSSLContext(X509_STORE *x509_store, const String& crlPath);
 void SetCipherListToSSLContext(const Shared<boost::asio::ssl::context>::Ptr& context, const String& cipherList);
 void SetTlsProtocolminToSSLContext(const Shared<boost::asio::ssl::context>::Ptr& context, const String& tlsProtocolmin);
 
@@ -51,7 +52,7 @@ String SHA1(const String& s, bool binary = false);
 String SHA256(const String& s);
 String RandomString(int length);
 
-bool VerifyCertificate(const std::shared_ptr<X509>& caCertificate, const std::shared_ptr<X509>& certificate);
+bool VerifyCertificate(const std::shared_ptr<X509>& caCertificate, const std::shared_ptr<X509>& certificate, const String& crlFile);
 bool IsCa(const std::shared_ptr<X509>& cacert);
 int GetCertificateVersion(const std::shared_ptr<X509>& cert);
 String GetSignatureAlgorithm(const std::shared_ptr<X509>& cert);
@@ -62,8 +63,6 @@ class openssl_error : virtual public std::exception, virtual public boost::excep
 struct errinfo_openssl_error_;
 typedef boost::error_info<struct errinfo_openssl_error_, unsigned long> errinfo_openssl_error;
 
-std::string to_string(const errinfo_openssl_error& e);
-
 }
 
 #endif /* TLSUTILITY_H */


=====================================
lib/cli/pkiverifycommand.cpp
=====================================
@@ -28,12 +28,13 @@ void PKIVerifyCommand::InitParameters(boost::program_options::options_descriptio
 	visibleDesc.add_options()
 		("cn", po::value<std::string>(), "Common Name (optional). Use with '--cert' to check the CN in the certificate.")
 		("cert", po::value<std::string>(), "Certificate file path (optional). Standalone: print certificate. With '--cacert': Verify against CA.")
-		("cacert", po::value<std::string>(), "CA certificate file path (optional). If passed standalone, verifies whether this is a CA certificate");
+		("cacert", po::value<std::string>(), "CA certificate file path (optional). If passed standalone, verifies whether this is a CA certificate")
+		("crl", po::value<std::string>(), "CRL file path (optional). Check the certificate against this revocation list when verifying against CA.");
 }
 
 std::vector<String> PKIVerifyCommand::GetArgumentSuggestions(const String& argument, const String& word) const
 {
-	if (argument == "cert" || argument == "cacert")
+	if (argument == "cert" || argument == "cacert" || argument == "crl")
 		return GetBashCompletionSuggestions("file", word);
 	else
 		return CLICommand::GetArgumentSuggestions(argument, word);
@@ -46,7 +47,7 @@ std::vector<String> PKIVerifyCommand::GetArgumentSuggestions(const String& argum
  */
 int PKIVerifyCommand::Run(const boost::program_options::variables_map& vm, const std::vector<std::string>& ap) const
 {
-	String cn, certFile, caCertFile;
+	String cn, certFile, caCertFile, crlFile;
 
 	if (vm.count("cn"))
 		cn = vm["cn"].as<std::string>();
@@ -57,6 +58,9 @@ int PKIVerifyCommand::Run(const boost::program_options::variables_map& vm, const
 	if (vm.count("cacert"))
 		caCertFile = vm["cacert"].as<std::string>();
 
+	if (vm.count("crl"))
+		crlFile = vm["crl"].as<std::string>();
+
 	/* Verify CN in certificate. */
 	if (!cn.IsEmpty() && !certFile.IsEmpty()) {
 		std::shared_ptr<X509> cert;
@@ -126,10 +130,15 @@ int PKIVerifyCommand::Run(const boost::program_options::variables_map& vm, const
 		bool signedByCA;
 
 		try {
-			signedByCA = VerifyCertificate(cacert, cert);
+			signedByCA = VerifyCertificate(cacert, cert, crlFile);
 		} catch (const std::exception& ex) {
-			Log(LogCritical, "cli")
-				<< "CRITICAL: Certificate with CN '" << certCN << "' is NOT signed by CA: " << DiagnosticInformation(ex, false);
+			Log logmsg (LogCritical, "cli");
+			logmsg << "CRITICAL: Certificate with CN '" << certCN << "' is NOT signed by CA: ";
+			if (const unsigned long *openssl_code = boost::get_error_info<errinfo_openssl_error>(ex)) {
+				logmsg << X509_verify_cert_error_string(*openssl_code) << " (code " << *openssl_code << ")";
+			} else {
+				logmsg << DiagnosticInformation(ex, false);
+			}
 
 			return ServiceCritical;
 		}


=====================================
lib/icinga/checkable-check.cpp
=====================================
@@ -242,6 +242,20 @@ void Checkable::ProcessCheckResult(const CheckResult::Ptr& cr, const MessageOrig
 			OnReachabilityChanged(this, cr, children, origin);
 	}
 
+	if (recovery) {
+		for (auto& child : children) {
+			if (child->GetProblem() && child->GetEnableActiveChecks()) {
+				auto nextCheck (now + Utility::Random() % 60);
+
+				ObjectLock oLock (child);
+
+				if (nextCheck < child->GetNextCheck()) {
+					child->SetNextCheck(nextCheck);
+				}
+			}
+		}
+	}
+
 	if (!reachable)
 		SetLastStateUnreachable(Utility::GetTime());
 


=====================================
lib/icinga/checkable-notification.cpp
=====================================
@@ -10,6 +10,7 @@
 #include "base/exception.hpp"
 #include "base/context.hpp"
 #include "base/convert.hpp"
+#include "base/lazy-init.hpp"
 #include "remote/apilistener.hpp"
 
 using namespace icinga;
@@ -145,35 +146,58 @@ static void FireSuppressedNotifications(Checkable* checkable)
 
 	int subtract = 0;
 
-	for (auto type : {NotificationProblem, NotificationRecovery, NotificationFlappingStart, NotificationFlappingEnd}) {
-		if (suppressed_types & type) {
-			bool still_applies = checkable->NotificationReasonApplies(type);
-
-			if (still_applies) {
-				bool still_suppressed;
-
-				switch (type) {
-					case NotificationProblem:
-						/* Fall through. */
-					case NotificationRecovery:
-						still_suppressed = !checkable->IsReachable(DependencyNotification) || checkable->IsInDowntime() || checkable->IsAcknowledged();
-						break;
-					case NotificationFlappingStart:
-						/* Fall through. */
-					case NotificationFlappingEnd:
-						still_suppressed = checkable->IsInDowntime();
-						break;
-					default:
-						break;
+	{
+		LazyInit<bool> wasLastParentRecoveryRecent ([&checkable]() {
+			auto cr (checkable->GetLastCheckResult());
+
+			if (!cr) {
+				return true;
+			}
+
+			auto threshold (cr->GetExecutionStart());
+
+			for (auto& dep : checkable->GetDependencies()) {
+				auto parent (dep->GetParent());
+				ObjectLock oLock (parent);
+
+				if (!parent->GetProblem() && parent->GetLastStateChange() >= threshold) {
+					return true;
 				}
+			}
 
-				if (!still_suppressed && !checkable->IsLikelyToBeCheckedSoon()) {
-					Checkable::OnNotificationsRequested(checkable, type, checkable->GetLastCheckResult(), "", "", nullptr);
+			return false;
+		});
+
+		for (auto type : {NotificationProblem, NotificationRecovery, NotificationFlappingStart, NotificationFlappingEnd}) {
+			if (suppressed_types & type) {
+				bool still_applies = checkable->NotificationReasonApplies(type);
+
+				if (still_applies) {
+					bool still_suppressed;
+
+					switch (type) {
+						case NotificationProblem:
+							/* Fall through. */
+						case NotificationRecovery:
+							still_suppressed = !checkable->IsReachable(DependencyNotification) || checkable->IsInDowntime() || checkable->IsAcknowledged();
+							break;
+						case NotificationFlappingStart:
+							/* Fall through. */
+						case NotificationFlappingEnd:
+							still_suppressed = checkable->IsInDowntime();
+							break;
+						default:
+							break;
+					}
+
+					if (!still_suppressed && !checkable->IsLikelyToBeCheckedSoon() && !wasLastParentRecoveryRecent.Get()) {
+						Checkable::OnNotificationsRequested(checkable, type, checkable->GetLastCheckResult(), "", "", nullptr);
 
+						subtract |= type;
+					}
+				} else {
 					subtract |= type;
 				}
-			} else {
-				subtract |= type;
 			}
 		}
 	}


=====================================
lib/remote/apilistener-configsync.cpp
=====================================
@@ -74,7 +74,7 @@ Value ApiListener::ConfigUpdateObjectAPIHandler(const MessageOrigin::Ptr& origin
 
 	String objZone = params->Get("zone");
 
-	if (!Zone::GetByName(objZone)) {
+	if (!objZone.IsEmpty() && !Zone::GetByName(objZone)) {
 		Log(LogNotice, "ApiListener")
 			<< "Discarding 'config update object' message"
 			<< " from '" << identity << "' (endpoint: '" << endpoint->GetName() << "', zone: '" << endpointZone->GetName() << "')"
@@ -325,7 +325,11 @@ void ApiListener::UpdateConfigObject(const ConfigObject::Ptr& object, const Mess
 	params->Set("name", object->GetName());
 	params->Set("type", object->GetReflectionType()->GetName());
 	params->Set("version", object->GetVersion());
-	params->Set("zone", object->GetZoneName());
+
+	String zoneName = object->GetZoneName();
+
+	if (!zoneName.IsEmpty())
+		params->Set("zone", zoneName);
 
 	if (object->GetPackage() == "_api") {
 		String file;


=====================================
lib/remote/apilistener-filesync.cpp
=====================================
@@ -20,7 +20,7 @@ using namespace icinga;
 
 REGISTER_APIFUNCTION(Update, config, &ApiListener::ConfigUpdateHandler);
 
-SpinLock ApiListener::m_ConfigSyncStageLock;
+std::mutex ApiListener::m_ConfigSyncStageLock;
 
 /**
  * Entrypoint for updating all authoritative configs from /etc/zones.d, packages, etc.
@@ -330,7 +330,7 @@ void ApiListener::HandleConfigUpdate(const MessageOrigin::Ptr& origin, const Dic
 	/* Only one transaction is allowed, concurrent message handlers need to wait.
 	 * This affects two parent endpoints sending the config in the same moment.
 	 */
-	auto lock (Shared<std::unique_lock<SpinLock>>::Make(m_ConfigSyncStageLock));
+	std::lock_guard<std::mutex> lock(m_ConfigSyncStageLock);
 
 	String apiZonesStageDir = GetApiZonesStageDir();
 	String fromEndpointName = origin->FromClient->GetEndpoint()->GetName();
@@ -544,7 +544,7 @@ void ApiListener::HandleConfigUpdate(const MessageOrigin::Ptr& origin, const Dic
 		Log(LogInformation, "ApiListener")
 			<< "Received configuration updates (" << count << ") from endpoint '" << fromEndpointName
 			<< "' are different to production, triggering validation and reload.";
-		AsyncTryActivateZonesStage(relativePaths, lock);
+		TryActivateZonesStage(relativePaths);
 	} else {
 		Log(LogInformation, "ApiListener")
 			<< "Received configuration updates (" << count << ") from endpoint '" << fromEndpointName
@@ -554,17 +554,44 @@ void ApiListener::HandleConfigUpdate(const MessageOrigin::Ptr& origin, const Dic
 }
 
 /**
- * Callback for stage config validation.
- * When validation was successful, the configuration is copied from
- * stage to production and a restart is triggered.
- * On failure, there's no restart and this is logged.
+ * Spawns a new validation process with 'System.ZonesStageVarDir' set to override the config validation zone dirs with
+ * our current stage. Then waits for the validation result and if it was successful, the configuration is copied from
+ * stage to production and a restart is triggered. On validation failure, there is no restart and this is logged.
+ *
+ * The caller of this function must hold m_ConfigSyncStageLock.
  *
- * @param pr Result of the validation process.
  * @param relativePaths Collected paths including the zone name, which are copied from stage to current directories.
  */
-void ApiListener::TryActivateZonesStageCallback(const ProcessResult& pr,
-	const std::vector<String>& relativePaths)
+void ApiListener::TryActivateZonesStage(const std::vector<String>& relativePaths)
 {
+	VERIFY(Application::GetArgC() >= 1);
+
+	/* Inherit parent process args. */
+	Array::Ptr args = new Array({
+		Application::GetExePath(Application::GetArgV()[0]),
+	});
+
+	for (int i = 1; i < Application::GetArgC(); i++) {
+		String argV = Application::GetArgV()[i];
+
+		if (argV == "-d" || argV == "--daemonize")
+			continue;
+
+		args->Add(argV);
+	}
+
+	args->Add("--validate");
+
+	// Set the ZonesStageDir. This creates our own local chroot without any additional automated zone includes.
+	args->Add("--define");
+	args->Add("System.ZonesStageVarDir=" + GetApiZonesStageDir());
+
+	Process::Ptr process = new Process(Process::PrepareCommand(args));
+	process->SetTimeout(Application::GetReloadTimeout());
+
+	process->Run();
+	const ProcessResult& pr = process->WaitForResult();
+
 	String apiZonesDir = GetApiZonesDir();
 	String apiZonesStageDir = GetApiZonesStageDir();
 
@@ -628,44 +655,6 @@ void ApiListener::TryActivateZonesStageCallback(const ProcessResult& pr,
 		listener->UpdateLastFailedZonesStageValidation(pr.Output);
 }
 
-/**
- * Spawns a new validation process and waits for its output.
- * Sets 'System.ZonesStageVarDir' to override the config validation zone dirs with our current stage.
- *
- * @param relativePaths Required for later file operations in the callback. Provides the zone name plus path in a list.
- */
-void ApiListener::AsyncTryActivateZonesStage(const std::vector<String>& relativePaths, const Shared<std::unique_lock<SpinLock>>::Ptr& lock)
-{
-	VERIFY(Application::GetArgC() >= 1);
-
-	/* Inherit parent process args. */
-	Array::Ptr args = new Array({
-		Application::GetExePath(Application::GetArgV()[0]),
-	});
-
-	for (int i = 1; i < Application::GetArgC(); i++) {
-		String argV = Application::GetArgV()[i];
-
-		if (argV == "-d" || argV == "--daemonize")
-			continue;
-
-		args->Add(argV);
-	}
-
-	args->Add("--validate");
-
-	// Set the ZonesStageDir. This creates our own local chroot without any additional automated zone includes.
-	args->Add("--define");
-	args->Add("System.ZonesStageVarDir=" + GetApiZonesStageDir());
-
-	Process::Ptr process = new Process(Process::PrepareCommand(args));
-	process->SetTimeout(Application::GetReloadTimeout());
-
-	process->Run([relativePaths, lock](const ProcessResult& pr) {
-		TryActivateZonesStageCallback(pr, relativePaths);
-	});
-}
-
 /**
  * Update the structure from the last failed validation output.
  * Uses the current timestamp.


=====================================
lib/remote/apilistener.hpp
=====================================
@@ -11,7 +11,6 @@
 #include "base/configobject.hpp"
 #include "base/process.hpp"
 #include "base/shared.hpp"
-#include "base/spinlock.hpp"
 #include "base/timer.hpp"
 #include "base/workqueue.hpp"
 #include "base/tcpsocket.hpp"
@@ -188,7 +187,7 @@ private:
 	void RemoveStatusFile();
 
 	/* filesync */
-	static SpinLock m_ConfigSyncStageLock;
+	static std::mutex m_ConfigSyncStageLock;
 
 	void SyncLocalZoneDirs() const;
 	void SyncLocalZoneDir(const Zone::Ptr& zone) const;
@@ -200,9 +199,7 @@ private:
 	static ConfigDirInformation LoadConfigDir(const String& dir);
 	static void ConfigGlobHandler(ConfigDirInformation& config, const String& path, const String& file);
 
-	static void TryActivateZonesStageCallback(const ProcessResult& pr,
-		const std::vector<String>& relativePaths);
-	static void AsyncTryActivateZonesStage(const std::vector<String>& relativePaths, const Shared<std::unique_lock<SpinLock>>::Ptr& lock);
+	static void TryActivateZonesStage(const std::vector<String>& relativePaths);
 
 	static String GetChecksum(const String& content);
 	static bool CheckConfigChange(const ConfigDirInformation& oldConfig, const ConfigDirInformation& newConfig);


=====================================
lib/remote/jsonrpcconnection-pki.cpp
=====================================
@@ -55,13 +55,25 @@ Value RequestCertificateHandler(const MessageOrigin::Ptr& origin, const Dictiona
 
 	bool signedByCA = false;
 
-	try {
-		signedByCA = VerifyCertificate(cacert, cert);
-	} catch (const std::exception&) { } /* Swallow the exception on purpose, cacert will never be a non-CA certificate. */
-
-	Log(LogInformation, "JsonRpcConnection")
-		<< "Received certificate request for CN '" << cn << "'"
-		<< (signedByCA ? "" : " not") << " signed by our CA.";
+	{
+		Log logmsg(LogInformation, "JsonRpcConnection");
+		logmsg << "Received certificate request for CN '" << cn << "'";
+
+		try {
+			signedByCA = VerifyCertificate(cacert, cert, listener->GetCrlPath());
+			if (!signedByCA) {
+				logmsg << " not";
+			}
+			logmsg << " signed by our CA.";
+		} catch (const std::exception &ex) {
+			logmsg << " not signed by our CA";
+			if (const unsigned long *openssl_code = boost::get_error_info<errinfo_openssl_error>(ex)) {
+				logmsg << ": " << X509_verify_cert_error_string(long(*openssl_code)) << " (code " << *openssl_code << ")";
+			} else {
+				logmsg << ".";
+			}
+		}
+	}
 
 	if (signedByCA) {
 		time_t now;
@@ -204,7 +216,7 @@ Value RequestCertificateHandler(const MessageOrigin::Ptr& origin, const Dictiona
 	 * we're using for cluster connections (there's no point in sending a client
 	 * a certificate it wouldn't be able to use to connect to us anyway) */
 	try {
-		if (!VerifyCertificate(cacert, newcert)) {
+		if (!VerifyCertificate(cacert, newcert, listener->GetCrlPath())) {
 			Log(LogWarning, "JsonRpcConnection")
 				<< "The CA in '" << listener->GetDefaultCaPath() << "' does not match the CA which Icinga uses "
 				<< "for its own cluster connections. This is most likely a configuration problem.";


=====================================
lib/remote/pkiutility.cpp
=====================================
@@ -63,8 +63,9 @@ int PkiUtility::SignCsr(const String& csrfile, const String& certfile)
 	X509_REQ *req = PEM_read_bio_X509_REQ(csrbio, nullptr, nullptr, nullptr);
 
 	if (!req) {
+		ERR_error_string_n(ERR_peek_error(), errbuf, sizeof errbuf);
 		Log(LogCritical, "SSL")
-			<< "Could not read X509 certificate request from '" << csrfile << "': " << ERR_peek_error() << ", \"" << ERR_error_string(ERR_peek_error(), errbuf) << "\"";
+			<< "Could not read X509 certificate request from '" << csrfile << "': " << ERR_peek_error() << ", \"" << errbuf << "\"";
 		return 1;
 	}
 



View it on GitLab: https://salsa.debian.org/nagios-team/pkg-icinga2/-/commit/1684f2b546f66edcc5652e0f79bcc3e5d63f34eb

-- 
View it on GitLab: https://salsa.debian.org/nagios-team/pkg-icinga2/-/commit/1684f2b546f66edcc5652e0f79bcc3e5d63f34eb
You're receiving this email because of your account on salsa.debian.org.


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://alioth-lists.debian.net/pipermail/pkg-nagios-changes/attachments/20201216/29c1703f/attachment-0001.html>


More information about the pkg-nagios-changes mailing list