1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286
|
#!/bin/sh
#
# ensure we never fallback from a signed to a unsigned repo
#
# hash checks are done in
#
set -e
simulate_mitm_and_inject_evil_package()
{
redatereleasefiles '+1 hour'
rm -f "$APTARCHIVE/dists/unstable/InRelease" "$APTARCHIVE/dists/unstable/Release.gpg"
inject_evil_package
}
inject_evil_package()
{
cat > "$APTARCHIVE/dists/unstable/main/binary-i386/Packages" <<EOF
Package: evil
Installed-Size: 29
Maintainer: Joe Sixpack <joe@example.org>
Architecture: all
Version: 1.0
Filename: pool/evil_1.0_all.deb
Size: 1270
Description: an autogenerated evil package
EOF
# avoid ims hit
touch -d '+1hour' aptarchive/dists/unstable/main/binary-i386/Packages
compressfile aptarchive/dists/unstable/main/binary-i386/Packages
}
assert_update_is_refused_and_last_good_state_used()
{
testfailuremsg "E: The repository 'file:${APTARCHIVE} unstable Release' is no longer signed.
N: Updating from such a repository can't be done securely, and is therefore disabled by default.
N: See apt-secure(8) manpage for repository creation and user configuration details." aptget update
assert_repo_is_intact
}
assert_repo_is_intact()
{
testsuccessequal 'foo/unstable 2.0 all' apt list -qq
testsuccess aptget install -y -s foo
testfailure aptget install -y evil
testsuccess aptget source foo --print-uris
LISTDIR=rootdir/var/lib/apt/lists
testempty find "$LISTDIR" -name 'InRelease' -o -name 'Release.gpg'
}
setupaptarchive_with_lists_clean()
{
setupaptarchive --no-update
rm -rf rootdir/var/lib/apt/lists
}
test_from_inrelease_to_unsigned()
{
export APT_DONT_SIGN='Release.gpg'
setupaptarchive_with_lists_clean
testsuccess aptget update
listcurrentlistsdirectory > lists.before
simulate_mitm_and_inject_evil_package
assert_update_is_refused_and_last_good_state_used
testfileequal lists.before "$(listcurrentlistsdirectory)"
}
test_from_release_gpg_to_unsigned()
{
export APT_DONT_SIGN='InRelease'
setupaptarchive_with_lists_clean
testsuccess aptget update
listcurrentlistsdirectory > lists.before
simulate_mitm_and_inject_evil_package
assert_update_is_refused_and_last_good_state_used
testfileequal lists.before "$(listcurrentlistsdirectory)"
}
test_from_inrelease_to_unsigned_with_override()
{
export APT_DONT_SIGN='Release.gpg'
# setup archive with InRelease file
setupaptarchive_with_lists_clean
testsuccess aptget update
# simulate moving to a unsigned but otherwise valid repo
simulate_mitm_and_inject_evil_package
generatereleasefiles '+2 hours'
find "$APTARCHIVE" -name '*Packages*' -exec touch -d '+2 hours' {} \;
# and ensure we can update to it (with enough force)
testfailure apt update
testfailure aptget update
testfailure aptget update --allow-insecure-repositories
testfailure aptget update --no-allow-insecure-repositories
sed -i 's#^deb\(-src\)\? #deb\1 [allow-downgrade-to-insecure=yes] #' rootdir/etc/apt/sources.list.d/*
testfailure aptget update --no-allow-insecure-repositories
testfailure apt update
testwarning apt update --allow-insecure-repositories \
-o Debug::pkgAcquire::Worker=1 -o Debug::pkgAcquire::Auth=1
sed -i 's#^deb\(-src\)\? \[allow-downgrade-to-insecure=yes\] #deb\1 #' rootdir/etc/apt/sources.list.d/*
# but that the individual packages are still considered untrusted
testfailureequal "WARNING: The following packages cannot be authenticated!
evil
E: There were unauthenticated packages and -y was used without --allow-unauthenticated" aptget install -qq -y evil
}
test_from_inrelease_to_norelease_with_override()
{
# setup archive with InRelease file
setupaptarchive_with_lists_clean
testsuccess aptget update
# simulate moving to a unsigned but otherwise valid repo
simulate_mitm_and_inject_evil_package
find "$APTARCHIVE" -name '*Release*' -delete
find "$APTARCHIVE" -name '*Packages*' -exec touch -d '+2 hours' {} \;
# and ensure we can update to it (with enough force)
testfailure aptget update
testfailure aptget update --allow-insecure-repositories
testwarning aptget update --allow-insecure-repositories \
-o Acquire::AllowDowngradeToInsecureRepositories=1 -o Debug::pkgAcquire::Worker=1 -o Debug::pkgAcquire::Auth=1
# but that the individual packages are still considered untrusted
testfailureequal "WARNING: The following packages cannot be authenticated!
evil
E: There were unauthenticated packages and -y was used without --allow-unauthenticated" aptget install -qq -y evil
}
test_cve_2012_0214()
{
# see https://bugs.launchpad.net/ubuntu/+source/apt/+bug/947108
#
# it was possible to MITM the download so that InRelease/Release.gpg
# are not delivered (404) and a altered Release file was send
#
# apt left the old InRelease file in /var/lib/apt/lists and downloaded
# the unauthenticated Release file too giving the false impression that
# Release was authenticated
#
# Note that this is pretty much impossible nowadays because:
# a) InRelease is left as is, not split to InRelease/Release as it was
# in the old days
# b) we refuse to go from signed->unsigned
#
# Still worth having a regression test the simulates the condition
export APT_DONT_SIGN='Release.gpg'
setupaptarchive_with_lists_clean
testsuccess aptget update
listcurrentlistsdirectory > lists.before
# do what CVE-2012-0214 did
rm -f "$APTARCHIVE/dists/unstable/InRelease" "$APTARCHIVE/dists/unstable/Release.gpg"
inject_evil_package
# build valid Release file
aptftparchive -qq release ./aptarchive > aptarchive/dists/unstable/Release
assert_update_is_refused_and_last_good_state_used
testfileequal lists.before "$(listcurrentlistsdirectory)"
# ensure there is no _Release file downloaded
testfailure ls rootdir/var/lib/apt/lists/*_Release
}
test_subvert_inrelease()
{
export APT_DONT_SIGN='Release.gpg'
setupaptarchive_with_lists_clean
testsuccess aptget update
listcurrentlistsdirectory > lists.before
# replace InRelease with something else
mv "$APTARCHIVE/dists/unstable/Release" "$APTARCHIVE/dists/unstable/InRelease"
testfailuremsg "E: Failed to fetch file:${APTARCHIVE}/dists/unstable/InRelease Clearsigned file isn't valid, got 'NOSPLIT' (does the network require authentication?)
E: The repository 'file:${APTARCHIVE} unstable InRelease' is no longer signed.
N: Updating from such a repository can't be done securely, and is therefore disabled by default.
N: See apt-secure(8) manpage for repository creation and user configuration details." aptget update
# ensure we keep the repo
testfileequal lists.before "$(listcurrentlistsdirectory)"
assert_repo_is_intact
}
test_inrelease_to_invalid_inrelease()
{
export APT_DONT_SIGN='Release.gpg'
setupaptarchive_with_lists_clean
testsuccess aptget update
listcurrentlistsdirectory > lists.before
# now remove InRelease and subvert Release do no longer verify
sed -i 's/^Codename:.*/Codename: evil!/' "$APTARCHIVE/dists/unstable/InRelease"
inject_evil_package
if test -e "${METHODSDIR}/sqv"; then
# FIXME: Do not assert sqv output
testwarningequal "W: An error occurred during the signature verification. The repository is not updated and the previous index files will be used. OpenPGP signature verification failed: file:${APTARCHIVE} unstable InRelease: Sub-process /usr/bin/sqv returned an error code (1), error message is: Verifying signature: Message has been manipulated
W: Failed to fetch file:${APTARCHIVE}/dists/unstable/InRelease Sub-process /usr/bin/sqv returned an error code (1), error message is: Verifying signature: Message has been manipulated
W: Some index files failed to download. They have been ignored, or old ones used instead." aptget update -qq
else
testwarningequal "W: An error occurred during the signature verification. The repository is not updated and the previous index files will be used. OpenPGP signature verification failed: file:${APTARCHIVE} unstable InRelease: The following signatures were invalid: BADSIG 5A90D141DBAC8DAE Joe Sixpack (APT Testcases Dummy) <joe@example.org>
W: Failed to fetch file:${APTARCHIVE}/dists/unstable/InRelease The following signatures were invalid: BADSIG 5A90D141DBAC8DAE Joe Sixpack (APT Testcases Dummy) <joe@example.org>
W: Some index files failed to download. They have been ignored, or old ones used instead." aptget update -qq
fi
# ensure we keep the repo
testfailure grep ': evil' rootdir/var/lib/apt/lists/*InRelease
testfileequal lists.before "$(listcurrentlistsdirectory)"
assert_repo_is_intact
}
test_release_gpg_to_invalid_release_release_gpg()
{
export APT_DONT_SIGN='InRelease'
setupaptarchive_with_lists_clean
testsuccess aptget update
listcurrentlistsdirectory > lists.before
# now subvert Release do no longer verify
echo "Some evil data" >> "$APTARCHIVE/dists/unstable/Release"
inject_evil_package
if test -e "${METHODSDIR}/sqv"; then
# FIXME: Do not assert sqv output
testwarningequal "W: An error occurred during the signature verification. The repository is not updated and the previous index files will be used. OpenPGP signature verification failed: file:${APTARCHIVE} unstable Release: Sub-process /usr/bin/sqv returned an error code (1), error message is: Verifying signature: Message has been manipulated
W: Failed to fetch file:${APTARCHIVE}/dists/unstable/Release.gpg Sub-process /usr/bin/sqv returned an error code (1), error message is: Verifying signature: Message has been manipulated
W: Some index files failed to download. They have been ignored, or old ones used instead." aptget update -qq
else
testwarningequal "W: An error occurred during the signature verification. The repository is not updated and the previous index files will be used. OpenPGP signature verification failed: file:${APTARCHIVE} unstable Release: The following signatures were invalid: BADSIG 5A90D141DBAC8DAE Joe Sixpack (APT Testcases Dummy) <joe@example.org>
W: Failed to fetch file:${APTARCHIVE}/dists/unstable/Release.gpg The following signatures were invalid: BADSIG 5A90D141DBAC8DAE Joe Sixpack (APT Testcases Dummy) <joe@example.org>
W: Some index files failed to download. They have been ignored, or old ones used instead." aptget update -qq
fi
testfailure grep 'evil data' rootdir/var/lib/apt/lists/*Release
testfileequal lists.before "$(listcurrentlistsdirectory)"
assert_repo_is_intact
}
TESTDIR="$(readlink -f "$(dirname "$0")")"
. "$TESTDIR/framework"
setupenvironment
configarchitecture "i386"
# a "normal" package with source and binary
buildsimplenativepackage 'foo' 'all' '2.0'
# setup the archive and ensure we have a single package that installs fine
setupaptarchive
APTARCHIVE="$(readlink -f ./aptarchive)"
assert_repo_is_intact
# test the various cases where a repo may go from signed->unsigned
msgmsg "test_from_inrelease_to_unsigned"
test_from_inrelease_to_unsigned
msgmsg "test_from_release_gpg_to_unsigned"
test_from_release_gpg_to_unsigned
# ensure we do not regress on CVE-2012-0214
msgmsg "test_cve_2012_0214"
test_cve_2012_0214
# ensure InRelease can not be subverted
msgmsg "test_subvert_inrelease"
test_subvert_inrelease
# ensure we revert to last good state if InRelease does not verify
msgmsg "test_inrelease_to_invalid_inrelease"
test_inrelease_to_invalid_inrelease
# ensure we revert to last good state if Release/Release.gpg does not verify
msgmsg "test_release_gpg_to_invalid_release_release_gpg"
test_release_gpg_to_invalid_release_release_gpg
# ensure we can override the downgrade error
msgmsg "test_from_inrelease_to_unsigned_with_override"
test_from_inrelease_to_unsigned_with_override
msgmsg "test_from_inrelease_to_norelease_with_override"
test_from_inrelease_to_norelease_with_override
|