[bug#77667] guix-install.sh: Check fingerprint of downloaded PGP keys before importing
Commit Message
The prior "patch" I sent was in fact a diff; this one is formatted with
`git format-patch`. Content is almost exactly the same as the prior diff.
From 03bacc6e90063a3d75365354f037dbb943b1a98e Mon Sep 17 00:00:00 2001
From: user <>
Date: Mon, 5 May 2025 16:29:31 -0700
Subject: [PATCH] guix-install.sh: Check fingerprint of downloaded PGP keys
before importing
---
etc/guix-install.sh | 64 ++++++++++++++++++++++++++++++++++++++-------
1 file changed, 55 insertions(+), 9 deletions(-)
@@ -105,6 +105,12 @@ declare -A GPG_SIGNING_KEYS
GPG_SIGNING_KEYS[15145]=3CE464558A84FDC69DB40CFB090B11993D9AEBB5 # ludo
GPG_SIGNING_KEYS[127547]=27D586A4F8900854329FF09F1260E46482E63562 # maxim
+# TEST_FAIL_FETCH_KEY=no
+# if [ "${TEST_FAIL_FETCH_KEY}" = 'yes' ]; then
+# GPG_SIGNING_KEYS[15145]=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+# GPG_SIGNING_KEYS[127547]=BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
+# fi
+
# ------------------------------------------------------------------------------
#+UTILITIES
@@ -189,12 +195,49 @@ add_init_sys_require()
fi
}
+get_gpg_fpr_of_keyfile()
+{
+ local keyfile="$1"
+ local tmp_dir
+ tmp_dir="$(mktemp -t -d guix.gnupg.tmphomedir.XXXXXX)"
+ gpg -qq --homedir "$tmp_dir" --dry-run --no-keyring \
+ --with-colons --show-keys "$keyfile" \
+ | grep '^pub:' -A 1 | tail -n 1 \
+ | grep -oE '^([^:]*:){10}' | grep -oE '[^:]*:$' | tr -d ':'
+ # awk -F: '/^pub:/ { getline; print $10 }'
+
+ # Explanation: Get the line immediately after the line that starts
+ # with `pub:`. Then get the 10th field from that line, with colons as
+ # field separators.
+}
+
+# Check that a keyfile has expected fingerprint and import it to GPG keyring
+# only if so. If keyfile path is given as '-', it reads from STDIN. Returns 0
+# or 1 depending in its outcome, so it can be used in condition tests.
+import_key()
+{
+ local keyfile="$1"
+ local fpr="$2"
+ local tmp_dir
+
+ [ "$keyfile" = "-" ] && keyfile=/dev/stdin
+ tmp_dir="$(mktemp -t -d guix.gpg.XXXXXX)"
+ cp "$keyfile" "$tmp_dir/key.gpg"
+ # Copying to a temp dir ensures no unprivileged process can tamper
+ # with it between the time we check the fingerprint and gpg-import it.
+ if [ "$fpr" = "$(get_gpg_fpr_of_keyfile "$tmp_dir/key.gpg")" ]; then
+ gpg --import "$tmp_dir/key.gpg" && return 0
+ fi
+ return 1
+}
+
chk_gpg_keyring()
{ # Check whether the Guix release signing public key is present.
_debug "--- [ ${FUNCNAME[0]} ] ---"
local user_id
local gpg_key_id
local exit_flag
+ local tmp_dir
for user_id in "${!GPG_SIGNING_KEYS[@]}"; do
gpg_key_id=${GPG_SIGNING_KEYS[$user_id]}
@@ -209,17 +252,19 @@ Would you like me to fetch it for you?"; then
# Use a reasonable time-out here so users don't report silent
# ‘freezes’ when Savannah goes out to lunch, as has happened.
if wget "https://sv.gnu.org/people/viewgpg.php?user_id=$user_id" \
- --timeout=30 --no-verbose -O- | gpg --import -; then
+ --timeout=30 --no-verbose -O - | import_key - "$gpg_key_id"; then
continue
fi
fi
- # If we reach this point, the key is (still) missing. Report further
- # missing keys, if any, but then abort the installation.
+ # If we reach this point, the key is (still) missing. Report further
+ # missing keys, if any, but then abort the installation.
_err "Missing OpenPGP public key ($gpg_key_id).
Fetch it with this command:
- wget \"https://sv.gnu.org/people/viewgpg.php?user_id=$user_id\" -O - | \
-sudo -i gpg --import -"
+wget 'https://sv.gnu.org/people/viewgpg.php?user_id=${user_id}' -O - | \
+sudo sh $0 import_key - '$gpg_key_id'
+
+"
exit_flag=yes
done
if [ "$exit_flag" = yes ]; then
@@ -1030,14 +1075,15 @@ main_uninstall()
main()
{
- # expect no parameters
- # or '--uninstall'
if [ 0 -eq $# ]; then
main_install
else
- local uninstall_flag="$1"
- if [ '--uninstall' = "${uninstall_flag}" ]; then
+ local _flag="$1"
+ if [ "${_flag}" = '--uninstall' ]; then
main_uninstall
+ elif [ "${_flag}" = "import_key" ]; then
+ import_key "$2" "$3" && exit 0
+ exit 1
else
echo "unsupported parameters: $*"
exit 1
--
2.45.0