From c1b92756b594c5ce05fecb34d528b3b058b33eb4 Mon Sep 17 00:00:00 2001 From: Gunnar Beutner Date: Wed, 15 Oct 2014 16:01:15 +0200 Subject: [PATCH] Implement the "pki sign-csr" command refs #7274 --- lib/base/tlsutility.cpp | 77 +++++++++++++++++------------- lib/base/tlsutility.hpp | 1 + lib/cli/CMakeLists.txt | 2 +- lib/cli/pkisigncsrcommand.cpp | 90 +++++++++++++++++++++++++++++++++++ lib/cli/pkisigncsrcommand.hpp | 50 +++++++++++++++++++ 5 files changed, 185 insertions(+), 35 deletions(-) create mode 100644 lib/cli/pkisigncsrcommand.cpp create mode 100644 lib/cli/pkisigncsrcommand.hpp diff --git a/lib/base/tlsutility.cpp b/lib/base/tlsutility.cpp index 30317df82..ab7f6c901 100644 --- a/lib/base/tlsutility.cpp +++ b/lib/base/tlsutility.cpp @@ -253,8 +253,7 @@ int MakeX509CSR(const String& cn, const String& keyfile, const String& csrfile, Log(LogInformation, "base", "Writing private key to '" + keyfile + "'."); - BIO *bio = BIO_new(BIO_s_file()); - BIO_write_filename(bio, const_cast(keyfile.CStr())); + BIO *bio = BIO_new_file(const_cast(keyfile.CStr()), "w"); PEM_write_bio_RSAPrivateKey(bio, rsa, NULL, NULL, 0, NULL, NULL); BIO_free(bio); @@ -262,52 +261,33 @@ int MakeX509CSR(const String& cn, const String& keyfile, const String& csrfile, chmod(keyfile.CStr(), 0600); #endif /* _WIN32 */ - X509_REQ *req = X509_REQ_new(); - - if (!req) - return 0; - EVP_PKEY *key = EVP_PKEY_new(); EVP_PKEY_assign_RSA(key, rsa); 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 *subject = X509_NAME_new(); + X509_NAME_add_entry_by_txt(subject, "CN", MBSTRING_ASC, (unsigned char *)cn.CStr(), -1, -1, 0); + + X509 *cert = CreateCert(key, subject, subject, key, ca); + + X509_NAME_free(subject); - 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); - - if (ca) { - X509_EXTENSION *ext; - X509V3_CTX ctx; - X509V3_set_ctx_nodb(&ctx); - X509V3_set_ctx(&ctx, cert, cert, NULL, NULL, 0); - ext = X509V3_EXT_conf_nid(NULL, &ctx, NID_basic_constraints, const_cast("critical,CA:TRUE")); - - if (ext) - X509_add_ext(cert, ext, -1); - - X509_EXTENSION_free(ext); - } - - - 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); } if (!csrfile.IsEmpty()) { + X509_REQ *req = X509_REQ_new(); + + if (!req) + return 0; + X509_REQ_set_version(req, 0); X509_REQ_set_pubkey(req, key); @@ -331,6 +311,35 @@ int MakeX509CSR(const String& cn, const String& keyfile, const String& csrfile, return 1; } +X509 *CreateCert(EVP_PKEY *pubkey, X509_NAME *subject, X509_NAME *issuer, EVP_PKEY *cakey, bool ca, const String& serialfile) +{ + 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, pubkey); + + X509_set_subject_name(cert, subject); + X509_set_issuer_name(cert, issuer); + + if (ca) { + X509_EXTENSION *ext; + X509V3_CTX ctx; + X509V3_set_ctx_nodb(&ctx); + X509V3_set_ctx(&ctx, cert, cert, NULL, NULL, 0); + ext = X509V3_EXT_conf_nid(NULL, &ctx, NID_basic_constraints, const_cast("critical,CA:TRUE")); + + if (ext) + X509_add_ext(cert, ext, -1); + + X509_EXTENSION_free(ext); + } + + X509_sign(cert, cakey, EVP_sha1()); + + return cert; +} + String SHA256(const String& s) { std::ostringstream msgbuf; diff --git a/lib/base/tlsutility.hpp b/lib/base/tlsutility.hpp index 727d0133e..cb1472d23 100644 --- a/lib/base/tlsutility.hpp +++ b/lib/base/tlsutility.hpp @@ -39,6 +39,7 @@ void I2_BASE_API AddCRLToSSLContext(const shared_ptr& context, const St String I2_BASE_API GetCertificateCN(const shared_ptr& certificate); shared_ptr I2_BASE_API GetX509Certificate(const String& pemfile); int I2_BASE_API MakeX509CSR(const String& cn, const String& keyfile, const String& csrfile = String(), const String& certfile = String(), bool ca = false); +X509 * I2_BASE_API CreateCert(EVP_PKEY *pubkey, X509_NAME *subject, X509_NAME *issuer, EVP_PKEY *cakey, bool ca, const String& serialfile = 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 a4c0a145f..b3c55551c 100644 --- a/lib/cli/CMakeLists.txt +++ b/lib/cli/CMakeLists.txt @@ -18,7 +18,7 @@ set(cli_SOURCES featureenablecommand.cpp featuredisablecommand.cpp featurelistcommand.cpp objectlistcommand.cpp - pkinewcacommand.cpp pkinewcertcommand.cpp + pkinewcacommand.cpp pkinewcertcommand.cpp pkisigncsrcommand.cpp daemoncommand.cpp ) diff --git a/lib/cli/pkisigncsrcommand.cpp b/lib/cli/pkisigncsrcommand.cpp new file mode 100644 index 000000000..2d1586687 --- /dev/null +++ b/lib/cli/pkisigncsrcommand.cpp @@ -0,0 +1,90 @@ +/****************************************************************************** + * 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/pkisigncsrcommand.hpp" +#include "base/logger_fwd.hpp" +#include "base/clicommand.hpp" +#include "base/tlsutility.hpp" +#include "base/application.hpp" + +using namespace icinga; +namespace po = boost::program_options; + +REGISTER_CLICOMMAND("pki/sign-csr", PKISignCSRCommand); + +String PKISignCSRCommand::GetDescription(void) const +{ + return "Reads a Certificate Signing Request from stdin and prints a signed certificate on stdout."; +} + +String PKISignCSRCommand::GetShortDescription(void) const +{ + return "signs a CSR"; +} + +void PKISignCSRCommand::InitParameters(boost::program_options::options_description& visibleDesc, + boost::program_options::options_description& hiddenDesc, + ArgumentCompletionDescription& argCompletionDesc) const +{ + /* Command doesn't support any parameters. */ +} + +/** + * The entry point for the "pki sign-csr" CLI command. + * + * @returns An exit status. + */ +int PKISignCSRCommand::Run(const boost::program_options::variables_map& vm, const std::vector& ap) const +{ + BIO *csrbio = BIO_new_fp(stdin, BIO_NOCLOSE); + X509_REQ *req; + PEM_read_bio_X509_REQ(csrbio, &req, NULL, NULL); + BIO_free(csrbio); + + String cadir = Application::GetLocalStateDir() + "/lib/icinga2/ca"; + + String cakeyfile = cadir + "/ca.key"; + + RSA *rsa; + + BIO *cakeybio = BIO_new_file(const_cast(cakeyfile.CStr()), "r"); + rsa = PEM_read_bio_RSAPrivateKey(cakeybio, NULL, NULL, NULL); + BIO_free(cakeybio); + + String cacertfile = cadir + "/ca.crt"; + + BIO *cacertbio = BIO_new_file(const_cast(cacertfile.CStr()), "r"); + X509 *cacert = PEM_read_bio_X509(cacertbio, NULL, NULL, NULL); + BIO_free(cacertbio); + + EVP_PKEY *privkey = EVP_PKEY_new(); + EVP_PKEY_assign_RSA(privkey, rsa); + + EVP_PKEY *pubkey = X509_REQ_get_pubkey(req); + + X509 *cert = CreateCert(pubkey, X509_REQ_get_subject_name(req), X509_get_subject_name(cacert), privkey, false); + + X509_free(cacert); + + BIO *certbio = BIO_new_fp(stdout, BIO_NOCLOSE); + PEM_write_bio_X509(certbio, cert); + BIO_free(certbio); + + return 0; +} diff --git a/lib/cli/pkisigncsrcommand.hpp b/lib/cli/pkisigncsrcommand.hpp new file mode 100644 index 000000000..9d4c4ba2f --- /dev/null +++ b/lib/cli/pkisigncsrcommand.hpp @@ -0,0 +1,50 @@ +/****************************************************************************** + * 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 PKISIGNCSRCOMMAND_H +#define PKISIGNCSRCOMMAND_H + +#include "base/qstring.hpp" +#include "base/clicommand.hpp" + +namespace icinga +{ + +/** + * The "pki sign-csr" command. + * + * @ingroup cli + */ +class PKISignCSRCommand : public CLICommand +{ +public: + DECLARE_PTR_TYPEDEFS(PKISignCSRCommand); + + 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, + ArgumentCompletionDescription& argCompletionDesc) const; + virtual int Run(const boost::program_options::variables_map& vm, const std::vector& ap) const; + +}; + +} + +#endif /* PKISIGNCSRCOMMAND_H */ -- 2.40.0