summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Kalnischkies <david@kalnischkies.de>2015-07-07 11:46:39 +0200
committerDavid Kalnischkies <david@kalnischkies.de>2015-08-10 17:25:26 +0200
commit25f2731928f0b571f7521d7d7a7e301499d0f6ee (patch)
tree62be13a7e3b3835dc76479438825a4b84e4729b8
parentf14cde2ca702f72415486bf5c310208a7c500e9c (diff)
merge keyrings with cat instead of gpg in apt-key
If all keyrings are simple keyrings we can merge the keyrings with cat rather than doing a detour over gpg --export | --import (see #790665), which means 'apt-key verify' can do without gpg and just use gpgv as before the merging change. We declare this gpgv usage explicit now in the dependencies. This isn't a new dependency as gnupg as well as debian-archive-keyring depend on and we used it before unconditionally, just that we didn't declare it. The handling of the merged keyring needs to be slightly different as our merged keyring can end up containing the same key multiple times, but at least currently gpg does remove only the first occurrence with --delete-keys, so we move the handling to a if one is gone, all are gone rather than an (implicit) quid pro quo or even no effect. Thanks: Daniel Kahn Gillmor for the suggestion
-rw-r--r--cmdline/apt-key.in128
-rw-r--r--debian/control2
-rwxr-xr-xtest/integration/test-apt-key2
3 files changed, 88 insertions, 44 deletions
diff --git a/cmdline/apt-key.in b/cmdline/apt-key.in
index b15f71f6d..881f8a990 100644
--- a/cmdline/apt-key.in
+++ b/cmdline/apt-key.in
@@ -34,7 +34,8 @@ get_fingerprints_of_keyring() {
elif [ "${fprline%%:*}" != 'fpr' ]; then continue; fi
echo "$fprline" | cut -d':' -f 10
done
- done
+ # order in the keyring shouldn't be important
+ done | sort
}
add_keys_with_verify_against_master_keyring() {
@@ -167,8 +168,10 @@ remove_key_from_keyring() {
local GPG="$GPG_CMD --keyring $KEYRINGFILE"
for KEY in "$@"; do
- # check if the key is in this keyring: the key id is in the 5 column at the end
- if ! get_fingerprints_of_keyring "$KEYRINGFILE" | grep -iq "^[0-9A-F]*${KEY}$"; then
+ local FINGERPRINTS="${GPGHOMEDIR}/keyringfile.keylst"
+ get_fingerprints_of_keyring "$KEYRINGFILE" > "$FINGERPRINTS"
+ # check if the key is in this keyring
+ if ! grep -iq "^[0-9A-F]*${KEY}$" "$FINGERPRINTS"; then
continue
fi
if [ ! -w "$KEYRINGFILE" ]; then
@@ -176,7 +179,7 @@ remove_key_from_keyring() {
continue
fi
# check if it is the only key in the keyring and if so remove the keyring altogether
- if [ '1' = "$(get_fingerprints_of_keyring "$KEYRINGFILE" | wc -l)" ]; then
+ if [ '1' = "$(uniq "$FINGERPRINTS" | wc -l)" ]; then
mv -f "$KEYRINGFILE" "${KEYRINGFILE}~" # behave like gpg
return
fi
@@ -188,7 +191,7 @@ remove_key_from_keyring() {
cp -a "$REALTARGET" "$KEYRINGFILE"
fi
# delete the key from the keyring
- $GPG --batch --delete-key --yes "$KEY"
+ $GPG --batch --delete-keys --yes "$KEY"
if [ -n "$REALTARGET" ]; then
# the real backup is the old link, not the copy we made
mv -f "${KEYRINGFILE}.dpkg-tmp" "${KEYRINGFILE}~"
@@ -268,6 +271,33 @@ import_keyring_into_keyring() {
fi
}
+merge_all_trusted_keyrings_into_pubring() {
+ # does the same as:
+ # foreach_keyring_do 'import_keys_from_keyring' "${GPGHOMEDIR}/pubring.gpg"
+ # but without using gpg, just cat and find
+ local PUBRING="${GPGHOMEDIR}/pubring.gpg"
+ # if a --keyring was given, just use this one
+ if [ -n "$FORCED_KEYRING" ]; then
+ if [ -s "$FORCED_KEYRING" ]; then
+ cp --dereference "$FORCED_KEYRING" "$PUBRING"
+ fi
+ else
+ # otherwise all known keyrings are merged
+ local TRUSTEDPARTS="/etc/apt/trusted.gpg.d"
+ eval $(apt-config shell TRUSTEDPARTS Dir::Etc::TrustedParts/d)
+ if [ -d "$TRUSTEDPARTS" ]; then
+ # ignore errors mostly for non-existing $TRUSTEDFILE
+ cat /dev/null "$TRUSTEDFILE" $(find -L "$TRUSTEDPARTS" -type f -name '*.gpg') > "$PUBRING" 2>/dev/null || true
+ elif [ -s "$TRUSTEDFILE" ]; then
+ cp --dereference "$TRUSTEDFILE" "$PUBRING"
+ fi
+ fi
+
+ if [ ! -s "$PUBRING" ]; then
+ touch "$PUBRING"
+ fi
+}
+
import_keys_from_keyring() {
import_keyring_into_keyring "$1" "$2"
}
@@ -288,29 +318,29 @@ merge_back_changes() {
# look for keys which were added or removed
get_fingerprints_of_keyring "${GPGHOMEDIR}/pubring.orig.gpg" > "${GPGHOMEDIR}/pubring.orig.keylst"
get_fingerprints_of_keyring "${GPGHOMEDIR}/pubring.gpg" > "${GPGHOMEDIR}/pubring.keylst"
- sort "${GPGHOMEDIR}/pubring.keylst" "${GPGHOMEDIR}/pubring.orig.keylst" | uniq --unique | while read key; do
- if grep -q "^${key}$" "${GPGHOMEDIR}/pubring.orig.keylst"; then
- # key isn't part of new keyring, so remove
- foreach_keyring_do 'remove_key_from_keyring' "$key"
- elif grep -q "^${key}$" "${GPGHOMEDIR}/pubring.keylst"; then
- # key is part of new keyring, so we need to import it
- import_keyring_into_keyring '' "$TRUSTEDFILE" "$key"
- else
- echo >&2 "Errror: Key ${key} (dis)appeared out of nowhere"
- fi
+ comm -3 "${GPGHOMEDIR}/pubring.keylst" "${GPGHOMEDIR}/pubring.orig.keylst" > "${GPGHOMEDIR}/pubring.diff"
+ # key isn't part of new keyring, so remove
+ cut -f 2 "${GPGHOMEDIR}/pubring.diff" | while read key; do
+ if [ -z "$key" ]; then continue; fi
+ foreach_keyring_do 'remove_key_from_keyring' "$key"
+ done
+ # key is only part of new keyring, so we need to import it
+ cut -f 1 "${GPGHOMEDIR}/pubring.diff" | while read key; do
+ if [ -z "$key" ]; then continue; fi
+ import_keyring_into_keyring '' "$TRUSTEDFILE" "$key"
done
}
setup_merged_keyring() {
if [ -n "$FORCED_KEYID" ]; then
- foreach_keyring_do 'import_keys_from_keyring' "${GPGHOMEDIR}/pubring.gpg"
+ merge_all_trusted_keyrings_into_pubring
FORCED_KEYRING="${GPGHOMEDIR}/forcedkeyid.gpg"
TRUSTEDFILE="${FORCED_KEYRING}"
GPG="$GPG --keyring $TRUSTEDFILE"
# ignore error as this "just" means we haven't found the forced keyid and the keyring will be empty
import_keyring_into_keyring '' "$TRUSTEDFILE" "$FORCED_KEYID" || true
elif [ -z "$FORCED_KEYRING" ]; then
- foreach_keyring_do 'import_keys_from_keyring' "${GPGHOMEDIR}/pubring.gpg"
+ merge_all_trusted_keyrings_into_pubring
if [ -r "${GPGHOMEDIR}/pubring.gpg" ]; then
cp -a "${GPGHOMEDIR}/pubring.gpg" "${GPGHOMEDIR}/pubring.orig.gpg"
else
@@ -407,7 +437,23 @@ if [ -z "$command" ]; then
fi
shift
-if [ "$command" != "help" ]; then
+create_gpg_home() {
+ # gpg needs (in different versions more or less) files to function correctly,
+ # so we give it its own homedir and generate some valid content for it later on
+ if [ -n "$TMPDIR" ]; then
+ # tmpdir is a directory and current user has rwx access to it
+ # same tests as in apt-pkg/contrib/fileutl.cc GetTempDir()
+ if [ ! -d "$TMPDIR" ] || [ ! -r "$TMPDIR" ] || [ ! -w "$TMPDIR" ] || [ ! -x "$TMPDIR" ]; then
+ unset TMPDIR
+ fi
+ fi
+ GPGHOMEDIR="$(mktemp -d)"
+ CURRENTTRAP="${CURRENTTRAP} rm -rf '${GPGHOMEDIR}';"
+ trap "${CURRENTTRAP}" 0 HUP INT QUIT ILL ABRT FPE SEGV PIPE TERM
+ chmod 700 "$GPGHOMEDIR"
+}
+
+prepare_gpg_home() {
eval $(apt-config shell GPG_EXE Apt::Key::gpgcommand)
if [ -n "$GPG_EXE" ] && which "$GPG_EXE" >/dev/null 2>&1; then
@@ -418,7 +464,7 @@ if [ "$command" != "help" ]; then
GPG_EXE="gpg2"
else
echo >&2 "Error: gnupg or gnupg2 do not seem to be installed,"
- echo >&2 "Error: but apt-key requires gnupg or gnupg2 for operation."
+ echo >&2 "Error: but apt-key requires gnupg or gnupg2 for this operation."
echo >&2
exit 255
fi
@@ -428,19 +474,8 @@ if [ "$command" != "help" ]; then
fi
GPG_CMD="$GPG_EXE --ignore-time-conflict --no-options --no-default-keyring"
- # gpg needs (in different versions more or less) files to function correctly,
- # so we give it its own homedir and generate some valid content for it
- if [ -n "$TMPDIR" ]; then
- # tmpdir is a directory and current user has rwx access to it
- # same tests as in apt-pkg/contrib/fileutl.cc GetTempDir()
- if [ ! -d "$TMPDIR" ] || [ ! -r "$TMPDIR" ] || [ ! -w "$TMPDIR" ] || [ ! -x "$TMPDIR" ]; then
- unset TMPDIR
- fi
- fi
- GPGHOMEDIR="$(mktemp -d)"
- CURRENTTRAP="${CURRENTTRAP} rm -rf '${GPGHOMEDIR}';"
- trap "${CURRENTTRAP}" 0 HUP INT QUIT ILL ABRT FPE SEGV PIPE TERM
- chmod 700 "$GPGHOMEDIR"
+ create_gpg_home
+
# We don't use a secret keyring, of course, but gpg panics and
# implodes if there isn't one available - and writeable for imports
SECRETKEYRING="${GPGHOMEDIR}/secring.gpg"
@@ -466,6 +501,10 @@ if [ "$command" != "help" ]; then
# anyhow, so nothing actually happens, but its three lines of output
# nobody expects to see in apt-key context, so trigger it in silence
echo -n | $GPG --batch --import >/dev/null 2>&1 || true
+}
+
+if [ "$command" != 'help' ] && [ "$command" != 'verify' ]; then
+ prepare_gpg_home
fi
case "$command" in
@@ -500,7 +539,7 @@ case "$command" in
foreach_keyring_do 'run_cmd_on_keyring' --fingerprint "$@"
;;
export|exportall)
- foreach_keyring_do 'import_keys_from_keyring' "${GPGHOMEDIR}/pubring.gpg"
+ merge_all_trusted_keyrings_into_pubring
$GPG_CMD --keyring "${GPGHOMEDIR}/pubring.gpg" --armor --export "$@"
;;
adv*)
@@ -510,21 +549,26 @@ case "$command" in
merge_back_changes
;;
verify)
- setup_merged_keyring
GPGV=''
eval $(apt-config shell GPGV Apt::Key::gpgvcommand)
- if [ -n "$GPGV" ] && ! which "$GPGV" >/dev/null 2>&1; then GPGV='';
+ if [ -n "$GPGV" ] && which "$GPGV" >/dev/null 2>&1; then true;
elif which gpgv >/dev/null 2>&1; then GPGV='gpgv';
elif which gpgv2 >/dev/null 2>&1; then GPGV='gpgv2';
+ else
+ echo >&2 'ERROR: gpgv or gpgv2 required for verification'
+ exit 29
fi
- if [ -n "$GPGV" ]; then
- if [ -n "$FORCED_KEYRING" ]; then
- $GPGV --homedir "${GPGHOMEDIR}" --keyring "${FORCED_KEYRING}" --ignore-time-conflict "$@"
- else
- $GPGV --homedir "${GPGHOMEDIR}" --keyring "${GPGHOMEDIR}/pubring.gpg" --ignore-time-conflict "$@"
- fi
+ # for a forced keyid we need gpg --export, so full wrapping required
+ if [ -n "$FORCED_KEYID" ]; then
+ prepare_gpg_home
+ else
+ create_gpg_home
+ fi
+ setup_merged_keyring
+ if [ -n "$FORCED_KEYRING" ]; then
+ $GPGV --homedir "${GPGHOMEDIR}" --keyring "${FORCED_KEYRING}" --ignore-time-conflict "$@"
else
- $GPG --verify "$@"
+ $GPGV --homedir "${GPGHOMEDIR}" --keyring "${GPGHOMEDIR}/pubring.gpg" --ignore-time-conflict "$@"
fi
;;
help)
diff --git a/debian/control b/debian/control
index e71cb5103..7e2db7373 100644
--- a/debian/control
+++ b/debian/control
@@ -18,7 +18,7 @@ XS-Testsuite: autopkgtest
Package: apt
Architecture: any
-Depends: ${shlibs:Depends}, ${misc:Depends}, ${apt:keyring}, gnupg | gnupg2, adduser
+Depends: ${shlibs:Depends}, ${misc:Depends}, ${apt:keyring}, gpgv | gpgv2, gnupg | gnupg2, adduser
Replaces: manpages-pl (<< 20060617-3~), manpages-it (<< 2.80-4~), sun-java6-jdk (>> 0), sun-java5-jdk (>> 0), openjdk-6-jdk (<< 6b24-1.11-0ubuntu1~)
Breaks: manpages-pl (<< 20060617-3~), manpages-it (<< 2.80-4~), sun-java6-jdk (>> 0), sun-java5-jdk (>> 0), openjdk-6-jdk (<< 6b24-1.11-0ubuntu1~)
Conflicts: python-apt (<< 0.7.93.2~)
diff --git a/test/integration/test-apt-key b/test/integration/test-apt-key
index 4dbf3d66d..1226e7dc4 100755
--- a/test/integration/test-apt-key
+++ b/test/integration/test-apt-key
@@ -188,7 +188,7 @@ gpg: unchanged: 1' aptkey --fakeroot update
adv --batch --yes --default-key 'Marvin' --armor --detach-sign --sign --output signature.gpg signature
- for GPGV in 'gpgv' 'gpgv2' '/does/not/exist'; do
+ for GPGV in '' 'gpgv' 'gpgv2'; do
echo "APT::Key::GPGVCommand \"$GPGV\";" > rootdir/etc/apt/apt.conf.d/00gpgvcmd
msgtest 'Test verify a file' 'with all keys'