mirror of
https://github.com/Icinga/icinga2.git
synced 2025-04-08 17:05:25 +02:00
Merge pull request from GHSA-pcmr-2p2f-r7j6
Verify certificates against CRL before renewing them (2.11)
This commit is contained in:
commit
1cf1e3f6ab
18
CHANGELOG.md
18
CHANGELOG.md
@ -9,9 +9,25 @@ Released closed milestones can be found on [GitHub](https://github.com/Icinga/ic
|
||||
|
||||
## 2.11.8 (2020-12-15)
|
||||
|
||||
Version 2.11.8 mainly focuses on resolving issues with high load on Windows regarding the config sync
|
||||
Version 2.11.8 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 #8510
|
||||
|
@ -236,15 +236,26 @@ void SetTlsProtocolminToSSLContext(const std::shared_ptr<boost::asio::ssl::conte
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 std::shared_ptr<boost::asio::ssl::context>& 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());
|
||||
@ -821,7 +832,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();
|
||||
|
||||
@ -830,6 +841,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);
|
||||
|
||||
|
@ -22,10 +22,13 @@ namespace icinga
|
||||
|
||||
void InitializeOpenSSL();
|
||||
|
||||
std::shared_ptr<boost::asio::ssl::context> MakeAsioSslContext(const String& pubkey = String(), const String& privkey = String(), const String& cakey = String());
|
||||
void AddCRLToSSLContext(const std::shared_ptr<boost::asio::ssl::context>& context, const String& crlPath);
|
||||
void SetCipherListToSSLContext(const std::shared_ptr<boost::asio::ssl::context>& context, const String& cipherList);
|
||||
void SetTlsProtocolminToSSLContext(const std::shared_ptr<boost::asio::ssl::context>& context, const String& tlsProtocolmin);
|
||||
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);
|
||||
|
||||
String GetCertificateCN(const std::shared_ptr<X509>& certificate);
|
||||
std::shared_ptr<X509> GetX509Certificate(const String& pemfile);
|
||||
@ -45,7 +48,11 @@ 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);
|
||||
Array::Ptr GetSubjectAltNames(const std::shared_ptr<X509>& cert);
|
||||
|
||||
class openssl_error : virtual public std::exception, virtual public boost::exception { };
|
||||
|
||||
|
@ -53,11 +53,27 @@ Value RequestCertificateHandler(const MessageOrigin::Ptr& origin, const Dictiona
|
||||
|
||||
String cn = GetCertificateCN(cert);
|
||||
|
||||
bool signedByCA = VerifyCertificate(cacert, cert);
|
||||
bool signedByCA = false;
|
||||
|
||||
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;
|
||||
@ -199,12 +215,14 @@ Value RequestCertificateHandler(const MessageOrigin::Ptr& origin, const Dictiona
|
||||
* this ensures that the CA we have in /var/lib/icinga2/ca matches the one
|
||||
* 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) */
|
||||
if (!VerifyCertificate(cacert, newcert)) {
|
||||
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.";
|
||||
goto delayed_request;
|
||||
}
|
||||
try {
|
||||
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.";
|
||||
goto delayed_request;
|
||||
}
|
||||
} catch (const std::exception&) { } /* Swallow the exception on purpose, cacert will never be a non-CA certificate. */
|
||||
|
||||
/* Send the signed certificate update. */
|
||||
Log(LogInformation, "JsonRpcConnection")
|
||||
|
Loading…
x
Reference in New Issue
Block a user