From ced2573b3ec8513a2fe5eac96bf8cf1c6ef18a09 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Wed, 3 Jul 2024 11:58:38 +0200 Subject: [PATCH] GetCertificateCN(): if the CN is missing, fall back to the DNS SAN if exactly one is given and use it as CN. Currently Icinga 2 populates both the DNS SAN and the subject CN of a newly issued certificate with the endpoint name. But the CN is limited to 64 characters. So endpoint names are. The only clean solution is omitting the CN for too long names. Icinga 2 must be able to extract the endpoint name from such certificates in the first place and exactly one DNS SAN is a legit source. --- lib/base/tlsutility.cpp | 49 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 44 insertions(+), 5 deletions(-) diff --git a/lib/base/tlsutility.cpp b/lib/base/tlsutility.cpp index fb60e0221..b92bea14b 100644 --- a/lib/base/tlsutility.cpp +++ b/lib/base/tlsutility.cpp @@ -9,6 +9,7 @@ #include "base/application.hpp" #include "base/exception.hpp" #include +#include #include #include #include @@ -401,7 +402,7 @@ void AddCRLToSSLContext(X509_STORE *x509_store, const String& crlPath) X509_VERIFY_PARAM_free(param); } -static String GetX509NameCN(X509_NAME *name) +static String GetX509NameCN(X509_NAME* name, X509* fallback = nullptr) { char errbuf[256]; char buffer[256]; @@ -409,12 +410,50 @@ 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); + auto err (ERR_peek_error()); + int totalSans = 0; + int dnsSans = 0; + + // Subject CN missing, fall back to the DNS SAN if exactly one given + if (fallback) { + std::unique_ptr sans ( + (GENERAL_NAMES*)X509_get_ext_d2i(fallback, NID_subject_alt_name, nullptr, nullptr), + &GENERAL_NAMES_free + ); + + if (sans) { + const unsigned char* dnsSan = nullptr; + totalSans = sk_GENERAL_NAME_num(sans.get()); + + for (int i = 0; i < totalSans; ++i) { + auto gn (sk_GENERAL_NAME_value(sans.get(), i)); + + if (gn->type == GEN_DNS) { + auto asn1Str (gn->d.uniformResourceIdentifier); + +#if OPENSSL_VERSION_NUMBER < 0x10100000L + dnsSan = ASN1_STRING_data(asn1Str); +#else /* OPENSSL_VERSION_NUMBER < 0x10100000L */ + dnsSan = ASN1_STRING_get0_data(asn1Str); +#endif /* OPENSSL_VERSION_NUMBER < 0x10100000L */ + + ++dnsSans; + } + } + + if (dnsSans == 1) { + return Convert::ToString(dnsSan); + } + } + } + + ERR_error_string_n(err, errbuf, sizeof errbuf); Log(LogCritical, "SSL") - << "Error with x509 NAME getting text by NID: " << ERR_peek_error() << ", \"" << errbuf << "\""; + << "Error with x509 NAME getting text by NID: " << err << ", \"" << errbuf + << "\" (Also found " << dnsSans << " DNS SANs and " << totalSans - dnsSans << " others)"; BOOST_THROW_EXCEPTION(openssl_error() << boost::errinfo_api_function("X509_NAME_get_text_by_NID") - << errinfo_openssl_error(ERR_peek_error())); + << errinfo_openssl_error(err)); } return buffer; @@ -428,7 +467,7 @@ static String GetX509NameCN(X509_NAME *name) */ String GetCertificateCN(const std::shared_ptr& certificate) { - return GetX509NameCN(X509_get_subject_name(certificate.get())); + return GetX509NameCN(X509_get_subject_name(certificate.get()), certificate.get()); } /**