From aaf66f5eb47e26289d4a9d37ea4e1cb571ad975a Mon Sep 17 00:00:00 2001 From: CentOS Sources Date: Tue, 8 Nov 2022 01:43:22 -0500 Subject: [PATCH] import sscg-3.0.0-5.el8 --- .gitignore | 2 +- .sscg.metadata | 2 +- SOURCES/0001-Drop-usage-of-ERR_GET_FUNC.patch | 34 + SOURCES/0001-Generate-manpage.patch | 50 - ...aults-based-on-system-security-level.patch | 208 -- ...ect-certificate-lifetime-calculation.patch | 46 + ...aults-based-on-system-security-level.patch | 115 - SOURCES/0003-Truncate-IP-address-in-SAN.patch | 68 + ...004-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 ------ SPECS/sscg.spec | 44 +- 16 files changed, 167 insertions(+), 4584 deletions(-) create mode 100644 SOURCES/0001-Drop-usage-of-ERR_GET_FUNC.patch delete mode 100644 SOURCES/0001-Generate-manpage.patch delete mode 100644 SOURCES/0002-Adjust-defaults-based-on-system-security-level.patch create mode 100644 SOURCES/0002-Correct-certificate-lifetime-calculation.patch delete mode 100644 SOURCES/0003-Adjust-hash-defaults-based-on-system-security-level.patch create mode 100644 SOURCES/0003-Truncate-IP-address-in-SAN.patch delete mode 100644 SOURCES/0004-Properly-check-all-return-values.patch delete mode 100644 SOURCES/0005-Add-password-support-for-private-keys.patch delete mode 100644 SOURCES/0006-Allow-specifying-keyfile-password-by-file.patch delete mode 100644 SOURCES/0007-Add-support-for-client-certificates-and-dhparams.patch delete mode 100644 SOURCES/0008-Fix-client-cert-issues-found-by-CI-tests.patch delete mode 100644 SOURCES/0009-Fix-help-message-for-client-key-file.patch delete mode 100644 SOURCES/0010-Better-validation-of-command-line-arguments.patch diff --git a/.gitignore b/.gitignore index 5f094c1..4798d97 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1 @@ -SOURCES/sscg-2.3.3-stripped.tar.xz +SOURCES/sscg-3.0.0.tar.xz diff --git a/.sscg.metadata b/.sscg.metadata index 7151dd4..ba54818 100644 --- a/.sscg.metadata +++ b/.sscg.metadata @@ -1 +1 @@ -6e880fc36f7d1ebf4a9668dbcb9276b3afcb2904 SOURCES/sscg-2.3.3-stripped.tar.xz +81e3b33e118edff96583314ceb4bfde9a1e6b45c SOURCES/sscg-3.0.0.tar.xz diff --git a/SOURCES/0001-Drop-usage-of-ERR_GET_FUNC.patch b/SOURCES/0001-Drop-usage-of-ERR_GET_FUNC.patch new file mode 100644 index 0000000..5ad7b9d --- /dev/null +++ b/SOURCES/0001-Drop-usage-of-ERR_GET_FUNC.patch @@ -0,0 +1,34 @@ +From d2277e711bb16e3b98f43565e71b7865b5fed423 Mon Sep 17 00:00:00 2001 +From: Stephen Gallagher +Date: Sat, 7 Aug 2021 11:48:04 -0400 +Subject: [PATCH 1/2] Drop usage of ERR_GET_FUNC() + +This macro was dropped in OpenSSL 3.0 and has actually not been +providing a valid return code for some time. + +Related: rhbz#1964837 + +Signed-off-by: Stephen Gallagher +--- + include/sscg.h | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/include/sscg.h b/include/sscg.h +index faf86ba4f68e186bd35c7bc3ec77b98b8e37d253..851dc93175607e5223a70ef40a5feb24b7b69215 100644 +--- a/include/sscg.h ++++ b/include/sscg.h +@@ -94,11 +94,10 @@ + 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, \ +-- +2.33.0 + diff --git a/SOURCES/0001-Generate-manpage.patch b/SOURCES/0001-Generate-manpage.patch deleted file mode 100644 index cc55444..0000000 --- a/SOURCES/0001-Generate-manpage.patch +++ /dev/null @@ -1,50 +0,0 @@ -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/SOURCES/0002-Adjust-defaults-based-on-system-security-level.patch b/SOURCES/0002-Adjust-defaults-based-on-system-security-level.patch deleted file mode 100644 index 3fd62ce..0000000 --- a/SOURCES/0002-Adjust-defaults-based-on-system-security-level.patch +++ /dev/null @@ -1,208 +0,0 @@ -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/SOURCES/0002-Correct-certificate-lifetime-calculation.patch b/SOURCES/0002-Correct-certificate-lifetime-calculation.patch new file mode 100644 index 0000000..5a0b87b --- /dev/null +++ b/SOURCES/0002-Correct-certificate-lifetime-calculation.patch @@ -0,0 +1,46 @@ +From 87604820a935f87a8f533e3f294419d27c0514eb Mon Sep 17 00:00:00 2001 +From: Allison Karlitskaya +Date: Tue, 26 Oct 2021 12:32:13 +0200 +Subject: [PATCH 2/2] Correct certificate lifetime calculation + +sscg allows passing the certificate lifetime, as a number of days, as a +commandline argument. It converts this value to seconds using the +formula + + days * 24 * 3650 + +which is incorrect. The correct value is 3600. + +This effectively adds an extra 20 minutes to the lifetime of the +certificate for each day as given on the commandline, and was enough to +cause some new integration tests in cockpit to fail. + +Interestingly, 3650 is the old default value for the number of days of +certificate validity (~10 years) so this probably slipped in as a sort +of muscle-memory-assisted typo. + +Let's just write `24 * 60 * 60` to make things clear. +--- + src/x509.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/x509.c b/src/x509.c +index dc1594a4bdcb9d81607f0fe5ad2d4562e5edb533..7c7e4dfe56d5756862f3e0f851941e846ce96f31 100644 +--- a/src/x509.c ++++ b/src/x509.c +@@ -416,11 +416,11 @@ sscg_sign_x509_csr (TALLOC_CTX *mem_ctx, + X509_set_issuer_name (cert, X509_REQ_get_subject_name (csr)); + } + + /* set time */ + X509_gmtime_adj (X509_get_notBefore (cert), 0); +- X509_gmtime_adj (X509_get_notAfter (cert), days * 24 * 3650); ++ X509_gmtime_adj (X509_get_notAfter (cert), days * 24 * 60 * 60); + + /* set subject */ + subject = X509_NAME_dup (X509_REQ_get_subject_name (csr)); + sslret = X509_set_subject_name (cert, subject); + CHECK_SSL (sslret, X509_set_subject_name); +-- +2.33.0 + diff --git a/SOURCES/0003-Adjust-hash-defaults-based-on-system-security-level.patch b/SOURCES/0003-Adjust-hash-defaults-based-on-system-security-level.patch deleted file mode 100644 index 66e8224..0000000 --- a/SOURCES/0003-Adjust-hash-defaults-based-on-system-security-level.patch +++ /dev/null @@ -1,115 +0,0 @@ -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/SOURCES/0003-Truncate-IP-address-in-SAN.patch b/SOURCES/0003-Truncate-IP-address-in-SAN.patch new file mode 100644 index 0000000..c492f38 --- /dev/null +++ b/SOURCES/0003-Truncate-IP-address-in-SAN.patch @@ -0,0 +1,68 @@ +From 0875cd6169e876c4296a307631d49b801fc686dc Mon Sep 17 00:00:00 2001 +From: Stephen Gallagher +Date: Tue, 8 Mar 2022 16:33:35 -0500 +Subject: [PATCH] Truncate IP address in SAN + +In OpenSSL 1.1, this was done automatically when addind a SAN extension, +but in OpenSSL 3.0 it is rejected as an invalid input. + +Signed-off-by: Stephen Gallagher +--- + src/x509.c | 15 ++++++++++++++- + 1 file changed, 14 insertions(+), 1 deletion(-) + +diff --git a/src/x509.c b/src/x509.c +index 7c7e4dfe56d5756862f3e0f851941e846ce96f31..e828ec725b23d7ea79393151e7bb436e2f61bdb8 100644 +--- a/src/x509.c ++++ b/src/x509.c +@@ -131,10 +131,11 @@ sscg_x509v3_csr_new (TALLOC_CTX *mem_ctx, + size_t i; + X509_NAME *subject; + char *alt_name = NULL; + char *tmp = NULL; + char *san = NULL; ++ char *slash = NULL; + TALLOC_CTX *tmp_ctx; + X509_EXTENSION *ex = NULL; + struct sscg_x509_req *csr; + + /* Make sure we have a key available */ +@@ -265,10 +266,16 @@ sscg_x509v3_csr_new (TALLOC_CTX *mem_ctx, + tmp_ctx, "DNS:%s", certinfo->subject_alt_names[i]); + } + else + { + san = talloc_strdup (tmp_ctx, certinfo->subject_alt_names[i]); ++ /* SAN IP addresses cannot include the subnet mask */ ++ if ((slash = strchr (san, '/'))) ++ { ++ /* Truncate at the slash */ ++ *slash = '\0'; ++ } + } + CHECK_MEM (san); + + if (strnlen (san, MAXHOSTNAMELEN + 5) > MAXHOSTNAMELEN + 4) + { +@@ -287,11 +294,17 @@ sscg_x509v3_csr_new (TALLOC_CTX *mem_ctx, + alt_name = tmp; + } + } + + ex = X509V3_EXT_conf_nid (NULL, NULL, NID_subject_alt_name, alt_name); +- CHECK_MEM (ex); ++ if (!ex) ++ { ++ ret = EINVAL; ++ fprintf (stderr, "Invalid subjectAlternativeName: %s\n", alt_name); ++ goto done; ++ } ++ + sk_X509_EXTENSION_push (certinfo->extensions, ex); + + /* Set the public key for the certificate */ + sslret = X509_REQ_set_pubkey (csr->x509_req, spkey->evp_pkey); + CHECK_SSL (sslret, X509_REQ_set_pubkey (OU)); +-- +2.35.1 + diff --git a/SOURCES/0004-Properly-check-all-return-values.patch b/SOURCES/0004-Properly-check-all-return-values.patch deleted file mode 100644 index 9225fe7..0000000 --- a/SOURCES/0004-Properly-check-all-return-values.patch +++ /dev/null @@ -1,51 +0,0 @@ -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/SOURCES/0005-Add-password-support-for-private-keys.patch b/SOURCES/0005-Add-password-support-for-private-keys.patch deleted file mode 100644 index 4e21a9a..0000000 --- a/SOURCES/0005-Add-password-support-for-private-keys.patch +++ /dev/null @@ -1,273 +0,0 @@ -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/SOURCES/0006-Allow-specifying-keyfile-password-by-file.patch b/SOURCES/0006-Allow-specifying-keyfile-password-by-file.patch deleted file mode 100644 index 6487436..0000000 --- a/SOURCES/0006-Allow-specifying-keyfile-password-by-file.patch +++ /dev/null @@ -1,153 +0,0 @@ -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/SOURCES/0007-Add-support-for-client-certificates-and-dhparams.patch b/SOURCES/0007-Add-support-for-client-certificates-and-dhparams.patch deleted file mode 100644 index e22236e..0000000 --- a/SOURCES/0007-Add-support-for-client-certificates-and-dhparams.patch +++ /dev/null @@ -1,2651 +0,0 @@ -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/SOURCES/0008-Fix-client-cert-issues-found-by-CI-tests.patch b/SOURCES/0008-Fix-client-cert-issues-found-by-CI-tests.patch deleted file mode 100644 index 152464a..0000000 --- a/SOURCES/0008-Fix-client-cert-issues-found-by-CI-tests.patch +++ /dev/null @@ -1,98 +0,0 @@ -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/SOURCES/0009-Fix-help-message-for-client-key-file.patch b/SOURCES/0009-Fix-help-message-for-client-key-file.patch deleted file mode 100644 index 04e0777..0000000 --- a/SOURCES/0009-Fix-help-message-for-client-key-file.patch +++ /dev/null @@ -1,36 +0,0 @@ -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/SOURCES/0010-Better-validation-of-command-line-arguments.patch b/SOURCES/0010-Better-validation-of-command-line-arguments.patch deleted file mode 100644 index 7e934e9..0000000 --- a/SOURCES/0010-Better-validation-of-command-line-arguments.patch +++ /dev/null @@ -1,920 +0,0 @@ -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/SPECS/sscg.spec b/SPECS/sscg.spec index 235e7fb..52431b8 100644 --- a/SPECS/sscg.spec +++ b/SPECS/sscg.spec @@ -8,16 +8,13 @@ Name: sscg -Version: 2.3.3 -Release: 14%{?dist} +Version: 3.0.0 +Release: 5%{?dist} Summary: Simple SSL certificate generator -License: BSD +License: GPLv3+ with exceptions 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 +Source0: https://%{provider_prefix}/releases/download/%{repo}-%{version}/%{repo}-%{version}.tar.xz BuildRequires: gcc BuildRequires: libtalloc-devel @@ -28,23 +25,11 @@ 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 +Patch0001: 0001-Drop-usage-of-ERR_GET_FUNC.patch +Patch0002: 0002-Correct-certificate-lifetime-calculation.patch +Patch0003: 0003-Truncate-IP-address-in-SAN.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" @@ -54,7 +39,6 @@ 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 @@ -67,10 +51,7 @@ false signatures from the service certificate. %meson_install %check - -%ifnarch %{arm} -%meson_test -%endif +%meson_test -t 10 %files %license COPYING @@ -79,6 +60,15 @@ false signatures from the service certificate. %{_mandir}/man8/%{name}.8* %changelog +* Thu Jul 14 2022 Stephen Gallagher - 3.0.0-5 +- Rebase to sscg 3.0.0 +- Resolves: rhbz#2107369 +- Resolves: rhbz#2091525 + +* Thu Jun 02 2022 Stephen Gallagher - 2.3.3-15 +- Fix certificate lifetime calculation +- Resolves: rhbz#2091525 + * Tue Jan 21 2020 Stephen Gallagher - 2.3.3-14 - Properly handling reading long passphrase files.