From aaa5168d1231aa398a763d2bd4fa379f3b3f0913 Mon Sep 17 00:00:00 2001 From: James Antill Date: Thu, 26 May 2022 14:33:17 -0400 Subject: [PATCH] Auto sync2gitlab import of sscg-2.3.3-14.el8.src.rpm --- .gitignore | 1 + 0001-Generate-manpage.patch | 50 + ...aults-based-on-system-security-level.patch | 208 ++ ...aults-based-on-system-security-level.patch | 115 + 0004-Properly-check-all-return-values.patch | 51 + ...dd-password-support-for-private-keys.patch | 273 ++ ...-specifying-keyfile-password-by-file.patch | 153 + ...for-client-certificates-and-dhparams.patch | 2651 +++++++++++++++++ ...client-cert-issues-found-by-CI-tests.patch | 98 + ...Fix-help-message-for-client-key-file.patch | 36 + ...validation-of-command-line-arguments.patch | 920 ++++++ EMPTY | 1 - sources | 1 + sscg.spec | 269 ++ 14 files changed, 4826 insertions(+), 1 deletion(-) create mode 100644 .gitignore create mode 100644 0001-Generate-manpage.patch create mode 100644 0002-Adjust-defaults-based-on-system-security-level.patch create mode 100644 0003-Adjust-hash-defaults-based-on-system-security-level.patch create mode 100644 0004-Properly-check-all-return-values.patch create mode 100644 0005-Add-password-support-for-private-keys.patch create mode 100644 0006-Allow-specifying-keyfile-password-by-file.patch create mode 100644 0007-Add-support-for-client-certificates-and-dhparams.patch create mode 100644 0008-Fix-client-cert-issues-found-by-CI-tests.patch create mode 100644 0009-Fix-help-message-for-client-key-file.patch create mode 100644 0010-Better-validation-of-command-line-arguments.patch delete mode 100644 EMPTY create mode 100644 sources create mode 100644 sscg.spec diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f2101f8 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/sscg-2.3.3-stripped.tar.xz diff --git a/0001-Generate-manpage.patch b/0001-Generate-manpage.patch new file mode 100644 index 0000000..cc55444 --- /dev/null +++ b/0001-Generate-manpage.patch @@ -0,0 +1,50 @@ +From 71e2451c6ba4d5f17de9e24687b66b93f2e58954 Mon Sep 17 00:00:00 2001 +From: Stephen Gallagher +Date: Mon, 17 Sep 2018 09:58:25 -0400 +Subject: [PATCH 1/6] Generate manpage + +Signed-off-by: Stephen Gallagher +--- + meson.build | 22 +++++++++++++++++++++- + 1 file changed, 21 insertions(+), 1 deletion(-) + +diff --git a/meson.build b/meson.build +index e6f33475cce6891d17656bcd10e1afabd43bdc07..a2ca4ba1472bfff61fbbd30ba1ddc7ecc89e723c 100644 +--- a/meson.build ++++ b/meson.build +@@ -7,7 +7,7 @@ project('sscg', 'c', + 'b_asneeded=true', + ], + license : 'MIT', +- meson_version : '>=0.36.0') ++ meson_version : '>=0.40.0') + + cc = meson.get_compiler('c') + test_cflags = [ +@@ -141,3 +141,23 @@ configure_file( + output : 'config.h', + configuration : cdata) + ++# Generate a manpage from the POPT documentation ++help2man = find_program('help2man') ++ ++manpage = custom_target('manpage', ++ output : 'sscg.8', ++ capture : true, ++ command : [ ++ help2man, ++ '-s', '8', ++ '-n', 'Tool for generating x.509 certificates', ++ '-N', ++ sscg, ++ ], ++ install : true, ++ build_by_default : true, ++ install_dir : join_paths( ++ get_option('prefix'), ++ get_option('mandir'), ++ 'man8'), ++) +-- +2.23.0 + diff --git a/0002-Adjust-defaults-based-on-system-security-level.patch b/0002-Adjust-defaults-based-on-system-security-level.patch new file mode 100644 index 0000000..3fd62ce --- /dev/null +++ b/0002-Adjust-defaults-based-on-system-security-level.patch @@ -0,0 +1,208 @@ +From 942d9fa4f582a372af3d0bd499f073760dec2335 Mon Sep 17 00:00:00 2001 +From: Stephen Gallagher +Date: Tue, 27 Nov 2018 13:24:37 -0500 +Subject: [PATCH 2/6] Adjust defaults based on system security level + +Also permit arbitrary keylengths. + +Disallow keylengths smaller than the configured system minimum. + +Resolves: rhbz#1653323 + +Signed-off-by: Stephen Gallagher +--- + config.h.in | 1 - + include/sscg.h | 1 + + meson.build | 10 ++++++-- + src/sscg.c | 64 ++++++++++++++++++++++++++++++++++++++++++++++---- + 4 files changed, 68 insertions(+), 8 deletions(-) + delete mode 100644 config.h.in + +diff --git a/config.h.in b/config.h.in +deleted file mode 100644 +index 6044a4355f6c8bfac8d36e533f48f395c597e5ac..0000000000000000000000000000000000000000 +--- a/config.h.in ++++ /dev/null +@@ -1 +0,0 @@ +-#define PACKAGE_VERSION "@version@" +diff --git a/include/sscg.h b/include/sscg.h +index 2bd42bbee965c754efb91febd10b6a94af6f508e..3e97cfe49a5cd8fc734ecf43a94156e376227eb7 100644 +--- a/include/sscg.h ++++ b/include/sscg.h +@@ -139,6 +139,7 @@ struct sscg_options + + /* Encryption requirements */ + int key_strength; ++ int minimum_key_strength; + const EVP_MD *hash_fn; + + /* Output Files */ +diff --git a/meson.build b/meson.build +index a2ca4ba1472bfff61fbbd30ba1ddc7ecc89e723c..c7b08ed3d6dff686f08a90ca869ba5881a9e8aaa 100644 +--- a/meson.build ++++ b/meson.build +@@ -34,6 +34,7 @@ endforeach + + pkg = import('pkgconfig') + crypto = dependency('libcrypto') ++ssl = dependency('libssl') + path_utils = dependency('path_utils') + talloc = dependency('talloc') + +@@ -49,6 +50,10 @@ else + popt_incdirs = include_directories('subprojects/popt') + endif + ++has_get_sec_level = cc.has_function( ++ 'SSL_CTX_get_security_level', ++ dependencies: [ ssl]) ++ + sscg_lib_srcs = [ + 'src/authority.c', + 'src/bignum.c', +@@ -70,6 +75,7 @@ sscg_lib = static_library( + sources : sscg_lib_srcs, + dependencies : [ + crypto, ++ ssl, + talloc, + ], + install : false, +@@ -135,9 +141,9 @@ init_bignum_test = executable( + test('init_bignum_test', init_bignum_test) + + cdata = configuration_data() +-cdata.set('version', meson.project_version()) ++cdata.set_quoted('PACKAGE_VERSION', meson.project_version()) ++cdata.set('HAVE_SSL_CTX_GET_SECURITY_LEVEL', has_get_sec_level) + configure_file( +- input : 'config.h.in', + output : 'config.h', + configuration : cdata) + +diff --git a/src/sscg.c b/src/sscg.c +index b2c7cbbfd9dc69d9f55a18bc91ed6023c0e64c2e..85a42404aa94524b560755d506b893300a4414cd 100644 +--- a/src/sscg.c ++++ b/src/sscg.c +@@ -17,6 +17,7 @@ + Copyright 2017 by Stephen Gallagher + */ + ++#define _GNU_SOURCE + #include + #include + #include +@@ -25,6 +26,7 @@ + #include + #include + #include ++#include + #include + + #include "config.h" +@@ -32,11 +34,59 @@ + #include "include/authority.h" + #include "include/service.h" + ++static int ++get_security_level (void) ++{ ++#ifdef HAVE_SSL_CTX_GET_SECURITY_LEVEL ++ SSL_CTX *ssl_ctx = SSL_CTX_new (TLS_method ()); ++ int security_level = SSL_CTX_get_security_level (ssl_ctx); ++ SSL_CTX_free (ssl_ctx); ++ ssl_ctx = NULL; ++ return security_level; ++#else ++ return 0; ++#endif ++} ++ + static int + set_default_options (struct sscg_options *opts) + { ++ int security_level = get_security_level (); ++ + opts->lifetime = 3650; +- opts->key_strength = 2048; ++ ++ /* Select the default key strength based on the system security level ++ * See: ++ * https://www.openssl.org/docs/man1.1.0/ssl/SSL_CTX_get_security_level.html ++ * for the specification of the minimums. ++ */ ++ switch (security_level) ++ { ++ case 0: ++ case 1: ++ case 2: ++ /* Security level 2 and below permits lower key-strengths, but SSCG ++ * will set a minimum of 2048 bits ++ */ ++ opts->key_strength = 2048; ++ break; ++ ++ case 3: opts->key_strength = 3072; break; ++ ++ case 4: opts->key_strength = 7680; break; ++ ++ default: ++ /* Unknown security level. Default to the highest we know about */ ++ fprintf (stderr, ++ "Unknown system security level %d. Defaulting to highest-known " ++ "level.\n", ++ security_level); ++ /* Fall through */ ++ ++ case 5: opts->key_strength = 15360; break; ++ } ++ ++ opts->minimum_key_strength = opts->key_strength; + return 0; + } + +@@ -117,6 +167,7 @@ main (int argc, const char **argv) + size_t i; + poptContext pc; + struct sscg_options *options; ++ char *minimum_key_strength_help = NULL; + + char *country = NULL; + char *state = NULL; +@@ -172,6 +223,9 @@ main (int argc, const char **argv) + if (ret != EOK) + goto done; + ++ minimum_key_strength_help = ++ talloc_asprintf (main_ctx, "%d or larger", options->minimum_key_strength); ++ + options->verbosity = SSCG_DEFAULT; + struct poptOption long_options[] = { + POPT_AUTOHELP{ "quiet", +@@ -293,7 +347,7 @@ main (int argc, const char **argv) + &options->key_strength, + 0, + _ ("Strength of the certificate private keys in bits."), +- _ ("{512,1024,2048,4096}") }, ++ minimum_key_strength_help }, + { + "hash-alg", + '\0', +@@ -529,11 +583,11 @@ main (int argc, const char **argv) + } + } + +- if (options->key_strength != 512 && options->key_strength != 1024 && +- options->key_strength != 2048 && options->key_strength != 4096) ++ if (options->key_strength < options->minimum_key_strength) + { + fprintf (stderr, +- "Key strength must be one of {512, 1024, 2048, 4096}.\n"); ++ "Key strength must be at least %d bits.\n", ++ options->minimum_key_strength); + ret = EINVAL; + goto done; + } +-- +2.23.0 + diff --git a/0003-Adjust-hash-defaults-based-on-system-security-level.patch b/0003-Adjust-hash-defaults-based-on-system-security-level.patch new file mode 100644 index 0000000..66e8224 --- /dev/null +++ b/0003-Adjust-hash-defaults-based-on-system-security-level.patch @@ -0,0 +1,115 @@ +From 298015e8a7cf35cc0de581203b44826d2ae1d406 Mon Sep 17 00:00:00 2001 +From: Stephen Gallagher +Date: Wed, 28 Nov 2018 08:00:08 -0500 +Subject: [PATCH 3/6] Adjust hash defaults based on system security level + +Unlike the key-strength, this does not set a minimum level because +it's not a simple calculation. We will have to rely on libcrypto +rejecting any explicitly-set algorithms as a violation of policy. + +Signed-off-by: Stephen Gallagher +--- + include/sscg.h | 1 + + src/sscg.c | 40 +++++++++++++++++++++------------------- + 2 files changed, 22 insertions(+), 19 deletions(-) + +diff --git a/include/sscg.h b/include/sscg.h +index 3e97cfe49a5cd8fc734ecf43a94156e376227eb7..fc90b81a0060af28529f3be6922b1b1501559300 100644 +--- a/include/sscg.h ++++ b/include/sscg.h +@@ -140,6 +140,7 @@ struct sscg_options + /* Encryption requirements */ + int key_strength; + int minimum_key_strength; ++ char *hash_alg; + const EVP_MD *hash_fn; + + /* Output Files */ +diff --git a/src/sscg.c b/src/sscg.c +index 85a42404aa94524b560755d506b893300a4414cd..58855f764480d24d6c0f57460b22a3a83281e37e 100644 +--- a/src/sscg.c ++++ b/src/sscg.c +@@ -66,14 +66,21 @@ set_default_options (struct sscg_options *opts) + case 1: + case 2: + /* Security level 2 and below permits lower key-strengths, but SSCG +- * will set a minimum of 2048 bits ++ * will set a minimum of 2048 bits and the sha256 hash algorithm. + */ ++ opts->hash_alg = talloc_strdup (opts, "sha256"); + opts->key_strength = 2048; + break; + +- case 3: opts->key_strength = 3072; break; ++ case 3: ++ opts->hash_alg = talloc_strdup (opts, "sha256"); ++ opts->key_strength = 3072; ++ break; + +- case 4: opts->key_strength = 7680; break; ++ case 4: ++ opts->hash_alg = talloc_strdup (opts, "sha384"); ++ opts->key_strength = 7680; ++ break; + + default: + /* Unknown security level. Default to the highest we know about */ +@@ -83,7 +90,10 @@ set_default_options (struct sscg_options *opts) + security_level); + /* Fall through */ + +- case 5: opts->key_strength = 15360; break; ++ case 5: ++ opts->hash_alg = talloc_strdup (opts, "sha512"); ++ opts->key_strength = 15360; ++ break; + } + + opts->minimum_key_strength = opts->key_strength; +@@ -177,7 +187,6 @@ main (int argc, const char **argv) + char *email = NULL; + char *hostname = NULL; + char *packagename; +- char *hash_alg = NULL; + char **alternative_names = NULL; + + char *ca_file = NULL; +@@ -351,10 +360,10 @@ main (int argc, const char **argv) + { + "hash-alg", + '\0', +- POPT_ARG_STRING, +- &hash_alg, ++ POPT_ARG_STRING | POPT_ARGFLAG_SHOW_DEFAULT, ++ &options->hash_alg, + 0, +- _ ("Hashing algorithm to use for signing. (default: sha256)"), ++ _ ("Hashing algorithm to use for signing."), + _ ("{sha256,sha384,sha512}"), + }, + { +@@ -592,17 +601,10 @@ main (int argc, const char **argv) + goto done; + } + +- if (!hash_alg) +- { +- /* Default to SHA256 */ +- options->hash_fn = EVP_sha256 (); +- } +- else +- { +- /* 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 (hash_alg); +- } ++ /* 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); ++ + if (!options->hash_fn) + { + fprintf (stderr, "Unsupported hashing algorithm."); +-- +2.23.0 + diff --git a/0004-Properly-check-all-return-values.patch b/0004-Properly-check-all-return-values.patch new file mode 100644 index 0000000..9225fe7 --- /dev/null +++ b/0004-Properly-check-all-return-values.patch @@ -0,0 +1,51 @@ +From 9e4497d1dd2a337be1f69e0cfb24ce8080690ccf Mon Sep 17 00:00:00 2001 +From: Stephen Gallagher +Date: Wed, 28 Nov 2018 09:16:29 -0500 +Subject: [PATCH 4/6] Properly check all return values + +Signed-off-by: Stephen Gallagher +--- + src/authority.c | 1 + + src/service.c | 1 + + src/x509.c | 1 + + 3 files changed, 3 insertions(+) + +diff --git a/src/authority.c b/src/authority.c +index b735868416b7fb5d016f0854baf0f27cd5f98b26..4e0dccc6c1210beffb38acd9f7dfb6108ca4a4ad 100644 +--- a/src/authority.c ++++ b/src/authority.c +@@ -180,6 +180,7 @@ create_private_CA (TALLOC_CTX *mem_ctx, + + /* Finalize the CSR */ + ret = sscg_x509v3_csr_finalize (ca_certinfo, pkey, csr); ++ CHECK_OK (ret); + + if (options->verbosity >= SSCG_DEBUG) + { +diff --git a/src/service.c b/src/service.c +index b292e94063f032fd3c34a8134702063ea46bfa0c..34c976dbe905528000b181c24d1fa95da3cd1377 100644 +--- a/src/service.c ++++ b/src/service.c +@@ -126,6 +126,7 @@ create_service_cert (TALLOC_CTX *mem_ctx, + + /* Finalize the CSR */ + ret = sscg_x509v3_csr_finalize (svc_certinfo, pkey, csr); ++ CHECK_OK (ret); + + if (options->verbosity >= SSCG_DEBUG) + { +diff --git a/src/x509.c b/src/x509.c +index 6d152fc969d745cc5cf085116c8688866f9d6ab4..18f0627bc64e7cb503a9e81c36dbe726186d1144 100644 +--- a/src/x509.c ++++ b/src/x509.c +@@ -41,6 +41,7 @@ sscg_generate_serial (TALLOC_CTX *mem_ctx, struct sscg_bignum **serial) + } + + ret = sscg_init_bignum (tmp_ctx, 0, &bn); ++ CHECK_OK (ret); + + /* We'll create a random number of sizeof(unsigned long) - 1 bits + to use as the serial. We use unsigned long to ensure that it +-- +2.23.0 + diff --git a/0005-Add-password-support-for-private-keys.patch b/0005-Add-password-support-for-private-keys.patch new file mode 100644 index 0000000..4e21a9a --- /dev/null +++ b/0005-Add-password-support-for-private-keys.patch @@ -0,0 +1,273 @@ +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 + diff --git a/0006-Allow-specifying-keyfile-password-by-file.patch b/0006-Allow-specifying-keyfile-password-by-file.patch new file mode 100644 index 0000000..6487436 --- /dev/null +++ b/0006-Allow-specifying-keyfile-password-by-file.patch @@ -0,0 +1,153 @@ +From 9cb7daa54708dcf5e6500cd20ec7b1cc2f6f6350 Mon Sep 17 00:00:00 2001 +From: Stephen Gallagher +Date: Mon, 10 Jun 2019 10:15:42 -0400 +Subject: [PATCH 6/6] Allow specifying keyfile password by file + +Signed-off-by: Stephen Gallagher +--- + src/sscg.c | 84 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 84 insertions(+) + +diff --git a/src/sscg.c b/src/sscg.c +index 9dc926c77038105ca881a612cccd1913bc2d42f1..a02e4df66c6cf9ec1865f425b4a15da82fbfdc72 100644 +--- a/src/sscg.c ++++ b/src/sscg.c +@@ -34,6 +34,10 @@ + #include "include/authority.h" + #include "include/service.h" + ++ ++/* Same as OpenSSL CLI */ ++#define MAX_PW_LEN 1024 ++ + static int + get_security_level (void) + { +@@ -209,6 +213,44 @@ sscg_options_destructor (TALLOC_CTX *opts) + } + + ++static char * ++sscg_read_pw_file (TALLOC_CTX *mem_ctx, char *path) ++{ ++ int i; ++ BIO *pwdbio = NULL; ++ char tpass[MAX_PW_LEN]; ++ char *tmp = NULL; ++ char *password = NULL; ++ ++ pwdbio = BIO_new_file (path, "r"); ++ if (pwdbio == NULL) ++ { ++ fprintf (stderr, "Can't open file %s\n", path); ++ return NULL; ++ } ++ ++ i = BIO_gets (pwdbio, tpass, MAX_PW_LEN); ++ BIO_free_all (pwdbio); ++ pwdbio = NULL; ++ ++ if (i <= 0) ++ { ++ fprintf (stderr, "Error reading password from BIO\n"); ++ return NULL; ++ } ++ ++ tmp = strchr (tpass, '\n'); ++ if (tmp != NULL) ++ *tmp = 0; ++ ++ password = talloc_strdup (mem_ctx, tpass); ++ ++ memset (tpass, 0, MAX_PW_LEN); ++ ++ return password; ++} ++ ++ + int + main (int argc, const char **argv) + { +@@ -236,10 +278,12 @@ main (int argc, const char **argv) + int ca_mode = 0644; + int ca_key_mode = 0600; + char *ca_key_password = NULL; ++ char *ca_key_passfile = NULL; + + int cert_mode = 0644; + int cert_key_mode = 0600; + char *cert_key_password = NULL; ++ char *cert_key_passfile = NULL; + + char *create_mode = NULL; + +@@ -470,6 +514,16 @@ main (int argc, const char **argv) + NULL + }, + ++ { ++ "ca-key-passfile", ++ '\0', ++ POPT_ARG_STRING, ++ &ca_key_passfile, ++ 0, ++ _ ("A file containing the password to encrypt the CA key file."), ++ NULL ++ }, ++ + { + "ca-key-password-prompt", + 'C', +@@ -531,6 +585,16 @@ main (int argc, const char **argv) + NULL + }, + ++ { ++ "cert-key-passfile", ++ '\0', ++ POPT_ARG_STRING, ++ &cert_key_passfile, ++ 0, ++ _ ("A file containing the password to encrypt the service key file."), ++ NULL ++ }, ++ + { + "cert-key-password-prompt", + 'P', +@@ -697,12 +761,32 @@ main (int argc, const char **argv) + options->ca_key_pass = + sscg_secure_string_steal (options, ca_key_password); + } ++ else if (ca_key_passfile) ++ { ++ options->ca_key_pass = sscg_read_pw_file (options, ca_key_passfile); ++ if (!options->ca_key_pass) ++ { ++ fprintf ( ++ stderr, "Failed to read passphrase from %s", ca_key_passfile); ++ goto done; ++ } ++ } + + if (cert_key_password) + { + options->cert_key_pass = + sscg_secure_string_steal (options, cert_key_password); + } ++ else if (cert_key_passfile) ++ { ++ options->cert_key_pass = sscg_read_pw_file (options, cert_key_passfile); ++ if (!options->cert_key_pass) ++ { ++ fprintf ( ++ stderr, "Failed to read passphrase from %s", cert_key_passfile); ++ goto done; ++ } ++ } + + + if (options->key_strength < options->minimum_key_strength) +-- +2.23.0 + diff --git a/0007-Add-support-for-client-certificates-and-dhparams.patch b/0007-Add-support-for-client-certificates-and-dhparams.patch new file mode 100644 index 0000000..e22236e --- /dev/null +++ b/0007-Add-support-for-client-certificates-and-dhparams.patch @@ -0,0 +1,2651 @@ +From ceed1c19b6002164482eb358570a91a9563ce694 Mon Sep 17 00:00:00 2001 +From: Tim Burke +Date: Wed, 2 Oct 2019 13:10:23 -0700 +Subject: [PATCH 7/7] Add support for client certificates and dhparams + +Resolves: rhbz#1720667 + +Add --crl-file option + +... to (optionally) create an empty Certificate Revocation List file. + +Default the mode for the file to 0644, similar to the default for the CA +certificate. User can override this with a new --crl-mode option. + +Closes #10. + +Add function for DH parameter generation + +Signed-off-by: Stephen Gallagher + +Generate DH parameters file + +Adds CLI options to enable and control dhparam file generation. + +Signed-off-by: Stephen Gallagher + +Add serverAuth extendedKeyUsage for server certificates + +Related to https://github.com/sgallagher/sscg/issues/13 + +Signed-off-by: Stephen Gallagher + +Rename create_service_cert() to create_cert() + +Signed-off-by: Stephen Gallagher + +Use a common macro for default file modes + +Signed-off-by: Stephen Gallagher + +Add I/O utility routines + +Signed-off-by: Stephen Gallagher + +Rework output file handling + +SSCG will now verify that it can open all of the files before it +starts processing through them. In addition, it now has better +validation that it isn't storing two keys into the same file, or +dhparams/CRL in a file with other content. + +Signed-off-by: Stephen Gallagher + +Check for invalid file combinations + +Signed-off-by: Stephen Gallagher + +Add support for client certificates + +Fixes: https://github.com/sgallagher/sscg/issues/13 + +Signed-off-by: Stephen Gallagher +--- + include/{service.h => cert.h} | 19 +- + include/dhparams.h | 42 ++ + include/io_utils.h | 93 +++++ + include/sscg.h | 99 ++++- + meson.build | 51 ++- + meson_options.txt | 24 ++ + src/cert.c | 191 +++++++++ + src/dhparams.c | 146 +++++++ + src/io_utils.c | 424 +++++++++++++++++++ + src/service.c | 164 -------- + src/sscg.c | 753 +++++++++++++++++++++------------- + test/dhparams_test.c | 106 +++++ + 12 files changed, 1651 insertions(+), 461 deletions(-) + rename include/{service.h => cert.h} (67%) + create mode 100644 include/dhparams.h + create mode 100644 include/io_utils.h + create mode 100644 meson_options.txt + create mode 100644 src/cert.c + create mode 100644 src/dhparams.c + create mode 100644 src/io_utils.c + delete mode 100644 src/service.c + create mode 100644 test/dhparams_test.c + +diff --git a/include/service.h b/include/cert.h +similarity index 67% +rename from include/service.h +rename to include/cert.h +index 20083caf905f8e70360539053134c0e9702c304a..11d04e50ebb2e08af781d0aa14b9aec930ff2f50 100644 +--- a/include/service.h ++++ b/include/cert.h +@@ -21,15 +21,16 @@ + #include "x509.h" + #include "key.h" + +-#ifndef _SERVICE_H +-#define _SERVICE_H ++#ifndef _SSCG_CERT_H ++#define _SSCG_CERT_H + + int +-create_service_cert (TALLOC_CTX *mem_ctx, +- const struct sscg_options *options, +- struct sscg_x509_cert *ca_cert, +- struct sscg_evp_pkey *ca_key, +- struct sscg_x509_cert **_svc_cert, +- struct sscg_evp_pkey **_svc_key); ++create_cert (TALLOC_CTX *mem_ctx, ++ const struct sscg_options *options, ++ struct sscg_x509_cert *ca_cert, ++ struct sscg_evp_pkey *ca_key, ++ enum sscg_cert_type type, ++ struct sscg_x509_cert **_svc_cert, ++ struct sscg_evp_pkey **_svc_key); + +-#endif /* _SERVICE_H */ ++#endif /* _SSCG_CERT_H */ +diff --git a/include/dhparams.h b/include/dhparams.h +new file mode 100644 +index 0000000000000000000000000000000000000000..baa4fb3b297c5747fcce20f6950239779f71f19a +--- /dev/null ++++ b/include/dhparams.h +@@ -0,0 +1,42 @@ ++/* ++ This file is part of sscg. ++ ++ sscg 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 3 of the License, or ++ (at your option) any later version. ++ ++ sscg 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 sscg. If not, see . ++ ++ Copyright 2019 by Stephen Gallagher ++*/ ++ ++#ifndef _SSCG_DHPARAMS_H ++#define _SSCG_DHPARAMS_H ++ ++#include ++ ++#include "include/sscg.h" ++ ++struct sscg_dhparams ++{ ++ int prime_len; ++ int generator; ++ DH *dh; ++ BN_GENCB *cb; ++}; ++ ++int ++create_dhparams (TALLOC_CTX *mem_ctx, ++ enum sscg_verbosity options, ++ int prime_len, ++ int generator, ++ struct sscg_dhparams **_dhparams); ++ ++#endif /* _SSCG_DHPARAMS_H */ +diff --git a/include/io_utils.h b/include/io_utils.h +new file mode 100644 +index 0000000000000000000000000000000000000000..6a89a476b3d982447b6603153c6765835cd67464 +--- /dev/null ++++ b/include/io_utils.h +@@ -0,0 +1,93 @@ ++/* ++ This file is part of sscg. ++ ++ sscg 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 3 of the License, or ++ (at your option) any later version. ++ ++ sscg 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 sscg. If not, see . ++ ++ Copyright 2019 by Stephen Gallagher ++*/ ++ ++#ifndef _SSCG_IO_UTILS_H ++#define _SSCG_IO_UTILS_H ++ ++#include ++#include ++#include ++ ++#include "include/sscg.h" ++ ++ ++struct sscg_stream ++{ ++ BIO *bio; ++ char *path; ++ int mode; ++ int filetypes; ++}; ++ ++ ++int ++sscg_normalize_path (TALLOC_CTX *mem_ctx, ++ const char *path, ++ char **_normalized_path); ++ ++ ++struct sscg_stream * ++sscg_io_utils_get_stream_by_path (struct sscg_stream **streams, ++ const char *normalized_path); ++ ++ ++struct sscg_stream * ++sscg_io_utils_get_stream_by_type (struct sscg_stream **streams, ++ enum sscg_file_type filetype); ++ ++ ++BIO * ++sscg_io_utils_get_bio_by_type (struct sscg_stream **streams, ++ enum sscg_file_type filetype); ++ ++ ++const char * ++sscg_io_utils_get_path_by_type (struct sscg_stream **streams, ++ enum sscg_file_type filetype); ++ ++ ++/** ++ * sscg_io_utils_add_output_file: ++ * @streams: The array of streams from the sscg_options ++ * @filetype: ++ * @path: The path to the file on disk. ++ * @mode: The filesystem mode this file should have when written to disk. ++ * See chmod(1) for the possible values. ++ * @overwrite: If true, replace any existing file at @normalized_path. If ++ * false, opening will fail if it already exists and return an error. ++ * ++ * Prepares all output filenames to be opened. Files are not created until ++ * sscg_io_utils_open_output_files() is called. ++ */ ++int ++sscg_io_utils_add_output_file (struct sscg_stream **streams, ++ enum sscg_file_type filetype, ++ const char *path, ++ int mode); ++ ++ ++int ++sscg_io_utils_open_output_files (struct sscg_stream **streams, bool overwrite); ++ ++/* If this function fails, some of the output files may be left as 0400 */ ++int ++sscg_io_utils_finalize_output_files (struct sscg_stream **streams); ++ ++ ++#endif /* _SSCG_IO_UTILS_H */ +diff --git a/include/sscg.h b/include/sscg.h +index ce9a7916e9432d0843d82af61d56ea7238ded682..2744404c25c68ed905ca621bb955e0c04b33ca81 100644 +--- a/include/sscg.h ++++ b/include/sscg.h +@@ -27,6 +27,8 @@ + #include + #include + ++#include "include/io_utils.h" ++ + #ifndef _SSCG_H + #define _SSCG_H + +@@ -108,6 +110,13 @@ + } \ + while (0) + ++ ++#define SSCG_CERT_DEFAULT_MODE 0644 ++#define SSCG_CERT_DEFAULT_MODE_HELP _ ("0644") ++#define SSCG_KEY_DEFAULT_MODE 0600 ++#define SSCG_KEY_DEFAULT_MODE_HELP _ ("0600") ++ ++ + enum sscg_verbosity + { + SSCG_QUIET = -1, +@@ -116,6 +125,75 @@ enum sscg_verbosity + SSCG_DEBUG + }; + ++extern int verbosity; ++ ++const char *sscg_get_verbosity_name (enum sscg_verbosity); ++ ++#define SSCG_LOG(_level, _format, ...) \ ++ do \ ++ { \ ++ if (verbosity >= _level) \ ++ { \ ++ printf ("%s", sscg_get_verbosity_name (_level)); \ ++ printf (_format, ##__VA_ARGS__); \ ++ } \ ++ } \ ++ while (0) ++ ++#define SSCG_ERROR(_format, ...) \ ++ do \ ++ { \ ++ if (verbosity > SSCG_QUIET) \ ++ { \ ++ fprintf (stderr, "ERROR: "); \ ++ fprintf (stderr, _format, ##__VA_ARGS__); \ ++ } \ ++ } \ ++ while (0) ++ ++ ++enum sscg_file_type ++{ ++ SSCG_FILE_TYPE_UNKNOWN = -1, ++ SSCG_FILE_TYPE_CA, ++ SSCG_FILE_TYPE_CA_KEY, ++ SSCG_FILE_TYPE_SVC, ++ SSCG_FILE_TYPE_SVC_KEY, ++ SSCG_FILE_TYPE_CLIENT, ++ SSCG_FILE_TYPE_CLIENT_KEY, ++ SSCG_FILE_TYPE_CRL, ++ SSCG_FILE_TYPE_DHPARAMS, ++ ++ SSCG_NUM_FILE_TYPES ++}; ++ ++#define SSCG_FILE_TYPE_KEYS \ ++ ((1 << SSCG_FILE_TYPE_CA_KEY) | (1 << SSCG_FILE_TYPE_SVC_KEY) | \ ++ (1 << SSCG_FILE_TYPE_CLIENT_KEY)) ++ ++#define SSCG_FILE_TYPE_SVC_TYPES \ ++ ((1 << SSCG_FILE_TYPE_SVC) | (1 << SSCG_FILE_TYPE_SVC_KEY)) ++ ++#define SSCG_FILE_TYPE_CLIENT_TYPES \ ++ ((1 << SSCG_FILE_TYPE_CLIENT) | (1 << SSCG_FILE_TYPE_CLIENT_KEY)) ++ ++#define SSCG_FILE_TYPE_CA_TYPES \ ++ ((1 << SSCG_FILE_TYPE_CA) | (1 << SSCG_FILE_TYPE_CA_KEY)) ++ ++const char * ++sscg_get_file_type_name (enum sscg_file_type _type); ++ ++#define GET_BIO(_type) sscg_io_utils_get_bio_by_type (options->streams, _type) ++ ++#define GET_PATH(_type) \ ++ sscg_io_utils_get_path_by_type (options->streams, _type) ++ ++#define ANNOUNCE_WRITE(_type) \ ++ SSCG_LOG (SSCG_DEFAULT, \ ++ "Wrote %s to %s\n", \ ++ sscg_get_file_type_name (_type), \ ++ GET_PATH (_type)); ++ + struct sscg_options + { + /* How noisy to be when printing information */ +@@ -149,15 +227,28 @@ struct sscg_options + char *ca_key_pass; + bool cert_key_pass_prompt; + char *cert_key_pass; ++ bool client_key_pass_prompt; ++ char *client_key_pass; + + /* Output Files */ +- char *ca_file; +- char *ca_key_file; +- char *cert_file; +- char *cert_key_file; ++ struct sscg_stream **streams; ++ ++ /* Diffie-Hellman Parameters */ ++ int dhparams_prime_len; ++ int dhparams_generator; + + /* Overwrite the output files */ + bool overwrite; + }; + ++ ++enum sscg_cert_type ++{ ++ SSCG_CERT_TYPE_UNKNOWN = -1, ++ SSCG_CERT_TYPE_SERVER, ++ SSCG_CERT_TYPE_CLIENT, ++ ++ SSCG_NUM_CERT_TYPES ++}; ++ + #endif /* _SSCG_H */ +diff --git a/meson.build b/meson.build +index c7b08ed3d6dff686f08a90ca869ba5881a9e8aaa..eb339ea8c768adab6d576736fbe476b83529e78d 100644 +--- a/meson.build ++++ b/meson.build +@@ -52,21 +52,30 @@ endif + + has_get_sec_level = cc.has_function( + 'SSL_CTX_get_security_level', +- dependencies: [ ssl]) ++ dependencies: [ ssl ]) ++ ++has_generator_3 = cc.has_header_symbol( ++ 'openssl/dh.h', ++ 'DH_GENERATOR_3', ++ dependencies: [ ssl ]) + + sscg_lib_srcs = [ + 'src/authority.c', + 'src/bignum.c', ++ 'src/cert.c', ++ 'src/dhparams.c', ++ 'src/io_utils.c', + 'src/key.c', +- 'src/service.c', + 'src/x509.c', + ] + + sscg_lib_hdrs = [ + 'include/authority.h', + 'include/bignum.h', ++ 'include/cert.h', ++ 'include/dhparams.h', ++ 'include/io_utils.h', + 'include/key.h', +- 'include/service.h', + 'include/x509.h', + ] + +@@ -140,6 +149,42 @@ init_bignum_test = executable( + ) + test('init_bignum_test', init_bignum_test) + ++dhparams_test = executable( ++ 'dhparams_test', ++ 'test/dhparams_test.c', ++ link_with : sscg_lib, ++ dependencies: [], ++ install : false ++) ++ ++# Test generating 512-bit, 1024-bit and 2048-bit DH params with multiple ++# generators. 4096-bit and larger takes over ten minutes, so it's excluded from ++# the test suite by default. ++prime_lengths = [ 512, 1024 ] ++dhparam_timeout = 120 ++ ++if get_option('run_slow_tests') ++ prime_lengths = prime_lengths + [ 2048, 4096 ] ++ dhparam_timeout = 900 ++endif ++ ++generators = [ 2, 5 ] ++ ++if (has_generator_3) ++ generators += [ 3 ] ++endif ++ ++foreach prime_len : prime_lengths ++ foreach g : generators ++ test('dhparams_test_' + prime_len.to_string() + '_' + g.to_string(), ++ dhparams_test, ++ args: [ prime_len.to_string(), g.to_string() ], ++ timeout: dhparam_timeout) ++ endforeach ++endforeach ++ ++ ++ + cdata = configuration_data() + cdata.set_quoted('PACKAGE_VERSION', meson.project_version()) + cdata.set('HAVE_SSL_CTX_GET_SECURITY_LEVEL', has_get_sec_level) +diff --git a/meson_options.txt b/meson_options.txt +new file mode 100644 +index 0000000000000000000000000000000000000000..1d8ff959b3c3d3f2aa6dc928ba38efeedfbe5c96 +--- /dev/null ++++ b/meson_options.txt +@@ -0,0 +1,24 @@ ++# This file is part of sscg. ++# ++# sscg 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 3 of the License, or ++# (at your option) any later version. ++# ++# sscg 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 sscg. If not, see . ++# ++# Copyright 2019 by Stephen Gallagher ++ ++# Generating 4096-bit Diffie-Hellman parameters can take over ten minutes on a ++# fast system. We skip testing it by default. ++ ++# Some tests take a long time (dozens of seconds or even minutes) ++# For general development, we will skip them and run them only in the CI ++# environment. ++option('run_slow_tests', type : 'boolean', value : false) +diff --git a/src/cert.c b/src/cert.c +new file mode 100644 +index 0000000000000000000000000000000000000000..0377d1357a3881a9705fcb09fdfe2a9c78cc5ed4 +--- /dev/null ++++ b/src/cert.c +@@ -0,0 +1,191 @@ ++/* ++ This file is part of sscg. ++ ++ sscg 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 3 of the License, or ++ (at your option) any later version. ++ ++ sscg 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 sscg. If not, see . ++ ++ Copyright 2017 by Stephen Gallagher ++*/ ++ ++ ++#include "include/sscg.h" ++#include "include/cert.h" ++#include "include/x509.h" ++#include "include/key.h" ++ ++int ++create_cert (TALLOC_CTX *mem_ctx, ++ const struct sscg_options *options, ++ struct sscg_x509_cert *ca_cert, ++ struct sscg_evp_pkey *ca_key, ++ enum sscg_cert_type type, ++ struct sscg_x509_cert **_cert, ++ struct sscg_evp_pkey **_key) ++{ ++ int ret; ++ size_t i; ++ struct sscg_bignum *e; ++ struct sscg_bignum *serial; ++ struct sscg_cert_info *certinfo; ++ struct sscg_x509_req *csr; ++ struct sscg_evp_pkey *pkey; ++ struct sscg_x509_cert *cert; ++ X509_EXTENSION *ex = NULL; ++ EXTENDED_KEY_USAGE *extended; ++ TALLOC_CTX *tmp_ctx = NULL; ++ ++ tmp_ctx = talloc_new (NULL); ++ CHECK_MEM (tmp_ctx); ++ ++ /* create a serial number for this certificate */ ++ ret = sscg_generate_serial (tmp_ctx, &serial); ++ CHECK_OK (ret); ++ ++ certinfo = sscg_cert_info_new (tmp_ctx, options->hash_fn); ++ CHECK_MEM (certinfo); ++ ++ /* Populate cert_info from options */ ++ certinfo->country = talloc_strdup (certinfo, options->country); ++ CHECK_MEM (certinfo->country); ++ ++ certinfo->state = talloc_strdup (certinfo, options->state); ++ CHECK_MEM (certinfo->state); ++ ++ certinfo->locality = talloc_strdup (certinfo, options->locality); ++ CHECK_MEM (certinfo->locality); ++ ++ certinfo->org = talloc_strdup (certinfo, options->org); ++ CHECK_MEM (certinfo->org); ++ ++ certinfo->org_unit = talloc_strdup (certinfo, options->org_unit); ++ CHECK_MEM (certinfo->org_unit); ++ ++ certinfo->email = talloc_strdup (certinfo, options->email); ++ CHECK_MEM (certinfo->email); ++ ++ certinfo->cn = talloc_strdup (certinfo, options->hostname); ++ CHECK_MEM (certinfo->cn); ++ ++ if (options->subject_alt_names) ++ { ++ for (i = 0; options->subject_alt_names[i]; i++) ++ { ++ certinfo->subject_alt_names = talloc_realloc ( ++ certinfo, certinfo->subject_alt_names, char *, i + 2); ++ CHECK_MEM (certinfo->subject_alt_names); ++ ++ certinfo->subject_alt_names[i] = talloc_strdup ( ++ certinfo->subject_alt_names, options->subject_alt_names[i]); ++ CHECK_MEM (certinfo->subject_alt_names[i]); ++ ++ /* Add a NULL terminator to the end */ ++ certinfo->subject_alt_names[i + 1] = NULL; ++ } ++ } ++ ++ /* Ensure that this certificate may not sign other certificates */ ++ /* Add key extensions */ ++ ex = X509V3_EXT_conf_nid ( ++ NULL, NULL, NID_key_usage, "critical,digitalSignature,keyEncipherment"); ++ CHECK_MEM (ex); ++ sk_X509_EXTENSION_push (certinfo->extensions, ex); ++ ++ extended = sk_ASN1_OBJECT_new_null (); ++ ++ switch (type) ++ { ++ case SSCG_CERT_TYPE_SERVER: ++ sk_ASN1_OBJECT_push (extended, OBJ_nid2obj (NID_server_auth)); ++ break; ++ ++ case SSCG_CERT_TYPE_CLIENT: ++ sk_ASN1_OBJECT_push (extended, OBJ_nid2obj (NID_client_auth)); ++ break; ++ ++ default: ++ fprintf (stdout, "Unknown certificate type!"); ++ ret = EINVAL; ++ goto done; ++ } ++ ++ ++ ex = X509V3_EXT_i2d (NID_ext_key_usage, 0, extended); ++ sk_ASN1_OBJECT_pop_free (extended, ASN1_OBJECT_free); ++ sk_X509_EXTENSION_push (certinfo->extensions, ex); ++ ++ /* Mark it as not a CA */ ++ ex = X509V3_EXT_conf_nid (NULL, NULL, NID_basic_constraints, "CA:FALSE"); ++ CHECK_MEM (ex); ++ sk_X509_EXTENSION_push (certinfo->extensions, ex); ++ ++ /* Use an exponent value of RSA F4 aka 0x10001 (65537) */ ++ ret = sscg_init_bignum (tmp_ctx, RSA_F4, &e); ++ CHECK_OK (ret); ++ ++ /* Generate an RSA keypair for this CA */ ++ if (options->verbosity >= SSCG_VERBOSE) ++ { ++ fprintf (stdout, "Generating RSA key for certificate.\n"); ++ } ++ /* TODO: support DSA keys as well */ ++ ret = sscg_generate_rsa_key (tmp_ctx, options->key_strength, e, &pkey); ++ CHECK_OK (ret); ++ ++ /* Create a certificate signing request for the private CA */ ++ if (options->verbosity >= SSCG_VERBOSE) ++ { ++ fprintf (stdout, "Generating CSR for certificate.\n"); ++ } ++ ret = sscg_x509v3_csr_new (tmp_ctx, certinfo, pkey, &csr); ++ CHECK_OK (ret); ++ ++ /* Finalize the CSR */ ++ ret = sscg_x509v3_csr_finalize (certinfo, pkey, csr); ++ CHECK_OK (ret); ++ ++ if (options->verbosity >= SSCG_DEBUG) ++ { ++ const char *tempcert = ++ SSCG_CERT_TYPE_SERVER ? "./debug-service.csr" : "debug-client.csr"; ++ ++ fprintf (stderr, "DEBUG: Writing certificate CSR to %s\n", tempcert); ++ BIO *csr_out = BIO_new_file (tempcert, "w"); ++ int sslret = PEM_write_bio_X509_REQ (csr_out, csr->x509_req); ++ CHECK_SSL (sslret, PEM_write_bio_X509_REQ); ++ } ++ ++ /* Sign the certificate */ ++ ++ if (options->verbosity >= SSCG_VERBOSE) ++ { ++ fprintf (stdout, "Signing CSR for certificate. \n"); ++ } ++ ++ ret = sscg_sign_x509_csr (tmp_ctx, ++ csr, ++ serial, ++ options->lifetime, ++ ca_cert, ++ ca_key, ++ options->hash_fn, ++ &cert); ++ CHECK_OK (ret); ++ ++ *_cert = talloc_steal (mem_ctx, cert); ++ *_key = talloc_steal (mem_ctx, pkey); ++ ++ ret = EOK; ++done: ++ talloc_free (tmp_ctx); ++ return ret; ++} +diff --git a/src/dhparams.c b/src/dhparams.c +new file mode 100644 +index 0000000000000000000000000000000000000000..f9b64629709beb857a948a7f2f42eb805d76c557 +--- /dev/null ++++ b/src/dhparams.c +@@ -0,0 +1,146 @@ ++/* ++ This file is part of sscg. ++ ++ sscg 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 3 of the License, or ++ (at your option) any later version. ++ ++ sscg 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 sscg. If not, see . ++ ++ Copyright 2019 by Stephen Gallagher ++*/ ++ ++#include ++ ++#include "include/sscg.h" ++#include "include/dhparams.h" ++ ++ ++static int ++_sscg_dhparams_destructor (TALLOC_CTX *ctx); ++ ++static int ++dh_cb (int p, int n, BN_GENCB *cb); ++ ++int ++create_dhparams (TALLOC_CTX *mem_ctx, ++ enum sscg_verbosity verbosity, ++ int prime_len, ++ int generator, ++ struct sscg_dhparams **_dhparams) ++{ ++ int ret; ++ struct sscg_dhparams *dhparams = NULL; ++ TALLOC_CTX *tmp_ctx = NULL; ++ ++ /* First validate the input */ ++ assert (_dhparams && !*_dhparams); ++ ++ if (prime_len <= 0) ++ { ++ fprintf (stderr, "Prime length must be a positive integer"); ++ ret = ERANGE; ++ goto done; ++ } ++ ++ if (generator <= 0) ++ { ++ fprintf (stderr, "Generator must be a positive integer"); ++ ret = ERANGE; ++ goto done; ++ } ++ ++ tmp_ctx = talloc_new (NULL); ++ CHECK_MEM (tmp_ctx); ++ ++ dhparams = talloc_zero (tmp_ctx, struct sscg_dhparams); ++ CHECK_MEM (dhparams); ++ ++ dhparams->prime_len = prime_len; ++ dhparams->generator = generator; ++ talloc_set_destructor ((TALLOC_CTX *)dhparams, _sscg_dhparams_destructor); ++ ++ if (verbosity >= SSCG_DEFAULT) ++ { ++ fprintf (stdout, ++ "Generating DH parameters of length %d and generator %d. " ++ "This will take a long time.\n", ++ dhparams->prime_len, ++ dhparams->generator); ++ } ++ ++ dhparams->dh = DH_new (); ++ ++ if (verbosity >= SSCG_VERBOSE) ++ { ++#if OPENSSL_VERSION_NUMBER < 0x10100000L ++ dhparams->cb = talloc_zero (dhparams, BN_GENCB); ++#else ++ dhparams->cb = BN_GENCB_new (); ++#endif ++ if (dhparams->cb == NULL) ++ { ++ ERR_print_errors_fp (stderr); ++ ret = ENOMEM; ++ goto done; ++ } ++ ++ BN_GENCB_set (dhparams->cb, dh_cb, NULL); ++ } ++ ++ if (!DH_generate_parameters_ex ( ++ dhparams->dh, dhparams->prime_len, dhparams->generator, dhparams->cb)) ++ { ++ ERR_print_errors_fp (stderr); ++ ret = EIO; ++ goto done; ++ } ++ ++ ret = EOK; ++ *_dhparams = talloc_steal (mem_ctx, dhparams); ++ ++done: ++ talloc_free (tmp_ctx); ++ return ret; ++} ++ ++static int ++_sscg_dhparams_destructor (TALLOC_CTX *ctx) ++{ ++ struct sscg_dhparams *params = ++ talloc_get_type_abort (ctx, struct sscg_dhparams); ++ ++ if (params->dh != NULL) ++ { ++ DH_free (params->dh); ++ params->dh = NULL; ++ } ++ ++#if OPENSSL_VERSION_NUMBER >= 0x10100000L ++ if (params->cb != NULL) ++ { ++ BN_GENCB_free (params->cb); ++ params->cb = NULL; ++ } ++#endif ++ ++ return 0; ++} ++ ++static int ++dh_cb (int p, int n, BN_GENCB *cb) ++{ ++ static const char symbols[] = ".+*\n"; ++ char c = (p >= 0 && (size_t)p < sizeof (symbols) - 1) ? symbols[p] : '?'; ++ ++ fprintf (stdout, "%c", c); ++ ++ return 1; ++} +diff --git a/src/io_utils.c b/src/io_utils.c +new file mode 100644 +index 0000000000000000000000000000000000000000..809a1da0e455afa0dba0796a5f7ac406742328a1 +--- /dev/null ++++ b/src/io_utils.c +@@ -0,0 +1,424 @@ ++/* ++ This file is part of sscg. ++ ++ sscg 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 3 of the License, or ++ (at your option) any later version. ++ ++ sscg 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 sscg. If not, see . ++ ++ Copyright 2019 by Stephen Gallagher ++*/ ++ ++ ++#include ++#include ++#include ++#include ++ ++#include "include/io_utils.h" ++#include "include/sscg.h" ++ ++int ++sscg_normalize_path (TALLOC_CTX *mem_ctx, ++ const char *path, ++ char **_normalized_path) ++{ ++ int ret; ++ char *normalized_path = NULL; ++ ++ TALLOC_CTX *tmp_ctx = talloc_new (NULL); ++ CHECK_MEM (tmp_ctx); ++ ++ normalized_path = talloc_zero_array (tmp_ctx, char, PATH_MAX); ++ CHECK_MEM (normalized_path); ++ ++ ret = make_normalized_absolute_path (normalized_path, PATH_MAX, path); ++ CHECK_OK (ret); ++ ++ *_normalized_path = talloc_strdup (mem_ctx, normalized_path); ++ CHECK_MEM (*_normalized_path); ++ ++ ret = EOK; ++ ++done: ++ talloc_free (normalized_path); ++ talloc_free (tmp_ctx); ++ return ret; ++} ++ ++ ++static int ++sscg_stream_destructor (TALLOC_CTX *ptr) ++{ ++ struct sscg_stream *stream = talloc_get_type_abort (ptr, struct sscg_stream); ++ ++ BIO_free (stream->bio); ++ ++ return 0; ++} ++ ++ ++struct sscg_stream * ++sscg_io_utils_get_stream_by_path (struct sscg_stream **streams, ++ const char *normalized_path) ++{ ++ struct sscg_stream *stream = NULL; ++ ++ /* First see if this path already exists in the list */ ++ for (int i = 0; (stream = streams[i]) && i < SSCG_NUM_FILE_TYPES; i++) ++ { ++ if (strcmp (normalized_path, stream->path) == 0) ++ break; ++ } ++ ++ return stream; ++} ++ ++ ++struct sscg_stream * ++sscg_io_utils_get_stream_by_type (struct sscg_stream **streams, ++ enum sscg_file_type filetype) ++{ ++ struct sscg_stream *stream = NULL; ++ ++ /* First see if this path already exists in the list */ ++ for (int i = 0; (stream = streams[i]) && i < SSCG_NUM_FILE_TYPES; i++) ++ { ++ SSCG_LOG (SSCG_DEBUG, ++ "Checking for 0x%.4x in 0x%.4x\n", ++ (1 << filetype), ++ stream->filetypes); ++ if (stream->filetypes & (1 << filetype)) ++ { ++ SSCG_LOG (SSCG_DEBUG, ++ "Found file type %s in %s\n", ++ sscg_get_file_type_name (filetype), ++ stream->path); ++ break; ++ } ++ } ++ ++ if (!stream) ++ SSCG_LOG (SSCG_DEBUG, ++ "Could not locate file type: %s. Skipping.\n", ++ sscg_get_file_type_name (filetype)); ++ ++ return stream; ++} ++ ++ ++BIO * ++sscg_io_utils_get_bio_by_type (struct sscg_stream **streams, ++ enum sscg_file_type filetype) ++{ ++ struct sscg_stream *_tmp_stream = ++ sscg_io_utils_get_stream_by_type (streams, filetype); ++ ++ if (_tmp_stream) ++ { ++ return _tmp_stream->bio; ++ } ++ ++ return NULL; ++} ++ ++ ++const char * ++sscg_io_utils_get_path_by_type (struct sscg_stream **streams, ++ enum sscg_file_type filetype) ++{ ++ struct sscg_stream *_tmp_stream = ++ sscg_io_utils_get_stream_by_type (streams, filetype); ++ ++ if (_tmp_stream) ++ { ++ return _tmp_stream->path; ++ } ++ ++ return NULL; ++} ++ ++ ++int ++sscg_io_utils_add_output_file (struct sscg_stream **streams, ++ enum sscg_file_type filetype, ++ const char *path, ++ int mode) ++{ ++ int ret, i; ++ TALLOC_CTX *tmp_ctx = NULL; ++ struct sscg_stream *stream = NULL; ++ char *normalized_path = NULL; ++ ++ /* If we haven't been passed a path, just return; it's probably an optional ++ * output file ++ */ ++ if (path == NULL) ++ { ++ SSCG_LOG (SSCG_DEBUG, ++ "Got a NULL path with filetype: %s\n", ++ sscg_get_file_type_name (filetype)); ++ return EOK; ++ } ++ ++ tmp_ctx = talloc_new (NULL); ++ CHECK_MEM (tmp_ctx); ++ ++ /* Get the normalized version of the path */ ++ ret = sscg_normalize_path (tmp_ctx, path, &normalized_path); ++ CHECK_OK (ret); ++ ++ SSCG_LOG (SSCG_DEBUG, ++ "%s file path: %s\n", ++ sscg_get_file_type_name (filetype), ++ normalized_path); ++ ++ /* First see if this path already exists in the list */ ++ stream = sscg_io_utils_get_stream_by_path (streams, normalized_path); ++ ++ if (stream == NULL) ++ { ++ /* The path wasn't found, so open it and create it */ ++ ++ /* First advance the index to the end */ ++ for (i = 0; streams[i]; i++) ++ ; ++ ++ /* This should never happen. The streams array should always be ++ * sized to the maximum number of known types. If we are asked to add ++ * more entries to the array than we have known file types, it must be ++ * due to a bug. ++ */ ++ assert (i < SSCG_NUM_FILE_TYPES); ++ ++ stream = talloc_zero (tmp_ctx, struct sscg_stream); ++ CHECK_MEM (stream); ++ talloc_set_destructor ((TALLOC_CTX *)stream, sscg_stream_destructor); ++ ++ stream->path = talloc_steal (stream, normalized_path); ++ CHECK_MEM (stream->path); ++ ++ streams[i] = talloc_steal (streams, stream); ++ } ++ ++ /* Always set the mode to the most-restrictive one requested */ ++ SSCG_LOG (SSCG_DEBUG, "Requested mode: %o\n", mode); ++ if (stream->mode) ++ stream->mode &= mode; ++ else ++ stream->mode = mode; ++ SSCG_LOG (SSCG_DEBUG, "Actual mode: %o\n", stream->mode); ++ ++ /* Add the file type */ ++ stream->filetypes |= (1 << filetype); ++ ++ ret = EOK; ++ ++done: ++ talloc_free (tmp_ctx); ++ return ret; ++} ++ ++ ++enum io_utils_errors ++{ ++ IO_UTILS_OK = 0, ++ IO_UTILS_TOOMANYKEYS, ++ IO_UTILS_DHPARAMS_NON_EXCLUSIVE, ++ IO_UTILS_CRL_NON_EXCLUSIVE, ++ IO_UTILS_SVC_UNMATCHED, ++ IO_UTILS_CLIENT_UNMATCHED, ++ IO_UTILS_CA_UNMATCHED ++}; ++ ++static enum io_utils_errors ++io_utils_validate (struct sscg_stream **streams) ++{ ++ enum io_utils_errors ret; ++ struct sscg_stream *stream = NULL; ++ int keybits; ++ int allbits = 0; ++ ++ for (int i = 0; (stream = streams[i]) && i < SSCG_NUM_FILE_TYPES; i++) ++ { ++ SSCG_LOG (SSCG_DEBUG, "filetypes: 0x%.4x\n", stream->filetypes); ++ ++ allbits |= stream->filetypes; ++ ++ /* No file may contain two different private keys */ ++ /* First check if any private keys are in this file */ ++ if ((keybits = stream->filetypes & SSCG_FILE_TYPE_KEYS)) ++ { ++ /* Next check if there is exactly one private key in the remainder. ++ * The following bitwise magic checks whether the value is exactly a ++ * power of two (meaning only one bit is set). If the result is ++ * nonzero, more than one bit was set and we have been asked to ++ * include multiple keys into the same file. ++ */ ++ if (keybits & (keybits - 1)) ++ { ++ ret = IO_UTILS_TOOMANYKEYS; ++ goto done; ++ } ++ } ++ ++ /* The dhparams file may only contain dhparams */ ++ if ((stream->filetypes & (1 << SSCG_FILE_TYPE_DHPARAMS)) && ++ (stream->filetypes ^ (1 << SSCG_FILE_TYPE_DHPARAMS))) ++ { ++ ret = IO_UTILS_DHPARAMS_NON_EXCLUSIVE; ++ goto done; ++ } ++ ++ /* The CRL file may only contain certificate revocations */ ++ if ((stream->filetypes & (1 << SSCG_FILE_TYPE_CRL)) && ++ (stream->filetypes ^ (1 << SSCG_FILE_TYPE_CRL))) ++ { ++ ret = IO_UTILS_CRL_NON_EXCLUSIVE; ++ goto done; ++ } ++ } ++ ++ SSCG_LOG (SSCG_DEBUG, "allbits: 0x%.4x\n", allbits); ++ ++ /* If the public or private key is present for the service cert, the other ++ * must be present also ++ */ ++ if ((allbits & SSCG_FILE_TYPE_SVC_TYPES) && ++ ((allbits & SSCG_FILE_TYPE_SVC_TYPES) != SSCG_FILE_TYPE_SVC_TYPES)) ++ { ++ ret = IO_UTILS_SVC_UNMATCHED; ++ goto done; ++ } ++ ++ /* If the public or private key is present for the client cert, the other ++ * must be present also ++ */ ++ if ((allbits & SSCG_FILE_TYPE_CLIENT_TYPES) && ++ ((allbits & SSCG_FILE_TYPE_CLIENT_TYPES) != SSCG_FILE_TYPE_CLIENT_TYPES)) ++ { ++ ret = IO_UTILS_CLIENT_UNMATCHED; ++ goto done; ++ } ++ ++ /* If the private key is present for the CA cert, the public key must be ++ * present also ++ */ ++ if ((allbits & (1 << SSCG_FILE_TYPE_CA_KEY)) && ++ !(allbits & (1 << SSCG_FILE_TYPE_CA))) ++ { ++ ret = IO_UTILS_CA_UNMATCHED; ++ goto done; ++ } ++ ++ ++ ret = IO_UTILS_OK; ++ ++done: ++ return ret; ++} ++ ++ ++int ++sscg_io_utils_open_output_files (struct sscg_stream **streams, bool overwrite) ++{ ++ int ret; ++ TALLOC_CTX *tmp_ctx = NULL; ++ enum io_utils_errors validation_result; ++ char *create_mode = NULL; ++ struct sscg_stream *stream = NULL; ++ ++ validation_result = io_utils_validate (streams); ++ switch (validation_result) ++ { ++ case IO_UTILS_TOOMANYKEYS: ++ SSCG_ERROR ("Attempted to output multiple keys to the same file.\n"); ++ ret = EINVAL; ++ goto done; ++ ++ case IO_UTILS_CRL_NON_EXCLUSIVE: ++ SSCG_ERROR ("The CRL file may not include other content.\n"); ++ ret = EINVAL; ++ goto done; ++ ++ case IO_UTILS_DHPARAMS_NON_EXCLUSIVE: ++ SSCG_ERROR ("The dhparams file may not include other content.\n"); ++ ret = EINVAL; ++ goto done; ++ ++ case IO_UTILS_SVC_UNMATCHED: ++ SSCG_ERROR ( ++ "The service certificate must have both public and private key " ++ "locations specified.\n"); ++ ret = EINVAL; ++ goto done; ++ ++ case IO_UTILS_CLIENT_UNMATCHED: ++ SSCG_ERROR ( ++ "The client certificate must have both public and private key " ++ "locations specified.\n"); ++ ret = EINVAL; ++ goto done; ++ ++ case IO_UTILS_CA_UNMATCHED: ++ SSCG_ERROR ( ++ "The CA certificate must have a public key location specified.\n"); ++ ret = EINVAL; ++ goto done; ++ ++ case IO_UTILS_OK: break; ++ } ++ ++ tmp_ctx = talloc_new (NULL); ++ CHECK_MEM (tmp_ctx); ++ ++ if (overwrite) ++ create_mode = talloc_strdup (tmp_ctx, "w"); ++ else ++ create_mode = talloc_strdup (tmp_ctx, "wx"); ++ CHECK_MEM (create_mode); ++ ++ for (int i = 0; (stream = streams[i]) && i < SSCG_NUM_FILE_TYPES; i++) ++ { ++ SSCG_LOG (SSCG_DEBUG, "Opening %s\n", stream->path); ++ stream->bio = BIO_new_file (stream->path, create_mode); ++ CHECK_BIO (stream->bio, stream->path); ++ } ++ ++ ret = EOK; ++done: ++ talloc_free (tmp_ctx); ++ return ret; ++} ++ ++ ++int ++sscg_io_utils_finalize_output_files (struct sscg_stream **streams) ++{ ++ struct sscg_stream *stream = NULL; ++ FILE *fp; ++ ++ for (int i = 0; (stream = streams[i]) && i < SSCG_NUM_FILE_TYPES; i++) ++ { ++ /* Set the final permissions mode */ ++ SSCG_LOG (SSCG_DEBUG, ++ "Setting %s file permissions to %o\n", ++ stream->path, ++ stream->mode); ++ BIO_get_fp (stream->bio, &fp); ++ ++ errno = 0; ++ if (fchmod (fileno (fp), stream->mode) != 0) ++ return errno; ++ } ++ ++ return EOK; ++} +diff --git a/src/service.c b/src/service.c +deleted file mode 100644 +index 34c976dbe905528000b181c24d1fa95da3cd1377..0000000000000000000000000000000000000000 +--- a/src/service.c ++++ /dev/null +@@ -1,164 +0,0 @@ +-/* +- This file is part of sscg. +- +- sscg 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 3 of the License, or +- (at your option) any later version. +- +- sscg 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 sscg. If not, see . +- +- Copyright 2017 by Stephen Gallagher +-*/ +- +- +-#include "include/sscg.h" +-#include "include/service.h" +-#include "include/x509.h" +-#include "include/key.h" +- +-int +-create_service_cert (TALLOC_CTX *mem_ctx, +- const struct sscg_options *options, +- struct sscg_x509_cert *ca_cert, +- struct sscg_evp_pkey *ca_key, +- struct sscg_x509_cert **_svc_cert, +- struct sscg_evp_pkey **_svc_key) +-{ +- int ret; +- size_t i; +- struct sscg_bignum *e; +- struct sscg_bignum *serial; +- struct sscg_cert_info *svc_certinfo; +- struct sscg_x509_req *csr; +- struct sscg_evp_pkey *pkey; +- struct sscg_x509_cert *cert; +- X509_EXTENSION *ex = NULL; +- TALLOC_CTX *tmp_ctx = NULL; +- +- tmp_ctx = talloc_new (NULL); +- CHECK_MEM (tmp_ctx); +- +- /* create a serial number for this certificate */ +- ret = sscg_generate_serial (tmp_ctx, &serial); +- CHECK_OK (ret); +- +- svc_certinfo = sscg_cert_info_new (tmp_ctx, options->hash_fn); +- CHECK_MEM (svc_certinfo); +- +- /* Populate cert_info from options */ +- svc_certinfo->country = talloc_strdup (svc_certinfo, options->country); +- CHECK_MEM (svc_certinfo->country); +- +- svc_certinfo->state = talloc_strdup (svc_certinfo, options->state); +- CHECK_MEM (svc_certinfo->state); +- +- svc_certinfo->locality = talloc_strdup (svc_certinfo, options->locality); +- CHECK_MEM (svc_certinfo->locality); +- +- svc_certinfo->org = talloc_strdup (svc_certinfo, options->org); +- CHECK_MEM (svc_certinfo->org); +- +- svc_certinfo->org_unit = talloc_strdup (svc_certinfo, options->org_unit); +- CHECK_MEM (svc_certinfo->org_unit); +- +- svc_certinfo->email = talloc_strdup (svc_certinfo, options->email); +- CHECK_MEM (svc_certinfo->email); +- +- svc_certinfo->cn = talloc_strdup (svc_certinfo, options->hostname); +- CHECK_MEM (svc_certinfo->cn); +- +- if (options->subject_alt_names) +- { +- for (i = 0; options->subject_alt_names[i]; i++) +- { +- svc_certinfo->subject_alt_names = talloc_realloc ( +- svc_certinfo, svc_certinfo->subject_alt_names, char *, i + 2); +- CHECK_MEM (svc_certinfo->subject_alt_names); +- +- svc_certinfo->subject_alt_names[i] = talloc_strdup ( +- svc_certinfo->subject_alt_names, options->subject_alt_names[i]); +- CHECK_MEM (svc_certinfo->subject_alt_names[i]); +- +- /* Add a NULL terminator to the end */ +- svc_certinfo->subject_alt_names[i + 1] = NULL; +- } +- } +- +- /* Ensure that this certificate may not sign other certificates */ +- /* Add key extensions */ +- ex = X509V3_EXT_conf_nid ( +- NULL, NULL, NID_key_usage, "critical,digitalSignature,keyEncipherment"); +- CHECK_MEM (ex); +- sk_X509_EXTENSION_push (svc_certinfo->extensions, ex); +- +- /* Mark it as not a CA */ +- ex = X509V3_EXT_conf_nid (NULL, NULL, NID_basic_constraints, "CA:FALSE"); +- CHECK_MEM (ex); +- sk_X509_EXTENSION_push (svc_certinfo->extensions, ex); +- +- /* Use an exponent value of RSA F4 aka 0x10001 (65537) */ +- ret = sscg_init_bignum (tmp_ctx, RSA_F4, &e); +- CHECK_OK (ret); +- +- /* Generate an RSA keypair for this CA */ +- if (options->verbosity >= SSCG_VERBOSE) +- { +- fprintf (stdout, "Generating RSA key for service certificate.\n"); +- } +- /* TODO: support DSA keys as well */ +- ret = sscg_generate_rsa_key (tmp_ctx, options->key_strength, e, &pkey); +- CHECK_OK (ret); +- +- /* Create a certificate signing request for the private CA */ +- if (options->verbosity >= SSCG_VERBOSE) +- { +- fprintf (stdout, "Generating CSR for service certificate.\n"); +- } +- ret = sscg_x509v3_csr_new (tmp_ctx, svc_certinfo, pkey, &csr); +- CHECK_OK (ret); +- +- /* Finalize the CSR */ +- ret = sscg_x509v3_csr_finalize (svc_certinfo, pkey, csr); +- CHECK_OK (ret); +- +- if (options->verbosity >= SSCG_DEBUG) +- { +- fprintf (stderr, +- "DEBUG: Writing service certificate CSR to ./debug-svc.csr\n"); +- BIO *svc_csr_out = BIO_new_file ("./debug-svc.csr", "w"); +- int sslret = PEM_write_bio_X509_REQ (svc_csr_out, csr->x509_req); +- CHECK_SSL (sslret, PEM_write_bio_X509_REQ); +- } +- +- /* Sign the certificate */ +- +- if (options->verbosity >= SSCG_VERBOSE) +- { +- fprintf (stdout, "Signing CSR for service certificate. \n"); +- } +- +- ret = sscg_sign_x509_csr (tmp_ctx, +- csr, +- serial, +- options->lifetime, +- ca_cert, +- ca_key, +- options->hash_fn, +- &cert); +- CHECK_OK (ret); +- +- *_svc_cert = talloc_steal (mem_ctx, cert); +- *_svc_key = talloc_steal (mem_ctx, pkey); +- +- ret = EOK; +-done: +- talloc_free (tmp_ctx); +- return ret; +-} +diff --git a/src/sscg.c b/src/sscg.c +index a02e4df66c6cf9ec1865f425b4a15da82fbfdc72..470af815d91f5170a1e8fe00006dbaee4d07b209 100644 +--- a/src/sscg.c ++++ b/src/sscg.c +@@ -32,7 +32,12 @@ + #include "config.h" + #include "include/sscg.h" + #include "include/authority.h" +-#include "include/service.h" ++#include "include/cert.h" ++#include "include/dhparams.h" ++#include "include/io_utils.h" ++ ++ ++int verbosity; + + + /* Same as OpenSSL CLI */ +@@ -58,6 +63,8 @@ set_default_options (struct sscg_options *opts) + int security_level = get_security_level (); + + opts->lifetime = 3650; ++ opts->dhparams_prime_len = 2048; ++ opts->dhparams_generator = 2; + + /* Select the default key strength based on the system security level + * See: +@@ -132,51 +139,6 @@ print_options (struct sscg_options *opts) + fprintf (stdout, "=================\n"); + } + +-static int +-_sscg_normalize_path (TALLOC_CTX *mem_ctx, +- const char *path, +- const char *path_default, +- char **_normalized_path) +-{ +- int ret; +- char *orig_path = NULL; +- char *normalized_path = NULL; +- +- TALLOC_CTX *tmp_ctx = talloc_new (NULL); +- CHECK_MEM (tmp_ctx); +- +- if (path) +- { +- orig_path = talloc_strdup (tmp_ctx, path); +- } +- else +- { +- if (!path_default) +- { +- /* If no default is set and no path was provided, +- * return NULL */ +- *_normalized_path = NULL; +- ret = EOK; +- goto done; +- } +- orig_path = talloc_strdup (tmp_ctx, path_default); +- CHECK_MEM (orig_path); +- } +- +- normalized_path = talloc_zero_array (tmp_ctx, char, PATH_MAX); +- CHECK_MEM (normalized_path); +- +- ret = make_normalized_absolute_path (normalized_path, PATH_MAX, orig_path); +- CHECK_OK (ret); +- +- *_normalized_path = talloc_steal (mem_ctx, normalized_path); +- ret = EOK; +- +-done: +- talloc_free (tmp_ctx); +- 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. +@@ -251,6 +213,53 @@ sscg_read_pw_file (TALLOC_CTX *mem_ctx, char *path) + } + + ++const char * ++sscg_get_verbosity_name (enum sscg_verbosity type) ++{ ++ switch (type) ++ { ++ case SSCG_DEFAULT: ++ case SSCG_VERBOSE: return ""; ++ ++ case SSCG_DEBUG: return "DEBUG: "; ++ ++ default: break; ++ } ++ ++ /* If it wasn't one of these, we have a bug */ ++ return "Unknown Verbosity (bug):"; ++} ++ ++ ++const char * ++sscg_get_file_type_name (enum sscg_file_type type) ++{ ++ switch (type) ++ { ++ case SSCG_FILE_TYPE_CA: return "CA certificate"; ++ ++ case SSCG_FILE_TYPE_CA_KEY: return "CA certificate key"; ++ ++ case SSCG_FILE_TYPE_SVC: return "service certificate"; ++ ++ case SSCG_FILE_TYPE_SVC_KEY: return "service certificate key"; ++ ++ case SSCG_FILE_TYPE_CLIENT: return "client auth certificate"; ++ ++ case SSCG_FILE_TYPE_CLIENT_KEY: return "client auth certificate key"; ++ ++ case SSCG_FILE_TYPE_CRL: return "certificate revocation list"; ++ ++ case SSCG_FILE_TYPE_DHPARAMS: return "Diffie-Hellman parameters"; ++ ++ default: break; ++ } ++ ++ /* If it wasn't one of these, we have a bug */ ++ return "Unknown (bug)"; ++} ++ ++ + int + main (int argc, const char **argv) + { +@@ -274,36 +283,48 @@ main (int argc, const char **argv) + char *ca_key_file = NULL; + char *cert_file = NULL; + char *cert_key_file = NULL; ++ char *client_file = NULL; ++ char *client_key_file = NULL; ++ char *dhparams_file = NULL; + +- int ca_mode = 0644; +- int ca_key_mode = 0600; ++ int ca_mode = SSCG_CERT_DEFAULT_MODE; ++ int ca_key_mode = SSCG_KEY_DEFAULT_MODE; + char *ca_key_password = NULL; + char *ca_key_passfile = NULL; + +- int cert_mode = 0644; +- int cert_key_mode = 0600; ++ int crl_mode = SSCG_CERT_DEFAULT_MODE; ++ char *crl_file = NULL; ++ ++ int cert_mode = SSCG_CERT_DEFAULT_MODE; ++ int cert_key_mode = SSCG_KEY_DEFAULT_MODE; + char *cert_key_password = NULL; + char *cert_key_passfile = NULL; + +- char *create_mode = NULL; ++ int client_mode = SSCG_CERT_DEFAULT_MODE; ++ int client_key_mode = SSCG_KEY_DEFAULT_MODE; ++ char *client_key_password = NULL; ++ char *client_key_passfile = NULL; + + struct sscg_x509_cert *cacert; + struct sscg_evp_pkey *cakey; + struct sscg_x509_cert *svc_cert; + struct sscg_evp_pkey *svc_key; ++ struct sscg_x509_cert *client_cert; ++ struct sscg_evp_pkey *client_key; + +- BIO *ca_out = NULL; +- BIO *ca_key_out = NULL; +- BIO *cert_out = NULL; +- BIO *cert_key_out = NULL; +- +- FILE *fp; ++ int dhparams_mode = SSCG_CERT_DEFAULT_MODE; ++ struct sscg_dhparams *dhparams = NULL; + + /* Always use umask 0577 for generating certificates and keys + This means that it's opened as write-only by the effective + user. */ + umask (0577); + ++#if OPENSSL_VERSION_NUMBER < 0x10100000L ++ /* In OpenSSL <1.1.0, we need to initialize the library. */ ++ OpenSSL_add_all_algorithms (); ++#endif ++ + TALLOC_CTX *main_ctx = talloc_new (NULL); + if (!main_ctx) + { +@@ -315,6 +336,9 @@ main (int argc, const char **argv) + CHECK_MEM (options); + talloc_set_destructor ((TALLOC_CTX *)options, sscg_options_destructor); + ++ options->streams = ++ talloc_zero_array (options, struct sscg_stream *, SSCG_NUM_FILE_TYPES); ++ + ret = set_default_options (options); + if (ret != EOK) + goto done; +@@ -323,101 +347,144 @@ main (int argc, const char **argv) + talloc_asprintf (main_ctx, "%d or larger", options->minimum_key_strength); + + options->verbosity = SSCG_DEFAULT; ++ // clang-format off + struct poptOption long_options[] = { +- POPT_AUTOHELP{ "quiet", +- 'q', +- POPT_ARG_VAL, +- &options->verbosity, +- SSCG_QUIET, +- _ ("Display no output unless there is an error."), +- NULL }, +- { "verbose", ++ POPT_AUTOHELP ++ ++ { ++ "quiet", ++ 'q', ++ POPT_ARG_VAL, ++ &options->verbosity, ++ SSCG_QUIET, ++ ("Display no output unless there is an error."), ++ NULL ++ }, ++ ++ { ++ "verbose", + 'v', + POPT_ARG_VAL, + &options->verbosity, + SSCG_VERBOSE, + _ ("Display progress messages."), +- NULL }, +- { "debug", ++ NULL ++ }, ++ ++ { ++ "debug", + 'd', + POPT_ARG_VAL, + &options->verbosity, + SSCG_DEBUG, + _ ("Enable logging of debug messages. Implies verbose. Warning! " + "This will print private key information to the screen!"), +- NULL }, +- { "version", ++ NULL ++ }, ++ ++ { ++ "version", + 'V', + POPT_ARG_NONE, + &options->print_version, + 0, + _ ("Display the version number and exit."), +- NULL }, +- { "force", ++ NULL ++ }, ++ ++ { ++ "force", + 'f', + POPT_ARG_NONE, + &options->overwrite, + 0, + _ ("Overwrite any pre-existing files in the requested locations"), +- NULL }, +- { "lifetime", ++ NULL ++ }, ++ ++ { ++ "lifetime", + '\0', + POPT_ARG_INT | POPT_ARGFLAG_SHOW_DEFAULT, + &options->lifetime, + 0, + _ ("Certificate lifetime (days)."), +- _ ("1-3650") }, +- { "country", ++ _ ("1-3650") ++ }, ++ ++ { ++ "country", + '\0', + POPT_ARG_STRING, + &country, + 0, + _ ("Certificate DN: Country (C). (default: \"US\")"), +- _ ("US, CZ, etc.") }, +- { "state", ++ _ ("US, CZ, etc.") ++ }, ++ ++ { ++ "state", + '\0', + POPT_ARG_STRING, + &state, + 0, + _ ("Certificate DN: State or Province (ST)."), +- _ ("Massachusetts, British Columbia, etc.") }, +- { "locality", ++ _ ("Massachusetts, British Columbia, etc.") ++ }, ++ ++ { ++ "locality", + '\0', + POPT_ARG_STRING, + &locality, + 0, + _ ("Certificate DN: Locality (L)."), +- _ ("Westford, Paris, etc.") }, +- { "organization", ++ _ ("Westford, Paris, etc.") ++ }, ++ ++ { ++ "organization", + '\0', + POPT_ARG_STRING, + &organization, + 0, + _ ("Certificate DN: Organization (O). (default: \"Unspecified\")"), +- _ ("My Company") }, +- { "organizational-unit", ++ _ ("My Company") ++ }, ++ ++ { ++ "organizational-unit", + '\0', + POPT_ARG_STRING, + &organizational_unit, + 0, + _ ("Certificate DN: Organizational Unit (OU)."), +- _ ("Engineering, etc.") }, +- { "email", ++ _ ("Engineering, etc.") ++ }, ++ ++ { ++ "email", + '\0', + POPT_ARG_STRING, + &email, + 0, + _ ("Certificate DN: Email Address (Email)."), +- _ ("myname@example.com") }, +- { "hostname", ++ _ ("myname@example.com") ++ }, ++ ++ { ++ "hostname", + '\0', + POPT_ARG_STRING, + &hostname, + 0, + _ ("The valid hostname of the certificate. Must be an FQDN. (default: " + "current system FQDN)"), +- _ ("server.example.com") }, +- { "subject-alt-name", ++ _ ("server.example.com") ++ }, ++ ++ { ++ "subject-alt-name", + '\0', + POPT_ARG_ARGV, + &alternative_names, +@@ -427,7 +494,9 @@ main (int argc, const char **argv) + "supported by RFC 5280 such as " + "IP:xxx.xxx.xxx.xxx/yyy.yyy.yyy.yyy " + "May be specified multiple times."), +- _ ("alt.example.com") }, ++ _ ("alt.example.com") ++ }, ++ + { + "package", + '\0', +@@ -437,7 +506,9 @@ main (int argc, const char **argv) + _ ("Unused. Retained for compatibility with earlier versions of sscg."), + NULL, + }, +- { "key-strength", ++ ++ { ++ "key-strength", + '\0', + POPT_ARG_INT | POPT_ARGFLAG_SHOW_DEFAULT, + &options->key_strength, +@@ -453,6 +524,7 @@ main (int argc, const char **argv) + _ ("Hashing algorithm to use for signing."), + _ ("{sha256,sha384,sha512}"), + }, ++ + { + "cipher-alg", + '\0', +@@ -473,15 +545,17 @@ main (int argc, const char **argv) + "\"./ca.crt\")"), + NULL, + }, ++ + { + "ca-mode", + '\0', + POPT_ARG_INT, + &ca_mode, + 0, +- _ ("File mode of the created CA certificate. (default: 0644)"), +- _ ("0644"), ++ _ ("File mode of the created CA certificate."), ++ SSCG_CERT_DEFAULT_MODE_HELP, + }, ++ + { + "ca-key-file", + '\0', +@@ -492,15 +566,17 @@ main (int argc, const char **argv) + "the key will be destroyed rather than written to the disk."), + NULL, + }, ++ + { + "ca-key-mode", + '\0', + POPT_ARG_INT, + &ca_key_mode, + 0, +- _ ("File mode of the created CA key. (default: 0600)"), +- _ ("0600"), ++ _ ("File mode of the created CA key."), ++ SSCG_KEY_DEFAULT_MODE_HELP, + }, ++ + { + "ca-key-password", + '\0', +@@ -534,6 +610,28 @@ main (int argc, const char **argv) + NULL + }, + ++ { ++ "crl-file", ++ '\0', ++ POPT_ARG_STRING, ++ &crl_file, ++ 0, ++ _ ("Path where an (empty) Certificate Revocation List file will be " ++ "created, for applications that expect such a file to exist. If " ++ "unspecified, no such file will be created."), ++ NULL ++ }, ++ ++ { ++ "crl-mode", ++ '\0', ++ POPT_ARG_INT, ++ &crl_mode, ++ 0, ++ _ ("File mode of the created Certificate Revocation List."), ++ SSCG_CERT_DEFAULT_MODE_HELP, ++ }, ++ + { + "cert-file", + '\0', +@@ -544,15 +642,17 @@ main (int argc, const char **argv) + "(default \"./service.pem\")"), + NULL, + }, ++ + { + "cert-mode", + '\0', + POPT_ARG_INT, + &cert_mode, + 0, +- _ ("File mode of the created certificate. (default: 0644)"), +- _ ("0644"), ++ _ ("File mode of the created certificate."), ++ SSCG_CERT_DEFAULT_MODE_HELP, + }, ++ + { + "cert-key-file", + '\0', +@@ -563,15 +663,17 @@ main (int argc, const char **argv) + "(default \"service-key.pem\")"), + NULL, + }, ++ + { + "cert-key-mode", + '\0', + POPT_ARG_INT, + &cert_key_mode, + 0, +- _ ("File mode of the created certificate key. (default: 0600)"), +- _ ("0600"), ++ _ ("File mode of the created certificate key."), ++ SSCG_KEY_DEFAULT_MODE_HELP, + }, ++ + { + "cert-key-password", + 'p', +@@ -605,8 +707,115 @@ main (int argc, const char **argv) + NULL + }, + ++ { ++ "client-file", ++ '\0', ++ POPT_ARG_STRING, ++ &client_file, ++ 0, ++ _ ("Path where a client authentication certificate will be stored."), ++ NULL ++ }, ++ { ++ "client-mode", ++ '\0', ++ POPT_ARG_INT, ++ &client_mode, ++ 0, ++ _ ("File mode of the created certificate."), ++ SSCG_CERT_DEFAULT_MODE_HELP, ++ }, ++ ++ { ++ "client-key-file", ++ '\0', ++ POPT_ARG_STRING, ++ &client_key_file, ++ 0, ++ _ ("Path where the client's private key will be stored. " ++ "(default is client-file with a .key suffix, if " ++ "--client-file was passed, otherwise this file will not " ++ "be generated.)"), ++ NULL, ++ }, ++ ++ { ++ "client-key-mode", ++ '\0', ++ POPT_ARG_INT, ++ &client_key_mode, ++ 0, ++ _ ("File mode of the created certificate key."), ++ SSCG_KEY_DEFAULT_MODE_HELP, ++ }, ++ ++ { ++ "client-key-password", ++ '\0', ++ POPT_ARG_STRING, ++ &client_key_password, ++ 0, ++ _ ("Provide a password for the client 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 --client-keypassfile or " ++ "--client-key-password-prompt for secure password entry."), ++ NULL ++ }, ++ ++ { ++ "client-key-passfile", ++ '\0', ++ POPT_ARG_STRING, ++ &client_key_passfile, ++ 0, ++ _ ("A file containing the password to encrypt the client key file."), ++ NULL ++ }, ++ ++ { ++ "client-key-password-prompt", ++ '\0', ++ POPT_ARG_NONE, ++ &options->client_key_pass_prompt, ++ 0, ++ _ ("Prompt to enter a password for the client key file."), ++ NULL ++ }, ++ ++ { ++ "dhparams-file", ++ '\0', ++ POPT_ARG_STRING, ++ &dhparams_file, ++ 0, ++ _("A file to contain a set of generated Diffie-Hellman parameters. " ++ "If unspecified, no such file will be created."), ++ NULL ++ }, ++ ++ { ++ "dhparams-prime-len", ++ '\0', ++ POPT_ARG_INT | POPT_ARGFLAG_SHOW_DEFAULT, ++ &options->dhparams_prime_len, ++ 0, ++ _ ("The length of the prime number to generate for dhparams, in bits."), ++ NULL ++ }, ++ ++ { ++ "dhparams-generator", ++ '\0', ++ POPT_ARG_INT | POPT_ARGFLAG_SHOW_DEFAULT, ++ &options->dhparams_generator, ++ 0, ++ _ ("The generator value for dhparams."), ++ _("{2,3,5}") ++ }, ++ + POPT_TABLEEND + }; ++ // clang-format on + + pc = poptGetContext (argv[0], argc, argv, long_options, 0); + while ((opt = poptGetNextOpt (pc)) != -1) +@@ -630,6 +839,8 @@ main (int argc, const char **argv) + return 0; + } + ++ verbosity = options->verbosity; ++ + /* Process the Subject information */ + + if (country) +@@ -788,6 +999,22 @@ main (int argc, const char **argv) + } + } + ++ if (client_key_password) ++ { ++ options->client_key_pass = ++ sscg_secure_string_steal (options, client_key_password); ++ } ++ else if (client_key_passfile) ++ { ++ options->client_key_pass = ++ sscg_read_pw_file (options, client_key_passfile); ++ if (!options->client_key_pass) ++ { ++ fprintf ( ++ stderr, "Failed to read passphrase from %s", client_key_passfile); ++ goto done; ++ } ++ } + + if (options->key_strength < options->minimum_key_strength) + { +@@ -822,86 +1049,115 @@ main (int argc, const char **argv) + if (options->verbosity >= SSCG_VERBOSE) + print_options (options); + +- /* Get the paths of the output files */ +- ret = _sscg_normalize_path (options, ca_file, "./ca.crt", &options->ca_file); ++ /* Prepare the output files */ ++ ret = sscg_io_utils_add_output_file (options->streams, ++ SSCG_FILE_TYPE_CA, ++ ca_file ? ca_file : "./ca.crt", ++ ca_mode); + CHECK_OK (ret); + +- ret = +- _sscg_normalize_path (options, ca_key_file, NULL, &options->ca_key_file); ++ ret = sscg_io_utils_add_output_file ( ++ options->streams, SSCG_FILE_TYPE_CA_KEY, ca_key_file, ca_key_mode); + CHECK_OK (ret); +- if (options->verbosity >= SSCG_DEBUG) +- { +- fprintf (stdout, +- "DEBUG: CA Key file path: %s\n", +- options->ca_key_file ? options->ca_key_file : "(N/A)"); +- } +- +- ret = _sscg_normalize_path ( +- options, cert_file, "./service.pem", &options->cert_file); ++ ++ ret = sscg_io_utils_add_output_file (options->streams, ++ SSCG_FILE_TYPE_SVC, ++ cert_file ? cert_file : "./service.pem", ++ cert_mode); ++ CHECK_OK (ret); ++ ++ ret = sscg_io_utils_add_output_file (options->streams, ++ SSCG_FILE_TYPE_SVC_KEY, ++ cert_key_file ? cert_key_file : ++ "./service-key.pem", ++ cert_key_mode); ++ CHECK_OK (ret); ++ ++ ++ ret = sscg_io_utils_add_output_file ( ++ options->streams, SSCG_FILE_TYPE_CLIENT, client_file, client_mode); ++ CHECK_OK (ret); ++ ++ ++ ret = sscg_io_utils_add_output_file (options->streams, ++ SSCG_FILE_TYPE_CLIENT_KEY, ++ client_key_file ? client_key_file : ++ client_file, ++ client_key_mode); ++ CHECK_OK (ret); ++ ++ ret = sscg_io_utils_add_output_file ( ++ options->streams, SSCG_FILE_TYPE_CRL, crl_file, crl_mode); + CHECK_OK (ret); + +- ret = _sscg_normalize_path ( +- options, cert_key_file, "./service-key.pem", &options->cert_key_file); ++ ret = sscg_io_utils_add_output_file ( ++ options->streams, SSCG_FILE_TYPE_DHPARAMS, dhparams_file, dhparams_mode); + CHECK_OK (ret); + + poptFreeContext (pc); + +- /* Validate the file paths */ ++ /* Validate and open the file paths */ ++ ret = sscg_io_utils_open_output_files (options->streams, options->overwrite); ++ CHECK_OK (ret); + +- /* Only one key can exist in a single file */ +- if (options->ca_key_file && +- strcmp (options->ca_key_file, options->cert_key_file) == 0) +- { +- fprintf (stderr, +- "Certificate key and CA key may not be in the same file.\n"); +- ret = EINVAL; +- goto done; +- } +- +- /* The CA key must not be in the same file as the service cert */ +- if (options->ca_key_file && +- strcmp (options->ca_key_file, options->cert_file) == 0) +- { +- fprintf ( +- stderr, +- "CA key and service certificate may not be in the same file.\n"); +- ret = EINVAL; +- goto done; +- } + + /* Generate the private CA for the certificate */ + ret = create_private_CA (main_ctx, options, &cacert, &cakey); + CHECK_OK (ret); + + /* Generate the service certificate and sign it with the private CA */ +- ret = create_service_cert ( +- main_ctx, options, cacert, cakey, &svc_cert, &svc_key); ++ ret = create_cert (main_ctx, ++ options, ++ cacert, ++ cakey, ++ SSCG_CERT_TYPE_SERVER, ++ &svc_cert, ++ &svc_key); + CHECK_OK (ret); + ++ /* If requested, generate the client auth certificate and sign it with the ++ * private CA. ++ */ ++ if (GET_BIO (SSCG_FILE_TYPE_CLIENT)) ++ { ++ ret = create_cert (main_ctx, ++ options, ++ cacert, ++ cakey, ++ SSCG_CERT_TYPE_CLIENT, ++ &client_cert, ++ &client_key); ++ CHECK_OK (ret); ++ } ++ + + /* ==== Output the final files ==== */ + +- /* Set the file-creation mode */ +- if (options->overwrite) +- { +- create_mode = talloc_strdup (main_ctx, "w"); +- } +- else +- { +- create_mode = talloc_strdup (main_ctx, "wx"); +- } +- CHECK_MEM (create_mode); + +- /* Create certificate private key file */ +- if (options->verbosity >= SSCG_DEFAULT) ++ /* Write private keys first */ ++ ++ if (GET_BIO (SSCG_FILE_TYPE_CLIENT_KEY)) + { +- fprintf ( +- stdout, "Writing svc private key to %s \n", 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 ( ++ GET_BIO (SSCG_FILE_TYPE_CLIENT_KEY), ++ client_key->evp_pkey, ++ options->client_key_pass_prompt || options->client_key_pass ? ++ options->cipher : ++ NULL, ++ (unsigned char *)options->client_key_pass, ++ options->client_key_pass ? strlen (options->client_key_pass) : 0, ++ NULL, ++ NULL); ++ CHECK_SSL (sret, PEM_write_bio_PrivateKey (svc)); ++ ANNOUNCE_WRITE (SSCG_FILE_TYPE_SVC_KEY); + } + +- 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. + * +@@ -909,7 +1165,7 @@ main (int argc, const char **argv) + * to prompt for one. + */ + sret = PEM_write_bio_PrivateKey ( +- cert_key_out, ++ GET_BIO (SSCG_FILE_TYPE_SVC_KEY), + svc_key->evp_pkey, + options->cert_key_pass_prompt || options->cert_key_pass ? options->cipher : + NULL, +@@ -918,83 +1174,11 @@ main (int argc, const char **argv) + NULL, + NULL); + CHECK_SSL (sret, PEM_write_bio_PrivateKey (svc)); +- BIO_get_fp (cert_key_out, &fp); +- +- if (options->verbosity >= SSCG_DEBUG) +- { +- fprintf (stdout, +- "DEBUG: Setting svc key file permissions to %o\n", +- cert_key_mode); +- } +- fchmod (fileno (fp), cert_key_mode); +- +- BIO_free (cert_key_out); +- cert_key_out = NULL; +- +- +- /* Create service public certificate */ +- if (options->verbosity >= SSCG_DEFAULT) +- { +- fprintf (stdout, +- "Writing service public certificate to %s\n", +- options->cert_file); +- } +- if (strcmp (options->cert_key_file, options->cert_file) == 0) +- { +- cert_out = BIO_new_file (options->cert_file, "a"); +- } +- else +- { +- cert_out = BIO_new_file (options->cert_file, create_mode); +- } +- CHECK_BIO (cert_out, options->cert_file); +- +- sret = PEM_write_bio_X509 (cert_out, svc_cert->certificate); +- CHECK_SSL (sret, PEM_write_bio_X509 (svc)); +- BIO_get_fp (cert_out, &fp); +- +- /* If this file matches the keyfile, do not set its permissions */ +- if (strcmp (options->cert_file, options->cert_key_file) == 0) +- { +- if (options->verbosity >= SSCG_DEBUG) +- { +- fprintf (stdout, +- "DEBUG: Not setting service cert file permissions: " +- "superseded by the key\n"); +- } +- } +- else +- { +- if (options->verbosity >= SSCG_DEBUG) +- { +- fprintf (stdout, +- "DEBUG: Setting service cert file permissions to %o\n", +- cert_mode); +- } +- fchmod (fileno (fp), cert_mode); +- } +- BIO_free (cert_out); +- cert_out = NULL; +- ++ ANNOUNCE_WRITE (SSCG_FILE_TYPE_SVC_KEY); + + /* Create CA private key, if requested */ +- if (options->ca_key_file) ++ if (GET_BIO (SSCG_FILE_TYPE_CA_KEY)) + { +- if (options->verbosity >= SSCG_DEFAULT) +- { +- fprintf ( +- stdout, "Writing CA private key to %s\n", options->ca_key_file); +- } +- if (strcmp (options->ca_file, options->ca_key_file) == 0) +- { +- ca_key_out = BIO_new_file (options->ca_key_file, "a"); +- } +- else +- { +- ca_key_out = BIO_new_file (options->ca_key_file, create_mode); +- } +- 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. + * +@@ -1002,7 +1186,7 @@ main (int argc, const char **argv) + * to prompt for one. + */ + sret = PEM_write_bio_PrivateKey ( +- ca_key_out, ++ GET_BIO (SSCG_FILE_TYPE_CA_KEY), + cakey->evp_pkey, + options->ca_key_pass_prompt || options->ca_key_pass ? options->cipher : + NULL, +@@ -1011,73 +1195,80 @@ main (int argc, const char **argv) + NULL, + NULL); + CHECK_SSL (sret, PEM_write_bio_PrivateKey (CA)); +- BIO_get_fp (ca_key_out, &fp); +- if (options->verbosity >= SSCG_DEBUG) +- { +- fprintf (stdout, +- "DEBUG: Setting CA key file permissions to %o\n", +- ca_key_mode); +- } +- fchmod (fileno (fp), ca_key_mode); +- BIO_free (ca_key_out); +- ca_key_out = NULL; ++ ANNOUNCE_WRITE (SSCG_FILE_TYPE_CA_KEY); + } + ++ /* Public keys come next, in chain order */ ++ ++ /* Start with the client certificate */ ++ if (GET_BIO (SSCG_FILE_TYPE_CLIENT)) ++ { ++ sret = PEM_write_bio_X509 (GET_BIO (SSCG_FILE_TYPE_CLIENT), ++ client_cert->certificate); ++ CHECK_SSL (sret, PEM_write_bio_X509 (client)); ++ ANNOUNCE_WRITE (SSCG_FILE_TYPE_CLIENT); ++ } ++ ++ /* Create service public certificate */ ++ sret = ++ PEM_write_bio_X509 (GET_BIO (SSCG_FILE_TYPE_SVC), svc_cert->certificate); ++ CHECK_SSL (sret, PEM_write_bio_X509 (svc)); ++ ANNOUNCE_WRITE (SSCG_FILE_TYPE_SVC); ++ + + /* Create CA public certificate */ +- if (options->verbosity >= SSCG_DEFAULT) +- { +- fprintf ( +- stdout, "Writing CA public certificate to %s\n", options->ca_file); +- } +- if (strcmp (options->ca_file, options->cert_file) == 0) +- { +- ca_out = BIO_new_file (options->ca_file, "a"); +- } +- else +- { +- ca_out = BIO_new_file (options->ca_file, create_mode); +- } +- CHECK_BIO (ca_out, options->ca_file); +- +- sret = PEM_write_bio_X509 (ca_out, cacert->certificate); ++ struct sscg_stream *stream = ++ sscg_io_utils_get_stream_by_type (options->streams, SSCG_FILE_TYPE_CA); ++ sret = PEM_write_bio_X509 (stream->bio, cacert->certificate); + CHECK_SSL (sret, PEM_write_bio_X509 (CA)); +- BIO_get_fp (ca_out, &fp); +- /* If this file matches the keyfile, do not set its permissions */ +- if (options->ca_key_file && +- strcmp (options->ca_file, options->ca_key_file) == 0) ++ ANNOUNCE_WRITE (SSCG_FILE_TYPE_CA); ++ ++ ++ /* Then write any non-certificate files */ ++ ++ /* Create CRL file */ ++ if (GET_BIO (SSCG_FILE_TYPE_CRL)) + { +- if (options->verbosity >= SSCG_DEBUG) +- { +- fprintf ( +- stdout, +- "DEBUG: Not setting CA file permissions: superseded by a key\n"); +- } ++ /* The CRL file is left intentionally blank, so do nothing here. The ++ * file was created as empty, so it will just be closed and have its ++ * permissions set later. ++ */ ++ ANNOUNCE_WRITE (SSCG_FILE_TYPE_CRL); + } +- else ++ ++ ++ /* Create DH parameters file */ ++ if (GET_BIO (SSCG_FILE_TYPE_DHPARAMS)) + { +- if (options->verbosity >= SSCG_DEBUG) +- { +- fprintf ( +- stdout, "DEBUG: Setting CA file permissions to %o\n", ca_mode); +- } +- fchmod (fileno (fp), ca_mode); ++ /* Open the file before generating the parameters. This avoids wasting ++ * the time to generate them if the destination is not writable. ++ */ ++ ++ ret = create_dhparams (main_ctx, ++ options->verbosity, ++ options->dhparams_prime_len, ++ options->dhparams_generator, ++ &dhparams); ++ CHECK_OK (ret); ++ ++ /* Export the DH parameters to the file */ ++ sret = PEM_write_bio_DHparams (GET_BIO (SSCG_FILE_TYPE_DHPARAMS), ++ dhparams->dh); ++ CHECK_SSL (sret, PEM_write_bio_DHparams ()); ++ ANNOUNCE_WRITE (SSCG_FILE_TYPE_DHPARAMS); + } +- BIO_free (cert_out); +- cert_out = NULL; + + ++ /* Set the final file permissions */ ++ sscg_io_utils_finalize_output_files (options->streams); ++ + ret = EOK; ++ + done: +- BIO_free (cert_key_out); +- BIO_free (cert_out); +- BIO_free (ca_key_out); +- BIO_free (ca_out); +- + talloc_zfree (main_ctx); + if (ret != EOK) + { +- fprintf (stderr, "%s\n", strerror (ret)); ++ SSCG_ERROR ("%s\n", strerror (ret)); + } + return ret; + } +diff --git a/test/dhparams_test.c b/test/dhparams_test.c +new file mode 100644 +index 0000000000000000000000000000000000000000..b054b40ea73ca98870836bd89ea10677be8fb075 +--- /dev/null ++++ b/test/dhparams_test.c +@@ -0,0 +1,106 @@ ++/* ++ This file is part of sscg. ++ ++ sscg 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 3 of the License, or ++ (at your option) any later version. ++ ++ sscg 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 sscg. If not, see . ++ ++ Copyright 2019 by Stephen Gallagher ++*/ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include "include/dhparams.h" ++ ++int ++main (int argc, char **argv) ++{ ++ int ret, sret, prime_len, generator; ++ struct sscg_dhparams *params = NULL; ++ TALLOC_CTX *main_ctx = NULL; ++ ++ if (getenv ("SSCG_SKIP_DHPARAMS")) ++ { ++ /* Skip this test */ ++ return 77; ++ } ++ ++ errno = 0; ++ prime_len = strtol (argv[1], NULL, 0); ++ if (errno) ++ { ++ fprintf (stderr, "Prime length was not a valid integer."); ++ ret = errno; ++ goto done; ++ } ++ ++ errno = 0; ++ generator = strtol (argv[2], NULL, 0); ++ if (errno) ++ { ++ fprintf (stderr, "Generator was not a valid integer."); ++ ret = errno; ++ goto done; ++ } ++ ++ main_ctx = talloc_new (NULL); ++ ++ ret = create_dhparams (main_ctx, SSCG_DEBUG, prime_len, generator, ¶ms); ++ if (ret != EOK) ++ { ++ fprintf (stderr, ++ "Could not generate DH parameters: [%s]", ++ ERR_error_string (ERR_get_error (), NULL)); ++ goto done; ++ } ++ ++ if (!DH_check (params->dh, &sret)) ++ { ++ ERR_print_errors_fp (stderr); ++ goto done; ++ } ++ if (sret & DH_CHECK_P_NOT_PRIME) ++ fprintf (stderr, "p value is not prime\n"); ++ if (sret & DH_CHECK_P_NOT_SAFE_PRIME) ++ fprintf (stderr, "p value is not a safe prime\n"); ++ if (sret & DH_CHECK_Q_NOT_PRIME) ++ fprintf (stderr, "q value is not a prime\n"); ++ if (sret & DH_CHECK_INVALID_Q_VALUE) ++ fprintf (stderr, "q value is invalid\n"); ++ if (sret & DH_CHECK_INVALID_J_VALUE) ++ fprintf (stderr, "j value is invalid\n"); ++ if (sret & DH_UNABLE_TO_CHECK_GENERATOR) ++ fprintf (stderr, "unable to check the generator value\n"); ++ if (sret & DH_NOT_SUITABLE_GENERATOR) ++ fprintf (stderr, "the g value is not a generator\n"); ++ ++ if (sret != 0) ++ { ++ /* ++ * We have generated parameters but DH_check() indicates they are ++ * invalid! This should never happen! ++ */ ++ fprintf (stderr, "ERROR: Invalid parameters generated\n"); ++ ret = EIO; ++ goto done; ++ } ++ ++ ret = EOK; ++ ++done: ++ talloc_free (main_ctx); ++ return ret; ++} +-- +2.23.0 + diff --git a/0008-Fix-client-cert-issues-found-by-CI-tests.patch b/0008-Fix-client-cert-issues-found-by-CI-tests.patch new file mode 100644 index 0000000..152464a --- /dev/null +++ b/0008-Fix-client-cert-issues-found-by-CI-tests.patch @@ -0,0 +1,98 @@ +From 8afa0ce578ecd5cc3a397707fdb163cc169b9bd1 Mon Sep 17 00:00:00 2001 +From: Stephen Gallagher +Date: Fri, 13 Dec 2019 08:25:01 -0500 +Subject: [PATCH 8/8] Fix client-cert issues found by CI tests + +Resolves: rhbz#1720667 + +Better error message for client certs without public key file + +Signed-off-by: Stephen Gallagher + +Fix memory leak in sscg_sign_x509_csr() + +Signed-off-by: Stephen Gallagher + +Address clang-analyzer warning + +clang-analyzer determined that it was possible for the GET_BIO() +return value to have changed between conditional creation of the +client certificate and writing it out. This patch stores the result +of the lookup so it's certain to be consistent. + +Signed-off-by: Stephen Gallagher +--- + src/io_utils.c | 4 ++-- + src/sscg.c | 8 +++++--- + src/x509.c | 1 + + 3 files changed, 8 insertions(+), 5 deletions(-) + +diff --git a/src/io_utils.c b/src/io_utils.c +index 809a1da0e455afa0dba0796a5f7ac406742328a1..a2502afb20f4bcb536428f3528900c2bb06997f5 100644 +--- a/src/io_utils.c ++++ b/src/io_utils.c +@@ -363,8 +363,8 @@ sscg_io_utils_open_output_files (struct sscg_stream **streams, bool overwrite) + + case IO_UTILS_CLIENT_UNMATCHED: + SSCG_ERROR ( +- "The client certificate must have both public and private key " +- "locations specified.\n"); ++ "The client certificate must have the public key location " ++ "specified.\n"); + ret = EINVAL; + goto done; + +diff --git a/src/sscg.c b/src/sscg.c +index 470af815d91f5170a1e8fe00006dbaee4d07b209..f34a43b83e562d0bd7da9a77e25911762db83693 100644 +--- a/src/sscg.c ++++ b/src/sscg.c +@@ -300,6 +300,7 @@ main (int argc, const char **argv) + char *cert_key_password = NULL; + char *cert_key_passfile = NULL; + ++ bool build_client_cert = false; + int client_mode = SSCG_CERT_DEFAULT_MODE; + int client_key_mode = SSCG_KEY_DEFAULT_MODE; + char *client_key_password = NULL; +@@ -1118,7 +1119,8 @@ main (int argc, const char **argv) + /* If requested, generate the client auth certificate and sign it with the + * private CA. + */ +- if (GET_BIO (SSCG_FILE_TYPE_CLIENT)) ++ build_client_cert = !!(GET_BIO (SSCG_FILE_TYPE_CLIENT)); ++ if (build_client_cert) + { + ret = create_cert (main_ctx, + options, +@@ -1136,7 +1138,7 @@ main (int argc, const char **argv) + + /* Write private keys first */ + +- if (GET_BIO (SSCG_FILE_TYPE_CLIENT_KEY)) ++ if (build_client_cert) + { + /* This function has a default mechanism for prompting for the + * password if it is passed a cipher and gets a NULL password. +@@ -1201,7 +1203,7 @@ main (int argc, const char **argv) + /* Public keys come next, in chain order */ + + /* Start with the client certificate */ +- if (GET_BIO (SSCG_FILE_TYPE_CLIENT)) ++ if (build_client_cert) + { + sret = PEM_write_bio_X509 (GET_BIO (SSCG_FILE_TYPE_CLIENT), + client_cert->certificate); +diff --git a/src/x509.c b/src/x509.c +index 18f0627bc64e7cb503a9e81c36dbe726186d1144..c173f539791fbbc51e52e6b121e587dca43924d4 100644 +--- a/src/x509.c ++++ b/src/x509.c +@@ -482,5 +482,6 @@ done: + *_cert = talloc_steal (mem_ctx, scert); + } + X509_NAME_free (subject); ++ talloc_free(tmp_ctx); + return ret; + } +-- +2.23.0 + diff --git a/0009-Fix-help-message-for-client-key-file.patch b/0009-Fix-help-message-for-client-key-file.patch new file mode 100644 index 0000000..04e0777 --- /dev/null +++ b/0009-Fix-help-message-for-client-key-file.patch @@ -0,0 +1,36 @@ +From fa6be1a9bbc8c5d42a248e398e3aac08078e311e Mon Sep 17 00:00:00 2001 +From: Stephen Gallagher +Date: Fri, 13 Dec 2019 11:51:43 -0500 +Subject: [PATCH 9/9] Fix help message for --client-key-file + +Resolves: rhbz#1720667 + +Signed-off-by: Stephen Gallagher + +Further clarify --client-key-file help message + +Resolves: rhbz#1720667 + +Signed-off-by: Stephen Gallagher +--- + src/sscg.c | 4 +--- + 1 file changed, 1 insertion(+), 3 deletions(-) + +diff --git a/src/sscg.c b/src/sscg.c +index f34a43b83e562d0bd7da9a77e25911762db83693..4d009a67488e83c4332f58ee52f7d6ea72a8ddbd 100644 +--- a/src/sscg.c ++++ b/src/sscg.c +@@ -734,9 +734,7 @@ main (int argc, const char **argv) + &client_key_file, + 0, + _ ("Path where the client's private key will be stored. " +- "(default is client-file with a .key suffix, if " +- "--client-file was passed, otherwise this file will not " +- "be generated.)"), ++ "(default is the client-file)"), + NULL, + }, + +-- +2.24.1 + diff --git a/0010-Better-validation-of-command-line-arguments.patch b/0010-Better-validation-of-command-line-arguments.patch new file mode 100644 index 0000000..7e934e9 --- /dev/null +++ b/0010-Better-validation-of-command-line-arguments.patch @@ -0,0 +1,920 @@ +From 87530e9ebc872761c06506f3cb6a4fa5c494a614 Mon Sep 17 00:00:00 2001 +From: Stephen Gallagher +Date: Tue, 7 Jan 2020 14:32:01 -0500 +Subject: [PATCH 10/10] Better validation of command line arguments + +Check that key passphrases are within 4-1023 characters + +OpenSSL CLI tools cannot handle files with passphrases outside this +range. + +Resolves: rhbz#1784441 + +Signed-off-by: Stephen Gallagher + +Output private keys with 2048 iteration count + +Signed-off-by: Stephen Gallagher + +Rework passphrase handling + +Handle passphrases as part of the sscg_stream for a file. This will +allow us to check for relevance as well as reducing code duplication. + +Resolves: rhbz#1784443 + +Signed-off-by: Stephen Gallagher + +Fix wrong x509 version in CSR + +This was, fortunately, not causing any problems because the signing +process resulted in the certificates being generated with the +correct version. It's best to be correct anyway. + +Signed-off-by: Stephen Gallagher + +Fix memory leaks + +Signed-off-by: Stephen Gallagher + +Fix alignment issue with popt + +The boolean values need to be explicitly defined as int because +a bool may not be aligned properly. It was working prior to some +recent changes by lucky accident. + +Signed-off-by: Stephen Gallagher + +Prevent uninitialized read error + +Signed-off-by: Stephen Gallagher + +Add missing newline for error message + +Signed-off-by: Stephen Gallagher + +Fix OpenSSL 1.0 support + +The symbol UI_F_UI_SET_RESULT changed to UI_F_UI_SET_RESULT_EX in +OpenSSL 1.1, but no other semantics changed that we care about. + +Signed-off-by: Stephen Gallagher + +Fix formatting + +Signed-off-by: Stephen Gallagher + +Fix missing error check + +Signed-off-by: Stephen Gallagher + +Read long password files properly + +Long passphrase files may require more than a single call to BIO_read() +to gather the whole string. + +Signed-off-by: Stephen Gallagher +--- + include/io_utils.h | 37 ++++++- + include/key.h | 6 +- + include/sscg.h | 42 +++++--- + include/x509.h | 6 +- + meson.build | 1 + + src/io_utils.c | 199 ++++++++++++++++++++++++++++++++++++- + src/sscg.c | 239 +++++++-------------------------------------- + src/x509.c | 6 +- + 8 files changed, 310 insertions(+), 226 deletions(-) + +diff --git a/include/io_utils.h b/include/io_utils.h +index 6a89a476b3d982447b6603153c6765835cd67464..907097c7ff1f7ae3c3adf35d0dfba0f5763dc8c0 100644 +--- a/include/io_utils.h ++++ b/include/io_utils.h +@@ -24,6 +24,7 @@ + #include + #include + ++#include "include/key.h" + #include "include/sscg.h" + + +@@ -33,6 +34,9 @@ struct sscg_stream + char *path; + int mode; + int filetypes; ++ ++ bool pass_prompt; ++ char *passphrase; + }; + + +@@ -69,8 +73,6 @@ sscg_io_utils_get_path_by_type (struct sscg_stream **streams, + * @path: The path to the file on disk. + * @mode: The filesystem mode this file should have when written to disk. + * See chmod(1) for the possible values. +- * @overwrite: If true, replace any existing file at @normalized_path. If +- * false, opening will fail if it already exists and return an error. + * + * Prepares all output filenames to be opened. Files are not created until + * sscg_io_utils_open_output_files() is called. +@@ -82,9 +84,40 @@ sscg_io_utils_add_output_file (struct sscg_stream **streams, + int mode); + + ++/** ++ * sscg_io_utils_add_output_key: ++ * @streams: The array of streams from the sscg_options ++ * @filetype: ++ * @path: The path to the file on disk. ++ * @mode: The filesystem mode this file should have when written to disk. ++ * See chmod(1) for the possible values. ++ * @pass_prompt: Whether the user should be prompted to enter a passphrase ++ * interactively. ++ * @passphrase: The passphrase supplied at the command line. ++ * @passfile: The path to a file containing the passphrase. ++ * ++ * Prepares all output filenames to be opened. Files are not created until ++ * sscg_io_utils_open_output_files() is called. ++ */ ++int ++sscg_io_utils_add_output_key (struct sscg_stream **streams, ++ enum sscg_file_type filetype, ++ const char *path, ++ int mode, ++ bool pass_prompt, ++ char *passphrase, ++ char *passfile); ++ ++ + int + sscg_io_utils_open_output_files (struct sscg_stream **streams, bool overwrite); + ++int ++sscg_io_utils_write_privatekey (struct sscg_stream **streams, ++ enum sscg_file_type filetype, ++ struct sscg_evp_pkey *key, ++ struct sscg_options *options); ++ + /* If this function fails, some of the output files may be left as 0400 */ + int + sscg_io_utils_finalize_output_files (struct sscg_stream **streams); +diff --git a/include/key.h b/include/key.h +index ef871d6937e2fc805a445d6686263b023a38eaaa..4c32cad04950ee7fd75ec4144147eb919280c00a 100644 +--- a/include/key.h ++++ b/include/key.h +@@ -17,15 +17,15 @@ + Copyright 2017 by Stephen Gallagher + */ + ++#ifndef _SSCG_KEY_H ++#define _SSCG_KEY_H ++ + #include + #include + + #include "include/sscg.h" + #include "include/bignum.h" + +-#ifndef _SSCG_KEY_H +-#define _SSCG_KEY_H +- + struct sscg_evp_pkey + { + EVP_PKEY *evp_pkey; +diff --git a/include/sscg.h b/include/sscg.h +index 2744404c25c68ed905ca621bb955e0c04b33ca81..96b78152ccc492deafbbc61eb98702562a8fe5e6 100644 +--- a/include/sscg.h ++++ b/include/sscg.h +@@ -20,17 +20,18 @@ + /* This is a master header file that should be included by all + sscg source files. */ + ++ ++#ifndef _SSCG_H ++#define _SSCG_H ++ + #include + #include + #include ++#include + #include + #include + #include + +-#include "include/io_utils.h" +- +-#ifndef _SSCG_H +-#define _SSCG_H + + /* TODO: implement internationalization */ + +@@ -81,15 +82,34 @@ + } \ + while (0) + ++/* The function changed in 1.1, but the library and reason names did not */ ++#ifndef UI_F_UI_SET_RESULT_EX ++#define UI_F_UI_SET_RESULT_EX UI_F_UI_SET_RESULT ++#endif ++ + #define CHECK_SSL(_sslret, _fn) \ + do \ + { \ + if (_sslret != 1) \ + { \ + /* Get information about error from OpenSSL */ \ ++ unsigned long _ssl_error = ERR_get_error (); \ ++ if ((ERR_GET_LIB (_ssl_error) == ERR_LIB_UI) && \ ++ (ERR_GET_FUNC (_ssl_error) == UI_F_UI_SET_RESULT_EX) && \ ++ ((ERR_GET_REASON (_ssl_error) == UI_R_RESULT_TOO_LARGE) || \ ++ (ERR_GET_REASON (_ssl_error) == UI_R_RESULT_TOO_SMALL))) \ ++ { \ ++ fprintf ( \ ++ stderr, \ ++ "Passphrases must be between %d and %d characters. \n", \ ++ SSCG_MIN_KEY_PASS_LEN, \ ++ SSCG_MAX_KEY_PASS_LEN); \ ++ ret = EINVAL; \ ++ goto done; \ ++ } \ + fprintf (stderr, \ + "Error occurred in " #_fn ": [%s].\n", \ +- ERR_error_string (ERR_get_error (), NULL)); \ ++ ERR_error_string (_ssl_error, NULL)); \ + ret = EIO; \ + goto done; \ + } \ +@@ -223,12 +243,9 @@ struct sscg_options + 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; +- bool client_key_pass_prompt; +- char *client_key_pass; ++ int ca_key_pass_prompt; ++ int cert_key_pass_prompt; ++ int client_key_pass_prompt; + + /* Output Files */ + struct sscg_stream **streams; +@@ -251,4 +268,7 @@ enum sscg_cert_type + SSCG_NUM_CERT_TYPES + }; + ++#define SSCG_MIN_KEY_PASS_LEN 4 ++#define SSCG_MAX_KEY_PASS_LEN 1023 ++ + #endif /* _SSCG_H */ +diff --git a/include/x509.h b/include/x509.h +index 865cd0018d3ea77915cd86349e333ae6f4de2af0..cc7e498d06c4d2e503d7d8748dfd5386f9ad0794 100644 +--- a/include/x509.h ++++ b/include/x509.h +@@ -17,6 +17,9 @@ + Copyright 2017 by Stephen Gallagher + */ + ++#ifndef _SSCG_X509_H ++#define _SSCG_X509_H ++ + #include + #include + +@@ -24,9 +27,6 @@ + #include "include/bignum.h" + #include "include/key.h" + +-#ifndef _SSCG_X509_H +-#define _SSCG_X509_H +- + struct sscg_cert_info + { + /* === Input Data === */ +diff --git a/meson.build b/meson.build +index eb339ea8c768adab6d576736fbe476b83529e78d..3d8937ce73dc84f652f6fdad461a1468a532f0f2 100644 +--- a/meson.build ++++ b/meson.build +@@ -76,6 +76,7 @@ sscg_lib_hdrs = [ + 'include/dhparams.h', + 'include/io_utils.h', + 'include/key.h', ++ 'include/sscg.h', + 'include/x509.h', + ] + +diff --git a/src/io_utils.c b/src/io_utils.c +index a2502afb20f4bcb536428f3528900c2bb06997f5..1b8bc41c3849acbe4657ae14dfe55e3010957129 100644 +--- a/src/io_utils.c ++++ b/src/io_utils.c +@@ -24,8 +24,14 @@ + #include + + #include "include/io_utils.h" ++#include "include/key.h" + #include "include/sscg.h" + ++ ++/* Same as OpenSSL CLI */ ++#define MAX_PW_LEN 1024 ++ ++ + int + sscg_normalize_path (TALLOC_CTX *mem_ctx, + const char *path, +@@ -62,6 +68,12 @@ sscg_stream_destructor (TALLOC_CTX *ptr) + + BIO_free (stream->bio); + ++ /* Zero out the memory before freeing it so we don't leak passwords */ ++ if (stream->passphrase) ++ { ++ memset (stream->passphrase, 0, strnlen (stream->passphrase, MAX_PW_LEN)); ++ } ++ + return 0; + } + +@@ -147,11 +159,101 @@ sscg_io_utils_get_path_by_type (struct sscg_stream **streams, + } + + ++/* 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 ((void *)src, 0, strlen (src)); ++ ++ return dest; ++} ++ ++ ++static int ++validate_passphrase (struct sscg_stream *stream) ++{ ++ /* Ignore non-key types */ ++ if (!(stream->filetypes & SSCG_FILE_TYPE_KEYS)) ++ return EOK; ++ ++ /* Ignore unset passwords; these will be prompted for when writing out the ++ * key file ++ */ ++ if (!stream->passphrase) ++ return EOK; ++ ++ size_t pass_len = strnlen (stream->passphrase, SSCG_MAX_KEY_PASS_LEN + 1); ++ ++ if ((pass_len < SSCG_MIN_KEY_PASS_LEN) || (pass_len > SSCG_MAX_KEY_PASS_LEN)) ++ { ++ SSCG_ERROR ("Passphrases must be between %d and %d characters. \n", ++ SSCG_MIN_KEY_PASS_LEN, ++ SSCG_MAX_KEY_PASS_LEN); ++ return EINVAL; ++ } ++ return EOK; ++} ++ ++ ++static char * ++sscg_read_pw_file (TALLOC_CTX *mem_ctx, char *path) ++{ ++ int i; ++ BIO *pwdbio = NULL; ++ char tpass[MAX_PW_LEN + 1]; ++ int offset = 0; ++ char *tmp = NULL; ++ char *password = NULL; ++ ++ pwdbio = BIO_new_file (path, "r"); ++ if (pwdbio == NULL) ++ { ++ fprintf (stderr, "Can't open file %s\n", path); ++ return NULL; ++ } ++ ++ /* Read up to one more character than the MAX_PW_LEN */ ++ for (offset = 0; ++ (i = BIO_read (pwdbio, tpass + offset, MAX_PW_LEN + 1 - offset)) > 0 && ++ offset < (MAX_PW_LEN + 1); ++ offset += i) ++ ; ++ ++ tpass[MAX_PW_LEN] = '\0'; ++ ++ BIO_free_all (pwdbio); ++ pwdbio = NULL; ++ ++ if (i < 0) ++ { ++ fprintf (stderr, "Error reading password from BIO\n"); ++ return NULL; ++ } ++ ++ tmp = strchr (tpass, '\n'); ++ if (tmp != NULL) ++ *tmp = 0; ++ ++ password = talloc_strdup (mem_ctx, tpass); ++ ++ memset (tpass, 0, MAX_PW_LEN + 1); ++ ++ return password; ++} ++ ++ + int +-sscg_io_utils_add_output_file (struct sscg_stream **streams, +- enum sscg_file_type filetype, +- const char *path, +- int mode) ++sscg_io_utils_add_output_key (struct sscg_stream **streams, ++ enum sscg_file_type filetype, ++ const char *path, ++ int mode, ++ bool pass_prompt, ++ char *passphrase, ++ char *passfile) + { + int ret, i; + TALLOC_CTX *tmp_ctx = NULL; +@@ -163,6 +265,22 @@ sscg_io_utils_add_output_file (struct sscg_stream **streams, + */ + if (path == NULL) + { ++ if (pass_prompt) ++ { ++ SSCG_ERROR ( ++ "Passphrase prompt requested for %s, but no file path provided.\n", ++ sscg_get_file_type_name (filetype)); ++ return EINVAL; ++ } ++ ++ if (passphrase) ++ { ++ SSCG_ERROR ( ++ "Passphrase provided for %s, but no file path provided.\n", ++ sscg_get_file_type_name (filetype)); ++ return EINVAL; ++ } ++ + SSCG_LOG (SSCG_DEBUG, + "Got a NULL path with filetype: %s\n", + sscg_get_file_type_name (filetype)); +@@ -220,6 +338,31 @@ sscg_io_utils_add_output_file (struct sscg_stream **streams, + /* Add the file type */ + stream->filetypes |= (1 << filetype); + ++ ++ /* Set the password options */ ++ stream->pass_prompt = pass_prompt; ++ ++ if (passphrase) ++ { ++ stream->passphrase = sscg_secure_string_steal (stream, passphrase); ++ ret = validate_passphrase (stream); ++ if (ret != EOK) ++ goto done; ++ } ++ else if (passfile) ++ { ++ stream->passphrase = sscg_read_pw_file (stream, passfile); ++ if (!stream->passphrase) ++ { ++ fprintf (stderr, "Failed to read passphrase from %s", passfile); ++ ret = EIO; ++ goto done; ++ } ++ } ++ ret = validate_passphrase (stream); ++ if (ret != EOK) ++ goto done; ++ + ret = EOK; + + done: +@@ -228,6 +371,17 @@ done: + } + + ++int ++sscg_io_utils_add_output_file (struct sscg_stream **streams, ++ enum sscg_file_type filetype, ++ const char *path, ++ int mode) ++{ ++ return sscg_io_utils_add_output_key ( ++ streams, filetype, path, mode, false, NULL, NULL); ++} ++ ++ + enum io_utils_errors + { + IO_UTILS_OK = 0, +@@ -400,6 +554,43 @@ done: + } + + ++int ++sscg_io_utils_write_privatekey (struct sscg_stream **streams, ++ enum sscg_file_type filetype, ++ struct sscg_evp_pkey *key, ++ struct sscg_options *options) ++{ ++ int ret, sret; ++ ++ struct sscg_stream *stream = ++ sscg_io_utils_get_stream_by_type (streams, filetype); ++ if (stream) ++ { ++ /* 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_PKCS8PrivateKey ( ++ stream->bio, ++ key->evp_pkey, ++ stream->pass_prompt || stream->passphrase ? options->cipher : NULL, ++ stream->passphrase, ++ stream->passphrase ? strlen (stream->passphrase) : 0, ++ NULL, ++ NULL); ++ CHECK_SSL (sret, PEM_write_bio_PKCS8PrivateKey); ++ ANNOUNCE_WRITE (filetype); ++ } ++ ++ ret = EOK; ++ ++done: ++ return ret; ++} ++ ++ + int + sscg_io_utils_finalize_output_files (struct sscg_stream **streams) + { +diff --git a/src/sscg.c b/src/sscg.c +index 4d009a67488e83c4332f58ee52f7d6ea72a8ddbd..96a9be1232d890590e97c126f8f4a78d571d7247 100644 +--- a/src/sscg.c ++++ b/src/sscg.c +@@ -18,6 +18,7 @@ + */ + + #define _GNU_SOURCE ++#include + #include + #include + #include +@@ -40,9 +41,6 @@ + int verbosity; + + +-/* Same as OpenSSL CLI */ +-#define MAX_PW_LEN 1024 +- + static int + get_security_level (void) + { +@@ -140,79 +138,6 @@ print_options (struct sscg_options *opts) + } + + +-/* 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; +-} +- +- +-static char * +-sscg_read_pw_file (TALLOC_CTX *mem_ctx, char *path) +-{ +- int i; +- BIO *pwdbio = NULL; +- char tpass[MAX_PW_LEN]; +- char *tmp = NULL; +- char *password = NULL; +- +- pwdbio = BIO_new_file (path, "r"); +- if (pwdbio == NULL) +- { +- fprintf (stderr, "Can't open file %s\n", path); +- return NULL; +- } +- +- i = BIO_gets (pwdbio, tpass, MAX_PW_LEN); +- BIO_free_all (pwdbio); +- pwdbio = NULL; +- +- if (i <= 0) +- { +- fprintf (stderr, "Error reading password from BIO\n"); +- return NULL; +- } +- +- tmp = strchr (tpass, '\n'); +- if (tmp != NULL) +- *tmp = 0; +- +- password = talloc_strdup (mem_ctx, tpass); +- +- memset (tpass, 0, MAX_PW_LEN); +- +- return password; +-} +- +- + const char * + sscg_get_verbosity_name (enum sscg_verbosity type) + { +@@ -310,12 +235,14 @@ main (int argc, const char **argv) + struct sscg_evp_pkey *cakey; + struct sscg_x509_cert *svc_cert; + struct sscg_evp_pkey *svc_key; +- struct sscg_x509_cert *client_cert; +- struct sscg_evp_pkey *client_key; ++ struct sscg_x509_cert *client_cert = NULL; ++ struct sscg_evp_pkey *client_key = NULL; + + int dhparams_mode = SSCG_CERT_DEFAULT_MODE; + struct sscg_dhparams *dhparams = NULL; + ++ struct sscg_stream *stream = NULL; ++ + /* Always use umask 0577 for generating certificates and keys + This means that it's opened as write-only by the effective + user. */ +@@ -335,7 +262,6 @@ 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); + + options->streams = + talloc_zero_array (options, struct sscg_stream *, SSCG_NUM_FILE_TYPES); +@@ -965,56 +891,6 @@ main (int argc, const char **argv) + } + } + +- /* Password handling */ +- if (ca_key_password) +- { +- options->ca_key_pass = +- sscg_secure_string_steal (options, ca_key_password); +- } +- else if (ca_key_passfile) +- { +- options->ca_key_pass = sscg_read_pw_file (options, ca_key_passfile); +- if (!options->ca_key_pass) +- { +- fprintf ( +- stderr, "Failed to read passphrase from %s", ca_key_passfile); +- goto done; +- } +- } +- +- if (cert_key_password) +- { +- options->cert_key_pass = +- sscg_secure_string_steal (options, cert_key_password); +- } +- else if (cert_key_passfile) +- { +- options->cert_key_pass = sscg_read_pw_file (options, cert_key_passfile); +- if (!options->cert_key_pass) +- { +- fprintf ( +- stderr, "Failed to read passphrase from %s", cert_key_passfile); +- goto done; +- } +- } +- +- if (client_key_password) +- { +- options->client_key_pass = +- sscg_secure_string_steal (options, client_key_password); +- } +- else if (client_key_passfile) +- { +- options->client_key_pass = +- sscg_read_pw_file (options, client_key_passfile); +- if (!options->client_key_pass) +- { +- fprintf ( +- stderr, "Failed to read passphrase from %s", client_key_passfile); +- goto done; +- } +- } +- + if (options->key_strength < options->minimum_key_strength) + { + fprintf (stderr, +@@ -1055,8 +931,13 @@ main (int argc, const char **argv) + ca_mode); + CHECK_OK (ret); + +- ret = sscg_io_utils_add_output_file ( +- options->streams, SSCG_FILE_TYPE_CA_KEY, ca_key_file, ca_key_mode); ++ ret = sscg_io_utils_add_output_key (options->streams, ++ SSCG_FILE_TYPE_CA_KEY, ++ ca_key_file, ++ ca_key_mode, ++ options->ca_key_pass_prompt, ++ ca_key_password, ++ ca_key_passfile); + CHECK_OK (ret); + + ret = sscg_io_utils_add_output_file (options->streams, +@@ -1065,11 +946,14 @@ main (int argc, const char **argv) + cert_mode); + CHECK_OK (ret); + +- ret = sscg_io_utils_add_output_file (options->streams, +- SSCG_FILE_TYPE_SVC_KEY, +- cert_key_file ? cert_key_file : +- "./service-key.pem", +- cert_key_mode); ++ ret = sscg_io_utils_add_output_key (options->streams, ++ SSCG_FILE_TYPE_SVC_KEY, ++ cert_key_file ? cert_key_file : ++ "./service-key.pem", ++ cert_key_mode, ++ options->cert_key_pass_prompt, ++ cert_key_password, ++ cert_key_passfile); + CHECK_OK (ret); + + +@@ -1078,11 +962,14 @@ main (int argc, const char **argv) + CHECK_OK (ret); + + +- ret = sscg_io_utils_add_output_file (options->streams, +- SSCG_FILE_TYPE_CLIENT_KEY, +- client_key_file ? client_key_file : +- client_file, +- client_key_mode); ++ ret = sscg_io_utils_add_output_key (options->streams, ++ SSCG_FILE_TYPE_CLIENT_KEY, ++ client_key_file ? client_key_file : ++ client_file, ++ client_key_mode, ++ options->client_key_pass_prompt, ++ client_key_password, ++ client_key_passfile); + CHECK_OK (ret); + + ret = sscg_io_utils_add_output_file ( +@@ -1136,67 +1023,17 @@ main (int argc, const char **argv) + + /* Write private keys first */ + +- if (build_client_cert) +- { +- /* 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 ( +- GET_BIO (SSCG_FILE_TYPE_CLIENT_KEY), +- client_key->evp_pkey, +- options->client_key_pass_prompt || options->client_key_pass ? +- options->cipher : +- NULL, +- (unsigned char *)options->client_key_pass, +- options->client_key_pass ? strlen (options->client_key_pass) : 0, +- NULL, +- NULL); +- CHECK_SSL (sret, PEM_write_bio_PrivateKey (svc)); +- ANNOUNCE_WRITE (SSCG_FILE_TYPE_SVC_KEY); +- } ++ ret = sscg_io_utils_write_privatekey ( ++ options->streams, SSCG_FILE_TYPE_CLIENT_KEY, client_key, options); ++ CHECK_OK (ret); + +- /* 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 ( +- GET_BIO (SSCG_FILE_TYPE_SVC_KEY), +- 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)); +- ANNOUNCE_WRITE (SSCG_FILE_TYPE_SVC_KEY); ++ ret = sscg_io_utils_write_privatekey ( ++ options->streams, SSCG_FILE_TYPE_SVC_KEY, svc_key, options); ++ CHECK_OK (ret); + +- /* Create CA private key, if requested */ +- if (GET_BIO (SSCG_FILE_TYPE_CA_KEY)) +- { +- /* 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 ( +- GET_BIO (SSCG_FILE_TYPE_CA_KEY), +- 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)); +- ANNOUNCE_WRITE (SSCG_FILE_TYPE_CA_KEY); +- } ++ ret = sscg_io_utils_write_privatekey ( ++ options->streams, SSCG_FILE_TYPE_CA_KEY, cakey, options); ++ CHECK_OK (ret); + + /* Public keys come next, in chain order */ + +@@ -1217,7 +1054,7 @@ main (int argc, const char **argv) + + + /* Create CA public certificate */ +- struct sscg_stream *stream = ++ stream = + sscg_io_utils_get_stream_by_type (options->streams, SSCG_FILE_TYPE_CA); + sret = PEM_write_bio_X509 (stream->bio, cacert->certificate); + CHECK_SSL (sret, PEM_write_bio_X509 (CA)); +diff --git a/src/x509.c b/src/x509.c +index c173f539791fbbc51e52e6b121e587dca43924d4..42315d42d1e03460a8121e1592d8e7fcc0fef1df 100644 +--- a/src/x509.c ++++ b/src/x509.c +@@ -72,7 +72,7 @@ _sscg_certinfo_destructor (TALLOC_CTX *ctx) + struct sscg_cert_info *certinfo = + talloc_get_type_abort (ctx, struct sscg_cert_info); + +- sk_X509_EXTENSION_free (certinfo->extensions); ++ sk_X509_EXTENSION_pop_free (certinfo->extensions, X509_EXTENSION_free); + + return 0; + } +@@ -155,7 +155,7 @@ sscg_x509v3_csr_new (TALLOC_CTX *mem_ctx, + talloc_set_destructor ((TALLOC_CTX *)csr, _sscg_csr_destructor); + + /* We will generate only x509v3 certificates */ +- sslret = X509_REQ_set_version (csr->x509_req, 3); ++ sslret = X509_REQ_set_version (csr->x509_req, 2); + CHECK_SSL (sslret, X509_REQ_set_version); + + subject = X509_REQ_get_subject_name (csr->x509_req); +@@ -461,6 +461,8 @@ sscg_sign_x509_csr (TALLOC_CTX *mem_ctx, + } + sslret = X509_add_ext (cert, ext, -1); + CHECK_SSL (sslret, X509_add_ext); ++ ++ X509_EXTENSION_free (ext); + } + + /* Sign the new certificate */ +-- +2.24.1 + diff --git a/EMPTY b/EMPTY deleted file mode 100644 index 0519ecb..0000000 --- a/EMPTY +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/sources b/sources new file mode 100644 index 0000000..c1f1695 --- /dev/null +++ b/sources @@ -0,0 +1 @@ +SHA512 (sscg-2.3.3-stripped.tar.xz) = b39ebf7f22b8419442ff8832ff302bcc2cb0cd02e66ca631f44536eacb644cf7549ac9ad15872021519dc5f951ec7cedf566c170f49e75b0b6a8e83c21b7253c diff --git a/sscg.spec b/sscg.spec new file mode 100644 index 0000000..235e7fb --- /dev/null +++ b/sscg.spec @@ -0,0 +1,269 @@ +%global provider github +%global provider_tld com +%global project sgallagher +%global repo sscg +# https://github.com/sgallagher/sscg +%global provider_prefix %{provider}.%{provider_tld}/%{project}/%{repo} +%global import_path %{provider_prefix} + + +Name: sscg +Version: 2.3.3 +Release: 14%{?dist} +Summary: Simple SSL certificate generator + +License: BSD +URL: https://%{provider_prefix} + +# Run ./sscg-strip.sh to produce a tarball with the bundled popt library +# stripped out to reduce license issues. +Source0: https://%{provider_prefix}/releases/download/%{repo}-%{version}/%{repo}-%{version}-stripped.tar.xz + +BuildRequires: gcc +BuildRequires: libtalloc-devel +BuildRequires: openssl-devel +BuildRequires: popt-devel +BuildRequires: libpath_utils-devel +BuildRequires: meson +BuildRequires: ninja-build +BuildRequires: help2man + +# Patches +Patch0001: 0001-Generate-manpage.patch +Patch0002: 0002-Adjust-defaults-based-on-system-security-level.patch +Patch0003: 0003-Adjust-hash-defaults-based-on-system-security-level.patch +Patch0004: 0004-Properly-check-all-return-values.patch + +# RHBZ #1717880 +Patch0005: 0005-Add-password-support-for-private-keys.patch +Patch0006: 0006-Allow-specifying-keyfile-password-by-file.patch + +# RHBZ #1720667 +Patch0007: 0007-Add-support-for-client-certificates-and-dhparams.patch +Patch0008: 0008-Fix-client-cert-issues-found-by-CI-tests.patch +Patch0009: 0009-Fix-help-message-for-client-key-file.patch + +# RHBZ #1784441 and 1784443 +Patch0010: 0010-Better-validation-of-command-line-arguments.patch + +%description +A utility to aid in the creation of more secure "self-signed" +certificates. The certificates created by this tool are generated in a +way so as to create a CA certificate that can be safely imported into a +client machine to trust the service certificate without needing to set +up a full PKI environment and without exposing the machine to a risk of +false signatures from the service certificate. + + +%prep +%autosetup -p1 + + +%build +%meson +%meson_build + +%install +%meson_install + +%check + +%ifnarch %{arm} +%meson_test +%endif + +%files +%license COPYING +%doc README.md +%{_bindir}/%{name} +%{_mandir}/man8/%{name}.8* + +%changelog +* Tue Jan 21 2020 Stephen Gallagher - 2.3.3-14 +- Properly handling reading long passphrase files. + +* Tue Jan 21 2020 Stephen Gallagher - 2.3.3-13 +- Fix missing error check for --*-key-passfile + +* Thu Jan 09 2020 Stephen Gallagher - 2.3.3-12 +- Improve validation of command-line arguments +- Resolves: rhbz#1784441 +- Resolves: rhbz#1784443 + +* Tue Jan 07 2020 Stephen Gallagher - 2.3.3-11 +- Further improve --client-key-file help message +- Resolves: rhbz#1720667 + +* Fri Dec 13 2019 Stephen Gallagher - 2.3.3-10 +- Fix incorrect help message +- Resolves: rhbz#1720667 + +* Fri Dec 13 2019 Stephen Gallagher - 2.3.3-9 +- Fix null-dereference and memory leak issues with client certs +- Resolves: rhbz#1720667 + +* Wed Dec 11 2019 Stephen Gallagher - 2.3.3-8 +- Add support for generating client authentication certificates +- Resolves: rhbz#1720667 + +* Fri Nov 01 2019 Stephen Gallagher - 2.3.3-7 +- Add support for password-protecting the private key files +- Resolves: rhbz#1717880 + +* Wed Nov 28 2018 Stephen Gallagher - 2.3.3-6 +- Fixes for issues detected by automated testing. +- Resolves: rhbz#1653323 + +* Wed Nov 28 2018 Stephen Gallagher - 2.3.3-5 +- Autodetect the minimum key strength from the system security level. +- Autodetect the hash algorithm to use from the system security level. +- Disallow setting a key strength below the system minimum. +- Resolves: rhbz#1653323 + +* Mon Sep 17 2018 Stephen Gallagher - 2.3.3-4 +- Add a manpage for sscg. + +* Thu Jul 05 2018 Stephen Gallagher - 2.3.3-3 +- Strip out bundled popt since RHEL 8 has a new-enough version. + +* Fri Feb 09 2018 Fedora Release Engineering - 2.3.3-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_28_Mass_Rebuild + +* Fri Feb 02 2018 Stephen Gallagher - 2.3.3-1 +- Update to 2.3.3 +- Do not overwrite destination files without --force + +* Thu Jan 25 2018 Stephen Gallagher - 2.3.2-1 +- Update to 2.3.2 +- Properly support hostnames up to 64 characters +- Resolves: rhbz#1535537 + +* Tue Jan 02 2018 Stephen Gallagher - 2.3.1-2 +- Skip tests on 32-bit ARM for now + +* Tue Jan 02 2018 Stephen Gallagher - 2.3.1-1 +- Update to 2.3.1 +- Bundle popt 1.16 on older releases like EPEL. + +* Mon Dec 18 2017 Stephen Gallagher - 2.3.0-1 +- Update to 2.3.0 +- Switch to meson build system +- Add support for non-DNS subjectAlternativeName values (issue #4) + +* Thu Sep 21 2017 Stephen Gallagher - 2.2.0-1 +- Reorder combined PEM file +- Resolves: RHBZ#1494208 + +* Wed Sep 20 2017 Stephen Gallagher - 2.1.0-1 +- Add --email argument for setting emailAddress in the issuer + +* Thu Aug 03 2017 Fedora Release Engineering - 2.0.4-4 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Binutils_Mass_Rebuild + +* Thu Jul 27 2017 Fedora Release Engineering - 2.0.4-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Mass_Rebuild + +* Mon Apr 03 2017 Stephen Gallagher - 2.0.4-2 +- Bump release to perform taskotron tests + +* Tue Mar 21 2017 Stephen Gallagher - 2.0.4-1 +- Update to 2.0.4 +- Addresses a potential race-condition when the key and certificate share the + same file. + +* Wed Mar 08 2017 Stephen Gallagher - 2.0.3-1 +- Update to 2.0.3 +- Adds support for setting the file mode on the output certificates + and keys. + +* Fri Mar 03 2017 Stephen Gallagher - 2.0.2-1 +- Update to 2.0.2 +- Always run with umask(077) + +* Fri Mar 03 2017 Stephen Gallagher - 2.0.1-1 +- Update to 2.0.1 +- Fix an issue with passing certificate lifetime explicitly + +* Thu Feb 16 2017 Stephen Gallagher - 2.0.0-1 +- Update to 2.0.0 + +* Thu Feb 16 2017 Stephen Gallagher - 1.1.0-6 +- Exclude PPC64 from the build since it doesn't support linking to OpenSSL + +* Sat Feb 11 2017 Fedora Release Engineering - 1.1.0-5 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_26_Mass_Rebuild + +* Wed Nov 23 2016 Stephen Gallagher - 1.1.0-4 +- Use compat-openssl10-devel on F26+ + +* Thu Jul 21 2016 Fedora Release Engineering - 1.1.0-3 +- https://fedoraproject.org/wiki/Changes/golang1.7 + +* Tue May 31 2016 Stephen Gallagher - 1.1.0-2 +- Debundle spacelog + +* Wed May 25 2016 Stephen Gallagher - 1.1.0-1 +- Update to 1.1.0 +- Add support for signing service keys with an existing CA + +* Wed May 25 2016 Stephen Gallagher - 1.0.4-1 +- Add support for exporting the CA private key +- Fix incorrect output from -version +- Add README.md + +* Tue May 24 2016 Stephen Gallagher - 1.0.3-1 +- Only sign certificates after all extensions have been added + +* Mon May 23 2016 Stephen Gallagher - 1.0.2-1 +- Generate x509v3 certificates + +* Mon May 23 2016 Stephen Gallagher - 1.0.1-1 +- Fix issue with temporary file creation + +* Mon May 23 2016 Stephen Gallagher - 1.0.0-1 +- New upstream release 1.0.0 +- Rewritten in Go +- Runtime depends only on OpenSSL, no more Python +- Support for writing certificate and key in a single file + +* Wed May 18 2016 Stephen Gallagher - 0.4.1-4 +- Add requirement on python-setuptools + +* Fri Feb 05 2016 Fedora Release Engineering - 0.4.1-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_24_Mass_Rebuild + +* Tue Nov 10 2015 Fedora Release Engineering - 0.4.1-2 +- Rebuilt for https://fedoraproject.org/wiki/Changes/python3.5 + +* Fri Jun 19 2015 Fedora Release Engineering - 0.4.1-1 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_23_Mass_Rebuild + +* Mon Mar 30 2015 Stephen Gallagher 0.4.1-1 +- Change default CA location to match service certificate +- Improve error handling + +* Tue Mar 24 2015 Stephen Gallagher 0.4.0-1 +- Spec file cleanups +- PEP8 Cleanups +- Make location arguments optional + +* Mon Mar 23 2015 Stephen Gallagher 0.3.0-1 +- Rename to sscg +- Only build with default python interpreter + +* Tue Mar 17 2015 Stephen Gallagher 0.2.1-1 +- Include the LICENSE file in the tarball + +* Tue Mar 17 2015 Stephen Gallagher 0.2-2 +- Include the license in the build RPMs + +* Tue Mar 17 2015 Stephen Gallagher 0.2-1 +- Add support for namedConstraints +- Add support for subjectAltNames +- Fix packaging issues from Fedora package review + +* Mon Mar 16 2015 Stephen Gallagher 0.1-2 +- Update BuildRequires + +* Mon Mar 16 2015 Stephen Gallagher 0.1-1 +- First packaging