From 835fea358806f6e0ce4dba0bfa757e28711370a4 Mon Sep 17 00:00:00 2001 From: AlmaLinux RelEng Bot Date: Tue, 19 May 2026 18:58:05 -0400 Subject: [PATCH] import UBI sscg-4.0.3-2.el10 --- .gitignore | 2 +- 0001-Extend-maximum-DNS-name-to-255.patch | 205 --- ...-defaulting-to-dhparams.pem-creation.patch | 119 ++ ...fault-on-receiving-bad-CLI-arguments.patch | 38 + ...DME.md-with-latest-usage-information.patch | 109 -- 0003-Restore-error-message.patch | 29 + 0003-x509-Use-proper-version-for-CSR.patch | 31 - ...critical-basicConstraint-for-CA-cert.patch | 43 - ...andling-in-CA-certificate-SAN-constr.patch | 1204 ----------------- sources | 2 +- sscg.spec | 65 +- 11 files changed, 234 insertions(+), 1613 deletions(-) delete mode 100644 0001-Extend-maximum-DNS-name-to-255.patch create mode 100644 0001-Restore-defaulting-to-dhparams.pem-creation.patch create mode 100644 0002-Avoid-segfault-on-receiving-bad-CLI-arguments.patch delete mode 100644 0002-Update-README.md-with-latest-usage-information.patch create mode 100644 0003-Restore-error-message.patch delete mode 100644 0003-x509-Use-proper-version-for-CSR.patch delete mode 100644 0004-Ensure-critical-basicConstraint-for-CA-cert.patch delete mode 100644 0005-Fix-IP-address-handling-in-CA-certificate-SAN-constr.patch diff --git a/.gitignore b/.gitignore index 6c1f038..8324441 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1 @@ -sscg-3.0.5.tar.gz +sscg-4.0.3.tar.gz diff --git a/0001-Extend-maximum-DNS-name-to-255.patch b/0001-Extend-maximum-DNS-name-to-255.patch deleted file mode 100644 index 7f18273..0000000 --- a/0001-Extend-maximum-DNS-name-to-255.patch +++ /dev/null @@ -1,205 +0,0 @@ -From d3a4452d7cc78589fb6077e98b228e09e9e76e3f Mon Sep 17 00:00:00 2001 -From: Stephen Gallagher -Date: Wed, 15 Feb 2023 15:49:38 -0500 -Subject: [PATCH 1/3] Extend maximum DNS name to 255 - -The hostname part is still restricted to 63 characters - -See RFC 1035, section 2.3.4 - -Signed-off-by: Stephen Gallagher ---- - include/sscg.h | 3 +++ - src/arguments.c | 35 +++++++++++++++++++++++++++-------- - src/authority.c | 26 +++++++++++++++++++++++--- - src/cert.c | 5 +++++ - src/x509.c | 6 +++--- - 5 files changed, 61 insertions(+), 14 deletions(-) - -diff --git a/include/sscg.h b/include/sscg.h -index 0f35631018dc2745e986cd1e7e094e3e37be8e54..f0c6d93b871e4bd3f2c805be8dfa7485ec34746a 100644 ---- a/include/sscg.h -+++ b/include/sscg.h -@@ -313,6 +313,9 @@ enum sscg_cert_type - #define SSCG_MIN_KEY_PASS_LEN 4 - #define SSCG_MAX_KEY_PASS_LEN 1023 - -+/* RFC 1035, section 2.3.4 (Size Limits) */ -+#define MAX_HOST_LEN 63 -+#define MAX_FQDN_LEN 255 - - int - sscg_handle_arguments (TALLOC_CTX *mem_ctx, -diff --git a/src/arguments.c b/src/arguments.c -index 0b7a060d31bed97130c7cb9b7feacf0876e25c0d..2f412bee1bee9620f28b6e84aed4aef17aee3a6a 100644 ---- a/src/arguments.c -+++ b/src/arguments.c -@@ -786,10 +786,19 @@ sscg_handle_arguments (TALLOC_CTX *mem_ctx, - } - CHECK_MEM (options->hostname); - -- if (strnlen (options->hostname, MAXHOSTNAMELEN + 1) > MAXHOSTNAMELEN) -+ if (strnlen (options->hostname, MAX_FQDN_LEN + 1) > MAX_FQDN_LEN) - { -- fprintf ( -- stderr, "Hostnames may not exceed %d characters\n", MAXHOSTNAMELEN); -+ fprintf (stderr, "FQDNs may not exceed %d characters\n", MAX_FQDN_LEN); -+ ret = EINVAL; -+ goto done; -+ } -+ -+ if ((strchr (options->hostname, '.') - options->hostname) > MAX_HOST_LEN + 4) -+ { -+ fprintf (stderr, -+ "Hostnames may not exceed %d characters in Subject " -+ "Alternative Names\n", -+ MAX_HOST_LEN); - ret = EINVAL; - goto done; - } -@@ -798,25 +807,35 @@ sscg_handle_arguments (TALLOC_CTX *mem_ctx, - options struct. It's not the most efficient approach, but - it's only done one time, so there is no sense in optimizing - it. */ -+ size_t i = 0; - if (alternative_names) - { -- size_t i = 0; - while (alternative_names[i] != NULL) - { - options->subject_alt_names = talloc_realloc ( -- options, options->subject_alt_names, char *, i + 2); -+ options, options->subject_alt_names, char *, i + 1); - CHECK_MEM (options->subject_alt_names); - - options->subject_alt_names[i] = - talloc_strdup (options->subject_alt_names, alternative_names[i]); - CHECK_MEM (options->subject_alt_names[i]); -- -- /* Add a NULL terminator to the end */ -- options->subject_alt_names[i + 1] = NULL; - i++; - } - } - -+ /* -+ The hostname must always be listed in SubjectAlternativeNames as well. -+ Note that the realloc also adds an extra entry for the NULL terminator -+ */ -+ options->subject_alt_names = -+ talloc_realloc (options, options->subject_alt_names, char *, i + 2); -+ CHECK_MEM (options->subject_alt_names); -+ options->subject_alt_names[i] = -+ talloc_strdup (options->subject_alt_names, options->hostname); -+ CHECK_MEM (options->subject_alt_names[i]); -+ /* Add a NULL terminator to the end */ -+ options->subject_alt_names[i + 1] = NULL; -+ - if (options->key_strength < options->minimum_key_strength) - { - fprintf (stderr, -diff --git a/src/authority.c b/src/authority.c -index 4efaa9e730964b9762b59d0e6698c1623901ccfe..f509fd4316c3b7b230f99de6464491c319fc5d45 100644 ---- a/src/authority.c -+++ b/src/authority.c -@@ -56,6 +56,7 @@ create_private_CA (TALLOC_CTX *mem_ctx, - char *name_constraint; - char *san; - char *tmp; -+ char *dot; - - tmp_ctx = talloc_new (NULL); - CHECK_MEM (tmp_ctx); -@@ -89,6 +90,26 @@ create_private_CA (TALLOC_CTX *mem_ctx, - - ca_certinfo->cn = talloc_strdup (ca_certinfo, options->hostname); - CHECK_MEM (ca_certinfo->cn); -+ /* Truncate the CN at the first dot */ -+ if ((dot = strchr (ca_certinfo->cn, '.'))) -+ *dot = '\0'; -+ -+ if (options->subject_alt_names) -+ { -+ for (i = 0; options->subject_alt_names[i]; i++) -+ { -+ ca_certinfo->subject_alt_names = talloc_realloc ( -+ ca_certinfo, ca_certinfo->subject_alt_names, char *, i + 2); -+ CHECK_MEM (ca_certinfo->subject_alt_names); -+ -+ ca_certinfo->subject_alt_names[i] = talloc_strdup ( -+ ca_certinfo->subject_alt_names, options->subject_alt_names[i]); -+ CHECK_MEM (ca_certinfo->subject_alt_names[i]); -+ -+ /* Add a NULL terminator to the end */ -+ ca_certinfo->subject_alt_names[i + 1] = NULL; -+ } -+ } - - /* Make this a CA certificate */ - -@@ -106,10 +127,9 @@ create_private_CA (TALLOC_CTX *mem_ctx, - CHECK_MEM (ex); - sk_X509_EXTENSION_push (ca_certinfo->extensions, ex); - -- /* Restrict signing to the hostname and subjectAltNames of the -- service certificate */ -+ /* Restrict signing to the CN and subjectAltNames of the service certificate */ - name_constraint = -- talloc_asprintf (tmp_ctx, "permitted;DNS:%s", options->hostname); -+ talloc_asprintf (tmp_ctx, "permitted;DNS:%s", ca_certinfo->cn); - CHECK_MEM (name_constraint); - - if (options->subject_alt_names) -diff --git a/src/cert.c b/src/cert.c -index 99d9109f5981ef408aeb7d05a8327e1a38d5700a..e36de71e7ca9b34f87734542d5646b466cd61d4c 100644 ---- a/src/cert.c -+++ b/src/cert.c -@@ -31,6 +31,7 @@ - */ - - -+#include - #include "include/sscg.h" - #include "include/cert.h" - #include "include/x509.h" -@@ -52,6 +53,7 @@ create_cert (TALLOC_CTX *mem_ctx, - struct sscg_x509_req *csr; - struct sscg_evp_pkey *pkey; - struct sscg_x509_cert *cert; -+ char *dot; - X509_EXTENSION *ex = NULL; - EXTENDED_KEY_USAGE *extended; - TALLOC_CTX *tmp_ctx = NULL; -@@ -87,6 +89,9 @@ create_cert (TALLOC_CTX *mem_ctx, - - certinfo->cn = talloc_strdup (certinfo, options->hostname); - CHECK_MEM (certinfo->cn); -+ /* Truncate the CN at the first dot */ -+ if ((dot = strchr (certinfo->cn, '.'))) -+ *dot = '\0'; - - if (options->subject_alt_names) - { -diff --git a/src/x509.c b/src/x509.c -index 4f3f11cd3411f00cf6de3a72ba897adc97944e35..9f6f21b49c2dd70629fed67d327027374eb21b15 100644 ---- a/src/x509.c -+++ b/src/x509.c -@@ -290,12 +290,12 @@ sscg_x509v3_csr_new (TALLOC_CTX *mem_ctx, - } - CHECK_MEM (san); - -- if (strnlen (san, MAXHOSTNAMELEN + 5) > MAXHOSTNAMELEN + 4) -+ if (strnlen (san, MAX_FQDN_LEN + 5) > MAX_FQDN_LEN + 4) - { - fprintf (stderr, -- "Hostnames may not exceed %d characters in Subject " -+ "FQDNs may not exceed %d characters in Subject " - "Alternative Names\n", -- MAXHOSTNAMELEN); -+ MAX_FQDN_LEN); - ret = EINVAL; - goto done; - } --- -2.49.0 - diff --git a/0001-Restore-defaulting-to-dhparams.pem-creation.patch b/0001-Restore-defaulting-to-dhparams.pem-creation.patch new file mode 100644 index 0000000..f1c692c --- /dev/null +++ b/0001-Restore-defaulting-to-dhparams.pem-creation.patch @@ -0,0 +1,119 @@ +From 771a7663bccbd360f017c4c22358a46abcdfa93f Mon Sep 17 00:00:00 2001 +From: Stephen Gallagher +Date: Mon, 27 Oct 2025 14:58:11 -0400 +Subject: [PATCH] Restore defaulting to dhparams.pem creation + +This was disabled upstream, but for backwards-compatibility in the RHEL +9 and RHEL 10 lifecycle, we'll continue to do so there. + +This reverts commit 0e5e011acc2dc19f3c2fcb5699cf8fa662a2b135. + +Signed-off-by: Stephen Gallagher +--- + src/arguments.c | 4 ++-- + src/sscg.c | 39 +++++++++++++++++++++++++--------- + test/test_dhparams_creation.sh | 6 +----- + 3 files changed, 32 insertions(+), 17 deletions(-) + +diff --git a/src/arguments.c b/src/arguments.c +index 38c8740c1f159368d6fc92d51ba48d83700c3320..4ff75fdf86728592e7ca05db4cf4ac88bf79ca2e 100644 +--- a/src/arguments.c ++++ b/src/arguments.c +@@ -682,7 +682,7 @@ sscg_handle_arguments (TALLOC_CTX *mem_ctx, + &options->dhparams_file, + 0, + _("A file to contain a set of Diffie-Hellman parameters. " +- "(Default: not created)"), ++ "(Default: \"./dhparams.pem\")"), + NULL + }, + +@@ -692,7 +692,7 @@ sscg_handle_arguments (TALLOC_CTX *mem_ctx, + POPT_ARG_NONE | POPT_ARGFLAG_DOC_HIDDEN, + &options->skip_dhparams, + 0, +- _ ("Deprecated: Retained for backwards compatibility. To be removed in SSCG 5.0."), ++ _ ("Do not create the dhparams file"), + NULL + }, + +diff --git a/src/sscg.c b/src/sscg.c +index b9b191f109300f6447262858f57a3a8321a14966..d2dce334cff1342d975e9867a2c82a222d76925e 100644 +--- a/src/sscg.c ++++ b/src/sscg.c +@@ -166,19 +166,38 @@ main (int argc, const char **argv) + options->crl_mode); + CHECK_OK (ret); + +- if (options->dhparams_file) ++ if (!options->skip_dhparams) + { +- dhparams_file = talloc_strdup (main_ctx, options->dhparams_file); +- CHECK_MEM (dhparams_file); ++ if (options->dhparams_file) ++ { ++ dhparams_file = talloc_strdup (main_ctx, options->dhparams_file); ++ CHECK_MEM (dhparams_file); + +- ret = sscg_io_utils_add_output_file (options->streams, +- SSCG_FILE_TYPE_DHPARAMS, +- dhparams_file, +- options->overwrite, +- options->dhparams_mode); +- CHECK_OK (ret); ++ ret = sscg_io_utils_add_output_file (options->streams, ++ SSCG_FILE_TYPE_DHPARAMS, ++ dhparams_file, ++ options->overwrite, ++ options->dhparams_mode); ++ CHECK_OK (ret); ++ } ++ else ++ { ++ dhparams_file = talloc_strdup (main_ctx, "./dhparams.pem"); ++ CHECK_MEM (dhparams_file); ++ ++ ret = sscg_io_utils_add_output_file (options->streams, ++ SSCG_FILE_TYPE_DHPARAMS, ++ dhparams_file, ++ options->overwrite, ++ options->dhparams_mode); ++ SSCG_LOG (SSCG_VERBOSE, ++ "Could not open dhparams file %s: %s\n", ++ dhparams_file, ++ strerror (ret)); ++ /* This is non-fatal if the file path was not explicitly passed */ ++ ret = EOK; ++ } + } +- + /* Validate and open the file paths */ + ret = sscg_io_utils_open_BIOs (options->streams); + CHECK_OK (ret); +diff --git a/test/test_dhparams_creation.sh b/test/test_dhparams_creation.sh +index d0b4cbb71f3cd1656f1422524c4da7b30fbf3e0a..49f2b08d23246c90663eb7d2e5078817eb42139b 100755 +--- a/test/test_dhparams_creation.sh ++++ b/test/test_dhparams_creation.sh +@@ -42,10 +42,6 @@ + # just warn and ignore it if it was not (returning 0). However, if it is + # explicitly requested on the command-line and cannot be written to that + # location, it should fail with an error code. +-# +-# Updated 2025-10-21: SSCG 4.0 no longer creates the dhparams file by default. +-# It should not attempt to create it unless explicitly requested using the +-# --dhparams-file option. + + set -e + +@@ -181,7 +177,7 @@ run_test \ + "" \ + 0 \ + "$WRITABLE_DIR/dhparams.pem" \ +- "false" \ ++ "true" \ + "$WRITABLE_DIR" + + # Test 2: No --dhparams-file, readonly directory, no existing file +-- +2.52.0 + diff --git a/0002-Avoid-segfault-on-receiving-bad-CLI-arguments.patch b/0002-Avoid-segfault-on-receiving-bad-CLI-arguments.patch new file mode 100644 index 0000000..6da94b3 --- /dev/null +++ b/0002-Avoid-segfault-on-receiving-bad-CLI-arguments.patch @@ -0,0 +1,38 @@ +From f40d0070641543a140428d70211d53d36fd2c34b Mon Sep 17 00:00:00 2001 +From: Stephen Gallagher +Date: Tue, 2 Dec 2025 12:12:26 -0500 +Subject: [PATCH 2/3] Avoid segfault on receiving bad CLI arguments + +Signed-off-by: Stephen Gallagher +--- + src/sscg.c | 7 +++++-- + 1 file changed, 5 insertions(+), 2 deletions(-) + +diff --git a/src/sscg.c b/src/sscg.c +index d2dce334cff1342d975e9867a2c82a222d76925e..070d567bb189d42a20fd0a80f8fe2f7caae4d9eb 100644 +--- a/src/sscg.c ++++ b/src/sscg.c +@@ -59,7 +59,7 @@ int + main (int argc, const char **argv) + { + int ret, sret; +- struct sscg_options *options; ++ struct sscg_options *options = NULL; + bool build_client_cert = false; + char *dhparams_file = NULL; + +@@ -361,7 +361,10 @@ main (int argc, const char **argv) + done: + if (ret != EOK) + { +- sscg_io_utils_delete_output_files (options->streams); ++ if (options) ++ { ++ sscg_io_utils_delete_output_files (options->streams); ++ } + } + talloc_zfree (main_ctx); + if (getenv ("SSCG_TALLOC_REPORT")) +-- +2.52.0 + diff --git a/0002-Update-README.md-with-latest-usage-information.patch b/0002-Update-README.md-with-latest-usage-information.patch deleted file mode 100644 index e295244..0000000 --- a/0002-Update-README.md-with-latest-usage-information.patch +++ /dev/null @@ -1,109 +0,0 @@ -From 14df7d212d020f247587e2d850ec27dbd16add38 Mon Sep 17 00:00:00 2001 -From: Stephen Gallagher -Date: Fri, 1 Sep 2023 08:19:01 -0400 -Subject: [PATCH 2/3] Update README.md with latest usage information - -Signed-off-by: Stephen Gallagher ---- - README.md | 55 +++++++++++++++++++++++++------------------------------ - 1 file changed, 25 insertions(+), 30 deletions(-) - -diff --git a/README.md b/README.md -index d15c3d955d03026e8a68c04870a5f97a20eb03d9..4d57138895443f228212a6c77209350432eecbd7 100644 ---- a/README.md -+++ b/README.md -@@ -26,8 +26,8 @@ Usage of sscg: - Usage: sscg [OPTION...] - -q, --quiet Display no output unless there is an error. - -v, --verbose Display progress messages. -- -d, --debug Enable logging of debug messages. Implies verbose. Warning! This will print -- private key information to the screen! -+ -d, --debug Enable logging of debug messages. Implies verbose. Warning! This will print private key information to the -+ screen! - -V, --version Display the version number and exit. - -f, --force Overwrite any pre-existing files in the requested locations - --lifetime=1-3650 Certificate lifetime (days). (default: 398) -@@ -37,57 +37,52 @@ Usage: sscg [OPTION...] - --organization=My Company Certificate DN: Organization (O). (default: "Unspecified") - --organizational-unit=Engineering, etc. Certificate DN: Organizational Unit (OU). - --email=myname@example.com Certificate DN: Email Address (Email). -- --hostname=server.example.com The valid hostname of the certificate. Must be an FQDN. (default: current system -- FQDN) -- --subject-alt-name alt.example.com Optional additional valid hostnames for the certificate. In addition to hostnames, -- this option also accepts explicit values supported by RFC 5280 such as -- IP:xxx.xxx.xxx.xxx/yyy.yyy.yyy.yyy May be specified multiple times. -+ --hostname=server.example.com The valid hostname of the certificate. Must be an FQDN. (default: current system FQDN) -+ --subject-alt-name alt.example.com Optional additional valid hostnames for the certificate. In addition to hostnames, this option also accepts -+ explicit values supported by RFC 5280 such as IP:xxx.xxx.xxx.xxx/yyy.yyy.yyy.yyy May be specified multiple -+ times. - --package=STRING Unused. Retained for compatibility with earlier versions of sscg. - --key-strength=2048 or larger Strength of the certificate private keys in bits. (default: 2048) - --hash-alg={sha256,sha384,sha512} Hashing algorithm to use for signing. (default: "sha256") - --cipher-alg={des-ede3-cbc,aes-256-cbc} Cipher to use for encrypting key files. (default: "aes-256-cbc") - --ca-file=STRING Path where the public CA certificate will be stored. (default: "./ca.crt") - --ca-mode=0644 File mode of the created CA certificate. -- --ca-key-file=STRING Path where the CA's private key will be stored. If unspecified, the key will be -- destroyed rather than written to the disk. -+ --ca-key-file=STRING Path where the CA's private key will be stored. If unspecified, the key will be destroyed rather than written -+ to the disk. - --ca-key-mode=0600 File mode of the created CA key. -- --ca-key-password=STRING 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. -+ --ca-key-password=STRING 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. - --ca-key-passfile=STRING A file containing the password to encrypt the CA key file. - -C, --ca-key-password-prompt Prompt to enter a password for the CA key file. -- --crl-file=STRING 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. -+ --crl-file=STRING 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. - --crl-mode=0644 File mode of the created Certificate Revocation List. - --cert-file=STRING Path where the public service certificate will be stored. (default "./service.pem") - --cert-mode=0644 File mode of the created certificate. - --cert-key-file=STRING Path where the service's private key will be stored. (default "service-key.pem") - --cert-key-mode=0600 File mode of the created certificate key. -- -p, --cert-key-password=STRING 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. -+ -p, --cert-key-password=STRING 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. - --cert-key-passfile=STRING A file containing the password to encrypt the service key file. - -P, --cert-key-password-prompt Prompt to enter a password for the service key file. - --client-file=STRING Path where a client authentication certificate will be stored. - --client-mode=0644 File mode of the created certificate. - --client-key-file=STRING Path where the client's private key will be stored. (default is the client-file) - --client-key-mode=0600 File mode of the created certificate key. -- --client-key-password=STRING 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. -+ --client-key-password=STRING 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. - --client-key-passfile=STRING A file containing the password to encrypt the client key file. - --client-key-password-prompt Prompt to enter a password for the client key file. - --dhparams-file=STRING A file to contain a set of Diffie-Hellman parameters. (Default: "./dhparams.pem") -- --dhparams-named-group=STRING Output well-known DH parameters. The available named groups are: ffdhe2048, -- ffdhe3072, ffdhe4096, ffdhe6144, ffdhe8192, modp_2048, modp_3072, modp_4096, -- modp_6144, modp_8192, modp_1536, dh_1024_160, dh_2048_224, dh_2048_256. (Default: -- "ffdhe4096") -- --dhparams-prime-len=INT The length of the prime number to generate for dhparams, in bits. If set to -- non-zero, the parameters will be generated rather than using a well-known group. -- (default: 0) -+ --no-dhparams-file Do not create the dhparams file -+ --dhparams-named-group=STRING Output well-known DH parameters. The available named groups are: ffdhe2048, ffdhe3072, ffdhe4096, ffdhe6144, -+ ffdhe8192, modp_2048, modp_3072, modp_4096, modp_6144, modp_8192, modp_1536, dh_1024_160, dh_2048_224, -+ dh_2048_256. (Default: "ffdhe4096") -+ --dhparams-prime-len=INT The length of the prime number to generate for dhparams, in bits. If set to non-zero, the parameters will be -+ generated rather than using a well-known group. (default: 0) - --dhparams-generator={2,3,5} The generator value for dhparams. (default: 2) - - Help options: --- -2.49.0 - diff --git a/0003-Restore-error-message.patch b/0003-Restore-error-message.patch new file mode 100644 index 0000000..dd66fd6 --- /dev/null +++ b/0003-Restore-error-message.patch @@ -0,0 +1,29 @@ +From 08dacb632cc331027f39dcfa0b782aeb6f2f893a Mon Sep 17 00:00:00 2001 +From: Stephen Gallagher +Date: Tue, 2 Dec 2025 12:19:04 -0500 +Subject: [PATCH 3/3] Restore error message + +This was dropped in 4.0, but should be retained in RHEL 9 and 10 for +compatibility, particularly with existing tests that look for specific +messages. + +Signed-off-by: Stephen Gallagher +--- + src/sscg.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/src/sscg.c b/src/sscg.c +index 070d567bb189d42a20fd0a80f8fe2f7caae4d9eb..9f46cd622a4d55bd634a370ccc81ff063422b5af 100644 +--- a/src/sscg.c ++++ b/src/sscg.c +@@ -361,6 +361,7 @@ main (int argc, const char **argv) + done: + if (ret != EOK) + { ++ SSCG_ERROR ("%s\n", strerror (ret)); + if (options) + { + sscg_io_utils_delete_output_files (options->streams); +-- +2.52.0 + diff --git a/0003-x509-Use-proper-version-for-CSR.patch b/0003-x509-Use-proper-version-for-CSR.patch deleted file mode 100644 index cca0fc5..0000000 --- a/0003-x509-Use-proper-version-for-CSR.patch +++ /dev/null @@ -1,31 +0,0 @@ -From 70b0a4742a67616a5223a0cdc2067effccf081e9 Mon Sep 17 00:00:00 2001 -From: Sebastian Andrzej Siewior -Date: Sat, 19 Oct 2024 15:43:20 +0200 -Subject: [PATCH 3/3] x509: Use proper version for CSR. - -RFC 2986 only defines a single version for CSRs: X509_VERSION_1 (0). -OpenSSL starting with 3.4 rejects everything else. - -Use X509_VERSION_1 as version for X509_REQ_set_version. - -Signed-off-by: Sebastian Andrzej Siewior ---- - src/x509.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/src/x509.c b/src/x509.c -index 9f6f21b49c2dd70629fed67d327027374eb21b15..503b7b1b51ed45909104d1b5e593129ee9e8dee2 100644 ---- a/src/x509.c -+++ b/src/x509.c -@@ -169,7 +169,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, 2); -+ sslret = X509_REQ_set_version (csr->x509_req, X509_VERSION_1); - CHECK_SSL (sslret, X509_REQ_set_version); - - subject = X509_REQ_get_subject_name (csr->x509_req); --- -2.49.0 - diff --git a/0004-Ensure-critical-basicConstraint-for-CA-cert.patch b/0004-Ensure-critical-basicConstraint-for-CA-cert.patch deleted file mode 100644 index 8dbb327..0000000 --- a/0004-Ensure-critical-basicConstraint-for-CA-cert.patch +++ /dev/null @@ -1,43 +0,0 @@ -From 276698d206e9bad55d628eb2db8a71ba469a2eaf Mon Sep 17 00:00:00 2001 -From: Stephen Gallagher -Date: Tue, 22 Apr 2025 13:09:32 -0400 -Subject: [PATCH 4/4] Ensure 'critical' basicConstraint for CA cert - -Fixes: https://github.com/sgallagher/sscg/issues/74 - -Signed-off-by: Stephen Gallagher ---- - src/authority.c | 3 ++- - test/test_cert_validity.sh | 2 +- - 2 files changed, 3 insertions(+), 2 deletions(-) - -diff --git a/src/authority.c b/src/authority.c -index f509fd4316c3b7b230f99de6464491c319fc5d45..68000c5e695460abd65c9641b44c187f4aa617e3 100644 ---- a/src/authority.c -+++ b/src/authority.c -@@ -123,7 +123,8 @@ create_private_CA (TALLOC_CTX *mem_ctx, - sk_X509_EXTENSION_push (ca_certinfo->extensions, ex); - - /* Mark it as a CA */ -- ex = X509V3_EXT_conf_nid (NULL, NULL, NID_basic_constraints, "CA:TRUE"); -+ ex = X509V3_EXT_conf_nid ( -+ NULL, NULL, NID_basic_constraints, "critical,CA:TRUE"); - CHECK_MEM (ex); - sk_X509_EXTENSION_push (ca_certinfo->extensions, ex); - -diff --git a/test/test_cert_validity.sh b/test/test_cert_validity.sh -index 1e4df5ce57a45981878cec017f710b9699b77c6a..e20e02a384156a1a481e2560308f10a82258986e 100755 ---- a/test/test_cert_validity.sh -+++ b/test/test_cert_validity.sh -@@ -204,7 +204,7 @@ key_strength=$(openssl pkey -text -noout -in service-key.pem -passin pass:mypass - test "$key_strength" -eq "$_arg_key_strength" - - # Validate the certificates --openssl verify -CAfile ca.crt service.pem -+openssl verify -x509_strict -CAfile ca.crt service.pem - - popd # $TMPDIR - --- -2.49.0 - diff --git a/0005-Fix-IP-address-handling-in-CA-certificate-SAN-constr.patch b/0005-Fix-IP-address-handling-in-CA-certificate-SAN-constr.patch deleted file mode 100644 index 6e467cb..0000000 --- a/0005-Fix-IP-address-handling-in-CA-certificate-SAN-constr.patch +++ /dev/null @@ -1,1204 +0,0 @@ -From 040f4daa86102c59ab61debdfec1d86aab71a92c Mon Sep 17 00:00:00 2001 -From: Stephen Gallagher -Date: Mon, 21 Jul 2025 15:13:31 -0400 -Subject: [PATCH 5/5] Fix IP address handling in CA certificate SAN constraints - -- Add automatic single-IP subnet mask to IP addresses in CA name constraints -- Update help text to show simplified IP format without subnet mask -- Add comprehensive test for basicConstraints - -Signed-off-by: Stephen Gallagher ---- - src/arguments.c | 2 +- - src/authority.c | 84 ++++ - test/create_ca_test.c | 1042 ++++++++++++++++++++++++++++++++++++++++- - 3 files changed, 1125 insertions(+), 3 deletions(-) - -diff --git a/src/arguments.c b/src/arguments.c -index 2f412bee1bee9620f28b6e84aed4aef17aee3a6a..0ad8df8a5727659ff5de3fe0e3a057ab38f75b97 100644 ---- a/src/arguments.c -+++ b/src/arguments.c -@@ -322,7 +322,7 @@ sscg_handle_arguments (TALLOC_CTX *mem_ctx, - _ ("Optional additional valid hostnames for the certificate. " - "In addition to hostnames, this option also accepts explicit values " - "supported by RFC 5280 such as " -- "IP:xxx.xxx.xxx.xxx/yyy.yyy.yyy.yyy " -+ "IP:xxx.xxx.xxx.xxx " - "May be specified multiple times."), - _ ("alt.example.com") - }, -diff --git a/src/authority.c b/src/authority.c -index 68000c5e695460abd65c9641b44c187f4aa617e3..1f0d0786adf70a98ef0385021aca7aa266b8da98 100644 ---- a/src/authority.c -+++ b/src/authority.c -@@ -142,6 +142,90 @@ create_private_CA (TALLOC_CTX *mem_ctx, - san = talloc_asprintf ( - tmp_ctx, "DNS:%s", options->subject_alt_names[i]); - } -+ else if (strncmp (options->subject_alt_names[i], "IP:", 3) == 0) -+ { -+ char *ip_addr = options->subject_alt_names[i] + 3; -+ char *slash = strchr (ip_addr, '/'); -+ char *clean_ip = ip_addr; -+ const char *netmask_str = NULL; -+ -+ if (slash) -+ { -+ /* Extract IP and netmask parts */ -+ clean_ip = -+ talloc_strndup (tmp_ctx, ip_addr, slash - ip_addr); -+ char *cidr_str = slash + 1; -+ int cidr_bits = atoi (cidr_str); -+ -+ /* Convert CIDR to appropriate netmask format */ -+ if (strchr (clean_ip, ':')) -+ { -+ /* IPv6 - convert CIDR to hex netmask */ -+ if (cidr_bits == 128) -+ { -+ netmask_str = -+ "FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF"; -+ } -+ else if (cidr_bits == 64) -+ { -+ netmask_str = "FFFF:FFFF:FFFF:FFFF:0:0:0:0"; -+ } -+ else -+ { -+ /* For other values, default to /128 */ -+ netmask_str = -+ "FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF"; -+ } -+ } -+ else -+ { -+ /* IPv4 - convert CIDR to dotted decimal */ -+ if (cidr_bits == 32) -+ { -+ netmask_str = "255.255.255.255"; -+ } -+ else if (cidr_bits == 24) -+ { -+ netmask_str = "255.255.255.0"; -+ } -+ else if (cidr_bits == 16) -+ { -+ netmask_str = "255.255.0.0"; -+ } -+ else if (cidr_bits == 8) -+ { -+ netmask_str = "255.0.0.0"; -+ } -+ else -+ { -+ /* For other values, default to /32 */ -+ netmask_str = "255.255.255.255"; -+ } -+ } -+ } -+ else -+ { -+ /* No netmask provided - add single host netmask */ -+ if (strchr (clean_ip, ':')) -+ { -+ /* IPv6 - use /128 netmask */ -+ netmask_str = "FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF"; -+ } -+ else -+ { -+ /* IPv4 - use /32 netmask */ -+ netmask_str = "255.255.255.255"; -+ } -+ } -+ -+ san = -+ talloc_asprintf (tmp_ctx, "IP:%s/%s", clean_ip, netmask_str); -+ -+ if (slash && clean_ip != ip_addr) -+ { -+ talloc_free (clean_ip); -+ } -+ } - else - { - san = talloc_strdup (tmp_ctx, options->subject_alt_names[i]); -diff --git a/test/create_ca_test.c b/test/create_ca_test.c -index c1fe67da225d59342a50bf4cdbe7be1c7cf379f5..352b65ce192788a0f95f4295002242cb676682ed 100644 ---- a/test/create_ca_test.c -+++ b/test/create_ca_test.c -@@ -34,9 +34,861 @@ - #include - #include - #include -+#include -+#include - - #include "include/sscg.h" - #include "include/x509.h" -+#include "include/authority.h" -+ -+static int -+verify_subject_alt_names (struct sscg_x509_cert *cert) -+{ -+ X509 *x509 = cert->certificate; -+ STACK_OF (GENERAL_NAME) *san_names = NULL; -+ GENERAL_NAME *name = NULL; -+ ASN1_STRING *san_str = NULL; -+ int san_count = 0; -+ int found_primary_cn = 0; -+ int found_alt1 = 0; -+ int found_alt2 = 0; -+ int found_ip4_1 = 0; -+ int found_ip4_2 = 0; -+ int found_ip6 = 0; -+ int found_ip4_netmask = 0; -+ int found_ip6_netmask = 0; -+ int found_email = 0; -+ int found_uri = 0; -+ int found_wildcard = 0; -+ int found_subdomain = 0; -+ int found_international = 0; -+ char *name_str = NULL; -+ -+ /* Get the Subject Alternative Name extension */ -+ san_names = X509_get_ext_d2i (x509, NID_subject_alt_name, NULL, NULL); -+ if (!san_names) -+ { -+ printf ("Certificate missing Subject Alternative Name extension.\n"); -+ return EINVAL; -+ } -+ -+ san_count = sk_GENERAL_NAME_num (san_names); -+ printf ("\n Processing %d Subject Alternative Names:\n", san_count); -+ -+ /* Check each SAN entry */ -+ for (int i = 0; i < san_count; i++) -+ { -+ name = sk_GENERAL_NAME_value (san_names, i); -+ -+ switch (name->type) -+ { -+ case GEN_DNS: -+ san_str = name->d.dNSName; -+ name_str = (char *)ASN1_STRING_get0_data (san_str); -+ printf (" DNS: %s\n", name_str); -+ -+ if (strcmp (name_str, "server.example.com") == 0) -+ found_primary_cn = 1; -+ else if (strcmp (name_str, "alt1.example.com") == 0) -+ found_alt1 = 1; -+ else if (strcmp (name_str, "alt2.example.com") == 0) -+ found_alt2 = 1; -+ else if (strcmp (name_str, "*.wildcard.example.com") == 0) -+ found_wildcard = 1; -+ else if (strcmp (name_str, "subdomain.alt1.example.com") == 0) -+ found_subdomain = 1; -+ else if (strcmp (name_str, "xn--nxasmq6b.example.com") == 0) -+ found_international = 1; -+ break; -+ -+ case GEN_IPADD: -+ san_str = name->d.iPAddress; -+ /* IP addresses are stored as binary data */ -+ if (ASN1_STRING_length (san_str) == 4) /* IPv4 */ -+ { -+ const unsigned char *ip_data = ASN1_STRING_get0_data (san_str); -+ printf (" IP (IPv4): %d.%d.%d.%d\n", -+ ip_data[0], -+ ip_data[1], -+ ip_data[2], -+ ip_data[3]); -+ -+ if (ip_data[0] == 192 && ip_data[1] == 168 && ip_data[2] == 1 && -+ ip_data[3] == 100) -+ found_ip4_1 = 1; -+ else if (ip_data[0] == 10 && ip_data[1] == 0 && -+ ip_data[2] == 0 && ip_data[3] == 1) -+ found_ip4_2 = 1; -+ else if (ip_data[0] == 203 && ip_data[1] == 0 && -+ ip_data[2] == 113 && ip_data[3] == 0) -+ found_ip4_netmask = 1; -+ } -+ else if (ASN1_STRING_length (san_str) == 16) /* IPv6 */ -+ { -+ const unsigned char *ip_data = ASN1_STRING_get0_data (san_str); -+ printf (" IP (IPv6): "); -+ for (int j = 0; j < 16; j += 2) -+ { -+ printf ("%02x%02x", ip_data[j], ip_data[j + 1]); -+ if (j < 14) -+ printf (":"); -+ } -+ printf ("\n"); -+ -+ /* Check for 2001:db8::1 */ -+ if (ip_data[0] == 0x20 && ip_data[1] == 0x01 && -+ ip_data[2] == 0x0d && ip_data[3] == 0xb8 && -+ ip_data[4] == 0x00 && ip_data[5] == 0x00 && -+ ip_data[6] == 0x00 && ip_data[7] == 0x00 && -+ ip_data[8] == 0x00 && ip_data[9] == 0x00 && -+ ip_data[10] == 0x00 && ip_data[11] == 0x00 && -+ ip_data[12] == 0x00 && ip_data[13] == 0x00 && -+ ip_data[14] == 0x00 && ip_data[15] == 0x01) -+ found_ip6 = 1; -+ /* Check for 2001:db8:85a3:: (netmask stripped) */ -+ else if (ip_data[0] == 0x20 && ip_data[1] == 0x01 && -+ ip_data[2] == 0x0d && ip_data[3] == 0xb8 && -+ ip_data[4] == 0x85 && ip_data[5] == 0xa3 && -+ ip_data[6] == 0x00 && ip_data[7] == 0x00 && -+ ip_data[8] == 0x00 && ip_data[9] == 0x00 && -+ ip_data[10] == 0x00 && ip_data[11] == 0x00 && -+ ip_data[12] == 0x00 && ip_data[13] == 0x00 && -+ ip_data[14] == 0x00 && ip_data[15] == 0x00) -+ found_ip6_netmask = 1; -+ } -+ break; -+ -+ case GEN_EMAIL: -+ san_str = name->d.rfc822Name; -+ name_str = (char *)ASN1_STRING_get0_data (san_str); -+ printf (" Email: %s\n", name_str); -+ -+ if (strcmp (name_str, "admin@example.com") == 0) -+ found_email = 1; -+ break; -+ -+ case GEN_URI: -+ san_str = name->d.uniformResourceIdentifier; -+ name_str = (char *)ASN1_STRING_get0_data (san_str); -+ printf (" URI: %s\n", name_str); -+ -+ if (strcmp (name_str, "https://www.example.com/service") == 0) -+ found_uri = 1; -+ break; -+ -+ default: printf (" Other type: %d\n", name->type); break; -+ } -+ } -+ -+ GENERAL_NAMES_free (san_names); -+ -+ /* Verify all expected SANs were found */ -+ int missing_count = 0; -+ -+ if (!found_primary_cn) -+ { -+ printf ( -+ " MISSING: Primary CN not found in Subject Alternative Names.\n"); -+ missing_count++; -+ } -+ -+ if (!found_alt1) -+ { -+ printf ( -+ " MISSING: alt1.example.com not found in Subject Alternative " -+ "Names.\n"); -+ missing_count++; -+ } -+ -+ if (!found_alt2) -+ { -+ printf ( -+ " MISSING: alt2.example.com not found in Subject Alternative " -+ "Names.\n"); -+ missing_count++; -+ } -+ -+ if (!found_ip4_1) -+ { -+ printf ( -+ " MISSING: IPv4 192.168.1.100 not found in Subject Alternative " -+ "Names.\n"); -+ missing_count++; -+ } -+ -+ if (!found_ip4_2) -+ { -+ printf ( -+ " MISSING: IPv4 10.0.0.1 not found in Subject Alternative " -+ "Names.\n"); -+ missing_count++; -+ } -+ -+ if (!found_ip6) -+ { -+ printf ( -+ " MISSING: IPv6 2001:db8::1 not found in Subject Alternative " -+ "Names.\n"); -+ missing_count++; -+ } -+ -+ if (!found_ip4_netmask) -+ { -+ printf ( -+ " MISSING: IPv4 203.0.113.0 (from 203.0.113.0/24, netmask " -+ "stripped) not found in Subject Alternative Names.\n"); -+ missing_count++; -+ } -+ -+ if (!found_ip6_netmask) -+ { -+ printf ( -+ " MISSING: IPv6 2001:db8:85a3:: (from 2001:db8:85a3::/64, netmask " -+ "stripped) not found in Subject Alternative Names.\n"); -+ missing_count++; -+ } -+ -+ if (!found_email) -+ { -+ printf ( -+ " MISSING: Email admin@example.com not found in Subject " -+ "Alternative Names.\n"); -+ missing_count++; -+ } -+ -+ if (!found_uri) -+ { -+ printf ( -+ " MISSING: URI https://www.example.com/service not found in " -+ "Subject Alternative Names.\n"); -+ missing_count++; -+ } -+ -+ if (!found_wildcard) -+ { -+ printf ( -+ " MISSING: Wildcard *.wildcard.example.com not found in Subject " -+ "Alternative Names.\n"); -+ missing_count++; -+ } -+ -+ if (!found_subdomain) -+ { -+ printf ( -+ " MISSING: Subdomain subdomain.alt1.example.com not found in " -+ "Subject Alternative Names.\n"); -+ missing_count++; -+ } -+ -+ if (!found_international) -+ { -+ printf ( -+ " MISSING: International domain xn--nxasmq6b.example.com not found " -+ "in Subject Alternative Names.\n"); -+ missing_count++; -+ } -+ -+ if (missing_count > 0) -+ { -+ printf (" %d expected SAN entries were missing.\n", missing_count); -+ return EINVAL; -+ } -+ -+ printf (" All expected SAN entries found successfully.\n"); -+ return EOK; -+} -+ -+static int -+test_san_edge_cases (struct sscg_x509_cert *cert) -+{ -+ X509 *x509 = cert->certificate; -+ STACK_OF (GENERAL_NAME) *san_names = NULL; -+ GENERAL_NAME *name = NULL; -+ ASN1_STRING *san_str = NULL; -+ int san_count = 0; -+ int dns_count = 0; -+ int ip_count = 0; -+ int email_count = 0; -+ int uri_count = 0; -+ char *name_str = NULL; -+ -+ /* Get the Subject Alternative Name extension */ -+ san_names = X509_get_ext_d2i (x509, NID_subject_alt_name, NULL, NULL); -+ if (!san_names) -+ { -+ printf ("Certificate missing Subject Alternative Name extension.\n"); -+ return EINVAL; -+ } -+ -+ san_count = sk_GENERAL_NAME_num (san_names); -+ -+ printf ("\n Performing comprehensive SAN validation:\n"); -+ -+ /* Count and validate all SAN types */ -+ for (int i = 0; i < san_count; i++) -+ { -+ name = sk_GENERAL_NAME_value (san_names, i); -+ -+ switch (name->type) -+ { -+ case GEN_DNS: -+ dns_count++; -+ san_str = name->d.dNSName; -+ name_str = (char *)ASN1_STRING_get0_data (san_str); -+ -+ /* Validate DNS name format */ -+ if (strlen (name_str) == 0) -+ { -+ printf (" ERROR: Empty DNS name found in SANs.\n"); -+ GENERAL_NAMES_free (san_names); -+ return EINVAL; -+ } -+ -+ /* Allow wildcards and validate domain format */ -+ if (name_str[0] != '*' && !strchr (name_str, '.')) -+ { -+ printf (" ERROR: DNS name '%s' missing domain part.\n", -+ name_str); -+ GENERAL_NAMES_free (san_names); -+ return EINVAL; -+ } -+ -+ /* Validate wildcard format */ -+ if (name_str[0] == '*' && name_str[1] != '.') -+ { -+ printf (" ERROR: Invalid wildcard DNS name '%s'.\n", -+ name_str); -+ GENERAL_NAMES_free (san_names); -+ return EINVAL; -+ } -+ break; -+ -+ case GEN_IPADD: -+ ip_count++; -+ san_str = name->d.iPAddress; -+ -+ /* Validate IP address length */ -+ int ip_len = ASN1_STRING_length (san_str); -+ if (ip_len != 4 && ip_len != 16) /* IPv4 or IPv6 */ -+ { -+ printf (" ERROR: Invalid IP address length: %d bytes.\n", -+ ip_len); -+ GENERAL_NAMES_free (san_names); -+ return EINVAL; -+ } -+ break; -+ -+ case GEN_EMAIL: -+ email_count++; -+ san_str = name->d.rfc822Name; -+ name_str = (char *)ASN1_STRING_get0_data (san_str); -+ -+ /* Validate email format */ -+ if (!strchr (name_str, '@')) -+ { -+ printf (" ERROR: Invalid email address '%s' - missing @.\n", -+ name_str); -+ GENERAL_NAMES_free (san_names); -+ return EINVAL; -+ } -+ break; -+ -+ case GEN_URI: -+ uri_count++; -+ san_str = name->d.uniformResourceIdentifier; -+ name_str = (char *)ASN1_STRING_get0_data (san_str); -+ -+ /* Validate URI format - must have scheme */ -+ if (!strstr (name_str, "://")) -+ { -+ printf (" ERROR: Invalid URI '%s' - missing scheme.\n", -+ name_str); -+ GENERAL_NAMES_free (san_names); -+ return EINVAL; -+ } -+ break; -+ -+ default: -+ /* Other SAN types are acceptable but not validated here */ -+ break; -+ } -+ } -+ -+ printf (" Found %d total SANs: %d DNS, %d IP, %d Email, %d URI.\n", -+ san_count, -+ dns_count, -+ ip_count, -+ email_count, -+ uri_count); -+ -+ /* Validate expected counts for comprehensive test */ -+ int expected_dns = -+ 6; /* CN + alt1 + alt2 + wildcard + subdomain + international */ -+ int expected_ip = 5; /* IPv4 x2 + IPv6 x1 + IPv4 netmask + IPv6 netmask */ -+ int expected_email = 1; -+ int expected_uri = 1; -+ -+ if (dns_count < expected_dns) -+ { -+ printf (" ERROR: Expected at least %d DNS names, found %d.\n", -+ expected_dns, -+ dns_count); -+ GENERAL_NAMES_free (san_names); -+ return EINVAL; -+ } -+ -+ if (ip_count < expected_ip) -+ { -+ printf (" ERROR: Expected at least %d IP addresses, found %d.\n", -+ expected_ip, -+ ip_count); -+ GENERAL_NAMES_free (san_names); -+ return EINVAL; -+ } -+ -+ if (email_count < expected_email) -+ { -+ printf (" ERROR: Expected at least %d email addresses, found %d.\n", -+ expected_email, -+ email_count); -+ GENERAL_NAMES_free (san_names); -+ return EINVAL; -+ } -+ -+ if (uri_count < expected_uri) -+ { -+ printf (" ERROR: Expected at least %d URIs, found %d.\n", -+ expected_uri, -+ uri_count); -+ GENERAL_NAMES_free (san_names); -+ return EINVAL; -+ } -+ -+ printf (" All SAN format validations passed successfully.\n"); -+ -+ GENERAL_NAMES_free (san_names); -+ return EOK; -+} -+ -+static int -+test_ip_netmask_handling (struct sscg_x509_cert *cert) -+{ -+ X509 *x509 = cert->certificate; -+ STACK_OF (GENERAL_NAME) *san_names = NULL; -+ GENERAL_NAME *name = NULL; -+ ASN1_STRING *san_str = NULL; -+ int san_count = 0; -+ int found_netmask_ipv4 = 0; -+ int found_netmask_ipv6 = 0; -+ -+ /* Get the Subject Alternative Name extension */ -+ san_names = X509_get_ext_d2i (x509, NID_subject_alt_name, NULL, NULL); -+ if (!san_names) -+ { -+ printf ("Certificate missing Subject Alternative Name extension.\n"); -+ return EINVAL; -+ } -+ -+ san_count = sk_GENERAL_NAME_num (san_names); -+ -+ printf ("\n Testing IP address netmask stripping:\n"); -+ -+ /* Look specifically for IP addresses that had netmasks stripped */ -+ for (int i = 0; i < san_count; i++) -+ { -+ name = sk_GENERAL_NAME_value (san_names, i); -+ -+ if (name->type == GEN_IPADD) -+ { -+ san_str = name->d.iPAddress; -+ -+ if (ASN1_STRING_length (san_str) == 4) /* IPv4 */ -+ { -+ const unsigned char *ip_data = ASN1_STRING_get0_data (san_str); -+ -+ /* Check for 203.0.113.0 (from original 203.0.113.0/24) */ -+ if (ip_data[0] == 203 && ip_data[1] == 0 && ip_data[2] == 113 && -+ ip_data[3] == 0) -+ { -+ printf ( -+ " ✓ IPv4 netmask stripped: 203.0.113.0/24 → " -+ "203.0.113.0\n"); -+ found_netmask_ipv4 = 1; -+ } -+ } -+ else if (ASN1_STRING_length (san_str) == 16) /* IPv6 */ -+ { -+ const unsigned char *ip_data = ASN1_STRING_get0_data (san_str); -+ -+ /* Check for 2001:db8:85a3:: (from original 2001:db8:85a3::/64) */ -+ if (ip_data[0] == 0x20 && ip_data[1] == 0x01 && -+ ip_data[2] == 0x0d && ip_data[3] == 0xb8 && -+ ip_data[4] == 0x85 && ip_data[5] == 0xa3 && -+ ip_data[6] == 0x00 && ip_data[7] == 0x00 && -+ ip_data[8] == 0x00 && ip_data[9] == 0x00 && -+ ip_data[10] == 0x00 && ip_data[11] == 0x00 && -+ ip_data[12] == 0x00 && ip_data[13] == 0x00 && -+ ip_data[14] == 0x00 && ip_data[15] == 0x00) -+ { -+ printf ( -+ " ✓ IPv6 netmask stripped: 2001:db8:85a3::/64 → " -+ "2001:db8:85a3::\n"); -+ found_netmask_ipv6 = 1; -+ } -+ } -+ } -+ } -+ -+ GENERAL_NAMES_free (san_names); -+ -+ /* Verify that netmask stripping worked correctly */ -+ if (!found_netmask_ipv4) -+ { -+ printf (" ERROR: IPv4 netmask stripping test failed.\n"); -+ return EINVAL; -+ } -+ -+ if (!found_netmask_ipv6) -+ { -+ printf (" ERROR: IPv6 netmask stripping test failed.\n"); -+ return EINVAL; -+ } -+ -+ printf (" All IP address netmask tests passed successfully.\n"); -+ return EOK; -+} -+ -+static int -+verify_name_constraints (struct sscg_x509_cert *ca_cert, -+ char **expected_san_list) -+{ -+ X509 *x509 = ca_cert->certificate; -+ X509_EXTENSION *name_constraints_ext = NULL; -+ ASN1_OCTET_STRING *ext_data = NULL; -+ BIO *bio = NULL; -+ char *ext_str = NULL; -+ char *line = NULL; -+ char *saveptr = NULL; -+ size_t ext_str_len = 0; -+ int found_constraints[20] = { -+ 0 -+ }; /* Track which expected constraints we found */ -+ int missing_count = 0; -+ int j; -+ -+ printf ("\n Verifying name constraints in CA certificate:\n"); -+ -+ /* Find the name constraints extension */ -+ int ext_idx = X509_get_ext_by_NID (x509, NID_name_constraints, -1); -+ if (ext_idx < 0) -+ { -+ printf ( -+ " ERROR: CA certificate missing Name Constraints extension.\n"); -+ return EINVAL; -+ } -+ -+ name_constraints_ext = X509_get_ext (x509, ext_idx); -+ if (!name_constraints_ext) -+ { -+ printf (" ERROR: Failed to get Name Constraints extension.\n"); -+ return EINVAL; -+ } -+ -+ /* Get the extension data */ -+ ext_data = X509_EXTENSION_get_data (name_constraints_ext); -+ if (!ext_data) -+ { -+ printf (" ERROR: Failed to get Name Constraints extension data.\n"); -+ return EINVAL; -+ } -+ -+ /* Convert the extension to a readable string using BIO */ -+ bio = BIO_new (BIO_s_mem ()); -+ if (!bio) -+ { -+ printf (" ERROR: Failed to create BIO for extension parsing.\n"); -+ return EINVAL; -+ } -+ -+ /* Print the extension to the BIO */ -+ if (!X509V3_EXT_print (bio, name_constraints_ext, 0, 0)) -+ { -+ printf (" ERROR: Failed to print Name Constraints extension.\n"); -+ BIO_free (bio); -+ return EINVAL; -+ } -+ -+ /* Get the string representation */ -+ ext_str_len = BIO_get_mem_data (bio, &ext_str); -+ if (ext_str_len <= 0 || !ext_str) -+ { -+ printf (" ERROR: Failed to get extension string data.\n"); -+ BIO_free (bio); -+ return EINVAL; -+ } -+ -+ /* Null-terminate the string for parsing */ -+ char *ext_str_copy = malloc (ext_str_len + 1); -+ if (!ext_str_copy) -+ { -+ printf ( -+ " ERROR: Failed to allocate memory for extension parsing.\n"); -+ BIO_free (bio); -+ return ENOMEM; -+ } -+ memcpy (ext_str_copy, ext_str, ext_str_len); -+ ext_str_copy[ext_str_len] = '\0'; -+ -+ printf (" Name Constraints content:\n%s\n", ext_str_copy); -+ -+ /* Parse the extension string to find constraints */ -+ line = strtok_r (ext_str_copy, "\n", &saveptr); -+ while (line) -+ { -+ /* Look for "Permitted:" sections and DNS/IP entries */ -+ if (strstr (line, "DNS:")) -+ { -+ char *dns_start = strstr (line, "DNS:"); -+ if (dns_start) -+ { -+ dns_start += 4; /* Skip "DNS:" */ -+ /* Trim whitespace */ -+ while (*dns_start == ' ' || *dns_start == '\t') -+ dns_start++; -+ -+ printf (" Found DNS constraint: %s\n", dns_start); -+ -+ /* Check if this matches our expected CN (truncated) */ -+ if (strstr (dns_start, "server")) -+ { -+ found_constraints[0] = 1; -+ } -+ -+ /* Check against our expected SAN list */ -+ if (expected_san_list) -+ { -+ for (j = 0; expected_san_list[j]; j++) -+ { -+ char *expected_dns = NULL; -+ -+ if (!strchr (expected_san_list[j], ':')) -+ { -+ expected_dns = expected_san_list[j]; -+ } -+ else if (strncmp (expected_san_list[j], "DNS:", 4) == 0) -+ { -+ expected_dns = expected_san_list[j] + 4; -+ } -+ -+ if (expected_dns && strstr (dns_start, expected_dns)) -+ { -+ found_constraints[j + 1] = 1; -+ } -+ } -+ } -+ } -+ } -+ else if (strstr (line, "IP:")) -+ { -+ char *ip_start = strstr (line, "IP:"); -+ if (ip_start) -+ { -+ ip_start += 3; /* Skip "IP:" */ -+ while (*ip_start == ' ' || *ip_start == '\t') -+ ip_start++; -+ -+ printf (" Found IP constraint: %s\n", ip_start); -+ -+ /* Check against expected IP SANs */ -+ if (expected_san_list) -+ { -+ for (j = 0; expected_san_list[j]; j++) -+ { -+ if (strncmp (expected_san_list[j], "IP:", 3) == 0) -+ { -+ char *expected_ip = expected_san_list[j] + 3; -+ char *slash = strchr (expected_ip, '/'); -+ char expected_constraint[128]; -+ char clean_ip[64]; -+ -+ /* Extract IP and netmask parts */ -+ if (slash) -+ { -+ int ip_len = slash - expected_ip; -+ strncpy (clean_ip, expected_ip, ip_len); -+ clean_ip[ip_len] = '\0'; -+ -+ /* Parse the CIDR netmask */ -+ char *cidr_str = slash + 1; -+ int cidr_bits = atoi (cidr_str); -+ -+ /* Convert to constraint format with proper netmask */ -+ if (strchr (clean_ip, ':')) -+ { -+ /* IPv6 - convert CIDR to hex netmask */ -+ const char *netmask; -+ if (cidr_bits == 128) -+ netmask = -+ "FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:" -+ "FFFF"; -+ else if (cidr_bits == 64) -+ netmask = "FFFF:FFFF:FFFF:FFFF:0:0:0:0"; -+ else -+ netmask = -+ "FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:" -+ "FFFF"; /* default to /128 */ -+ -+ /* Handle compressed IPv6 forms */ -+ if (strstr (clean_ip, "2001:db8::1")) -+ { -+ snprintf (expected_constraint, -+ sizeof (expected_constraint), -+ "IP:2001:DB8:0:0:0:0:0:1/%s", -+ netmask); -+ } -+ else if (strstr (clean_ip, -+ "2001:db8:85a3::")) -+ { -+ snprintf ( -+ expected_constraint, -+ sizeof (expected_constraint), -+ "IP:2001:DB8:85A3:0:0:0:0:0/%s", -+ netmask); -+ } -+ else -+ { -+ snprintf (expected_constraint, -+ sizeof (expected_constraint), -+ "IP:%s/%s", -+ clean_ip, -+ netmask); -+ } -+ } -+ else -+ { -+ /* IPv4 - convert CIDR to dotted decimal */ -+ const char *netmask; -+ if (cidr_bits == 32) -+ netmask = "255.255.255.255"; -+ else if (cidr_bits == 24) -+ netmask = "255.255.255.0"; -+ else if (cidr_bits == 16) -+ netmask = "255.255.0.0"; -+ else if (cidr_bits == 8) -+ netmask = "255.0.0.0"; -+ else -+ netmask = -+ "255.255.255.255"; /* default to /32 */ -+ -+ snprintf (expected_constraint, -+ sizeof (expected_constraint), -+ "IP:%s/%s", -+ clean_ip, -+ netmask); -+ } -+ } -+ else -+ { -+ /* No netmask - add single host netmask */ -+ strcpy (clean_ip, expected_ip); -+ -+ if (strchr (clean_ip, ':')) -+ { -+ /* IPv6 with /128 netmask */ -+ if (strstr (clean_ip, "2001:db8::1")) -+ { -+ snprintf (expected_constraint, -+ sizeof (expected_constraint), -+ "IP:2001:DB8:0:0:0:0:0:1/" -+ "FFFF:FFFF:FFFF:FFFF:FFFF:" -+ "FFFF:FFFF:FFFF"); -+ } -+ else if (strstr (clean_ip, -+ "2001:db8:85a3::")) -+ { -+ snprintf (expected_constraint, -+ sizeof (expected_constraint), -+ "IP:2001:DB8:85A3:0:0:0:0:0/" -+ "FFFF:FFFF:FFFF:FFFF:FFFF:" -+ "FFFF:FFFF:FFFF"); -+ } -+ else -+ { -+ snprintf (expected_constraint, -+ sizeof (expected_constraint), -+ "IP:%s/" -+ "FFFF:FFFF:FFFF:FFFF:FFFF:" -+ "FFFF:FFFF:FFFF", -+ clean_ip); -+ } -+ } -+ else -+ { -+ /* IPv4 with /32 netmask */ -+ snprintf (expected_constraint, -+ sizeof (expected_constraint), -+ "IP:%s/255.255.255.255", -+ clean_ip); -+ } -+ } -+ -+ /* Check if this expected constraint matches what we found */ -+ /* Skip the "IP:" prefix for comparison since ip_start doesn't include it */ -+ char *constraint_without_prefix = -+ expected_constraint + 3; /* Skip "IP:" */ -+ if (strcmp (ip_start, constraint_without_prefix) == -+ 0) -+ { -+ found_constraints[j + 1] = 1; -+ } -+ } -+ } -+ } -+ } -+ } -+ -+ line = strtok_r (NULL, "\n", &saveptr); -+ } -+ -+ free (ext_str_copy); -+ BIO_free (bio); -+ -+ /* Verify that we found all expected constraints */ -+ if (!found_constraints[0]) -+ { -+ printf (" MISSING: CN constraint 'server' not found.\n"); -+ missing_count++; -+ } -+ -+ if (expected_san_list) -+ { -+ for (j = 0; expected_san_list[j]; j++) -+ { -+ if (!found_constraints[j + 1]) -+ { -+ /* Only report missing DNS and IP constraints, skip email/URI */ -+ if (!strchr (expected_san_list[j], ':') || -+ strncmp (expected_san_list[j], "DNS:", 4) == 0 || -+ strncmp (expected_san_list[j], "IP:", 3) == 0) -+ { -+ printf (" MISSING: Constraint for '%s' not found.\n", -+ expected_san_list[j]); -+ missing_count++; -+ } -+ } -+ } -+ } -+ -+ if (missing_count > 0) -+ { -+ printf (" %d expected name constraints were missing.\n", -+ missing_count); -+ return EINVAL; -+ } -+ -+ printf (" All expected name constraints found successfully.\n"); -+ return EOK; -+} - - int - main (int argc, char **argv) -@@ -48,6 +900,11 @@ main (int argc, char **argv) - struct sscg_evp_pkey *pkey = NULL; - struct sscg_x509_cert *cert = NULL; - -+ /* Variables for CA testing */ -+ struct sscg_x509_cert *ca_cert = NULL; -+ struct sscg_evp_pkey *ca_key = NULL; -+ struct sscg_options ca_options; -+ - TALLOC_CTX *tmp_ctx = talloc_new (NULL); - if (!tmp_ctx) - { -@@ -88,7 +945,87 @@ main (int argc, char **argv) - certinfo->cn = talloc_strdup (certinfo, "server.example.com"); - CHECK_MEM (certinfo->cn); - -- /* TODO: include subject alt names */ -+ /* Set up comprehensive subject alternative names covering all supported formats -+ * -+ * SSCG SAN Format Support Summary: -+ * - DNS names: Supported (both implicit and explicit "DNS:" prefix) -+ * - IPv4/IPv6 addresses: Supported (with "IP:" prefix) -+ * - Email addresses: Supported (with "email:" prefix) -+ * - URIs: Partial support (limitation: slashes get truncated due to IP subnet mask handling) -+ * - Wildcards: Supported in DNS names -+ * - Internationalized domains: Limited (needs ACE encoding) -+ * -+ * Known limitations: -+ * 1. URI paths with slashes get truncated (affects https://example.com/path) -+ * 2. Only basic SAN formats supported (no otherName, directoryName, etc.) -+ * -+ * IP Address Netmask Handling: -+ * - SSCG automatically strips netmask suffixes (e.g., /24, /64) from IP addresses -+ * - This is intentional behavior to ensure clean IP address encoding in certificates -+ */ -+ certinfo->subject_alt_names = talloc_zero_array (certinfo, char *, 13); -+ CHECK_MEM (certinfo->subject_alt_names); -+ -+ /* DNS names (both implicit and explicit) */ -+ certinfo->subject_alt_names[0] = -+ talloc_strdup (certinfo->subject_alt_names, "alt1.example.com"); -+ CHECK_MEM (certinfo->subject_alt_names[0]); -+ -+ certinfo->subject_alt_names[1] = -+ talloc_strdup (certinfo->subject_alt_names, "DNS:alt2.example.com"); -+ CHECK_MEM (certinfo->subject_alt_names[1]); -+ -+ /* IPv4 addresses */ -+ certinfo->subject_alt_names[2] = -+ talloc_strdup (certinfo->subject_alt_names, "IP:192.168.1.100"); -+ CHECK_MEM (certinfo->subject_alt_names[2]); -+ -+ certinfo->subject_alt_names[3] = -+ talloc_strdup (certinfo->subject_alt_names, "IP:10.0.0.1"); -+ CHECK_MEM (certinfo->subject_alt_names[3]); -+ -+ /* IPv6 address */ -+ certinfo->subject_alt_names[4] = -+ talloc_strdup (certinfo->subject_alt_names, "IP:2001:db8::1"); -+ CHECK_MEM (certinfo->subject_alt_names[4]); -+ -+ /* IPv4 address with netmask (SSCG will strip the /24 part) */ -+ certinfo->subject_alt_names[5] = -+ talloc_strdup (certinfo->subject_alt_names, "IP:203.0.113.0/24"); -+ CHECK_MEM (certinfo->subject_alt_names[5]); -+ -+ /* IPv6 address with netmask (SSCG will strip the /64 part) */ -+ certinfo->subject_alt_names[6] = -+ talloc_strdup (certinfo->subject_alt_names, "IP:2001:db8:85a3::/64"); -+ CHECK_MEM (certinfo->subject_alt_names[6]); -+ -+ /* Email addresses */ -+ certinfo->subject_alt_names[7] = -+ talloc_strdup (certinfo->subject_alt_names, "email:admin@example.com"); -+ CHECK_MEM (certinfo->subject_alt_names[7]); -+ -+ /* URI (proper format - let's see what SSCG actually does with it) */ -+ certinfo->subject_alt_names[8] = talloc_strdup ( -+ certinfo->subject_alt_names, "URI:https://www.example.com/service"); -+ CHECK_MEM (certinfo->subject_alt_names[8]); -+ -+ /* Wildcard DNS name */ -+ certinfo->subject_alt_names[9] = -+ talloc_strdup (certinfo->subject_alt_names, "*.wildcard.example.com"); -+ CHECK_MEM (certinfo->subject_alt_names[9]); -+ -+ /* Subdomain */ -+ certinfo->subject_alt_names[10] = -+ talloc_strdup (certinfo->subject_alt_names, "subdomain.alt1.example.com"); -+ CHECK_MEM (certinfo->subject_alt_names[10]); -+ -+ /* International domain (ACE encoded) */ -+ certinfo->subject_alt_names[11] = -+ talloc_strdup (certinfo->subject_alt_names, "xn--nxasmq6b.example.com"); -+ CHECK_MEM (certinfo->subject_alt_names[11]); -+ -+ /* NULL terminator */ -+ certinfo->subject_alt_names[12] = NULL; - - /* Generate an RSA keypair */ - bits = 4096; -@@ -111,7 +1048,108 @@ main (int argc, char **argv) - tmp_ctx, csr, serial, 3650, NULL, pkey, EVP_sha512 (), &cert); - CHECK_OK (ret); - -- ret = EOK; -+ /* ============= SERVICE CERTIFICATE TESTS ============= */ -+ -+ /* Verify that subject alternative names were properly included */ -+ printf ("Verifying subject alternative names in service certificate. "); -+ int verify_ret = verify_subject_alt_names (cert); -+ if (verify_ret != EOK) -+ { -+ printf ("FAILED.\n"); -+ ret = verify_ret; /* Store first failure but continue testing */ -+ } -+ else -+ { -+ printf ("SUCCESS.\n"); -+ } -+ -+ /* Test additional SAN verification scenarios */ -+ printf ("Testing SAN edge cases and validation. "); -+ int edge_ret = test_san_edge_cases (cert); -+ if (edge_ret != EOK) -+ { -+ printf ("FAILED.\n"); -+ if (ret == EOK) -+ ret = edge_ret; /* Store first failure */ -+ } -+ else -+ { -+ printf ("SUCCESS.\n"); -+ } -+ -+ /* Test IP address netmask handling */ -+ printf ("Testing IP address netmask stripping functionality. "); -+ int netmask_ret = test_ip_netmask_handling (cert); -+ if (netmask_ret != EOK) -+ { -+ printf ("FAILED.\n"); -+ if (ret == EOK) -+ ret = netmask_ret; /* Store first failure */ -+ } -+ else -+ { -+ printf ("SUCCESS.\n"); -+ } -+ -+ /* ============= CA CERTIFICATE TESTS ============= */ -+ -+ printf ("\n=== CA CERTIFICATE TESTS ===\n"); -+ -+ /* Set up options for CA creation */ -+ memset (&ca_options, 0, sizeof (ca_options)); -+ ca_options.country = "US"; -+ ca_options.state = ""; -+ ca_options.locality = ""; -+ ca_options.org = "Unspecified"; -+ ca_options.email = ""; -+ ca_options.hostname = "server.example.com"; -+ ca_options.hash_fn = EVP_sha256 (); -+ ca_options.lifetime = 3650; -+ ca_options.verbosity = SSCG_QUIET; -+ -+ /* Set up the same subject alternative names for the CA */ -+ ca_options.subject_alt_names = certinfo->subject_alt_names; -+ -+ /* Create the private CA */ -+ printf ("Creating private CA certificate. "); -+ ret = create_private_CA (tmp_ctx, &ca_options, &ca_cert, &ca_key); -+ if (ret != EOK) -+ { -+ printf ("FAILED.\n"); -+ goto done; -+ } -+ else -+ { -+ printf ("SUCCESS.\n"); -+ } -+ -+ /* Verify name constraints in the CA certificate */ -+ printf ("Verifying name constraints in CA certificate. "); -+ int ca_constraints_ret = -+ verify_name_constraints (ca_cert, certinfo->subject_alt_names); -+ if (ca_constraints_ret != EOK) -+ { -+ printf ("FAILED.\n"); -+ if (ret == EOK) -+ ret = ca_constraints_ret; -+ } -+ else -+ { -+ printf ("SUCCESS.\n"); -+ } -+ -+ /* Summary of all test results */ -+ printf ("\n=== TEST SUMMARY ===\n"); -+ printf ("Service cert SAN verification: %s\n", -+ verify_ret == EOK ? "PASS" : "FAIL"); -+ printf ("Service cert edge case validation: %s\n", -+ edge_ret == EOK ? "PASS" : "FAIL"); -+ printf ("Service cert netmask handling: %s\n", -+ netmask_ret == EOK ? "PASS" : "FAIL"); -+ printf ("CA certificate creation: %s\n", ca_cert ? "PASS" : "FAIL"); -+ printf ("CA name constraints verification: %s\n", -+ ca_constraints_ret == EOK ? "PASS" : "FAIL"); -+ - done: - if (ret != EOK) - { --- -2.50.1 - diff --git a/sources b/sources index e51b29d..ebbdfd3 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -SHA512 (sscg-3.0.5.tar.gz) = da4db537096608683726084ea342cf3e06ec25da16c4475a29e83a466486a4ace8b58253520034eb263d8cefde14e21f3fe69d23fa75686cab5e3a7f8e170442 +SHA512 (sscg-4.0.3.tar.gz) = f629cf7e32d4d4e7c1f58c4a53be925b96980e6fb3106e3a36a72f85c723bd79fba6aecdbf092b50f915a8833297bc7c6c1ccbe04fef488db38bbdc1e3a95b96 diff --git a/sscg.spec b/sscg.spec index 7a8ce6b..17b5f32 100644 --- a/sscg.spec +++ b/sscg.spec @@ -1,8 +1,8 @@ ## START: Set by rpmautospec -## (rpmautospec version 0.6.5) +## (rpmautospec version 0.8.3) ## RPMAUTOSPEC: autorelease, autochangelog %define autorelease(e:s:pb:n) %{?-p:0.}%{lua: - release_number = 12; + release_number = 2; base_release_number = tonumber(rpm.expand("%{?-b*}%{!?-b:1}")); print(release_number + base_release_number - 1); }%{?-e:.%{-e*}}%{?-s:.%{-s*}}%{!?-n:%{?dist}} @@ -19,36 +19,34 @@ %{!?meson_test: %global meson_test %{__meson} test -C %{_vpath_builddir} --num-processes %{_smp_build_ncpus} --print-errorlogs} Name: sscg -Version: 3.0.5 +Version: 4.0.3 Release: %autorelease -Summary: Simple SSL certificate generator +Summary: Simple Signed Certificate Generator License: GPL-3.0-or-later WITH cryptsetup-OpenSSL-exception URL: https://%{provider_prefix} -Source0: sscg-3.0.5.tar.gz -# Extend maximum DNS name to 255 -# Author: Stephen Gallagher -Patch: 0001-Extend-maximum-DNS-name-to-255.patch -# Update the README documentation -Patch: 0002-Update-README.md-with-latest-usage-information.patch -# Set Certificate Signing Request version to 1 instead of 3(which doesn't exist) -Patch: 0003-x509-Use-proper-version-for-CSR.patch -# Ensure 'critical' basicConstraint for CA cert -Patch: 0004-Ensure-critical-basicConstraint-for-CA-cert.patch -# Handle IP addresses with and without CIDR netmasks in subject alt names -Patch: 0005-Fix-IP-address-handling-in-CA-certificate-SAN-constr.patch - +Source0: %{URL}/archive/refs/tags/sscg-%{version}.tar.gz BuildRequires: gcc BuildRequires: libtalloc-devel BuildRequires: openssl BuildRequires: openssl-devel BuildRequires: popt-devel -BuildRequires: libpath_utils-devel BuildRequires: meson BuildRequires: ninja-build BuildRequires: help2man +# For backwards-compatibility in RHEL, revert the 4.0 patch that disables +# dhparam file generation by default. +Patch: 0001-Restore-defaulting-to-dhparams.pem-creation.patch + +# Upstream patch to avoid segfault when receiving bad CLI arguments +Patch: 0002-Avoid-segfault-on-receiving-bad-CLI-arguments.patch + +# Downstream patch to restore error message at the end of execution that is +# checked by certain tests +Patch: 0003-Restore-error-message.patch + %description A utility to aid in the creation of more secure "self-signed" certificates. The certificates created by this tool are generated in a @@ -58,7 +56,7 @@ up a full PKI environment and without exposing the machine to a risk of false signatures from the service certificate. %prep -%autosetup -p1 -n sscg-3.0.5 +%autosetup -p1 -n sscg-sscg-%{version} %build @@ -79,6 +77,35 @@ false signatures from the service certificate. %changelog ## START: Generated by rpmautospec +* Tue Dec 02 2025 Stephen Gallagher - 4.0.3-2 +- Fix issues discovered by OSCI tests + +* Tue Dec 02 2025 Stephen Gallagher - 4.0.3-1 +- Update to SSCG 4.0.3 + +* Tue Dec 02 2025 Stephen Gallagher - 4.0.1-1 +- Update to SSCG 4.0.1 + +* Mon Oct 27 2025 Stephen Gallagher - 4.0.0-2 +- Restore creation of dhparams file by default + +* Mon Oct 27 2025 Stephen Gallagher - 4.0.0-1 +- Release SSCG 4.0.0 +- Support for ML-DSA keys for post-quantum cryptography (PQC) when built + against OpenSSL 3.5 or later +- Support for EC-DSA keys for elliptic curves +- RSA certificate authority is now created with a minimum key strength of + 4096 bits +- Improved --help output +- Deprecated support for generating custom DH parameters +- Stopped producing the DH parameter file by default (it may be requested + explicitly with the --dhparams-file argument) +- Minimum OpenSSL version is now 3.0 +- Drop upstreamed patches + +* Wed Aug 13 2025 Zdenek Dohnal - 3.0.5-13 +- Migrate FMF tests into metadata format used in rhel-stacks + * Mon Aug 11 2025 Stephen Gallagher - 3.0.5-12 - Fix IP address handling in CA certificate SAN constraints - Add automatic single-IP subnet mask to IP addresses in CA name