rred is responsible for unpacking and reading the patch files in one go,
but we currently only have hashes for the uncompressed patch files, so
the handler read the entire patch file before dispatching it to the
worker which would read it again – both with an implicit uncompress.
Worse, while the workers operate in parallel the handler is the central
orchestration unit, so having it busy with work means the workers do
(potentially) nothing.
This means rred is working with 'untrusted' data, which is bad. Yet,
having the unpack in the handler meant that the untrusted uncompress was
done as root which isn't better either. Now, we have it at least
contained in a binary which we can harden a bit better. In the long run,
we want hashes for the compressed patch files through to be safe.
+static std::string GetMergeDiffsPatchFileName(std::string const &Final, std::string const &Patch)/*{{{*/
+{
+ // rred expects the patch as $FinalFile.ed.$patchname.gz
+ return Final + ".ed." + Patch + ".gz";
+}
+ /*}}}*/
+static std::string GetDiffsPatchFileName(std::string const &Final) /*{{{*/
+{
+ // rred expects the patch as $FinalFile.ed
+ return Final + ".ed";
+}
+ /*}}}*/
+
static bool AllowInsecureRepositories(indexRecords const * const MetaIndexParser, pkgAcqMetaBase * const TransactionManager, pkgAcquire::Item * const I) /*{{{*/
{
if(MetaIndexParser->IsAlwaysTrusted() || _config->FindB("Acquire::AllowInsecureRepositories") == true)
static bool AllowInsecureRepositories(indexRecords const * const MetaIndexParser, pkgAcqMetaBase * const TransactionManager, pkgAcquire::Item * const I) /*{{{*/
{
if(MetaIndexParser->IsAlwaysTrusted() || _config->FindB("Acquire::AllowInsecureRepositories") == true)
<< "Falling back to normal index file acquire" << std::endl;
DestFile = GetPartialFileNameFromURI(Target->URI);
RenameOnError(PDiffError);
<< "Falling back to normal index file acquire" << std::endl;
DestFile = GetPartialFileNameFromURI(Target->URI);
RenameOnError(PDiffError);
+ std::string const patchname = GetDiffsPatchFileName(DestFile);
+ if (RealFileExists(patchname))
+ rename(patchname.c_str(), std::string(patchname + ".FAILED").c_str());
new pkgAcqIndex(Owner, TransactionManager, Target);
Finish();
}
new pkgAcqIndex(Owner, TransactionManager, Target);
Finish();
}
Item::Done(Message, Hashes, Cnf);
Item::Done(Message, Hashes, Cnf);
- // FIXME: verify this download too before feeding it to rred
std::string const FinalFile = GetPartialFileNameFromURI(Target->URI);
std::string const FinalFile = GetPartialFileNameFromURI(Target->URI);
+ std::string const PatchFile = GetDiffsPatchFileName(FinalFile);
// success in downloading a diff, enter ApplyDiff state
if(State == StateFetchDiff)
{
// success in downloading a diff, enter ApplyDiff state
if(State == StateFetchDiff)
{
- FileFd fd(DestFile, FileFd::ReadOnly, FileFd::Gzip);
- class Hashes LocalHashesCalc;
- LocalHashesCalc.AddFD(fd);
- HashStringList const LocalHashes = LocalHashesCalc.GetHashStringList();
-
- if (fd.Size() != available_patches[0].patch_size ||
- available_patches[0].patch_hashes != LocalHashes)
- {
- // patchfiles are dated, so bad indicates a bad download, so kill it
- unlink(DestFile.c_str());
- Failed("Patch has Size/Hashsum mismatch", NULL);
- return;
- }
-
- // rred excepts the patch as $FinalFile.ed
- Rename(DestFile,FinalFile+".ed");
+ Rename(DestFile, PatchFile);
if(Debug)
std::clog << "Sending to rred method: " << FinalFile << std::endl;
if(Debug)
std::clog << "Sending to rred method: " << FinalFile << std::endl;
QueueURI(Desc);
SetActiveSubprocess("rred");
return;
QueueURI(Desc);
SetActiveSubprocess("rred");
return;
// success in download/apply a diff, queue next (if needed)
if(State == StateApplyDiff)
{
// remove the just applied patch
available_patches.erase(available_patches.begin());
// success in download/apply a diff, queue next (if needed)
if(State == StateApplyDiff)
{
// remove the just applied patch
available_patches.erase(available_patches.begin());
- unlink((FinalFile + ".ed").c_str());
+ unlink(PatchFile.c_str());
{
std::clog << "Moving patched file in place: " << std::endl
<< DestFile << " -> " << FinalFile << std::endl;
{
std::clog << "Moving patched file in place: " << std::endl
<< DestFile << " -> " << FinalFile << std::endl;
+std::string pkgAcqIndexDiffs::Custom600Headers() const /*{{{*/
+{
+ if(State != StateApplyDiff)
+ return pkgAcqBaseIndex::Custom600Headers();
+ std::ostringstream patchhashes;
+ HashStringList const ExpectedHashes = available_patches[0].patch_hashes;
+ for (HashStringList::const_iterator hs = ExpectedHashes.begin(); hs != ExpectedHashes.end(); ++hs)
+ patchhashes << "\nPatch-0-" << hs->HashType() << "-Hash: " << hs->HashValue();
+ patchhashes << pkgAcqBaseIndex::Custom600Headers();
+ return patchhashes.str();
+}
+ /*}}}*/
// AcqIndexMergeDiffs::AcqIndexMergeDiffs - Constructor /*{{{*/
pkgAcqIndexMergeDiffs::pkgAcqIndexMergeDiffs(pkgAcquire * const Owner,
// AcqIndexMergeDiffs::AcqIndexMergeDiffs - Constructor /*{{{*/
pkgAcqIndexMergeDiffs::pkgAcqIndexMergeDiffs(pkgAcquire * const Owner,
std::clog << "Falling back to normal index file acquire" << std::endl;
DestFile = GetPartialFileNameFromURI(Target->URI);
RenameOnError(PDiffError);
std::clog << "Falling back to normal index file acquire" << std::endl;
DestFile = GetPartialFileNameFromURI(Target->URI);
RenameOnError(PDiffError);
+ std::string const patchname = GetMergeDiffsPatchFileName(DestFile, patch.file);
+ if (RealFileExists(patchname))
+ rename(patchname.c_str(), std::string(patchname + ".FAILED").c_str());
new pkgAcqIndex(Owner, TransactionManager, Target);
}
/*}}}*/
new pkgAcqIndex(Owner, TransactionManager, Target);
}
/*}}}*/
Item::Done(Message, Hashes, Cnf);
Item::Done(Message, Hashes, Cnf);
- // FIXME: verify download before feeding it to rred
string const FinalFile = GetPartialFileNameFromURI(Target->URI);
string const FinalFile = GetPartialFileNameFromURI(Target->URI);
if (State == StateFetchDiff)
{
if (State == StateFetchDiff)
{
- FileFd fd(DestFile, FileFd::ReadOnly, FileFd::Gzip);
- class Hashes LocalHashesCalc;
- LocalHashesCalc.AddFD(fd);
- HashStringList const LocalHashes = LocalHashesCalc.GetHashStringList();
-
- if (fd.Size() != patch.patch_size || patch.patch_hashes != LocalHashes)
- {
- // patchfiles are dated, so bad indicates a bad download, so kill it
- unlink(DestFile.c_str());
- Failed("Patch has Size/Hashsum mismatch", NULL);
- return;
- }
-
- // rred expects the patch as $FinalFile.ed.$patchname.gz
- Rename(DestFile, FinalFile + ".ed." + patch.file + ".gz");
+ Rename(DestFile, GetMergeDiffsPatchFileName(FinalFile, patch.file));
// check if this is the last completed diff
State = StateDoneDiff;
// check if this is the last completed diff
State = StateDoneDiff;
I != allPatches->end(); ++I)
{
std::string const PartialFile = GetPartialFileNameFromURI(Target->URI);
I != allPatches->end(); ++I)
{
std::string const PartialFile = GetPartialFileNameFromURI(Target->URI);
- std::string patch = PartialFile + ".ed." + (*I)->patch.file + ".gz";
+ std::string const patch = GetMergeDiffsPatchFileName(PartialFile, (*I)->patch.file);
unlink(patch.c_str());
}
unlink(FinalFile.c_str());
unlink(patch.c_str());
}
unlink(FinalFile.c_str());
+std::string pkgAcqIndexMergeDiffs::Custom600Headers() const /*{{{*/
+{
+ if(State != StateApplyDiff)
+ return pkgAcqBaseIndex::Custom600Headers();
+ std::ostringstream patchhashes;
+ unsigned int seen_patches = 0;
+ for (std::vector<pkgAcqIndexMergeDiffs *>::const_iterator I = allPatches->begin();
+ I != allPatches->end(); ++I)
+ {
+ HashStringList const ExpectedHashes = (*I)->patch.patch_hashes;
+ for (HashStringList::const_iterator hs = ExpectedHashes.begin(); hs != ExpectedHashes.end(); ++hs)
+ patchhashes << "\nPatch-" << seen_patches << "-" << hs->HashType() << "-Hash: " << hs->HashValue();
+ ++seen_patches;
+ }
+ patchhashes << pkgAcqBaseIndex::Custom600Headers();
+ return patchhashes.str();
+}
+ /*}}}*/
// AcqIndex::AcqIndex - Constructor /*{{{*/
pkgAcqIndex::pkgAcqIndex(pkgAcquire * const Owner,
// AcqIndex::AcqIndex - Constructor /*{{{*/
pkgAcqIndex::pkgAcqIndex(pkgAcquire * const Owner,
virtual void Failed(std::string const &Message,pkgAcquire::MethodConfig const * const Cnf);
virtual void Done(std::string const &Message, HashStringList const &Hashes,
pkgAcquire::MethodConfig const * const Cnf);
virtual void Failed(std::string const &Message,pkgAcquire::MethodConfig const * const Cnf);
virtual void Done(std::string const &Message, HashStringList const &Hashes,
pkgAcquire::MethodConfig const * const Cnf);
+ virtual std::string Custom600Headers() const;
virtual std::string DescURI() const {return Target->URI + "Index";};
virtual HashStringList GetExpectedHashes() const;
virtual bool HashesRequired() const;
virtual std::string DescURI() const {return Target->URI + "Index";};
virtual HashStringList GetExpectedHashes() const;
virtual bool HashesRequired() const;
virtual void Done(std::string const &Message, HashStringList const &Hashes,
pkgAcquire::MethodConfig const * const Cnf);
virtual void Done(std::string const &Message, HashStringList const &Hashes,
pkgAcquire::MethodConfig const * const Cnf);
+ virtual std::string Custom600Headers() const;
virtual std::string DescURI() const {return Target->URI + "IndexDiffs";};
virtual HashStringList GetExpectedHashes() const;
virtual bool HashesRequired() const;
virtual std::string DescURI() const {return Target->URI + "IndexDiffs";};
virtual HashStringList GetExpectedHashes() const;
virtual bool HashesRequired() const;
*I = Tmp;
if (QueueBack == 0)
QueueBack = Tmp;
*I = Tmp;
if (QueueBack == 0)
QueueBack = Tmp;
// Notify that this item is to be fetched.
// Notify that this item is to be fetched.
- if (Fetch(Tmp) == false)
+ if (URIAcquire(Message, Tmp) == false)
}
/*}}}*/
// AcqMethod::PrintStatus - privately really send a log/status message /*{{{*/
}
/*}}}*/
// AcqMethod::PrintStatus - privately really send a log/status message /*{{{*/
-// ---------------------------------------------------------------------
-/* */
void pkgAcqMethod::PrintStatus(char const * const header, const char* Format,
va_list &args) const
{
void pkgAcqMethod::PrintStatus(char const * const header, const char* Format,
va_list &args) const
{
std::string FailReason;
std::string UsedMirror;
std::string IP;
std::string FailReason;
std::string UsedMirror;
std::string IP;
// Handlers for messages
virtual bool Configuration(std::string Message);
virtual bool Fetch(FetchItem * /*Item*/) {return true;};
// Handlers for messages
virtual bool Configuration(std::string Message);
virtual bool Fetch(FetchItem * /*Item*/) {return true;};
+ virtual bool URIAcquire(std::string const &/*Message*/, FetchItem *Itm) { return Fetch(Itm); };
+
// Outgoing messages
void Fail(bool Transient = false);
inline void Fail(const char *Why, bool Transient = false) {Fail(std::string(Why),Transient);};
// Outgoing messages
void Fail(bool Transient = false);
inline void Fail(const char *Why, bool Transient = false) {Fail(std::string(Why),Transient);};
- void read_diff(FileFd &f)
+ void read_diff(FileFd &f, Hashes * const h)
{
char buffer[BLOCK_SIZE];
bool cmdwanted = true;
{
char buffer[BLOCK_SIZE];
bool cmdwanted = true;
Change ch(0);
while(f.ReadLine(buffer, sizeof(buffer)))
{
Change ch(0);
while(f.ReadLine(buffer, sizeof(buffer)))
{
+ if (h != NULL)
+ h->Add(buffer);
if (cmdwanted) {
char *m, *c;
size_t s, e;
if (cmdwanted) {
char *m, *c;
size_t s, e;
+ struct PDiffFile {
+ std::string FileName;
+ HashStringList ExpectedHashes;
+ PDiffFile(std::string const &FileName, HashStringList const &ExpectedHashes) :
+ FileName(FileName), ExpectedHashes(ExpectedHashes) {}
+ };
+
+ HashStringList ReadExpectedHashesForPatch(unsigned int const patch, std::string const &Message)
+ {
+ HashStringList ExpectedHashes;
+ for (char const * const * type = HashString::SupportedHashes(); *type != NULL; ++type)
+ {
+ std::string tagname;
+ strprintf(tagname, "Patch-%d-%s-Hash", patch, *type);
+ std::string const hashsum = LookupTag(Message, tagname.c_str());
+ if (hashsum.empty() == false)
+ ExpectedHashes.push_back(HashString(*type, hashsum));
+ }
+ return ExpectedHashes;
+ }
+
- virtual bool Fetch(FetchItem *Itm) {
+ virtual bool URIAcquire(std::string const &Message, FetchItem *Itm) {
Debug = _config->FindB("Debug::pkgAcquire::RRed", false);
URI Get = Itm->Uri;
std::string Path = Get.Host + Get.Path; // rred:/path - no host
Debug = _config->FindB("Debug::pkgAcquire::RRed", false);
URI Get = Itm->Uri;
std::string Path = Get.Host + Get.Path; // rred:/path - no host
- std::vector<std::string> patchpaths;
+ std::vector<PDiffFile> patchfiles;
Patch patch;
if (FileExists(Path + ".ed") == true)
Patch patch;
if (FileExists(Path + ".ed") == true)
- patchpaths.push_back(Path + ".ed");
+ {
+ HashStringList const ExpectedHashes = ReadExpectedHashesForPatch(0, Message);
+ std::string const FileName = Path + ".ed";
+ if (ExpectedHashes.usable() == false)
+ return _error->Error("No hashes found for uncompressed patch: %s", FileName.c_str());
+ patchfiles.push_back(PDiffFile(FileName, ExpectedHashes));
+ }
else
{
_error->PushToStack();
else
{
_error->PushToStack();
_error->RevertToStack();
std::string const baseName = Path + ".ed.";
_error->RevertToStack();
std::string const baseName = Path + ".ed.";
+ unsigned int seen_patches = 0;
for (std::vector<std::string>::const_iterator p = patches.begin();
p != patches.end(); ++p)
for (std::vector<std::string>::const_iterator p = patches.begin();
p != patches.end(); ++p)
if (p->compare(0, baseName.length(), baseName) == 0)
if (p->compare(0, baseName.length(), baseName) == 0)
- patchpaths.push_back(*p);
+ {
+ HashStringList const ExpectedHashes = ReadExpectedHashesForPatch(seen_patches, Message);
+ if (ExpectedHashes.usable() == false)
+ return _error->Error("No hashes found for uncompressed patch %d: %s", seen_patches, p->c_str());
+ patchfiles.push_back(PDiffFile(*p, ExpectedHashes));
+ ++seen_patches;
+ }
+ }
}
std::string patch_name;
}
std::string patch_name;
- for (std::vector<std::string>::iterator I = patchpaths.begin();
- I != patchpaths.end();
+ for (std::vector<PDiffFile>::iterator I = patchfiles.begin();
+ I != patchfiles.end();
+ patch_name = I->FileName;
if (Debug == true)
std::clog << "Patching " << Path << " with " << patch_name
<< std::endl;
if (Debug == true)
std::clog << "Patching " << Path << " with " << patch_name
<< std::endl;
_error->DumpErrors(std::cerr);
abort();
}
_error->DumpErrors(std::cerr);
abort();
}
+ Hashes patch_hash(I->ExpectedHashes);
+ patch.read_diff(p, &patch_hash);
+ HashStringList const hsl = patch_hash.GetHashStringList();
+ if (hsl != I->ExpectedHashes)
+ return _error->Error("Patch %s doesn't have the expected hashsum", patch_name.c_str());
_error->DumpErrors(std::cerr);
exit(1);
}
_error->DumpErrors(std::cerr);
exit(1);
}
+ patch.read_diff(p, NULL);
generatereleasefiles '+1hour'
signreleasefiles
testsuccess aptget update "$@"
generatereleasefiles '+1hour'
signreleasefiles
testsuccess aptget update "$@"
+ cp -f rootdir/tmp/testsuccess.output rootdir/tmp/aptgetupdate.output
+ testsuccess grep 'have the expected hashsum' rootdir/tmp/aptgetupdate.output
testnopackage oldstuff
testsuccessequal "$(cat ${PKGFILE}-new)
" aptcache show apt newstuff
testnopackage oldstuff
testsuccessequal "$(cat ${PKGFILE}-new)
" aptcache show apt newstuff