Просмотр исходного кода

support gpg 2.1.x in apt-key

The output of gpg slightly changes in 2.1 which breaks the testcase, but
the real problem is that this branch introduces a new default keyring
format (which is called keybox) and mixing it with simple keyrings (the
previous default format) has various problems like failing in the keybox
to keyring import (#790665) or [older] gpgv versions not being able to
deal with keyboxes (and newer versions as well currently:
https://bugs.gnupg.org/gnupg/issue2025).

We fix this by being a bit more careful in who creates keyrings (aka: we
do it or we take a simple keyring as base) to ensure we always have a
keyring instead of a keybox. This way we can ensure that any version
combination of gpv/gpgv2 and gnupg/gnupg2 without doing explicit version
checks and use the same code for all of them.

Closes: 781042
David Kalnischkies лет назад: 11
Родитель
Сommit
f14cde2ca7
2 измененных файлов с 124 добавлено и 76 удалено
  1. 60 25
      cmdline/apt-key.in
  2. 64 51
      test/integration/test-apt-key

+ 60 - 25
cmdline/apt-key.in

@@ -145,7 +145,7 @@ update() {
     # attacker might as well replace the master-archive-keyring file
     # in the package and add his own keys. so this check wouldn't
     # add any security. we *need* this check on net-update though
-    $GPG_CMD --quiet --batch --keyring $ARCHIVE_KEYRING --export | $GPG --import
+    import_keyring_into_keyring "$ARCHIVE_KEYRING" '' && cat "${GPGHOMEDIR}/gpgoutput.log"
 
     if [ -r "$REMOVED_KEYS" ]; then
 	# remove no-longer supported/used keys
@@ -231,22 +231,49 @@ run_cmd_on_keyring() {
     $GPG_CMD --keyring "$KEYRINGFILE" --batch "$@" 2>/dev/null || true
 }
 
-import_keys_from_keyring() {
-    local IMPORT="$1"
-    local KEYRINGFILE="$2"
-    if ! $GPG_CMD --keyring "$KEYRINGFILE" --batch --import "$IMPORT" > "${GPGHOMEDIR}/gpgoutput.log" 2>&1; then
-	cat >&2 "${GPGHOMEDIR}/gpgoutput.log"
-	false
+import_keyring_into_keyring() {
+    local FROM="${1:-${GPGHOMEDIR}/pubring.gpg}"
+    local TO="${2:-${GPGHOMEDIR}/pubring.gpg}"
+    shift 2
+    rm -f "${GPGHOMEDIR}/gpgoutput.log"
+    # the idea is simple: We take keys from one keyring and copy it to another
+    # we do this with so many checks inbetween to ensure that WE control the
+    # creation, so we know that the (potentially) created $TO keyring is a
+    # simple keyring rather than a keybox as gpg2 would create it which in turn
+    # can't be read by gpgv.
+    # BEWARE: This is designed more in the way to work with the current
+    # callers, than to have a well defined it would be easy to add new callers to.
+    if [ ! -s "$TO" ]; then
+	if [ -s "$FROM" ]; then
+	    if [ -z "$2" ]; then
+		if ! $GPG_CMD --keyring "$FROM" --export ${1:+"$1"} > "$TO" 2> "${GPGHOMEDIR}/gpgoutput.log"; then
+		    cat >&2 "${GPGHOMEDIR}/gpgoutput.log"
+		    false
+		else
+		    chmod 0644 -- "$TO"
+		fi
+	    else
+		create_new_keyring "$TO"
+	    fi
+	else
+	    create_new_keyring "$TO"
+	fi
+    elif [ -s "$FROM" ]; then
+	local EXPORTLIMIT="$1"
+	if [ -n "$1$2" ]; then shift; fi
+	if ! $GPG_CMD --keyring "$FROM" --export ${EXPORTLIMIT:+"$EXPORTLIMIT"} | $GPG_CMD --keyring "$TO" --batch --import "$@" > "${GPGHOMEDIR}/gpgoutput.log" 2>&1; then
+	    cat >&2 "${GPGHOMEDIR}/gpgoutput.log"
+	    false
+	fi
     fi
 }
 
+import_keys_from_keyring() {
+    import_keyring_into_keyring "$1" "$2"
+}
+
 merge_keys_into_keyrings() {
-    local KEYRINGFILE="$1"
-    local IMPORT="$2"
-    if ! $GPG_CMD --keyring "$KEYRINGFILE" --batch --import --import-options 'merge-only' "$IMPORT" > "${GPGHOMEDIR}/gpgoutput.log" 2>&1; then
-	cat >&2 "${GPGHOMEDIR}/gpgoutput.log"
-	false
-    fi
+    import_keyring_into_keyring "$2" "$1" '' --import-options 'merge-only'
 }
 
 merge_back_changes() {
@@ -267,11 +294,7 @@ merge_back_changes() {
 	    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
-	    create_new_keyring "$TRUSTEDFILE"
-	    if ! $GPG --batch --yes --export "$key" | $GPG_CMD --keyring "$TRUSTEDFILE" --batch --yes --import > "${GPGHOMEDIR}/gpgoutput.log" 2>&1; then
-	       cat >&2 "${GPGHOMEDIR}/gpgoutput.log"
-	       false
-	    fi
+	    import_keyring_into_keyring '' "$TRUSTEDFILE" "$key"
 	else
 	    echo >&2 "Errror: Key ${key} (dis)appeared out of nowhere"
 	fi
@@ -280,12 +303,12 @@ merge_back_changes() {
 
 setup_merged_keyring() {
     if [ -n "$FORCED_KEYID" ]; then
-	foreach_keyring_do 'import_keys_from_keyring' "${GPGHOMEDIR}/allrings.gpg"
+	foreach_keyring_do 'import_keys_from_keyring' "${GPGHOMEDIR}/pubring.gpg"
 	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
-	$GPG_CMD --batch --yes --keyring "${GPGHOMEDIR}/allrings.gpg" --export "$FORCED_KEYID" | $GPG --batch --yes --import || true
+	import_keyring_into_keyring '' "$TRUSTEDFILE" "$FORCED_KEYID" || true
     elif [ -z "$FORCED_KEYRING" ]; then
 	foreach_keyring_do 'import_keys_from_keyring' "${GPGHOMEDIR}/pubring.gpg"
 	if [ -r "${GPGHOMEDIR}/pubring.gpg" ]; then
@@ -295,8 +318,8 @@ setup_merged_keyring() {
 	fi
 	GPG="$GPG --keyring ${GPGHOMEDIR}/pubring.gpg"
     else
-	GPG="$GPG --keyring $TRUSTEDFILE"
 	create_new_keyring "$TRUSTEDFILE"
+	GPG="$GPG --keyring $TRUSTEDFILE"
     fi
 }
 
@@ -345,7 +368,7 @@ while [ -n "$1" ]; do
 	 ;;
       --readonly)
 	 merge_back_changes() { true; }
-	 create_new_keyring() { true; }
+	 create_new_keyring() { if [ ! -r $FORCED_KEYRING ]; then TRUSTEDFILE='/dev/null'; FORCED_KEYRING="$TRUSTEDFILE"; fi; }
 	 ;;
       --fakeroot)
 	 requires_root() { true; }
@@ -437,6 +460,12 @@ if [ "$command" != "help" ]; then
 	rm -f "$SECRETKEYRING"
 	cp -a "$FORCED_SECRET_KEYRING" "$SECRETKEYRING"
     fi
+
+    # older gpg versions need a secring file, but newer versions take it as
+    # a hint to start a migration from earlier versions. The file is empty
+    # 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
 fi
 
 case "$command" in
@@ -482,11 +511,17 @@ case "$command" in
 	;;
     verify)
 	setup_merged_keyring
-	if which gpgv >/dev/null 2>&1; then
+	GPGV=''
+	eval $(apt-config shell GPGV Apt::Key::gpgvcommand)
+	if [ -n "$GPGV" ] && ! which "$GPGV" >/dev/null 2>&1; then GPGV='';
+	elif which gpgv >/dev/null 2>&1; then GPGV='gpgv';
+	elif which gpgv2 >/dev/null 2>&1; then GPGV='gpgv2';
+	fi
+	if [ -n "$GPGV" ]; then
 	    if [ -n "$FORCED_KEYRING" ]; then
-		gpgv --homedir "${GPGHOMEDIR}" --keyring "${FORCED_KEYRING}" --ignore-time-conflict "$@"
+		$GPGV --homedir "${GPGHOMEDIR}" --keyring "${FORCED_KEYRING}" --ignore-time-conflict "$@"
 	    else
-		gpgv --homedir "${GPGHOMEDIR}" --keyring "${GPGHOMEDIR}/pubring.gpg" --ignore-time-conflict "$@"
+		$GPGV --homedir "${GPGHOMEDIR}" --keyring "${GPGHOMEDIR}/pubring.gpg" --ignore-time-conflict "$@"
 	    fi
 	else
 	    $GPG --verify "$@"

+ 64 - 51
test/integration/test-apt-key

@@ -13,11 +13,33 @@ cleanplate() {
 	mkdir rootdir/etc/apt/trusted.gpg.d/
 }
 
+createlistofkeys() {
+	while [ -n "$1" ]; do
+		# gpg 2.1 has a slightly different output format
+		if grep -q ' rsa2048/' aptkey.list; then
+			case "$1" in
+				*Joe*|*Sixpack*) echo 'pub   rsa2048/DBAC8DAE 2010-08-18';;
+				*Rex*|*Expired*) echo 'pub   rsa2048/27CE74F9 2013-07-12 [expired: 2013-07-13]';;
+				*Marvin*|*Paranoid*) echo 'pub   rsa2048/528144E2 2011-01-16';;
+				*) echo 'UNKNOWN KEY';;
+			esac
+		else
+			case "$1" in
+				*Joe*|*Sixpack*) echo 'pub   2048R/DBAC8DAE 2010-08-18';;
+				*Rex*|*Expired*) echo 'pub   2048R/27CE74F9 2013-07-12 [expired: 2013-07-13]';;
+				*Marvin*|*Paranoid*) echo 'pub   2048R/528144E2 2011-01-16';;
+				*) echo 'UNKNOWN KEY';;
+			esac
+		fi
+		shift
+	done
+}
+
 testaptkeys() {
 	if ! aptkey list | grep '^pub' > aptkey.list; then
 		echo -n > aptkey.list
 	fi
-	testfileequal './aptkey.list' "$1"
+	testfileequal './aptkey.list' "$(createlistofkeys "$@")"
 }
 
 echo 'APT::Key::ArchiveKeyring "./keys/joesixpack.pub";
@@ -32,16 +54,15 @@ testrun() {
 
 	msgtest 'Check that paths in finger output are not' 'double-slashed'
 	aptkey finger 2>&1 | grep -q '//' && msgfail || msgpass
-
-	testaptkeys 'pub   2048R/DBAC8DAE 2010-08-18'
+	testaptkeys 'Joe Sixpack'
 
 	testsuccessequal 'gpg: key DBAC8DAE: "Joe Sixpack (APT Testcases Dummy) <joe@example.org>" not changed
 gpg: Total number processed: 1
 gpg:              unchanged: 1' aptkey --fakeroot update
 
-	testaptkeys 'pub   2048R/DBAC8DAE 2010-08-18'
-
+	testaptkeys 'Joe Sixpack'
 	testfailure test -e rootdir/etc/apt/trusted.gpg
+
 	testsuccess aptkey --fakeroot add ./keys/rexexpired.pub
 	msgtest 'Check if trusted.gpg is created with permissions set to' '0644'
 	if [ "$(stat -c '%a' rootdir/etc/apt/trusted.gpg )" = '644' ]; then
@@ -50,8 +71,7 @@ gpg:              unchanged: 1' aptkey --fakeroot update
 		msgfail
 	fi
 
-	testaptkeys 'pub   2048R/27CE74F9 2013-07-12 [expired: 2013-07-13]
-pub   2048R/DBAC8DAE 2010-08-18'
+	testaptkeys 'Rex Expired' 'Joe Sixpack'
 
 	msgtest 'Check that Sixpack key can be' 'exported'
 	aptkey export 'Sixpack' > aptkey.export
@@ -63,12 +83,12 @@ pub   2048R/DBAC8DAE 2010-08-18'
 	msgtest 'Execute update again to trigger removal of' 'Rex Expired key'
 	testsuccess --nomsg aptkey --fakeroot update
 
-	testaptkeys 'pub   2048R/DBAC8DAE 2010-08-18'
+	testaptkeys 'Joe Sixpack'
 
 	msgtest "Try to remove a key which exists, but isn't in the" 'forced keyring'
 	testsuccess --nomsg aptkey --fakeroot --keyring rootdir/etc/apt/trusted.gpg del DBAC8DAE
 
-	testaptkeys 'pub   2048R/DBAC8DAE 2010-08-18'
+	testaptkeys 'Joe Sixpack'
 
 	testsuccess aptkey --fakeroot del DBAC8DAE
 	testempty aptkey list
@@ -114,22 +134,21 @@ pub   2048R/DBAC8DAE 2010-08-18'
 	cleanplate
 	testsuccess aptkey --fakeroot add ./keys/joesixpack.pub
 	testsuccess aptkey --fakeroot add ./keys/marvinparanoid.pub
-	testaptkeys 'pub   2048R/DBAC8DAE 2010-08-18
-pub   2048R/528144E2 2011-01-16'
+	testaptkeys 'Joe Sixpack' 'Marvin Paranoid'
 	cp -a rootdir/etc/apt/trusted.gpg keys/testcase-multikey.pub # store for reuse
 
 	msgtest 'Test key removal with' 'multi key in real file'
 	cleanplate
 	cp -a keys/testcase-multikey.pub rootdir/etc/apt/trusted.gpg.d/multikey.gpg
 	testsuccess --nomsg aptkey --fakeroot del DBAC8DAE
-	testaptkeys 'pub   2048R/528144E2 2011-01-16'
+	testaptkeys 'Marvin Paranoid'
 	testsuccess cmp keys/testcase-multikey.pub rootdir/etc/apt/trusted.gpg.d/multikey.gpg~
 
 	msgtest 'Test key removal with' 'multi key in softlink'
 	cleanplate
 	ln -s $(readlink -f ./keys/testcase-multikey.pub) rootdir/etc/apt/trusted.gpg.d/multikey.gpg
 	testsuccess --nomsg aptkey --fakeroot del DBAC8DAE
-	testaptkeys 'pub   2048R/528144E2 2011-01-16'
+	testaptkeys 'Marvin Paranoid'
 	testsuccess cmp keys/testcase-multikey.pub rootdir/etc/apt/trusted.gpg.d/multikey.gpg~
 	testfailure test -L rootdir/etc/apt/trusted.gpg.d/multikey.gpg
 	testsuccess test -L rootdir/etc/apt/trusted.gpg.d/multikey.gpg~
@@ -139,7 +158,7 @@ pub   2048R/528144E2 2011-01-16'
 	cp -a keys/joesixpack.pub rootdir/etc/apt/trusted.gpg.d/joesixpack.gpg
 	cp -a keys/testcase-multikey.pub rootdir/etc/apt/trusted.gpg.d/multikey.gpg
 	testsuccess --nomsg aptkey --fakeroot del DBAC8DAE
-	testaptkeys 'pub   2048R/528144E2 2011-01-16'
+	testaptkeys 'Marvin Paranoid'
 	testfailure test -e rootdir/etc/apt/trusted.gpg.d/joesixpack.gpg
 	testsuccess cmp keys/joesixpack.pub rootdir/etc/apt/trusted.gpg.d/joesixpack.gpg~
 	testsuccess cmp keys/testcase-multikey.pub rootdir/etc/apt/trusted.gpg.d/multikey.gpg~
@@ -147,25 +166,18 @@ pub   2048R/528144E2 2011-01-16'
 	cleanplate
 	cp -a keys/joesixpack.pub rootdir/etc/apt/trusted.gpg.d/joesixpack.gpg
 	cp -a keys/testcase-multikey.pub rootdir/etc/apt/trusted.gpg.d/multikey.gpg
-	testaptkeys 'pub   2048R/DBAC8DAE 2010-08-18
-pub   2048R/DBAC8DAE 2010-08-18
-pub   2048R/528144E2 2011-01-16'
+	testaptkeys 'Joe Sixpack' 'Joe Sixpack' 'Marvin Paranoid'
 	msgtest 'Test merge-back of' 'added keys'
 	testsuccess --nomsg aptkey adv --batch --yes --import keys/rexexpired.pub
-	testaptkeys 'pub   2048R/27CE74F9 2013-07-12 [expired: 2013-07-13]
-pub   2048R/DBAC8DAE 2010-08-18
-pub   2048R/DBAC8DAE 2010-08-18
-pub   2048R/528144E2 2011-01-16'
+	testaptkeys 'Rex Expired' 'Joe Sixpack' 'Joe Sixpack' 'Marvin Paranoid'
 
 	msgtest 'Test merge-back of' 'removed keys'
 	testsuccess --nomsg aptkey adv --batch --yes --delete-keys 27CE74F9
-	testaptkeys 'pub   2048R/DBAC8DAE 2010-08-18
-pub   2048R/DBAC8DAE 2010-08-18
-pub   2048R/528144E2 2011-01-16'
+	testaptkeys 'Joe Sixpack' 'Joe Sixpack' 'Marvin Paranoid'
 
 	msgtest 'Test merge-back of' 'removed duplicate keys'
 	testsuccess --nomsg aptkey adv --batch --yes --delete-keys DBAC8DAE
-	testaptkeys 'pub   2048R/528144E2 2011-01-16'
+	testaptkeys 'Marvin Paranoid'
 
 	cleanplate
 	cp -a keys/joesixpack.pub rootdir/etc/apt/trusted.gpg.d/joesixpack.gpg
@@ -175,43 +187,44 @@ pub   2048R/528144E2 2011-01-16'
 	testsuccess --nomsg aptkey --quiet --keyring keys/marvinparanoid.pub --secret-keyring keys/marvinparanoid.sec --readonly \
 		adv --batch --yes --default-key 'Marvin' --armor --detach-sign --sign --output signature.gpg signature
 
-	msgtest 'Test verify a file' 'with all keys'
-	testsuccess --nomsg aptkey --quiet --readonly verify signature.gpg signature
 
-	msgtest 'Test verify a file' 'with good keyring'
-	testsuccess --nomsg aptkey --quiet --readonly --keyring keys/testcase-multikey.pub verify signature.gpg signature
+	for GPGV in 'gpgv' 'gpgv2' '/does/not/exist'; do
+		echo "APT::Key::GPGVCommand \"$GPGV\";" > rootdir/etc/apt/apt.conf.d/00gpgvcmd
+
+		msgtest 'Test verify a file' 'with all keys'
+		testsuccess --nomsg aptkey --quiet --readonly verify signature.gpg signature
 
-	msgtest 'Test fail verify a file' 'with bad keyring'
-	testfailure --nomsg aptkey --quiet --readonly --keyring keys/joesixpack.pub verify signature.gpg signature
+		msgtest 'Test verify a file' 'with good keyring'
+		testsuccess --nomsg aptkey --quiet --readonly --keyring keys/testcase-multikey.pub verify signature.gpg signature
 
-	msgtest 'Test fail verify a file' 'with non-existing keyring'
-	testfailure --nomsg aptkey --quiet --readonly --keyring keys/does-not-exist.pub verify signature.gpg signature
-	testfailure test -e keys/does-not-exist.pub
+		msgtest 'Test fail verify a file' 'with bad keyring'
+		testfailure --nomsg aptkey --quiet --readonly --keyring keys/joesixpack.pub verify signature.gpg signature
 
-	msgtest 'Test verify a file' 'with good keyid'
-	testsuccess --nomsg aptkey --quiet --readonly --keyid 'Paranoid' verify signature.gpg signature
+		msgtest 'Test fail verify a file' 'with non-existing keyring'
+		testfailure --nomsg aptkey --quiet --readonly --keyring keys/does-not-exist.pub verify signature.gpg signature
+		testfailure test -e keys/does-not-exist.pub
 
-	msgtest 'Test fail verify a file' 'with bad keyid'
-	testfailure --nomsg aptkey --quiet --readonly --keyid 'Sixpack' verify signature.gpg signature
+		msgtest 'Test verify a file' 'with good keyid'
+		testsuccess --nomsg aptkey --quiet --readonly --keyid 'Paranoid' verify signature.gpg signature
 
-	msgtest 'Test fail verify a file' 'with non-existing keyid'
-	testfailure --nomsg aptkey --quiet --readonly --keyid 'Kalnischkies' verify signature.gpg signature
+		msgtest 'Test fail verify a file' 'with bad keyid'
+		testfailure --nomsg aptkey --quiet --readonly --keyid 'Sixpack' verify signature.gpg signature
 
-	msgtest 'Test verify fails on' 'bad file'
-	echo 'lalalalala' > signature
-	testfailure --nomsg aptkey --quiet --readonly verify signature.gpg signature
+		msgtest 'Test fail verify a file' 'with non-existing keyid'
+		testfailure --nomsg aptkey --quiet --readonly --keyid 'Kalnischkies' verify signature.gpg signature
+
+		msgtest 'Test verify fails on' 'bad file'
+		echo 'lalalalala' > signature2
+		testfailure --nomsg aptkey --quiet --readonly verify signature.gpg signature2
+	done
 }
 
 setupgpgcommand() {
 	echo "APT::Key::GPGCommand \"$1\";" > rootdir/etc/apt/apt.conf.d/00gpgcmd
-	msgtest 'Test that apt-key uses for the following tests command' "$1"
-	aptkey adv --version >aptkey.version 2>&1
-	if grep -q "^Executing: $1 --" aptkey.version; then
-		msgpass
-	else
-		cat aptkey.version
-		msgfail
-	fi
+	msgmsg 'Force tests to be run with' "$1"
+	testsuccess aptkey --readonly adv --version
+	cp rootdir/tmp/testsuccess.output aptkey.version
+	testsuccess grep "^Executing: $1 --" aptkey.version
 }
 
 # run with default (whatever this is)