Item::Failed(Message,Cnf);
}
/*}}}*/
+// pkgAcqIndex::GetFinalFilename - Return the full final file path /*{{{*/
+std::string pkgAcqIndex::GetFinalFilename(std::string const &URI,
+ std::string const &compExt)
+{
+ std::string FinalFile = _config->FindDir("Dir::State::lists");
+ FinalFile += URItoFileName(URI);
+ if (_config->FindB("Acquire::GzipIndexes",false) && compExt == "gz")
+ FinalFile += ".gz";
+ return FinalFile;
+}
+ /*}}}*/
+// AcqIndex::ReverifyAfterIMS - Reverify index after an ims-hit /*{{{*/
+void pkgAcqIndex::ReverifyAfterIMS(std::string const &FileName)
+{
+ std::string const compExt = CompressionExtension.substr(0, CompressionExtension.find(' '));
+ if (_config->FindB("Acquire::GzipIndexes",false) && compExt == "gz")
+ DestFile += ".gz";
+
+ string FinalFile = GetFinalFilename(RealURI, compExt);
+ Rename(FinalFile, FileName);
+ Decompression = true;
+ Desc.URI = "copy:" + FileName;
+ QueueURI(Desc);
+}
+ /*}}}*/
// AcqIndex::Done - Finished a fetch /*{{{*/
// ---------------------------------------------------------------------
/* This goes through a number of states.. On the initial fetch the
pkgAcquire::MethodConfig *Cfg)
{
Item::Done(Message,Size,Hash,Cfg);
+ std::string const compExt = CompressionExtension.substr(0, CompressionExtension.find(' '));
if (Decompression == true)
{
if (!ExpectedHash.empty() && ExpectedHash.toStr() != Hash)
{
+ Desc.URI = RealURI;
RenameOnError(HashSumMismatch);
return;
}
/* Always verify the index file for correctness (all indexes must
* have a Package field) (LP: #346386) (Closes: #627642)
*/
- FileFd fd(DestFile, FileFd::ReadOnly);
+ FileFd fd(DestFile, FileFd::ReadOnlyGzip);
// Only test for correctness if the file is not empty (empty is ok)
- if (fd.FileSize() > 0)
+ if (fd.Size() > 0)
{
pkgTagSection sec;
pkgTagFile tag(&fd);
}
// Done, move it into position
- string FinalFile = _config->FindDir("Dir::State::lists");
- FinalFile += URItoFileName(RealURI);
+ string FinalFile = GetFinalFilename(RealURI, compExt);
Rename(DestFile,FinalFile);
chmod(FinalFile.c_str(),0644);
will work OK */
DestFile = _config->FindDir("Dir::State::lists") + "partial/";
DestFile += URItoFileName(RealURI);
-
+ if (_config->FindB("Acquire::GzipIndexes",false) && compExt == "gz")
+ DestFile += ".gz";
+
// Remove the compressed version.
if (Erase == true)
unlink(DestFile.c_str());
{
// The files timestamp matches
if (StringToBool(LookupTag(Message,"Alt-IMS-Hit"),false) == true)
- return;
+ {
+ ReverifyAfterIMS(FileName);
+ return;
+ }
Decompression = true;
Local = true;
DestFile += ".decomp";
ErrorText = "Method gave a blank filename";
}
- std::string const compExt = CompressionExtension.substr(0, CompressionExtension.find(' '));
-
// The files timestamp matches
- if (StringToBool(LookupTag(Message,"IMS-Hit"),false) == true) {
- if (_config->FindB("Acquire::GzipIndexes",false) && compExt == "gz")
- // Update DestFile for .gz suffix so that the clean operation keeps it
- DestFile += ".gz";
+ if (StringToBool(LookupTag(Message,"IMS-Hit"),false) == true)
+ {
+ ReverifyAfterIMS(FileName);
return;
- }
+ }
if (FileName == DestFile)
Erase = true;
string decompProg;
- // If we enable compressed indexes and already have gzip, keep it
- if (_config->FindB("Acquire::GzipIndexes",false) && compExt == "gz" && !Local) {
- string FinalFile = _config->FindDir("Dir::State::lists");
- FinalFile += URItoFileName(RealURI) + ".gz";
- Rename(DestFile,FinalFile);
- chmod(FinalFile.c_str(),0644);
-
- // Update DestFile for .gz suffix so that the clean operation keeps it
- DestFile = _config->FindDir("Dir::State::lists") + "partial/";
+ // If we enable compressed indexes, queue for hash verification
+ if (_config->FindB("Acquire::GzipIndexes",false) && compExt == "gz" && !Local)
+ {
+ DestFile = _config->FindDir("Dir::State::lists");
DestFile += URItoFileName(RealURI) + ".gz";
+
+ Decompression = true;
+ Desc.URI = "copy:" + FileName;
+ QueueURI(Desc);
+
return;
}
string Final = _config->FindDir("Dir::State::lists");
Final += URItoFileName(RealURI);
+ if (_config->FindB("Acquire::GzipIndexes",false))
+ Final += ".gz";
+
struct stat Buf;
if (stat(Final.c_str(),&Buf) != 0)
return "\nFail-Ignore: true\nIndex-File: true";
std::cerr << "Signature verification succeeded: "
<< DestFile << std::endl;
+ // do not trust any previously unverified content that we may have
+ string LastGoodSigFile = _config->FindDir("Dir::State::lists").append("partial/").append(URItoFileName(RealURI));
+ if (DestFile != SigFile)
+ LastGoodSigFile.append(".gpg");
+ LastGoodSigFile.append(".reverify");
+ if(IMSHit == false && RealFileExists(LastGoodSigFile) == false)
+ {
+ for (vector <struct IndexTarget*>::const_iterator Target = IndexTargets->begin();
+ Target != IndexTargets->end();
+ ++Target)
+ {
+ // remove old indexes
+ std::string index = _config->FindDir("Dir::State::lists") +
+ URItoFileName((*Target)->URI);
+ unlink(index.c_str());
+ // and also old gzipindexes
+ index += ".gz";
+ unlink(index.c_str());
+ }
+ }
+
+
// Download further indexes with verification
QueueIndexes(true);
#include <apt-pkg/acquire-method.h>
#include <apt-pkg/error.h>
#include <apt-pkg/hashes.h>
+#include <apt-pkg/configuration.h>
#include <string>
#include <sys/stat.h>
class CopyMethod : public pkgAcqMethod
{
virtual bool Fetch(FetchItem *Itm);
+ void CalculateHashes(FetchResult &Res);
public:
- CopyMethod() : pkgAcqMethod("1.0",SingleInstance) {};
+ CopyMethod() : pkgAcqMethod("1.0",SingleInstance | SendConfig) {};
};
+void CopyMethod::CalculateHashes(FetchResult &Res)
+{
+ // For gzip indexes we need to look inside the gzip for the hash
+ // We can not use the extension here as its not used in partial
+ // on a IMS hit
+ FileFd::OpenMode OpenMode = FileFd::ReadOnly;
+ if (_config->FindB("Acquire::GzipIndexes", false) == true)
+ OpenMode = FileFd::ReadOnlyGzip;
+
+ Hashes Hash;
+ FileFd Fd(Res.Filename, OpenMode);
+ Hash.AddFD(Fd);
+ Res.TakeHashes(Hash);
+}
+
// CopyMethod::Fetch - Fetch a file /*{{{*/
// ---------------------------------------------------------------------
/* */
Res.IMSHit = false;
URIStart(Res);
+ // just calc the hashes if the source and destination are identical
+ if (File == Itm->DestFile)
+ {
+ CalculateHashes(Res);
+ URIDone(Res);
+ return true;
+ }
+
// See if the file exists
FileFd From(File,FileFd::ReadOnly);
FileFd To(Itm->DestFile,FileFd::WriteAtomic);
if (utimes(Res.Filename.c_str(), times) != 0)
return _error->Errno("utimes",_("Failed to set modification time"));
- Hashes Hash;
- FileFd Fd(Res.Filename, FileFd::ReadOnly);
- Hash.AddFD(Fd);
- Res.TakeHashes(Hash);
+ CalculateHashes(Res);
URIDone(Res);
return true;
--- /dev/null
+#!/bin/sh
+#
+# Ensure that a MITM can not stale the Packages/Sources without
+# raising a error message. Note that the Release file is protected
+# via the "Valid-Until" header
+#
+set -e
+
+TESTDIR=$(readlink -f $(dirname $0))
+. $TESTDIR/framework
+
+setupenvironment
+configarchitecture "i386"
+
+insertpackage 'unstable' 'foo' 'all' '1.0'
+
+setupaptarchive
+changetowebserver
+aptget update -qq
+
+# insert new version
+mkdir aptarchive/dists/unstable/main/binary-i386/saved
+cp -p aptarchive/dists/unstable/main/binary-i386/Packages* \
+ aptarchive/dists/unstable/main/binary-i386/saved
+insertpackage 'unstable' 'foo' 'all' '2.0'
+
+# not using compressfile for compat with older apt releases
+gzip -c aptarchive/dists/unstable/main/binary-i386/Packages > \
+ aptarchive/dists/unstable/main/binary-i386/Packages.gz
+generatereleasefiles
+signreleasefiles
+
+# ensure that we do not get a I-M-S hit for the Release file
+touch -d "+1hour" aptarchive/dists/unstable/*Release*
+
+# but now only deliver the previous Packages file instead of the new one
+# (simulating a stale attack)
+cp -p aptarchive/dists/unstable/main/binary-i386/saved/Packages* \
+ aptarchive/dists/unstable/main/binary-i386/
+
+# ensure this raises a error
+testequal "W: Failed to fetch http://localhost:8080/dists/unstable/main/binary-i386/Packages Hash Sum mismatch
+
+E: Some index files failed to download. They have been ignored, or old ones used instead." aptget update -qq
+
+