Browse Source

apt-key: warn instead of fail on unreadable keyrings

apt-key has inconsistent behaviour if it can't read a keyring file:
Commands like 'list' skipped silently over such keyrings while 'verify'
failed hard resulting in apt to report cconfusing gpg errors (#834973).

As a first step we teach apt-key to be more consistent here skipping in
all commands over unreadable keyrings, but issuing a warning in the
process, which is as usual for apt commands displayed at the end of the
run.
David Kalnischkies 10 years ago
parent
commit
105503b4b4
2 changed files with 55 additions and 26 deletions
  1. 31 26
      cmdline/apt-key.in
  2. 24 0
      test/integration/test-apt-key

+ 31 - 26
cmdline/apt-key.in

@@ -232,6 +232,17 @@ remove_key_from_keyring() {
     done
 }
 
+accessible_file_exists() {
+   if ! test -s "$1"; then
+      return 1
+   fi
+   if test -r "$1"; then
+      return 0
+   fi
+   warn "The key(s) in the keyring $1 are ignored as the file is not readable by user '$USER' executing apt-key."
+   return 1
+}
+
 foreach_keyring_do() {
    local ACTION="$1"
    shift
@@ -240,7 +251,7 @@ foreach_keyring_do() {
 	$ACTION "$FORCED_KEYRING" "$@"
    else
 	# otherwise all known keyrings are up for inspection
-	if [ -s "$TRUSTEDFILE" ]; then
+	if accessible_file_exists "$TRUSTEDFILE"; then
 	    $ACTION "$TRUSTEDFILE" "$@"
 	fi
 	local TRUSTEDPARTS="/etc/apt/trusted.gpg.d"
@@ -249,7 +260,7 @@ foreach_keyring_do() {
 	    TRUSTEDPARTS="$(readlink -f "$TRUSTEDPARTS")"
 	    local TRUSTEDPARTSLIST="$(cd /; find "$TRUSTEDPARTS" -mindepth 1 -maxdepth 1 -name '*.gpg')"
 	    for trusted in $(echo "$TRUSTEDPARTSLIST" | sort); do
-		if [ -s "$trusted" ]; then
+		if accessible_file_exists "$trusted"; then
 		    $ACTION "$trusted" "$@"
 		fi
 	    done
@@ -302,35 +313,18 @@ import_keyring_into_keyring() {
     fi
 }
 
+catfile() {
+   cat "$1" >> "$2"
+}
+
 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="$(readlink -f "${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
-	    rm -f "$PUBRING"
-	    if [ -s "$TRUSTEDFILE" ]; then
-		cat "$TRUSTEDFILE" > "$PUBRING"
-	    fi
-	    TRUSTEDPARTS="$(readlink -f "$TRUSTEDPARTS")"
-	    (cd /; find "$TRUSTEDPARTS" -mindepth 1 -maxdepth 1 -name '*.gpg' -exec cat {} + >> "$PUBRING";)
-	elif [ -s "$TRUSTEDFILE" ]; then
-	    cp --dereference "$TRUSTEDFILE" "$PUBRING"
-	fi
-    fi
-
-    if [ ! -s "$PUBRING" ]; then
-	touch "$PUBRING"
-    fi
+    rm -f "$PUBRING"
+    touch "$PUBRING"
+    foreach_keyring_do 'catfile' "$PUBRING"
 }
 
 import_keys_from_keyring() {
@@ -480,8 +474,19 @@ if [ -z "$command" ]; then
 fi
 shift
 
+warn() {
+    if [ -z "$GPGHOMEDIR" ]; then
+	echo >&2 'W:' "$@"
+    else
+	echo 'W:' "$@" > "${GPGHOMEDIR}/aptwarnings.log"
+    fi
+}
+
 cleanup_gpg_home() {
     if [ -z "$GPGHOMEDIR" ]; then return; fi
+    if [ -s "$GPGHOMEDIR/aptwarnings.log" ]; then
+	cat >&2 "$GPGHOMEDIR/aptwarnings.log"
+    fi
     if command_available 'gpgconf'; then
 	GNUPGHOME="${GPGHOMEDIR}" gpgconf --kill gpg-agent >/dev/null 2>&1 || true
     fi

+ 24 - 0
test/integration/test-apt-key

@@ -81,6 +81,20 @@ gpg:              unchanged: 1' aptkey --fakeroot update
 	testsuccess --nomsg aptkey --fakeroot del d141dbac8dae
 	testempty aptkey list
 
+	if [ "$(id -u)" != '0' ]; then
+		msgtest 'Test key removal with' 'unreadable key'
+		cleanplate
+		cp -a "${KEYDIR}/joesixpack.pub" "${ROOTDIR}/etc/apt/trusted.gpg.d/joesixpack.gpg"
+		echo 'foobar' > "${ROOTDIR}/etc/apt/trusted.gpg.d/unreadablekey.gpg"
+		chmod 000 "${ROOTDIR}/etc/apt/trusted.gpg.d/unreadablekey.gpg"
+		testwarning --nomsg aptkey --fakeroot del d141dbac8dae
+		testwarning aptkey list
+		chmod 644 "${ROOTDIR}/etc/apt/trusted.gpg.d/unreadablekey.gpg"
+		rm -f "${ROOTDIR}/etc/apt/trusted.gpg.d/unreadablekey.gpg"
+		grep -v '^W: ' "${ROOTDIR}/tmp/testwarning.output" > "${ROOTDIR}/aptkeylist.output" || true
+		testempty cat "${ROOTDIR}/aptkeylist.output"
+	fi
+
 	msgtest 'Test key removal with' 'single key in real file'
 	cleanplate
 	cp -a "${KEYDIR}/joesixpack.pub" "${ROOTDIR}/etc/apt/trusted.gpg.d/joesixpack.gpg"
@@ -202,6 +216,16 @@ gpg:              unchanged: 1' aptkey --fakeroot update
 		msgtest 'Test verify a file' 'with all keys'
 		testsuccess --nomsg aptkey --quiet --readonly verify "${SIGNATURE}.gpg" "${SIGNATURE}"
 
+		if [ "$(id -u)" != '0' ]; then
+			msgtest 'Test verify a file' 'with unreadable key'
+			echo 'foobar' > "${ROOTDIR}/etc/apt/trusted.gpg.d/unreadablekey.gpg"
+			chmod 000 "${ROOTDIR}/etc/apt/trusted.gpg.d/unreadablekey.gpg"
+			testwarning --nomsg aptkey --quiet --readonly verify "${SIGNATURE}.gpg" "${SIGNATURE}"
+			testwarning aptkey list
+			chmod 644 "${ROOTDIR}/etc/apt/trusted.gpg.d/unreadablekey.gpg"
+			rm -f "${ROOTDIR}/etc/apt/trusted.gpg.d/unreadablekey.gpg"
+		fi
+
 		msgtest 'Test verify a file' 'with good keyring'
 		testsuccess --nomsg aptkey --quiet --readonly --keyring "${KEYDIR}/testcase-multikey.pub" verify "${SIGNATURE}.gpg" "${SIGNATURE}"