From c7a072acc08ebd32e33addef73165821e2c10741 Mon Sep 17 00:00:00 2001 From: Panu Matilainen Date: Oct 24 2024 10:16:09 +0000 Subject: Support switching between Sequoia and GnuPG for signing packages Add new rpmsign-sequoia and rpmsign-gnupg subpackages which can be used to switch between the two OpenPGP implementations for signing, rpm-sign-libs just requires one of them to be present. It's worth noting that unlike GnuPG, Sequoia doesn't accept names or email addresses as the signer identifier, one needs to supply the actual key fingerprint. Resolves: RHEL-56363 --- diff --git a/0001-Refactor-sign-command-expand-and-parse-out-of-runGPG.patch b/0001-Refactor-sign-command-expand-and-parse-out-of-runGPG.patch new file mode 100644 index 0000000..dec7288 --- /dev/null +++ b/0001-Refactor-sign-command-expand-and-parse-out-of-runGPG.patch @@ -0,0 +1,143 @@ +From 3b0a150af79668052bf5842b68341adbde016005 Mon Sep 17 00:00:00 2001 +Message-ID: <3b0a150af79668052bf5842b68341adbde016005.1728896192.git.pmatilai@redhat.com> +From: Panu Matilainen +Date: Thu, 5 Sep 2024 09:07:26 +0300 +Subject: [PATCH 1/3] Refactor sign command expand and parse out of runGPG() + +We'll need the wider visibility of the executing command for the next +steps. While at it, ensure the parsed signing command is minimally +sufficient for what the code expects, ie has at least two items in +the array. + +We now need two exit points, one for the case where we forked and one +where we didn't. Also the case where waitpid() failed entirely must +not return directly to avoid leaking, so merge it with the rest of +the error handling if instead. + +(cherry picked from commit 2c9ad2bbc1d00010880076cd5c73e97ffcb946ed) +--- + sign/rpmgensig.c | 51 ++++++++++++++++++++++++++++++---------------- + tests/rpmsigdig.at | 8 ++++++++ + 2 files changed, 42 insertions(+), 17 deletions(-) + +diff --git a/sign/rpmgensig.c b/sign/rpmgensig.c +index a9c3c3e06..7bbd63216 100644 +--- a/sign/rpmgensig.c ++++ b/sign/rpmgensig.c +@@ -188,6 +188,29 @@ exit: + return sigtd; + } + ++char ** signCmd(const char *sigfile) ++{ ++ int argc = 0; ++ char **argv = NULL; ++ ++ rpmPushMacro(NULL, "__plaintext_filename", NULL, "-", -1); ++ rpmPushMacro(NULL, "__signature_filename", NULL, sigfile, -1); ++ ++ char *cmd = rpmExpand("%{?__gpg_sign_cmd}", NULL); ++ ++ rpmPopMacro(NULL, "__plaintext_filename"); ++ rpmPopMacro(NULL, "__signature_filename"); ++ ++ if (poptParseArgvString(cmd, &argc, (const char ***)&argv) < 0 || argc < 2) { ++ rpmlog(RPMLOG_ERR, _("Invalid sign command: %s\n"), cmd); ++ argv = _free(argv); ++ } ++ ++ free(cmd); ++ ++ return argv; ++} ++ + static int runGPG(sigTarget sigt, const char *sigfile) + { + int pid = 0, status; +@@ -198,18 +221,17 @@ static int runGPG(sigTarget sigt, const char *sigfile) + ssize_t wantCount; + rpm_loff_t size; + int rc = 1; /* assume failure */ ++ char **argv = NULL; ++ ++ if ((argv = signCmd(sigfile)) == NULL) ++ goto exit_nowait; + + if (pipe(pipefd) < 0) { + rpmlog(RPMLOG_ERR, _("Could not create pipe for signing: %m\n")); +- goto exit; ++ goto exit_nowait; + } + +- rpmPushMacro(NULL, "__plaintext_filename", NULL, "-", -1); +- rpmPushMacro(NULL, "__signature_filename", NULL, sigfile, -1); +- + if (!(pid = fork())) { +- char *const *av; +- char *cmd = NULL; + const char *tty = ttyname(STDIN_FILENO); + const char *gpg_path = NULL; + +@@ -223,19 +245,13 @@ static int runGPG(sigTarget sigt, const char *sigfile) + dup2(pipefd[0], STDIN_FILENO); + close(pipefd[1]); + +- cmd = rpmExpand("%{?__gpg_sign_cmd}", NULL); +- rc = poptParseArgvString(cmd, NULL, (const char ***)&av); +- if (!rc) +- rc = execve(av[0], av+1, environ); ++ rc = execve(argv[0], argv+1, environ); + + rpmlog(RPMLOG_ERR, _("Could not exec %s: %s\n"), "gpg", + strerror(errno)); + _exit(EXIT_FAILURE); + } + +- rpmPopMacro(NULL, "__plaintext_filename"); +- rpmPopMacro(NULL, "__signature_filename"); +- + close(pipefd[0]); + fpipe = fdopen(pipefd[1], "w"); + if (!fpipe) { +@@ -280,14 +296,15 @@ exit: + + if (reaped == -1) { + rpmlog(RPMLOG_ERR, _("gpg waitpid failed (%s)\n"), strerror(errno)); +- return rc; +- } +- +- if (!WIFEXITED(status) || WEXITSTATUS(status)) { ++ } else if (!WIFEXITED(status) || WEXITSTATUS(status)) { + rpmlog(RPMLOG_ERR, _("gpg exec failed (%d)\n"), WEXITSTATUS(status)); + } else { + rc = 0; + } ++ ++exit_nowait: ++ free(argv); ++ + return rc; + } + +diff --git a/tests/rpmsigdig.at b/tests/rpmsigdig.at +index b726e79ef..14dffc27a 100644 +--- a/tests/rpmsigdig.at ++++ b/tests/rpmsigdig.at +@@ -1028,6 +1028,14 @@ cmp -s ${ORIG} ${NEW}; echo $? + ], + []) + ++RPMTEST_CHECK([ ++run rpmsign --define "__gpg_sign_cmd mumble" --key-id 1964C5FC --addsign "${RPMTEST}"/tmp/hello-2.0-1.x86_64.rpm > /dev/null ++], ++[1], ++[], ++[error: Invalid sign command: mumble ++]) ++ + # rpmsign --addsign + RPMTEST_CHECK([ + RPMDB_INIT +-- +2.47.0 + diff --git a/0002-Eliminate-hardcoded-GPG-references-from-user-visible.patch b/0002-Eliminate-hardcoded-GPG-references-from-user-visible.patch new file mode 100644 index 0000000..d943ff6 --- /dev/null +++ b/0002-Eliminate-hardcoded-GPG-references-from-user-visible.patch @@ -0,0 +1,129 @@ +From 3c1055628380d66934578060a4a6c678f1261456 Mon Sep 17 00:00:00 2001 +Message-ID: <3c1055628380d66934578060a4a6c678f1261456.1728896192.git.pmatilai@redhat.com> +In-Reply-To: <3b0a150af79668052bf5842b68341adbde016005.1728896192.git.pmatilai@redhat.com> +References: <3b0a150af79668052bf5842b68341adbde016005.1728896192.git.pmatilai@redhat.com> +From: Panu Matilainen +Date: Thu, 5 Sep 2024 09:44:40 +0300 +Subject: [PATCH 2/3] Eliminate hardcoded GPG references from user visible + messages + +Use the OpenPGP standard name or the configured+parsed signing command +in messages as appropriate. Also detect if we're specifically using +gpg and only set up its environment in that case to avoid bleeding +those messages to innocent bypassers. + +Fixes: #3274 +(backported from commit a3cf4f674dd59c1c80f97780643c184e705518ce) +--- + sign/rpmgensig.c | 42 +++++++++++++++++++++++++----------------- + tests/rpmsigdig.at | 9 +++++++++ + 2 files changed, 34 insertions(+), 17 deletions(-) + +diff --git a/sign/rpmgensig.c b/sign/rpmgensig.c +index 7bbd63216..fb7368e14 100644 +--- a/sign/rpmgensig.c ++++ b/sign/rpmgensig.c +@@ -232,23 +232,29 @@ static int runGPG(sigTarget sigt, const char *sigfile) + } + + if (!(pid = fork())) { +- const char *tty = ttyname(STDIN_FILENO); +- const char *gpg_path = NULL; +- +- if (!getenv("GPG_TTY") && (!tty || setenv("GPG_TTY", tty, 0))) +- rpmlog(RPMLOG_WARNING, _("Could not set GPG_TTY to stdin: %m\n")); +- +- gpg_path = rpmExpand("%{?_gpg_path}", NULL); +- if (gpg_path && *gpg_path != '\0') +- (void) setenv("GNUPGHOME", gpg_path, 1); ++ /* GnuPG needs extra setup, try to see if that's what we're running */ ++ char *out = rpmExpand("%(", argv[0], " --version 2> /dev/null)", NULL); ++ int using_gpg = (strstr(out, "GnuPG") != NULL); ++ if (using_gpg) { ++ const char *tty = ttyname(STDIN_FILENO); ++ const char *gpg_path = NULL; ++ ++ if (!getenv("GPG_TTY") && (!tty || setenv("GPG_TTY", tty, 0))) ++ rpmlog(RPMLOG_WARNING, _("Could not set GPG_TTY to stdin: %m\n")); ++ ++ gpg_path = rpmExpand("%{?_gpg_path}", NULL); ++ if (gpg_path && *gpg_path != '\0') ++ (void) setenv("GNUPGHOME", gpg_path, 1); ++ } ++ free(out); + + dup2(pipefd[0], STDIN_FILENO); + close(pipefd[1]); + + rc = execve(argv[0], argv+1, environ); + +- rpmlog(RPMLOG_ERR, _("Could not exec %s: %s\n"), "gpg", +- strerror(errno)); ++ rpmlog(RPMLOG_ERR, _("Could not exec %s: %s\n"), argv[0], ++ strerror(errno)); + _exit(EXIT_FAILURE); + } + +@@ -295,9 +301,11 @@ exit: + } while (reaped == -1 && errno == EINTR); + + if (reaped == -1) { +- rpmlog(RPMLOG_ERR, _("gpg waitpid failed (%s)\n"), strerror(errno)); ++ rpmlog(RPMLOG_ERR, _("%s waitpid failed (%s)\n"), argv[0], ++ strerror(errno)); + } else if (!WIFEXITED(status) || WEXITSTATUS(status)) { +- rpmlog(RPMLOG_ERR, _("gpg exec failed (%d)\n"), WEXITSTATUS(status)); ++ rpmlog(RPMLOG_ERR, _("%s exec failed (%d)\n"), argv[0], ++ WEXITSTATUS(status)); + } else { + rc = 0; + } +@@ -328,13 +336,13 @@ static rpmtd makeGPGSignature(Header sigh, int ishdr, sigTarget sigt) + goto exit; + + if (stat(sigfile, &st)) { +- /* GPG failed to write signature */ +- rpmlog(RPMLOG_ERR, _("gpg failed to write signature\n")); ++ /* External command failed to write signature */ ++ rpmlog(RPMLOG_ERR, _("failed to write signature\n")); + goto exit; + } + + pktlen = st.st_size; +- rpmlog(RPMLOG_DEBUG, "GPG sig size: %zd\n", pktlen); ++ rpmlog(RPMLOG_DEBUG, "OpenPGP sig size: %zd\n", pktlen); + pkt = xmalloc(pktlen); + + { FD_t fd; +@@ -351,7 +359,7 @@ static rpmtd makeGPGSignature(Header sigh, int ishdr, sigTarget sigt) + } + } + +- rpmlog(RPMLOG_DEBUG, "Got %zd bytes of GPG sig\n", pktlen); ++ rpmlog(RPMLOG_DEBUG, "Got %zd bytes of OpenPGP sig\n", pktlen); + + /* Parse the signature, change signature tag as appropriate. */ + sigtd = makeSigTag(sigh, ishdr, pkt, pktlen); +diff --git a/tests/rpmsigdig.at b/tests/rpmsigdig.at +index 14dffc27a..d19f85d04 100644 +--- a/tests/rpmsigdig.at ++++ b/tests/rpmsigdig.at +@@ -1036,6 +1036,15 @@ run rpmsign --define "__gpg_sign_cmd mumble" --key-id 1964C5FC --addsign "${RPMT + [error: Invalid sign command: mumble + ]) + ++RPMTEST_CHECK([ ++run rpmsign --define "__gpg /gnus/not/here" --key-id 1964C5FC --addsign "${RPMTEST}"/tmp/hello-2.0-1.x86_64.rpm > /dev/null ++], ++[1], ++[], ++[error: Could not exec /gnus/not/here: No such file or directory ++error: /gnus/not/here exec failed (1) ++]) ++ + # rpmsign --addsign + RPMTEST_CHECK([ + RPMDB_INIT +-- +2.47.0 + diff --git a/0003-Declare-signCmd-static.patch b/0003-Declare-signCmd-static.patch new file mode 100644 index 0000000..609121b --- /dev/null +++ b/0003-Declare-signCmd-static.patch @@ -0,0 +1,35 @@ +From 2029533d7878a58874cda061d13c6188f4b3aed1 Mon Sep 17 00:00:00 2001 +Message-ID: <2029533d7878a58874cda061d13c6188f4b3aed1.1728896192.git.pmatilai@redhat.com> +In-Reply-To: <3b0a150af79668052bf5842b68341adbde016005.1728896192.git.pmatilai@redhat.com> +References: <3b0a150af79668052bf5842b68341adbde016005.1728896192.git.pmatilai@redhat.com> +From: Michal Domonkos +Date: Mon, 9 Sep 2024 15:19:52 +0200 +Subject: [PATCH 3/3] Declare signCmd() static + +Commit 2c9ad2bbc1d00010880076cd5c73e97ffcb946ed added this new helper +function for internal use and depite a missing declaration, the compiler +defaulting to WITH_CXX=ON on master chugged along just fine... only +until porting the same commit to a C-only branch (hello rpm-4.20.x) +where it now produces a warning, oops. + +(cherry picked from commit a7784eccd9de674e97fc9577434334060b3abd23) +--- + sign/rpmgensig.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/sign/rpmgensig.c b/sign/rpmgensig.c +index fb7368e14..d7d58fd4f 100644 +--- a/sign/rpmgensig.c ++++ b/sign/rpmgensig.c +@@ -188,7 +188,7 @@ exit: + return sigtd; + } + +-char ** signCmd(const char *sigfile) ++static char ** signCmd(const char *sigfile) + { + int argc = 0; + char **argv = NULL; +-- +2.47.0 + diff --git a/macros.rpmsign-gnupg b/macros.rpmsign-gnupg new file mode 100644 index 0000000..110ef26 --- /dev/null +++ b/macros.rpmsign-gnupg @@ -0,0 +1,22 @@ +#============================================================================== +# ---- GPG signature macros. +# The signature to use and the location of configuration files for +# signing packages with GNU gpg. +# +#%_gpg_name +#%_gpg_path + +%__gpg /usr/bin/gpg2 + +# Macro(s) to hold the arguments passed to GPG/PGP for package +# signing. Expansion result is parsed by popt, so be sure to use +# %{shescape} where needed. +# +%__gpg_sign_cmd %{shescape:%{__gpg}} \ + gpg --no-verbose --no-armor --no-secmem-warning \ + %{?_gpg_digest_algo:--digest-algo=%{_gpg_digest_algo}} \ + %{?_gpg_sign_cmd_extra_args} \ + %{?_gpg_name:-u %{shescape:%{_gpg_name}}} \ + -sbo %{shescape:%{?__signature_filename}} \ + %{?__plaintext_filename:-- %{shescape:%{__plaintext_filename}}} + diff --git a/macros.rpmsign-sequoia b/macros.rpmsign-sequoia new file mode 100644 index 0000000..dcb9d55 --- /dev/null +++ b/macros.rpmsign-sequoia @@ -0,0 +1,23 @@ +#============================================================================== +# ---- Sequoia signature macros. +# The signature to use and the location of configuration files for +# signing packages with Sequoia. +# +# Unlike GnuPG, Sequoia doesn't support specifying the signer key by +# email or name match, you need to supply the hex fingerprint (or keyid) +#%_gpg_name +#%_gpg_path + +%__gpg /usr/bin/sq + +# Macro(s) to hold the arguments passed to Sequoia for package +# signing. Expansion result is parsed by popt, so be sure to use +# %{shescape} where needed. +# + +%__gpg_sign_cmd %{__gpg} %{__gpg} sign \ + %{?_gpg_sign_cmd_extra_args} \ + %{?_gpg_name:--signer-key %{_gpg_name}} \ + --detached --output %{shescape:%{?__signature_filename}} \ + %{?__plaintext_filename:-- %{shescape:%{__plaintext_filename}}} + diff --git a/rpm-4.19.1.1-nogpg.patch b/rpm-4.19.1.1-nogpg.patch new file mode 100644 index 0000000..8d21d4a --- /dev/null +++ b/rpm-4.19.1.1-nogpg.patch @@ -0,0 +1,59 @@ +diff -up rpm-4.19.1.1/macros.in.nogpg rpm-4.19.1.1/macros.in +--- rpm-4.19.1.1/macros.in.nogpg 2024-10-14 10:01:22.265773552 +0300 ++++ rpm-4.19.1.1/macros.in 2024-10-14 10:02:32.245317535 +0300 +@@ -30,7 +30,6 @@ + %__chown @__CHOWN@ + %__cp @__CP@ + %__file @__FILE@ +-%__gpg @__GPG@ + %__grep @__GREP@ + %__gzip @__GZIP@ + %__id @__ID@ +@@ -321,12 +320,6 @@ Supplements: (%{name} = %{version}-%{r + # marked as %doc should be installed. + #%_excludedocs + +-# The signature to use and the location of configuration files for +-# signing packages with GNU gpg. +-# +-#%_gpg_name +-#%_gpg_path +- + # The port and machine name of an HTTP proxy host (used for FTP/HTTP). + # + #%_httpport +@@ -595,10 +588,10 @@ Supplements: (%{name} = %{version}-%{r + %_fileattrsdir %{_rpmconfigdir}/fileattrs + + # This macro defines how much space (in bytes) in package should be +-# reserved for gpg signatures during building of a package. If this space is +-# big enough for gpg signatures to fit into it then signing of the packages is ++# reserved for OpenPGP signatures during building of a package. If this space ++# big enough for the signature to fit into it then signing of the packages is + # very quick because it is not necessary to rewrite the whole package to make +-# some space for gpg signatures. ++# some space for the signature. + %__gpg_reserved_space 4096 + + #============================================================================== +@@ -613,20 +606,6 @@ Supplements: (%{name} = %{version}-%{r + %_db_backend @DB_BACKEND@ + + #============================================================================== +-# ---- GPG/PGP/PGP5 signature macros. +-# Macro(s) to hold the arguments passed to GPG/PGP for package +-# signing. Expansion result is parsed by popt, so be sure to use +-# %{shescape} where needed. +-# +-%__gpg_sign_cmd %{shescape:%{__gpg}} \ +- gpg --no-verbose --no-armor --no-secmem-warning \ +- %{?_gpg_digest_algo:--digest-algo=%{_gpg_digest_algo}} \ +- %{?_gpg_sign_cmd_extra_args} \ +- %{?_gpg_name:-u %{shescape:%{_gpg_name}}} \ +- -sbo %{shescape:%{?__signature_filename}} \ +- %{?__plaintext_filename:-- %{shescape:%{__plaintext_filename}}} +- +-#============================================================================== + # ---- Transaction macros. + # Macro(s) used to parameterize transactions. + # diff --git a/rpm.spec b/rpm.spec index 03de2f1..21c9ef0 100644 --- a/rpm.spec +++ b/rpm.spec @@ -27,7 +27,7 @@ %global rpmver 4.19.1.1 #global snapver rc1 -%global baserelease 3 +%global baserelease 4 %global sover 10 %global srcver %{rpmver}%{?snapver:-%{snapver}} @@ -46,6 +46,9 @@ Source10: rpmdb-rebuild.service Source20: rpmdb-migrate.service Source21: rpmdb_migrate +Source30: macros.rpmsign-sequoia +Source31: macros.rpmsign-gnupg + Requires: coreutils Requires: popt%{_isa} >= 1.10.2.1 Requires: curl @@ -135,11 +138,18 @@ rpm-4.9.90-no-man-dirs.patch rpm-4.18.92-disable-sysusers.patch rpm-4.18.90-weak-user-group.patch +# We supply gpg/sq config separately, remove gpg stuff from main macros +rpm-4.19.1.1-nogpg.patch + # Patches already upstream: 0001-Fix-potential-use-of-uninitialized-pipe-array.patch 0001-Fix-potential-use-of-uninitialized-pgp-struct.patch 0001-Fix-memory-leak-in-rpmsign.patch +0001-Refactor-sign-command-expand-and-parse-out-of-runGPG.patch +0002-Eliminate-hardcoded-GPG-references-from-user-visible.patch +0003-Declare-signCmd-static.patch + # These are not yet upstream rpm-4.7.1-geode-i686.patch @@ -174,11 +184,29 @@ This package contains the RPM shared libraries for building packages. %package sign-libs Summary: Libraries for signing RPM packages Requires: rpm-libs%{_isa} = %{version}-%{release} -Requires: %{_bindir}/gpg2 +Requires(meta): (rpm-sign-gnupg or rpm-sign-sequoia) %description sign-libs This package contains the RPM shared libraries for signing packages. +%package sign-gnupg +Summary: Support for signing RPM packages using GnuPG +Requires: gnupg2 +Requires(meta): rpm-sign-libs%{_isa} >= %{version}-%{release} +Conflicts: sign-sequoia + +%description sign-gnupg +This package provides configuration for signing RPM packages using GnuPG. + +%package sign-sequoia +Summary: Support for signing RPM packages using Sequoia +Requires: sequoia-sq +Requires(meta): rpm-sign-libs%{_isa} >= %{version}-%{release} +Conflicts: sign-gnupg + +%description sign-sequoia +This package provides configuration for signing RPM packages using Sequoia. + %package devel Summary: Development files for manipulating RPM packages License: GPL-2.0-or-later OR LGPL-2.1-or-later @@ -423,6 +451,9 @@ rm -rf $RPM_BUILD_ROOT/var/tmp # workaround for https://github.com/rpm-software-management/rpm/issues/2811 rm $RPM_BUILD_ROOT/%{_defaultdocdir}/rpm/README.md +# Signing macros for Sequoia and GnuPG +install -m 644 %{SOURCE30} %{SOURCE31} $RPM_BUILD_ROOT/%{rpmhome}/macros.d + %pre # Symlink all rpmdb files to the new location if we're still using /var/lib/rpm if [ -d /var/lib/rpm ]; then @@ -485,7 +516,7 @@ fi %attr(0755, root, root) %dir %{rpmhome} %{rpmhome}/macros -%{rpmhome}/macros.d +%dir %{rpmhome}/macros.d %{rpmhome}/lua %{rpmhome}/rpmpopt* %{rpmhome}/rpmrc @@ -560,6 +591,12 @@ fi %{_libdir}/librpmsign.so.%{sover} %{_libdir}/librpmsign.so.%{sover}.* +%files sign-sequoia +%{rpmhome}/macros.d/macros.rpmsign-sequoia + +%files sign-gnupg +%{rpmhome}/macros.d/macros.rpmsign-gnupg + %files build %{_bindir}/rpmbuild %{_bindir}/gendiff @@ -616,6 +653,10 @@ fi %doc %{_defaultdocdir}/rpm/API/ %changelog +* Mon Oct 14 2024 Panu Matilainen - 4.19.1.1-4 +- Remove hardcoded GPG references from signing error messages +- Support switching between GnuPG and Sequoia for package signing (RHEL-56363) + * Tue Aug 13 2024 Michal Domonkos - 4.19.1.1-3 - Fix potential use of uninitialized pipe array (RHEL-54012) - Fix potential use of uninitialized pgp struct (RHEL-54013)