From: Gunnar Beutner Date: Mon, 13 Oct 2014 10:34:31 +0000 (+0200) Subject: Implement the "pki new-csr" command X-Git-Tag: v2.2.0~405 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=4ba3d74c0e7682a5cd3e12e1ed49bef6dba4737f;p=icinga2 Implement the "pki new-csr" command refs #7247 --- diff --git a/lib/base/tlsutility.cpp b/lib/base/tlsutility.cpp index 50c4e2840..5e285b66c 100644 --- a/lib/base/tlsutility.cpp +++ b/lib/base/tlsutility.cpp @@ -246,17 +246,23 @@ shared_ptr GetX509Certificate(const String& pemfile) return shared_ptr(cert, X509_free); } -int MakeX509CSR(const char *cn, const char *keyfile, const char *csrfile) +int MakeX509CSR(const String& cn, const String& keyfile, const String& csrfile, const String& certfile) { InitializeOpenSSL(); RSA *rsa = RSA_generate_key(4096, RSA_F4, NULL, NULL); + Log(LogInformation, "base", "Writing private key to '" + keyfile + "'."); + BIO *bio = BIO_new(BIO_s_file()); - BIO_write_filename(bio, const_cast(keyfile)); + BIO_write_filename(bio, const_cast(keyfile.CStr())); PEM_write_bio_RSAPrivateKey(bio, rsa, NULL, NULL, 0, NULL, NULL); BIO_free(bio); +#ifndef _WIN32 + chmod(keyfile.CStr(), 0600); +#endif /* _WIN32 */ + X509_REQ *req = X509_REQ_new(); if (!req) @@ -266,16 +272,40 @@ int MakeX509CSR(const char *cn, const char *keyfile, const char *csrfile) EVP_PKEY_assign_RSA(key, rsa); X509_REQ_set_version(req, 0); X509_REQ_set_pubkey(req, key); + + if (!certfile.IsEmpty()) { + X509 *cert = X509_new(); + ASN1_INTEGER_set(X509_get_serialNumber(cert), 1); + X509_gmtime_adj(X509_get_notBefore(cert), 0); + X509_gmtime_adj(X509_get_notAfter(cert), 365 * 24 * 60 * 60 * 30); + X509_set_pubkey(cert, key); + + X509_NAME *name = X509_get_subject_name(cert); + X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC, (unsigned char *)cn.CStr(), -1, -1, 0); + X509_set_issuer_name(cert, name); + X509_sign(cert, key, EVP_sha1()); + + Log(LogInformation, "base", "Writing X509 certificate to '" + certfile + "'."); + + bio = BIO_new(BIO_s_file()); + BIO_write_filename(bio, const_cast(certfile.CStr())); + PEM_write_bio_X509(bio, cert); + BIO_free(bio); + + X509_free(cert); + } X509_NAME *name = X509_REQ_get_subject_name(req); - X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC, (unsigned char *)cn, -1, -1, 0); + X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC, (unsigned char *)cn.CStr(), -1, -1, 0); X509_REQ_sign(req, key, EVP_sha1()); EVP_PKEY_free(key); + Log(LogInformation, "base", "Writing certificate signing request to '" + certfile + "'."); + bio = BIO_new(BIO_s_file()); - BIO_write_filename(bio, const_cast(csrfile)); + BIO_write_filename(bio, const_cast(csrfile.CStr())); PEM_write_bio_X509_REQ(bio, req); BIO_free(bio); diff --git a/lib/base/tlsutility.hpp b/lib/base/tlsutility.hpp index b44b902c2..f3575bb44 100644 --- a/lib/base/tlsutility.hpp +++ b/lib/base/tlsutility.hpp @@ -37,7 +37,7 @@ shared_ptr I2_BASE_API MakeSSLContext(const String& pubkey, const Strin void I2_BASE_API AddCRLToSSLContext(const shared_ptr& context, const String& crlPath); String I2_BASE_API GetCertificateCN(const shared_ptr& certificate); shared_ptr I2_BASE_API GetX509Certificate(const String& pemfile); -extern "C" int I2_BASE_API MakeX509CSR(const char *cn, const char *keyfile, const char *csrfile); +int I2_BASE_API MakeX509CSR(const String& cn, const String& keyfile, const String& csrfile, const String& certfile = String()); String I2_BASE_API SHA256(const String& s); class I2_BASE_API openssl_error : virtual public std::exception, virtual public boost::exception { }; diff --git a/lib/cli/CMakeLists.txt b/lib/cli/CMakeLists.txt index ca9a1147b..8c54e665f 100644 --- a/lib/cli/CMakeLists.txt +++ b/lib/cli/CMakeLists.txt @@ -16,7 +16,7 @@ # Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. set(cli_SOURCES - cainitcommand.cpp daemoncommand.cpp + pkiinitcacommand.cpp pkinewcsrcommand.cpp daemoncommand.cpp ) if(ICINGA2_UNITY_BUILD) diff --git a/lib/cli/cainitcommand.cpp b/lib/cli/pkiinitcacommand.cpp similarity index 83% rename from lib/cli/cainitcommand.cpp rename to lib/cli/pkiinitcacommand.cpp index 01bc054d1..b4117b7af 100644 --- a/lib/cli/cainitcommand.cpp +++ b/lib/cli/pkiinitcacommand.cpp @@ -17,25 +17,25 @@ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * ******************************************************************************/ -#include "cli/cainitcommand.hpp" +#include "cli/pkiinitcacommand.hpp" #include "base/logger_fwd.hpp" #include "base/clicommand.hpp" using namespace icinga; -REGISTER_CLICOMMAND("ca/init", CAInitCommand); +REGISTER_CLICOMMAND("pki/init-ca", PKIInitCACommand); -String CAInitCommand::GetDescription(void) const +String PKIInitCACommand::GetDescription(void) const { return "Sets up a new Certificate Authority."; } -String CAInitCommand::GetShortDescription(void) const +String PKIInitCACommand::GetShortDescription(void) const { return "sets up a new CA"; } -void CAInitCommand::InitParameters(boost::program_options::options_description& visibleDesc, +void PKIInitCACommand::InitParameters(boost::program_options::options_description& visibleDesc, boost::program_options::options_description& hiddenDesc) const { /* Command doesn't support any parameters. */ @@ -46,7 +46,7 @@ void CAInitCommand::InitParameters(boost::program_options::options_description& * * @returns An exit status. */ -int CAInitCommand::Run(const boost::program_options::variables_map& vm) const +int PKIInitCACommand::Run(const boost::program_options::variables_map& vm) const { Log(LogNotice, "cli", "Test!"); Log(LogInformation, "cli", "Hello World!"); diff --git a/lib/cli/cainitcommand.hpp b/lib/cli/pkiinitcacommand.hpp similarity index 90% rename from lib/cli/cainitcommand.hpp rename to lib/cli/pkiinitcacommand.hpp index 7dabbc86c..66181ed48 100644 --- a/lib/cli/cainitcommand.hpp +++ b/lib/cli/pkiinitcacommand.hpp @@ -17,8 +17,8 @@ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * ******************************************************************************/ -#ifndef CAINITCOMMAND_H -#define CAINITCOMMAND_H +#ifndef PKIINITCACOMMAND_H +#define PKIINITCACOMMAND_H #include "base/qstring.hpp" #include "base/clicommand.hpp" @@ -27,14 +27,14 @@ namespace icinga { /** - * The "ca init" command. + * The "pki init-ca" command. * * @ingroup cli */ -class CAInitCommand : public CLICommand +class PKIInitCACommand : public CLICommand { public: - DECLARE_PTR_TYPEDEFS(CAInitCommand); + DECLARE_PTR_TYPEDEFS(PKIInitCACommand); virtual String GetDescription(void) const; virtual String GetShortDescription(void) const; @@ -46,4 +46,4 @@ public: } -#endif /* CAINITCOMMAND_H */ +#endif /* PKIINITCACOMMAND_H */ diff --git a/lib/cli/pkinewcsrcommand.cpp b/lib/cli/pkinewcsrcommand.cpp new file mode 100644 index 000000000..1710d73f0 --- /dev/null +++ b/lib/cli/pkinewcsrcommand.cpp @@ -0,0 +1,80 @@ +/****************************************************************************** + * Icinga 2 * + * Copyright (C) 2012-2014 Icinga Development Team (http://www.icinga.org) * + * * + * 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/pkinewcsrcommand.hpp" +#include "base/logger_fwd.hpp" +#include "base/clicommand.hpp" +#include "base/tlsutility.hpp" + +using namespace icinga; +namespace po = boost::program_options; + +REGISTER_CLICOMMAND("pki/new-csr", PKINewCSRCommand); + +String PKINewCSRCommand::GetDescription(void) const +{ + return "Creates a new Certificate Signing Request and optionally a self-signed X509 certificate."; +} + +String PKINewCSRCommand::GetShortDescription(void) const +{ + return "creates a new CSR"; +} + +void PKINewCSRCommand::InitParameters(boost::program_options::options_description& visibleDesc, + boost::program_options::options_description& hiddenDesc) const +{ + visibleDesc.add_options() + ("cn", po::value(), "Common Name") + ("keyfile", po::value(), "Key file path") + ("csrfile", po::value(), "CSR file path") + ("certfile", po::value(), "Certificate file path (optional)"); +} + +/** + * The entry point for the "ca init" CLI command. + * + * @returns An exit status. + */ +int PKINewCSRCommand::Run(const boost::program_options::variables_map& vm) const +{ + if (!vm.count("cn")) { + Log(LogCritical, "cli", "Common name (--cn) must be specified."); + return 1; + } + + if (!vm.count("keyfile")) { + Log(LogCritical, "cli", "Key file path (--keyfile) must be specified."); + return 1; + } + + if (!vm.count("csrfile")) { + Log(LogCritical, "cli", "CSR file path (--csrfile) must be specified."); + return 1; + } + + String certfile; + + if (vm.count("certfile")) + certfile = vm["certfile"].as(); + + MakeX509CSR(vm["cn"].as(), vm["keyfile"].as(), vm["csrfile"].as(), certfile); + + return 0; +} diff --git a/lib/cli/pkinewcsrcommand.hpp b/lib/cli/pkinewcsrcommand.hpp new file mode 100644 index 000000000..d0c31f42e --- /dev/null +++ b/lib/cli/pkinewcsrcommand.hpp @@ -0,0 +1,49 @@ +/****************************************************************************** + * Icinga 2 * + * Copyright (C) 2012-2014 Icinga Development Team (http://www.icinga.org) * + * * + * 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 PKINEWCSRCOMMAND_H +#define PKINEWCSRCOMMAND_H + +#include "base/qstring.hpp" +#include "base/clicommand.hpp" + +namespace icinga +{ + +/** + * The "pki new-csr" command. + * + * @ingroup cli + */ +class PKINewCSRCommand : public CLICommand +{ +public: + DECLARE_PTR_TYPEDEFS(PKINewCSRCommand); + + virtual String GetDescription(void) const; + virtual String GetShortDescription(void) const; + virtual void InitParameters(boost::program_options::options_description& visibleDesc, + boost::program_options::options_description& hiddenDesc) const; + virtual int Run(const boost::program_options::variables_map& vm) const; + +}; + +} + +#endif /* PKINEWCSRCOMMAND_H */