cherry-pick ubuntus (disabled) net-update fixes
authorDavid Kalnischkies <kalnischkies@gmail.com>
Sat, 30 Nov 2013 22:07:20 +0000 (23:07 +0100)
committerDavid Kalnischkies <kalnischkies@gmail.com>
Sun, 1 Dec 2013 14:51:33 +0000 (15:51 +0100)
With the net-update command a special keyring can be downloaded and
imported into apt, which must be signed by a master key. Its is
currently disabled because of security problems with it – and the only
known user before that was Ubuntu.

cmdline/apt-key
test/integration/exploid-keyring-with-dupe-keys.pub [new file with mode: 0644]
test/integration/exploid-keyring-with-dupe-subkeys.pub [new file with mode: 0644]
test/integration/test-apt-key-net-update [new file with mode: 0755]

index 713a41c..64cf5a6 100755 (executable)
@@ -25,17 +25,15 @@ GPG_CMD="$GPG_CMD --no-auto-check-trustdb --trust-model always"
 
 GPG="$GPG_CMD"
 
-MASTER_KEYRING=""
-#MASTER_KEYRING=/usr/share/keyrings/debian-master-keyring.gpg
+MASTER_KEYRING=''
 eval $(apt-config shell MASTER_KEYRING APT::Key::MasterKeyring)
-ARCHIVE_KEYRING_URI=""
-#ARCHIVE_KEYRING_URI=http://ftp.debian.org/debian/debian-archive-keyring.gpg
-eval $(apt-config shell ARCHIVE_KEYRING_URI APT::Key::ArchiveKeyringURI)
-
-ARCHIVE_KEYRING=/usr/share/keyrings/debian-archive-keyring.gpg
+ARCHIVE_KEYRING='/usr/share/keyrings/debian-archive-keyring.gpg'
 eval $(apt-config shell ARCHIVE_KEYRING APT::Key::ArchiveKeyring)
-REMOVED_KEYS=/usr/share/keyrings/debian-archive-removed-keys.gpg
+REMOVED_KEYS='/usr/share/keyrings/debian-archive-removed-keys.gpg'
 eval $(apt-config shell REMOVED_KEYS APT::Key::RemovedKeys)
+ARCHIVE_KEYRING_URI=''
+eval $(apt-config shell ARCHIVE_KEYRING_URI APT::Key::ArchiveKeyringURI)
+TMP_KEYRING=/var/lib/apt/keyrings/maybe-import-keyring.gpg
 
 requires_root() {
        if [ "$(id -u)" -ne 0 ]; then
@@ -57,7 +55,7 @@ init_keyring() {
 add_keys_with_verify_against_master_keyring() {
     ADD_KEYRING=$1
     MASTER=$2
-    
+
     if [ ! -f "$ADD_KEYRING" ]; then
        echo "ERROR: '$ADD_KEYRING' not found"
        return
@@ -72,12 +70,28 @@ add_keys_with_verify_against_master_keyring() {
     #   all keys that are exported must have a valid signature
     #   from a key in the $distro-master-keyring
     add_keys=`$GPG_CMD --keyring $ADD_KEYRING --with-colons --list-keys | grep ^pub | cut -d: -f5`
+    all_add_keys=`$GPG_CMD --keyring $ADD_KEYRING --with-colons --list-keys | grep ^[ps]ub | cut -d: -f5`
     master_keys=`$GPG_CMD --keyring $MASTER --with-colons --list-keys | grep ^pub | cut -d: -f5`
+
+    # ensure there are no colisions LP: #857472
+    for all_add_key in $all_add_keys; do
+       for master_key in $master_keys; do
+            if [ "$all_add_key" = "$master_key" ]; then
+                echo >&2 "Keyid collision for '$all_add_key' detected, operation aborted"
+                return 1
+            fi
+        done
+    done
+    
     for add_key in $add_keys; do
-       ADDED=0
+        # export the add keyring one-by-one
+        rm -f $TMP_KEYRING
+        $GPG_CMD --keyring $ADD_KEYRING --output $TMP_KEYRING --export $add_key 
+        # check if signed with the master key and only add in this case
+        ADDED=0
        for master_key in $master_keys; do
-           if $GPG_CMD --keyring $ADD_KEYRING --list-sigs --with-colons $add_key | grep ^sig | cut -d: -f5 | grep -q $master_key; then
-               $GPG_CMD --quiet --batch --keyring $ADD_KEYRING --export $add_key | $GPG --import
+           if $GPG_CMD --keyring $MASTER --keyring $TMP_KEYRING --check-sigs --with-colons $add_key | grep '^sig:!:' | cut -d: -f5 | grep -q $master_key; then
+               $GPG --import $TMP_KEYRING
                ADDED=1
            fi
        done
@@ -85,12 +99,16 @@ add_keys_with_verify_against_master_keyring() {
            echo >&2 "Key '$add_key' not added. It is not signed with a master key"
        fi
     done
+    rm -f $TMP_KEYRING
 }
 
 # update the current archive signing keyring from a network URI
 # the archive-keyring keys needs to be signed with the master key
 # (otherwise it does not make sense from a security POV)
 net_update() {
+    # Disabled for now as code is insecure (LP: #1013639 (and 857472, 1013128))
+    exit 1
+
     if [ -z "$ARCHIVE_KEYRING_URI" ]; then
        echo >&2 "ERROR: Your distribution is not supported in net-update as no uri for the archive-keyring is set"
        exit 1
@@ -110,7 +128,7 @@ net_update() {
     if [ -e $keyring ]; then
        old_mtime=$(stat -c %Y $keyring)
     fi
-    (cd  /var/lib/apt/keyrings; wget -q -N $ARCHIVE_KEYRING_URI)
+    (cd  /var/lib/apt/keyrings; wget --timeout=90 -q -N $ARCHIVE_KEYRING_URI)
     if [ ! -e $keyring ]; then
        return
     fi
diff --git a/test/integration/exploid-keyring-with-dupe-keys.pub b/test/integration/exploid-keyring-with-dupe-keys.pub
new file mode 100644 (file)
index 0000000..642952a
Binary files /dev/null and b/test/integration/exploid-keyring-with-dupe-keys.pub differ
diff --git a/test/integration/exploid-keyring-with-dupe-subkeys.pub b/test/integration/exploid-keyring-with-dupe-subkeys.pub
new file mode 100644 (file)
index 0000000..02d4e6e
Binary files /dev/null and b/test/integration/exploid-keyring-with-dupe-subkeys.pub differ
diff --git a/test/integration/test-apt-key-net-update b/test/integration/test-apt-key-net-update
new file mode 100755 (executable)
index 0000000..d520583
--- /dev/null
@@ -0,0 +1,95 @@
+#!/bin/sh
+set -e
+
+TESTDIR=$(readlink -f $(dirname $0))
+. $TESTDIR/framework
+
+setupenvironment
+configarchitecture "i386"
+
+# mock
+requires_root() {
+    return 0
+}
+
+# extract net_update() and import it
+func=$( sed -n -e '/^add_keys_with_verify_against_master_keyring/,/^}/p' ${BUILDDIRECTORY}/apt-key )
+eval "$func"
+
+mkdir -p ./etc/apt
+TRUSTEDFILE=./etc/apt/trusted.gpg
+mkdir -p ./var/lib/apt/keyrings
+TMP_KEYRING=./var/lib/apt/keyrings/maybe-import-keyring.gpg
+GPG_CMD="gpg --ignore-time-conflict --no-options --no-default-keyring"
+GPG="$GPG_CMD --keyring $TRUSTEDFILE"
+MASTER_KEYRING=/usr/share/keyrings/ubuntu-master-keyring.gpg
+
+
+msgtest "add_keys_with_verify_against_master_keyring"
+if [ ! -e $MASTER_KEYRING ]; then
+    echo -n "No $MASTER_KEYRING found"
+    msgskip 
+    exit 0
+fi
+
+# test bad keyring and ensure its not added (LP: #857472)
+ADD_KEYRING=./keys/exploid-keyring-with-dupe-keys.pub
+if add_keys_with_verify_against_master_keyring $ADD_KEYRING $MASTER_KEYRING; then
+    msgfail
+else
+    msgpass
+fi
+
+# ensure the keyring is still empty
+gpg_out=$($GPG --list-keys)
+msgtest "Test if keyring is empty"
+if [ -n "" ]; then
+    msgfail
+else
+    msgpass
+fi
+
+
+# test another possible attack vector using subkeys (LP: #1013128)
+msgtest "add_keys_with_verify_against_master_keyring with subkey attack"
+ADD_KEYRING=./keys/exploid-keyring-with-dupe-subkeys.pub
+if add_keys_with_verify_against_master_keyring $ADD_KEYRING $MASTER_KEYRING; then
+    msgfail
+else
+    msgpass
+fi
+
+# ensure the keyring is still empty
+gpg_out=$($GPG --list-keys)
+msgtest "Test if keyring is empty"
+if [ -n "" ]; then
+    msgfail
+else
+    msgpass
+fi
+
+
+# test good keyring and ensure we get no errors
+ADD_KEYRING=/usr/share/keyrings/ubuntu-archive-keyring.gpg
+if add_keys_with_verify_against_master_keyring $ADD_KEYRING $MASTER_KEYRING; then
+    msgpass
+else
+    msgfail
+fi
+
+testequal './etc/apt/trusted.gpg
+---------------------
+pub   1024D/437D05B5 2004-09-12
+uid                  Ubuntu Archive Automatic Signing Key <ftpmaster@ubuntu.com>
+sub   2048g/79164387 2004-09-12
+
+pub   1024D/FBB75451 2004-12-30
+uid                  Ubuntu CD Image Automatic Signing Key <cdimage@ubuntu.com>
+
+pub   4096R/C0B21F32 2012-05-11
+uid                  Ubuntu Archive Automatic Signing Key (2012) <ftpmaster@ubuntu.com>
+
+pub   4096R/EFE21092 2012-05-11
+uid                  Ubuntu CD Image Automatic Signing Key (2012) <cdimage@ubuntu.com>
+' $GPG --list-keys
+