Browse Source

promote filesize to a hashstring

It is a very simple hashstring, which is why it isn't contributing to
the usability of a list of them, but it is also trivial to check and
calculate, so it doesn't hurt checking it either as it can combined even
with the simplest other hashes greatly complicate attacks on them as you
suddenly need a same-size hash collision, which is usually a lot harder
to achieve.
tags/debian/1.1.exp8
David Kalnischkies 6 years ago
parent
commit
23397c9d7d
10 changed files with 166 additions and 16 deletions
  1. +27
    -5
      apt-pkg/contrib/hashes.cc
  2. +2
    -2
      apt-pkg/contrib/hashes.h
  3. +3
    -0
      apt-pkg/indexrecords.cc
  4. +4
    -0
      ftparchive/cachedb.cc
  5. +3
    -1
      ftparchive/writer.cc
  6. +55
    -0
      test/integration/test-apt-update-filesize-mismatch
  7. +49
    -0
      test/integration/test-apt-update-hashsum-mismatch
  8. +1
    -1
      test/integration/test-apt-update-ims
  9. +3
    -1
      test/interactive-helper/aptwebserver.cc
  10. +19
    -6
      test/libapt/hashsums_test.cc

+ 27
- 5
apt-pkg/contrib/hashes.cc View File

@@ -29,7 +29,7 @@

const char * HashString::_SupportedHashes[] =
{
"SHA512", "SHA256", "SHA1", "MD5Sum", NULL
"SHA512", "SHA256", "SHA1", "MD5Sum", "Checksum-FileSize", NULL
};

HashString::HashString()
@@ -111,6 +111,8 @@ std::string HashString::GetHashForFile(std::string filename) const /*{{{*/
SHA512.AddFD(Fd);
fileHash = (std::string)SHA512.Result();
}
else if (strcasecmp(Type.c_str(), "Checksum-FileSize") == 0)
strprintf(fileHash, "%llu", Fd.FileSize());
Fd.Close();

return fileHash;
@@ -147,7 +149,13 @@ bool HashStringList::usable() const /*{{{*/
return false;
std::string const forcedType = _config->Find("Acquire::ForceHash", "");
if (forcedType.empty() == true)
return true;
{
// FileSize alone isn't usable
for (std::vector<HashString>::const_iterator hs = list.begin(); hs != list.end(); ++hs)
if (hs->HashType() != "Checksum-FileSize")
return true;
return false;
}
return find(forcedType) != NULL;
}
/*}}}*/
@@ -201,6 +209,9 @@ bool HashStringList::VerifyFile(std::string filename) const /*{{{*/
HashString const * const hs = find(NULL);
if (hs == NULL || hs->VerifyFile(filename) == false)
return false;
HashString const * const hsf = find("Checksum-FileSize");
if (hsf != NULL && hsf->VerifyFile(filename) == false)
return false;
return true;
}
/*}}}*/
@@ -235,6 +246,14 @@ bool HashStringList::operator!=(HashStringList const &other) const
}
/*}}}*/

// PrivateHashes /*{{{*/
class PrivateHashes {
public:
unsigned long long FileSize;

PrivateHashes() : FileSize(0) {}
};
/*}}}*/
// Hashes::Add* - Add the contents of data or FD /*{{{*/
bool Hashes::Add(const unsigned char * const Data,unsigned long long const Size, unsigned int const Hashes)
{
@@ -254,6 +273,7 @@ bool Hashes::Add(const unsigned char * const Data,unsigned long long const Size,
#if __GNUC__ >= 4
#pragma GCC diagnostic pop
#endif
d->FileSize += Size;
return Res;
}
bool Hashes::AddFD(int const Fd,unsigned long long Size, unsigned int const Hashes)
@@ -314,15 +334,17 @@ HashStringList Hashes::GetHashStringList()
#if __GNUC__ >= 4
#pragma GCC diagnostic pop
#endif
std::string SizeStr;
strprintf(SizeStr, "%llu", d->FileSize);
hashes.push_back(HashString("Checksum-FileSize", SizeStr));
return hashes;
}
#if __GNUC__ >= 4
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#pragma GCC diagnostic ignored "-Wsuggest-attribute=const"
#endif
Hashes::Hashes() {}
Hashes::~Hashes() {}
Hashes::Hashes() { d = new PrivateHashes(); }
Hashes::~Hashes() { delete d; }
#if __GNUC__ >= 4
#pragma GCC diagnostic pop
#endif

+ 2
- 2
apt-pkg/contrib/hashes.h View File

@@ -161,10 +161,10 @@ class HashStringList
std::vector<HashString> list;
};

class PrivateHashes;
class Hashes
{
/** \brief dpointer placeholder */
void *d;
PrivateHashes *d;

public:
/* those will disappear in the future as it is hard to add new ones this way.


+ 3
- 0
apt-pkg/indexrecords.cc View File

@@ -116,6 +116,9 @@ bool indexRecords::Load(const string Filename) /*{{{*/
indexRecords::checkSum *Sum = new indexRecords::checkSum;
Sum->MetaKeyFilename = Name;
Sum->Size = Size;
std::string SizeStr;
strprintf(SizeStr, "%llu", Size);
Sum->Hashes.push_back(HashString("Checksum-FileSize", SizeStr));
#if __GNUC__ >= 4
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"


+ 4
- 0
ftparchive/cachedb.cc View File

@@ -473,6 +473,10 @@ bool CacheDB::GetHashes(bool const GenOnly, unsigned int const DoHashes)
hex2bytes(CurStat.MD5, hs->HashValue().data(), sizeof(CurStat.MD5));
CurStat.Flags |= FlMD5;
}
else if (strcasecmp(hs->HashType().c_str(), "Checksum-FileSize") == 0)
{
// we store it in a different field already
}
else
return _error->Error("Got unknown unrequested hashtype %s", hs->HashType().c_str());
}


+ 3
- 1
ftparchive/writer.cc View File

@@ -471,6 +471,8 @@ bool PackagesWriter::DoPackage(string FileName)
{
if (hs->HashType() == "MD5Sum")
Changes.push_back(SetTFRewriteData("MD5sum", hs->HashValue().c_str()));
else if (hs->HashType() == "Checksum-FileSize")
continue;
else
Changes.push_back(SetTFRewriteData(hs->HashType().c_str(), hs->HashValue().c_str()));
}
@@ -785,7 +787,7 @@ bool SourcesWriter::DoPackage(string FileName)

for (HashStringList::const_iterator hs = Db.HashesList.begin(); hs != Db.HashesList.end(); ++hs)
{
if (hs->HashType() == "MD5Sum")
if (hs->HashType() == "MD5Sum" || hs->HashType() == "Checksum-FileSize")
continue;
char const * fieldname;
std::ostream * out;


+ 55
- 0
test/integration/test-apt-update-filesize-mismatch View File

@@ -0,0 +1,55 @@
#!/bin/sh
set -e

TESTDIR=$(readlink -f $(dirname $0))
. $TESTDIR/framework
setupenvironment
configarchitecture 'i386'
configcompression 'gz'

insertpackage 'testing' 'foo' 'all' '1'
insertpackage 'testing' 'foo2' 'all' '1'
insertsource 'testing' 'foo' 'all' '1'
insertsource 'testing' 'foo2' 'all' '1'

setupaptarchive --no-update
changetowebserver

find aptarchive \( -name 'Packages' -o -name 'Sources' -o -name 'Translation-en' \) -delete
for release in $(find aptarchive -name 'Release'); do
cp "$release" "${release}.backup"
done

testsuccess aptget update
testsuccess aptcache show foo
testsuccess aptget install foo -s

for get in $(sed -n 's#^GET /\([^ ]\+\.gz\) HTTP.\+$#\1#p' aptarchive/webserver.log); do
for ext in '' '.gz'; do
COMPRESSFILE="$get"
get="${get}${ext}"
FILE="$(basename -s '.gz' "$get")"
msgmsg 'Test filesize mismatch with file' "$FILE"
rm -rf rootdir/var/lib/apt/lists

for release in $(find aptarchive -name 'Release'); do
SIZE="$(awk "/$FILE\$/ { print \$2; exit }" "${release}.backup")"
sed "s# $SIZE # $(($SIZE + 111)) #" "${release}.backup" > "$release"
done
signreleasefiles

TEST='testfailure'
if expr match "$COMPRESSFILE" '^.*Translation-.*$' >/dev/null; then
TEST='testsuccess'
unset COMPRESSFILE
fi
$TEST aptget update -o Debug::pkgAcquire::Worker=1
cp rootdir/tmp/${TEST}.output rootdir/tmp/update.output
testsuccess grep -E "$(basename -s '.gz' "$COMPRESSFILE").*Hash Sum mismatch" rootdir/tmp/update.output
$TEST aptcache show foo
$TEST aptget install foo -s

testfailure aptcache show bar
testfailure aptget install bar -s
done
done

+ 49
- 0
test/integration/test-apt-update-hashsum-mismatch View File

@@ -0,0 +1,49 @@
#!/bin/sh
set -e

TESTDIR=$(readlink -f $(dirname $0))
. $TESTDIR/framework
setupenvironment
configarchitecture 'i386'
configcompression 'gz'

insertpackage 'testing' 'foo' 'all' '1'
insertpackage 'testing' 'foo2' 'all' '1'
insertsource 'testing' 'foo' 'all' '1'
insertsource 'testing' 'foo2' 'all' '1'

setupaptarchive --no-update
changetowebserver

echo 'Package: bar
Maintainer: Doctor Evil <evil@example.com>
Description: come to the dark side
' > aptarchive/DoctorEvil
compressfile aptarchive/DoctorEvil

find aptarchive \( -name 'Packages' -o -name 'Sources' -o -name 'Translation-en' \) -delete

testsuccess aptget update
testsuccess aptcache show foo
testsuccess aptget install foo -s

for get in $(sed -n 's#^GET /\([^ ]\+\.gz\) HTTP.\+$#\1#p' aptarchive/webserver.log); do
msgmsg 'Test hashsum mismatch with file' "$get"
rm -rf rootdir/var/lib/apt/lists
webserverconfig 'aptwebserver::overwrite' ''
webserverconfig "aptwebserver::overwrite::$(printf '%s' "${get}" | sed 's#/#%2F#g' )::filename" '%2FDoctorEvil.gz'

TEST='testfailure'
if expr match "$get" '^.*Translation-.*$' >/dev/null; then
TEST='testsuccess'
unset get
fi
$TEST aptget update
cp rootdir/tmp/${TEST}.output rootdir/tmp/update.output
testsuccess grep -E "$(basename -s '.gz' "$get").*Hash Sum mismatch" rootdir/tmp/update.output
$TEST aptcache show foo
$TEST aptget install foo -s

testfailure aptcache show bar
testfailure aptget install bar -s
done

+ 1
- 1
test/integration/test-apt-update-ims View File

@@ -30,7 +30,7 @@ runtest() {
# ensure that we still do a hash check on ims hit
msgtest 'Test I-M-S' 'reverify'
aptget update -o Debug::pkgAcquire::Auth=1 2>&1 | grep -A1 'RecivedHash:' | grep -q -- '- SHA' && msgpass || msgfail
aptget update -o Debug::pkgAcquire::Auth=1 2>&1 | grep -A2 'RecivedHash:' | grep -q -- '- SHA' && msgpass || msgfail

# ensure no leftovers in partial
testfailure ls "rootdir/var/lib/apt/lists/partial/*"


+ 3
- 1
test/interactive-helper/aptwebserver.cc View File

@@ -499,9 +499,11 @@ static bool parseFirstLine(int const client, std::string const &request,/*{{{*/
return true;
}
/*}}}*/
static bool handleOnTheFlyReconfiguration(int const client, std::string const &request, std::vector<std::string> const &parts)/*{{{*/
static bool handleOnTheFlyReconfiguration(int const client, std::string const &request, std::vector<std::string> parts)/*{{{*/
{
size_t const pcount = parts.size();
for (size_t i = 0; i < pcount; ++i)
parts[i] = DeQuoteString(parts[i]);
if (pcount == 4 && parts[1] == "set")
{
_config->Set(parts[2], parts[3]);


+ 19
- 6
test/libapt/hashsums_test.cc View File

@@ -163,30 +163,34 @@ TEST(HashSumsTest, FileBased)

FileFd fd(__FILE__, FileFd::ReadOnly);
EXPECT_TRUE(fd.IsOpen());
std::string FileSize;
strprintf(FileSize, "%llu", fd.FileSize());

{
Hashes hashes;
hashes.AddFD(fd.Fd());
HashStringList list = hashes.GetHashStringList();
EXPECT_FALSE(list.empty());
EXPECT_EQ(4, list.size());
EXPECT_EQ(5, list.size());
EXPECT_EQ(md5.Value(), list.find("MD5Sum")->HashValue());
EXPECT_EQ(sha1.Value(), list.find("SHA1")->HashValue());
EXPECT_EQ(sha256.Value(), list.find("SHA256")->HashValue());
EXPECT_EQ(sha512.Value(), list.find("SHA512")->HashValue());
EXPECT_EQ(FileSize, list.find("Checksum-FileSize")->HashValue());
}
unsigned long sz = fd.FileSize();
unsigned long long sz = fd.FileSize();
fd.Seek(0);
{
Hashes hashes;
hashes.AddFD(fd.Fd(), sz);
HashStringList list = hashes.GetHashStringList();
EXPECT_FALSE(list.empty());
EXPECT_EQ(4, list.size());
EXPECT_EQ(5, list.size());
EXPECT_EQ(md5.Value(), list.find("MD5Sum")->HashValue());
EXPECT_EQ(sha1.Value(), list.find("SHA1")->HashValue());
EXPECT_EQ(sha256.Value(), list.find("SHA256")->HashValue());
EXPECT_EQ(sha512.Value(), list.find("SHA512")->HashValue());
EXPECT_EQ(FileSize, list.find("Checksum-FileSize")->HashValue());
}
fd.Seek(0);
{
@@ -279,37 +283,46 @@ TEST(HashSumsTest, HashStringList)
EXPECT_EQ(NULL, list.find(""));
EXPECT_EQ(NULL, list.find("MD5Sum"));

// empty lists aren't equal
HashStringList list2;
EXPECT_FALSE(list == list2);
EXPECT_TRUE(list != list2);

// some hashes don't really contribute to usability
list.push_back(HashString("Checksum-FileSize", "29"));
EXPECT_FALSE(list.empty());
EXPECT_FALSE(list.usable());

Hashes hashes;
hashes.Add("The quick brown fox jumps over the lazy dog");
list = hashes.GetHashStringList();
EXPECT_FALSE(list.empty());
EXPECT_TRUE(list.usable());
EXPECT_EQ(4, list.size());
EXPECT_EQ(5, list.size());
EXPECT_TRUE(NULL != list.find(NULL));
EXPECT_TRUE(NULL != list.find(""));
EXPECT_TRUE(NULL != list.find("MD5Sum"));
EXPECT_TRUE(NULL != list.find("Checksum-FileSize"));
EXPECT_TRUE(NULL == list.find("ROT26"));

_config->Set("Acquire::ForceHash", "MD5Sum");
EXPECT_FALSE(list.empty());
EXPECT_TRUE(list.usable());
EXPECT_EQ(4, list.size());
EXPECT_EQ(5, list.size());
EXPECT_TRUE(NULL != list.find(NULL));
EXPECT_TRUE(NULL != list.find(""));
EXPECT_TRUE(NULL != list.find("MD5Sum"));
EXPECT_TRUE(NULL != list.find("Checksum-FileSize"));
EXPECT_TRUE(NULL == list.find("ROT26"));

_config->Set("Acquire::ForceHash", "ROT26");
EXPECT_FALSE(list.empty());
EXPECT_FALSE(list.usable());
EXPECT_EQ(4, list.size());
EXPECT_EQ(5, list.size());
EXPECT_TRUE(NULL == list.find(NULL));
EXPECT_TRUE(NULL == list.find(""));
EXPECT_TRUE(NULL != list.find("MD5Sum"));
EXPECT_TRUE(NULL != list.find("Checksum-FileSize"));
EXPECT_TRUE(NULL == list.find("ROT26"));

_config->Clear("Acquire::ForceHash");


Loading…
Cancel
Save