From abdd4b307b177c1548f4e40f7ee8bda5a1f09f4b Mon Sep 17 00:00:00 2001 From: Gunnar Beutner Date: Wed, 23 Aug 2017 12:12:43 +0200 Subject: [PATCH] Implement the 'ca list' and 'ca sign' CLI commands refs #5450 --- lib/base/tlsutility.cpp | 5 +++ lib/base/tlsutility.hpp | 1 + lib/cli/CMakeLists.txt | 1 + lib/cli/calistcommand.cpp | 79 +++++++++++++++++++++++++++++++++++++++ lib/cli/calistcommand.hpp | 48 ++++++++++++++++++++++++ lib/cli/casigncommand.cpp | 74 ++++++++++++++++++++++++++++++++++++ lib/cli/casigncommand.hpp | 47 +++++++++++++++++++++++ 7 files changed, 255 insertions(+) create mode 100644 lib/cli/calistcommand.cpp create mode 100644 lib/cli/calistcommand.hpp create mode 100644 lib/cli/casigncommand.cpp create mode 100644 lib/cli/casigncommand.hpp diff --git a/lib/base/tlsutility.cpp b/lib/base/tlsutility.cpp index 408178032..d71dc5124 100644 --- a/lib/base/tlsutility.cpp +++ b/lib/base/tlsutility.cpp @@ -575,6 +575,11 @@ boost::shared_ptr CreateCertIcingaCA(EVP_PKEY *pubkey, X509_NAME *subject) return CreateCert(pubkey, subject, X509_get_subject_name(cacert.get()), privkey, false); } +boost::shared_ptr CreateCertIcingaCA(const boost::shared_ptr& cert) +{ + return CreateCertIcingaCA(X509_get_pubkey(cert.get()), X509_get_subject_name(cert.get())); +} + String CertificateToString(const boost::shared_ptr& cert) { BIO *mem = BIO_new(BIO_s_mem()); diff --git a/lib/base/tlsutility.hpp b/lib/base/tlsutility.hpp index 2cc824353..0657c94a5 100644 --- a/lib/base/tlsutility.hpp +++ b/lib/base/tlsutility.hpp @@ -50,6 +50,7 @@ String I2_BASE_API GetIcingaCADir(void); String I2_BASE_API CertificateToString(const boost::shared_ptr& cert); boost::shared_ptr I2_BASE_API StringToCertificate(const String& cert); boost::shared_ptr I2_BASE_API CreateCertIcingaCA(EVP_PKEY *pubkey, X509_NAME *subject); +boost::shared_ptr I2_BASE_API CreateCertIcingaCA(const boost::shared_ptr& cert); String I2_BASE_API PBKDF2_SHA1(const String& password, const String& salt, int iterations); String I2_BASE_API SHA1(const String& s, bool binary = false); String I2_BASE_API SHA256(const String& s); diff --git a/lib/cli/CMakeLists.txt b/lib/cli/CMakeLists.txt index 279082626..05ec67762 100644 --- a/lib/cli/CMakeLists.txt +++ b/lib/cli/CMakeLists.txt @@ -17,6 +17,7 @@ set(cli_SOURCES apisetupcommand.cpp apisetuputility.cpp + calistcommand.cpp casigncommand.cpp nodeaddcommand.cpp nodeblackandwhitelistcommand.cpp nodelistcommand.cpp noderemovecommand.cpp nodesetcommand.cpp nodesetupcommand.cpp nodeupdateconfigcommand.cpp nodewizardcommand.cpp nodeutility.cpp clicommand.cpp diff --git a/lib/cli/calistcommand.cpp b/lib/cli/calistcommand.cpp new file mode 100644 index 000000000..98012af9e --- /dev/null +++ b/lib/cli/calistcommand.cpp @@ -0,0 +1,79 @@ +/****************************************************************************** + * Icinga 2 * + * Copyright (C) 2012-2017 Icinga Development Team (https://www.icinga.com/) * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License * + * as published by the Free Software Foundation; either version 2 * + * of the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software Foundation * + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * + ******************************************************************************/ + +#include "cli/calistcommand.hpp" +#include "base/logger.hpp" +#include "base/application.hpp" +#include "base/tlsutility.hpp" + +using namespace icinga; + +REGISTER_CLICOMMAND("ca/list", CAListCommand); + +String CAListCommand::GetDescription(void) const +{ + return "Lists all certificate signing requests."; +} + +String CAListCommand::GetShortDescription(void) const +{ + return "lists all certificate signing requests"; +} + +void CAListCommand::PrintRequest(const String& requestFile) +{ + Dictionary::Ptr request = Utility::LoadJsonFile(requestFile); + + if (!request) + return; + + 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"); + + boost::shared_ptr certRequest = StringToCertificate(certRequestText); + + String cn = GetCertificateCN(certRequest); + + String certResponseText = request->Get("cert_response"); + + if (!certResponseText.IsEmpty()) { + boost::shared_ptr certResponse = StringToCertificate(certResponseText); + } + + std::cout << "CN: " << cn << "\n"; + std::cout << "Certificate (request): " << certRequestText << "\n"; + std::cout << "Certificate (response): " << certResponseText << "\n"; +} + +/** + * The entry point for the "ca list" CLI command. + * + * @returns An exit status. + */ +int CAListCommand::Run(const boost::program_options::variables_map& vm, const std::vector& ap) const +{ + Utility::Glob(Application::GetLocalStateDir() + "/lib/icinga2/pki-requests/*.json", &CAListCommand::PrintRequest, GlobFile); + + return 0; +} diff --git a/lib/cli/calistcommand.hpp b/lib/cli/calistcommand.hpp new file mode 100644 index 000000000..80ef37872 --- /dev/null +++ b/lib/cli/calistcommand.hpp @@ -0,0 +1,48 @@ +/****************************************************************************** + * Icinga 2 * + * Copyright (C) 2012-2017 Icinga Development Team (https://www.icinga.com/) * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License * + * as published by the Free Software Foundation; either version 2 * + * of the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software Foundation * + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * + ******************************************************************************/ + +#ifndef CALISTCOMMAND_H +#define CALISTCOMMAND_H + +#include "cli/clicommand.hpp" + +namespace icinga +{ + +/** + * The "ca list" command. + * + * @ingroup cli + */ +class CAListCommand : public CLICommand +{ +public: + DECLARE_PTR_TYPEDEFS(CAListCommand); + + virtual String GetDescription(void) const override; + virtual String GetShortDescription(void) const override; + virtual int Run(const boost::program_options::variables_map& vm, const std::vector& ap) const override; + +private: + static void PrintRequest(const String& requestFile); +}; + +} + +#endif /* CALISTCOMMAND_H */ diff --git a/lib/cli/casigncommand.cpp b/lib/cli/casigncommand.cpp new file mode 100644 index 000000000..247afebb3 --- /dev/null +++ b/lib/cli/casigncommand.cpp @@ -0,0 +1,74 @@ +/****************************************************************************** + * Icinga 2 * + * Copyright (C) 2012-2017 Icinga Development Team (https://www.icinga.com/) * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License * + * as published by the Free Software Foundation; either version 2 * + * of the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software Foundation * + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * + ******************************************************************************/ + +#include "cli/casigncommand.hpp" +#include "base/logger.hpp" +#include "base/application.hpp" +#include "base/tlsutility.hpp" + +using namespace icinga; + +REGISTER_CLICOMMAND("ca/sign", CASignCommand); + +String CASignCommand::GetDescription(void) const +{ + return "Signs an outstanding certificate request."; +} + +String CASignCommand::GetShortDescription(void) const +{ + return "signs an outstanding certificate request"; +} + +int CASignCommand::GetMinArguments(void) const +{ + return 1; +} + +ImpersonationLevel CASignCommand::GetImpersonationLevel(void) const +{ + return ImpersonateIcinga; +} + +/** + * The entry point for the "ca sign" CLI command. + * + * @returns An exit status. + */ +int CASignCommand::Run(const boost::program_options::variables_map& vm, const std::vector& ap) const +{ + String requestFile = Application::GetLocalStateDir() + "/lib/icinga2/pki-requests/" + ap[0] + ".json"; + + Dictionary::Ptr request = Utility::LoadJsonFile(requestFile); + + if (!request) + return 1; + + String certRequestText = request->Get("cert_request"); + + boost::shared_ptr certRequest = StringToCertificate(certRequestText); + + boost::shared_ptr certResponse = CreateCertIcingaCA(certRequest); + + request->Set("cert_response", CertificateToString(certResponse)); + + Utility::SaveJsonFile(requestFile, 0600, request); + + return 0; +} diff --git a/lib/cli/casigncommand.hpp b/lib/cli/casigncommand.hpp new file mode 100644 index 000000000..7c27a77d6 --- /dev/null +++ b/lib/cli/casigncommand.hpp @@ -0,0 +1,47 @@ +/****************************************************************************** + * Icinga 2 * + * Copyright (C) 2012-2017 Icinga Development Team (https://www.icinga.com/) * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License * + * as published by the Free Software Foundation; either version 2 * + * of the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software Foundation * + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * + ******************************************************************************/ + +#ifndef CASIGNCOMMAND_H +#define CASIGNCOMMAND_H + +#include "cli/clicommand.hpp" + +namespace icinga +{ + +/** + * The "ca sign" command. + * + * @ingroup cli + */ +class CASignCommand : public CLICommand +{ +public: + DECLARE_PTR_TYPEDEFS(CASignCommand); + + virtual String GetDescription(void) const override; + virtual String GetShortDescription(void) const override; + virtual int GetMinArguments(void) const override; + virtual ImpersonationLevel GetImpersonationLevel(void) const override; + virtual int Run(const boost::program_options::variables_map& vm, const std::vector& ap) const override; +}; + +} + +#endif /* CASIGNCOMMAND_H */