mirror of
https://github.com/Icinga/icinga2.git
synced 2025-07-24 14:14:45 +02:00
parent
774936bfe8
commit
6a533796e5
@ -21,8 +21,10 @@
|
||||
#include "base/logger.hpp"
|
||||
#include "base/application.hpp"
|
||||
#include "base/tlsutility.hpp"
|
||||
#include "base/json.hpp"
|
||||
|
||||
using namespace icinga;
|
||||
namespace po = boost::program_options;
|
||||
|
||||
REGISTER_CLICOMMAND("ca/list", CAListCommand);
|
||||
|
||||
@ -36,34 +38,57 @@ String CAListCommand::GetShortDescription(void) const
|
||||
return "lists all certificate signing requests";
|
||||
}
|
||||
|
||||
void CAListCommand::PrintRequest(const String& requestFile)
|
||||
void CAListCommand::InitParameters(boost::program_options::options_description& visibleDesc,
|
||||
boost::program_options::options_description& hiddenDesc) const
|
||||
{
|
||||
visibleDesc.add_options()
|
||||
("json", "encode output as JSON")
|
||||
;
|
||||
}
|
||||
static void CollectRequestHandler(const Dictionary::Ptr& requests, const String& requestFile)
|
||||
{
|
||||
Dictionary::Ptr request = Utility::LoadJsonFile(requestFile);
|
||||
|
||||
if (!request)
|
||||
return;
|
||||
|
||||
Dictionary::Ptr result = new Dictionary();
|
||||
|
||||
String fingerprint = Utility::BaseName(requestFile);
|
||||
fingerprint = fingerprint.SubStr(0, fingerprint.GetLength() - 5);
|
||||
|
||||
std::cout << "***\n";
|
||||
std::cout << "Fingerprint: " << fingerprint << "\n";
|
||||
|
||||
String certRequestText = request->Get("cert_request");
|
||||
result->Set("cert_request", certRequestText);
|
||||
|
||||
Value vcertResponseText;
|
||||
|
||||
if (request->Get("cert_response", &vcertResponseText)) {
|
||||
String certResponseText = vcertResponseText;
|
||||
result->Set("cert_response", certResponseText);
|
||||
}
|
||||
|
||||
boost::shared_ptr<X509> certRequest = StringToCertificate(certRequestText);
|
||||
|
||||
String cn = GetCertificateCN(certRequest);
|
||||
time_t now;
|
||||
time(&now);
|
||||
ASN1_TIME *tm = ASN1_TIME_adj(NULL, now, 0, 0);
|
||||
|
||||
String certResponseText = request->Get("cert_response");
|
||||
int day, sec;
|
||||
ASN1_TIME_diff(&day, &sec, tm, X509_get_notBefore(certRequest.get()));
|
||||
|
||||
if (!certResponseText.IsEmpty()) {
|
||||
boost::shared_ptr<X509> certResponse = StringToCertificate(certResponseText);
|
||||
}
|
||||
result->Set("timestamp", static_cast<double>(now) + day * 24 * 60 * 60 + sec);
|
||||
|
||||
std::cout << "CN: " << cn << "\n";
|
||||
std::cout << "Certificate (request): " << certRequestText << "\n";
|
||||
std::cout << "Certificate (response): " << certResponseText << "\n";
|
||||
BIO *out = BIO_new(BIO_s_mem());
|
||||
X509_NAME_print_ex(out, X509_get_subject_name(certRequest.get()), 0, XN_FLAG_ONELINE & ~ASN1_STRFLGS_ESC_MSB);
|
||||
|
||||
char *data;
|
||||
long length;
|
||||
length = BIO_get_mem_data(out, &data);
|
||||
|
||||
result->Set("subject", String(data, data + length));
|
||||
BIO_free(out);
|
||||
|
||||
requests->Set(fingerprint, result);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -73,7 +98,34 @@ void CAListCommand::PrintRequest(const String& requestFile)
|
||||
*/
|
||||
int CAListCommand::Run(const boost::program_options::variables_map& vm, const std::vector<std::string>& ap) const
|
||||
{
|
||||
Utility::Glob(Application::GetLocalStateDir() + "/lib/icinga2/pki-requests/*.json", &CAListCommand::PrintRequest, GlobFile);
|
||||
Dictionary::Ptr requests = new Dictionary();
|
||||
|
||||
String requestDir = Application::GetLocalStateDir() + "/lib/icinga2/pki-requests";
|
||||
|
||||
if (Utility::PathExists(requestDir))
|
||||
Utility::Glob(requestDir + "/*.json", boost::bind(&CollectRequestHandler, requests, _1), GlobFile);
|
||||
|
||||
if (vm.count("json"))
|
||||
std::cout << JsonEncode(requests);
|
||||
else {
|
||||
ObjectLock olock(requests);
|
||||
|
||||
std::cout << "Fingerprint | Timestamp | Signed | Subject\n";
|
||||
std::cout << "-----------------------------------------------------------------|---------------------|--------|--------\n";
|
||||
|
||||
for (auto& kv : requests) {
|
||||
Dictionary::Ptr request = kv.second;
|
||||
|
||||
std::cout << kv.first
|
||||
<< " | "
|
||||
<< Utility::FormatDateTime("%Y/%m/%d %H:%M:%S", request->Get("timestamp"))
|
||||
<< " | "
|
||||
<< (request->Contains("cert_response") ? "*" : " ") << " "
|
||||
<< " | "
|
||||
<< request->Get("subject")
|
||||
<< "\n";
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -37,6 +37,8 @@ public:
|
||||
|
||||
virtual String GetDescription(void) const override;
|
||||
virtual String GetShortDescription(void) const override;
|
||||
virtual void InitParameters(boost::program_options::options_description& visibleDesc,
|
||||
boost::program_options::options_description& hiddenDesc) const override;
|
||||
virtual int Run(const boost::program_options::variables_map& vm, const std::vector<std::string>& ap) const override;
|
||||
|
||||
private:
|
||||
|
@ -55,6 +55,12 @@ int CASignCommand::Run(const boost::program_options::variables_map& vm, const st
|
||||
{
|
||||
String requestFile = Application::GetLocalStateDir() + "/lib/icinga2/pki-requests/" + ap[0] + ".json";
|
||||
|
||||
if (!Utility::PathExists(requestFile)) {
|
||||
Log(LogCritical, "cli")
|
||||
<< "No request exists for fingerprint '" << ap[0] << "'.";
|
||||
return 1;
|
||||
}
|
||||
|
||||
Dictionary::Ptr request = Utility::LoadJsonFile(requestFile);
|
||||
|
||||
if (!request)
|
||||
@ -64,11 +70,35 @@ int CASignCommand::Run(const boost::program_options::variables_map& vm, const st
|
||||
|
||||
boost::shared_ptr<X509> certRequest = StringToCertificate(certRequestText);
|
||||
|
||||
if (!certRequest) {
|
||||
Log(LogCritical, "cli", "Certificate request is invalid. Could not parse X.509 certificate for the 'cert_request' attribute.");
|
||||
return 1;
|
||||
}
|
||||
|
||||
boost::shared_ptr<X509> certResponse = CreateCertIcingaCA(certRequest);
|
||||
|
||||
BIO *out = BIO_new(BIO_s_mem());
|
||||
X509_NAME_print_ex(out, X509_get_subject_name(certRequest.get()), 0, XN_FLAG_ONELINE & ~ASN1_STRFLGS_ESC_MSB);
|
||||
|
||||
char *data;
|
||||
long length;
|
||||
length = BIO_get_mem_data(out, &data);
|
||||
|
||||
String subject = String(data, data + length);
|
||||
BIO_free(out);
|
||||
|
||||
if (!certResponse) {
|
||||
Log(LogCritical, "cli")
|
||||
<< "Could not sign certificate for '" << subject << "'.";
|
||||
return 1;
|
||||
}
|
||||
|
||||
request->Set("cert_response", CertificateToString(certResponse));
|
||||
|
||||
Utility::SaveJsonFile(requestFile, 0600, request);
|
||||
|
||||
Log(LogInformation, "cli")
|
||||
<< "Signed certificate for '" << subject << "'.";
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -367,6 +367,9 @@ String PkiUtility::GetCertificateInformation(const boost::shared_ptr<X509>& cert
|
||||
|
||||
std::stringstream info;
|
||||
info << String(data, data + length);
|
||||
|
||||
BIO_free(out);
|
||||
|
||||
for (unsigned int i = 0; i < diglen; i++) {
|
||||
info << std::setfill('0') << std::setw(2) << std::uppercase
|
||||
<< std::hex << static_cast<int>(md[i]) << ' ';
|
||||
|
@ -54,6 +54,28 @@ Value RequestCertificateHandler(const MessageOrigin::Ptr& origin, const Dictiona
|
||||
else
|
||||
cert = StringToCertificate(certText);
|
||||
|
||||
ApiListener::Ptr listener = ApiListener::GetInstance();
|
||||
boost::shared_ptr<X509> cacert = GetX509Certificate(listener->GetCaPath());
|
||||
|
||||
bool signedByCA = VerifyCertificate(cacert, cert);
|
||||
|
||||
if (signedByCA) {
|
||||
time_t now;
|
||||
time(&now);
|
||||
|
||||
/* auto-renew all certificates which were created before 2017 to force an update of the CA,
|
||||
* because Icinga versions older than 2.4 sometimes create certificates with an invalid
|
||||
* serial number. */
|
||||
time_t forceRenewalEnd = 1483228800; /* January 1st, 2017 */
|
||||
time_t renewalStart = now + 30 * 24 * 60 * 60;
|
||||
|
||||
if (X509_cmp_time(X509_get_notBefore(cert.get()), &forceRenewalEnd) != -1 && X509_cmp_time(X509_get_notAfter(cert.get()), &renewalStart) != -1) {
|
||||
result->Set("status_code", 1);
|
||||
result->Set("error", "The certificate cannot be renewed yet.");
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int n;
|
||||
unsigned char digest[EVP_MAX_MD_SIZE];
|
||||
|
||||
@ -72,9 +94,6 @@ Value RequestCertificateHandler(const MessageOrigin::Ptr& origin, const Dictiona
|
||||
String requestDir = Application::GetLocalStateDir() + "/lib/icinga2/pki-requests";
|
||||
String requestPath = requestDir + "/" + certFingerprint + ".json";
|
||||
|
||||
ApiListener::Ptr listener = ApiListener::GetInstance();
|
||||
|
||||
boost::shared_ptr<X509> cacert = GetX509Certificate(listener->GetCaPath());
|
||||
result->Set("ca", CertificateToString(cacert));
|
||||
|
||||
if (Utility::PathExists(requestPath)) {
|
||||
@ -104,7 +123,7 @@ Value RequestCertificateHandler(const MessageOrigin::Ptr& origin, const Dictiona
|
||||
if (!Utility::PathExists(GetIcingaCADir() + "/ca.key"))
|
||||
goto delayed_request;
|
||||
|
||||
if (!VerifyCertificate(cacert, cert)) {
|
||||
if (!signedByCA) {
|
||||
String salt = listener->GetTicketSalt();
|
||||
|
||||
String ticket = params->Get("ticket");
|
||||
@ -119,19 +138,8 @@ Value RequestCertificateHandler(const MessageOrigin::Ptr& origin, const Dictiona
|
||||
result->Set("error", "Invalid ticket.");
|
||||
return result;
|
||||
}
|
||||
} else {
|
||||
time_t renewalStart;
|
||||
time(&renewalStart);
|
||||
renewalStart += 30 * 24 * 60 * 60;
|
||||
|
||||
if (X509_cmp_time(X509_get_notAfter(cert.get()), &renewalStart)) {
|
||||
result->Set("status_code", 1);
|
||||
result->Set("error", "The certificate cannot be renewed yet.");
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pubkey = boost::shared_ptr<EVP_PKEY>(X509_get_pubkey(cert.get()), EVP_PKEY_free);
|
||||
subject = X509_get_subject_name(cert.get());
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user