Blob Blame History Raw
From 7190d08e1a166455e767769492b8c6b9f41bc0da Mon Sep 17 00:00:00 2001
From: Stephen Gallagher <sgallagh@redhat.com>
Date: Wed, 5 Jun 2019 17:08:23 -0400
Subject: [PATCH 5/6] Add password support for private keys

Fixes: https://github.com/sgallagher/sscg/issues/14

Signed-off-by: Stephen Gallagher <sgallagh@redhat.com>
---
 include/sscg.h |   7 +++
 src/sscg.c     | 152 ++++++++++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 157 insertions(+), 2 deletions(-)

diff --git a/include/sscg.h b/include/sscg.h
index fc90b81a0060af28529f3be6922b1b1501559300..ce9a7916e9432d0843d82af61d56ea7238ded682 100644
--- a/include/sscg.h
+++ b/include/sscg.h
@@ -141,8 +141,15 @@ struct sscg_options
   int key_strength;
   int minimum_key_strength;
   char *hash_alg;
+  char *cipher_alg;
+  const EVP_CIPHER *cipher;
   const EVP_MD *hash_fn;
 
+  bool ca_key_pass_prompt;
+  char *ca_key_pass;
+  bool cert_key_pass_prompt;
+  char *cert_key_pass;
+
   /* Output Files */
   char *ca_file;
   char *ca_key_file;
diff --git a/src/sscg.c b/src/sscg.c
index 58855f764480d24d6c0f57460b22a3a83281e37e..9dc926c77038105ca881a612cccd1913bc2d42f1 100644
--- a/src/sscg.c
+++ b/src/sscg.c
@@ -97,6 +97,9 @@ set_default_options (struct sscg_options *opts)
     }
 
   opts->minimum_key_strength = opts->key_strength;
+
+  opts->cipher_alg = talloc_strdup (opts, "aes-256-cbc");
+
   return 0;
 }
 
@@ -170,6 +173,42 @@ done:
   return ret;
 }
 
+
+/* This function takes a copy of a string into a talloc hierarchy and memsets
+ * the original string to zeroes to avoid leaking it when that memory is freed.
+ */
+static char *
+sscg_secure_string_steal (TALLOC_CTX *mem_ctx, char *src)
+{
+  char *dest = talloc_strdup (mem_ctx, src);
+
+  memset (src, 0, strlen (src));
+
+  return dest;
+}
+
+
+static int
+sscg_options_destructor (TALLOC_CTX *opts)
+{
+  struct sscg_options *options =
+    talloc_get_type_abort (opts, struct sscg_options);
+
+  /* Zero out the memory before freeing it so we don't leak passwords */
+  if (options->ca_key_pass)
+    {
+      memset (options->ca_key_pass, 0, strlen (options->ca_key_pass));
+    }
+
+  if (options->cert_key_pass)
+    {
+      memset (options->cert_key_pass, 0, strlen (options->cert_key_pass));
+    }
+
+  return 0;
+}
+
+
 int
 main (int argc, const char **argv)
 {
@@ -196,8 +235,11 @@ main (int argc, const char **argv)
 
   int ca_mode = 0644;
   int ca_key_mode = 0600;
+  char *ca_key_password = NULL;
+
   int cert_mode = 0644;
   int cert_key_mode = 0600;
+  char *cert_key_password = NULL;
 
   char *create_mode = NULL;
 
@@ -227,6 +269,7 @@ main (int argc, const char **argv)
 
   options = talloc_zero (main_ctx, struct sscg_options);
   CHECK_MEM (options);
+  talloc_set_destructor ((TALLOC_CTX *)options, sscg_options_destructor);
 
   ret = set_default_options (options);
   if (ret != EOK)
@@ -366,6 +409,16 @@ main (int argc, const char **argv)
       _ ("Hashing algorithm to use for signing."),
       _ ("{sha256,sha384,sha512}"),
     },
+    {
+      "cipher-alg",
+      '\0',
+      POPT_ARG_STRING | POPT_ARGFLAG_SHOW_DEFAULT,
+      &options->cipher_alg,
+      0,
+      _ ("Cipher to use for encrypting key files."),
+      _ ("{des-ede3-cbc,aes-256-cbc}"),
+    },
+
     {
       "ca-file",
       '\0',
@@ -404,6 +457,29 @@ main (int argc, const char **argv)
       _ ("File mode of the created CA key. (default: 0600)"),
       _ ("0600"),
     },
+    {
+      "ca-key-password",
+      '\0',
+      POPT_ARG_STRING,
+      &ca_key_password,
+      0,
+      _ ("Provide a password for the CA key file. Note that this will be "
+         "visible in the process table for all users, so it should be used "
+         "for testing purposes only. Use --ca-keypassfile or "
+         "--ca-key-password-prompt for secure password entry."),
+      NULL
+    },
+
+    {
+      "ca-key-password-prompt",
+      'C',
+      POPT_ARG_NONE,
+      &options->ca_key_pass_prompt,
+      0,
+      _ ("Prompt to enter a password for the CA key file."),
+      NULL
+    },
+
     {
       "cert-file",
       '\0',
@@ -442,6 +518,29 @@ main (int argc, const char **argv)
       _ ("File mode of the created certificate key. (default: 0600)"),
       _ ("0600"),
     },
+    {
+      "cert-key-password",
+      'p',
+      POPT_ARG_STRING,
+      &cert_key_password,
+      0,
+      _ ("Provide a password for the service key file. Note that this will be "
+         "visible in the process table for all users, so this flag should be "
+         "used for testing purposes only. Use --cert-keypassfile or "
+         "--cert-key-password-prompt for secure password entry."),
+      NULL
+    },
+
+    {
+      "cert-key-password-prompt",
+      'P',
+      POPT_ARG_NONE,
+      &options->cert_key_pass_prompt,
+      0,
+      _ ("Prompt to enter a password for the service key file."),
+      NULL
+    },
+
     POPT_TABLEEND
   };
 
@@ -592,6 +691,20 @@ main (int argc, const char **argv)
         }
     }
 
+  /* Password handling */
+  if (ca_key_password)
+    {
+      options->ca_key_pass =
+        sscg_secure_string_steal (options, ca_key_password);
+    }
+
+  if (cert_key_password)
+    {
+      options->cert_key_pass =
+        sscg_secure_string_steal (options, cert_key_password);
+    }
+
+
   if (options->key_strength < options->minimum_key_strength)
     {
       fprintf (stderr,
@@ -601,6 +714,15 @@ main (int argc, const char **argv)
       goto done;
     }
 
+  /* Make sure we have a valid cipher */
+  options->cipher = EVP_get_cipherbyname (options->cipher_alg);
+  if (!options->cipher)
+    {
+      fprintf (stderr, "Invalid cipher specified: %s\n", options->cipher_alg);
+      ret = EINVAL;
+      goto done;
+    }
+
   /* TODO: restrict this to approved hashes.
    * For now, we'll only list SHA[256|384|512] in the help */
   options->hash_fn = EVP_get_digestbyname (options->hash_alg);
@@ -696,8 +818,21 @@ main (int argc, const char **argv)
   cert_key_out = BIO_new_file (options->cert_key_file, create_mode);
   CHECK_BIO (cert_key_out, options->cert_key_file);
 
+  /* This function has a default mechanism for prompting for the
+   * password if it is passed a cipher and gets a NULL password.
+   *
+   * Only pass the cipher if we have a password or were instructed
+   * to prompt for one.
+   */
   sret = PEM_write_bio_PrivateKey (
-    cert_key_out, svc_key->evp_pkey, NULL, NULL, 0, NULL, NULL);
+    cert_key_out,
+    svc_key->evp_pkey,
+    options->cert_key_pass_prompt || options->cert_key_pass ? options->cipher :
+                                                              NULL,
+    (unsigned char *)options->cert_key_pass,
+    options->cert_key_pass ? strlen (options->cert_key_pass) : 0,
+    NULL,
+    NULL);
   CHECK_SSL (sret, PEM_write_bio_PrivateKey (svc));
   BIO_get_fp (cert_key_out, &fp);
 
@@ -776,8 +911,21 @@ main (int argc, const char **argv)
         }
       CHECK_BIO (ca_key_out, options->ca_key_file);
 
+      /* This function has a default mechanism for prompting for the
+       * password if it is passed a cipher and gets a NULL password.
+       *
+       * Only pass the cipher if we have a password or were instructed
+       * to prompt for one.
+       */
       sret = PEM_write_bio_PrivateKey (
-        ca_key_out, cakey->evp_pkey, NULL, NULL, 0, NULL, NULL);
+        ca_key_out,
+        cakey->evp_pkey,
+        options->ca_key_pass_prompt || options->ca_key_pass ? options->cipher :
+                                                              NULL,
+        (unsigned char *)options->ca_key_pass,
+        options->ca_key_pass ? strlen (options->ca_key_pass) : 0,
+        NULL,
+        NULL);
       CHECK_SSL (sret, PEM_write_bio_PrivateKey (CA));
       BIO_get_fp (ca_key_out, &fp);
       if (options->verbosity >= SSCG_DEBUG)
-- 
2.23.0