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
This commit is contained in:
Panu Matilainen 2024-10-14 10:05:02 +03:00 committed by Michal Domonkos
parent 0c62987083
commit c7a072acc0
7 changed files with 455 additions and 3 deletions

View File

@ -0,0 +1,143 @@
From 3b0a150af79668052bf5842b68341adbde016005 Mon Sep 17 00:00:00 2001
Message-ID: <3b0a150af79668052bf5842b68341adbde016005.1728896192.git.pmatilai@redhat.com>
From: Panu Matilainen <pmatilai@redhat.com>
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 <signed>
RPMTEST_CHECK([
RPMDB_INIT
--
2.47.0

View File

@ -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 <pmatilai@redhat.com>
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 <signed>
RPMTEST_CHECK([
RPMDB_INIT
--
2.47.0

View File

@ -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 <mdomonko@redhat.com>
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

22
macros.rpmsign-gnupg Normal file
View File

@ -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}}}

23
macros.rpmsign-sequoia Normal file
View File

@ -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}}}

59
rpm-4.19.1.1-nogpg.patch Normal file
View File

@ -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.
#

View File

@ -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 <pmatilai@redhat.com> - 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 <mdomonko@redhat.com> - 4.19.1.1-3
- Fix potential use of uninitialized pipe array (RHEL-54012)
- Fix potential use of uninitialized pgp struct (RHEL-54013)