From 7190d08e1a166455e767769492b8c6b9f41bc0da Mon Sep 17 00:00:00 2001 From: Stephen Gallagher 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 --- 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