]> granicus.if.org Git - icinga2/commitdiff
Implement the "pki sign-csr" command
authorGunnar Beutner <gunnar.beutner@netways.de>
Wed, 15 Oct 2014 14:01:15 +0000 (16:01 +0200)
committerGunnar Beutner <gunnar.beutner@netways.de>
Wed, 15 Oct 2014 14:01:15 +0000 (16:01 +0200)
refs #7274

lib/base/tlsutility.cpp
lib/base/tlsutility.hpp
lib/cli/CMakeLists.txt
lib/cli/pkisigncsrcommand.cpp [new file with mode: 0644]
lib/cli/pkisigncsrcommand.hpp [new file with mode: 0644]

index 30317df82e2ec618e79b77bbce1a095dc29180ce..ab7f6c901104544c50cb715704f2c4c62dfa49c3 100644 (file)
@@ -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<char *>(keyfile.CStr()));
+       BIO *bio = BIO_new_file(const_cast<char *>(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<char *>("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<char *>(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<char *>("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;
index 727d0133e6f6532cfc1731c9c68c83ef3c5c0dfb..cb1472d23ca95055da5239bf4e363ee881e9fb7c 100644 (file)
@@ -39,6 +39,7 @@ void I2_BASE_API AddCRLToSSLContext(const shared_ptr<SSL_CTX>& context, const St
 String I2_BASE_API GetCertificateCN(const shared_ptr<X509>& certificate);
 shared_ptr<X509> 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 { };
index a4c0a145fcb9845566b375f9a1c4340a474c05bb..b3c55551cb4dbe4c1ccd4477285588fa6b4dd9c0 100644 (file)
@@ -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 (file)
index 0000000..2d15866
--- /dev/null
@@ -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<std::string>& 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<char *>(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<char *>(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 (file)
index 0000000..9d4c4ba
--- /dev/null
@@ -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<std::string>& ap) const;
+
+};
+
+}
+
+#endif /* PKISIGNCSRCOMMAND_H */