]> git.saurik.com Git - apt.git/commitdiff
treat older Release files than we already have as an IMSHit
authorDavid Kalnischkies <david@kalnischkies.de>
Mon, 18 May 2015 20:15:06 +0000 (22:15 +0200)
committerDavid Kalnischkies <david@kalnischkies.de>
Mon, 18 May 2015 20:15:06 +0000 (22:15 +0200)
Valid-Until protects us from long-living downgrade attacks, but not all
repositories have it and an attacker could still use older but still
valid files to downgrade us. While this makes it sounds like a security
improvement now, its a bit theoretical at best as an attacker with
capabilities to pull this off could just as well always keep us days
(but in the valid period) behind and always knows which state we have,
as we tell him with the If-Modified-Since header. This is also why this
is 'silently' ignored and treated as an IMSHit rather than screamed at
the user as this can at best be an annoyance for attackers.

An error here would 'regularily' be encountered by users by out-of-sync
mirrors serving a single run (e.g. load balancer) or in two consecutive
runs on the other hand, so it would just help teaching people ignore it.

That said, most of the code churn is caused by enforcing this additional
requirement. Crisscross from InRelease to Release.gpg is e.g. very
unlikely in practice, but if we would ignore it an attacker could
sidestep it this way.

12 files changed:
apt-pkg/acquire-item.cc
apt-pkg/acquire-item.h
apt-pkg/indexrecords.cc
apt-pkg/indexrecords.h
test/integration/framework
test/integration/test-apt-update-ims
test/integration/test-apt-update-nofallback
test/integration/test-apt-update-not-modified
test/integration/test-apt-update-rollback
test/integration/test-releasefile-date-older [new file with mode: 0755]
test/integration/test-releasefile-valid-until
test/integration/test-releasefile-verification

index 78dace12c69c2e6fdf6f1ba0450f6e6d6dfd084a..8155b9bfe530f37514ada9a8deeea89fc1e8fc61 100644 (file)
@@ -1637,7 +1637,7 @@ pkgAcqMetaBase::pkgAcqMetaBase(pkgAcquire *Owner,
       HashStringList const &ExpectedHashes,
       pkgAcqMetaBase *TransactionManager)
 : Item(Owner, ExpectedHashes, TransactionManager),
-   MetaIndexParser(MetaIndexParser), IndexTargets(IndexTargets),
+   MetaIndexParser(MetaIndexParser), LastMetaIndexParser(NULL), IndexTargets(IndexTargets),
    AuthPass(false), RealURI(RealURI), IMSHit(false)
 {
 }
@@ -1697,6 +1697,14 @@ void pkgAcqMetaBase::CommitTransaction()
    Transaction.clear();
 }
                                                                        /*}}}*/
+bool pkgAcqMetaBase::TransactionState(TransactionStates const state)   /*{{{*/
+{
+   // Do not remove InRelease on IMSHit of Release.gpg [yes, this is very edgecasey]
+   if (TransactionManager->IMSHit == false)
+      return pkgAcquire::Item::TransactionState(state);
+   return true;
+}
+                                                                       /*}}}*/
 // AcqMetaBase::TransactionStageCopy - Stage a file for copying                /*{{{*/
 void pkgAcqMetaBase::TransactionStageCopy(Item *I,
                                           const std::string &From,
@@ -1715,15 +1723,15 @@ void pkgAcqMetaBase::TransactionStageRemoval(Item *I,
 }
                                                                        /*}}}*/
 // AcqMetaBase::GenerateAuthWarning - Check gpg authentication error   /*{{{*/
-bool pkgAcqMetaBase::CheckStopAuthentication(const std::string &Message)
+bool pkgAcqMetaBase::CheckStopAuthentication(pkgAcquire::Item * const I, const std::string &Message)
 {
    // FIXME: this entire function can do now that we disallow going to
    //        a unauthenticated state and can cleanly rollback
 
-   string const Final = GetFinalFilename();
+   string const Final = I->GetFinalFilename();
    if(FileExists(Final))
    {
-      Status = StatTransientNetworkError;
+      I->Status = StatTransientNetworkError;
       _error->Warning(_("An error occurred during the signature "
                         "verification. The repository is not updated "
                         "and the previous index files will be used. "
@@ -1737,7 +1745,7 @@ bool pkgAcqMetaBase::CheckStopAuthentication(const std::string &Message)
       _error->Error(_("GPG error: %s: %s"),
                     Desc.Description.c_str(),
                     LookupTag(Message,"Message").c_str());
-      Status = StatError;
+      I->Status = StatError;
       return true;
    } else {
       _error->Warning(_("GPG error: %s: %s"),
@@ -1751,17 +1759,13 @@ bool pkgAcqMetaBase::CheckStopAuthentication(const std::string &Message)
                                                                        /*}}}*/
 // AcqMetaSig::AcqMetaSig - Constructor                                        /*{{{*/
 pkgAcqMetaSig::pkgAcqMetaSig(pkgAcquire *Owner,
-                             pkgAcqMetaBase *TransactionManager,
-                            string URI,string URIDesc,string ShortDesc,
-                             string MetaIndexFile,
-                            const vector<IndexTarget*>* IndexTargets,
-                            indexRecords* MetaIndexParser) :
-   pkgAcqMetaBase(Owner, IndexTargets, MetaIndexParser, URI,
-                  HashStringList(), TransactionManager),
-   MetaIndexFile(MetaIndexFile), URIDesc(URIDesc),
-   ShortDesc(ShortDesc)
+      pkgAcqMetaBase *TransactionManager,
+      string const &URI, string const &URIDesc,string const &ShortDesc,
+      pkgAcqMetaIndex * const MetaIndex) :
+   pkgAcquire::Item(Owner, HashStringList(), TransactionManager), MetaIndex(MetaIndex),
+   URIDesc(URIDesc), RealURI(URI)
 {
-   DestFile = GetPartialFileNameFromURI(RealURI);
+   DestFile = GetPartialFileNameFromURI(URI);
 
    // remove any partial downloaded sig-file in partial/.
    // it may confuse proxies and is too small to warrant a
@@ -1779,11 +1783,28 @@ pkgAcqMetaSig::pkgAcqMetaSig(pkgAcquire *Owner,
    Desc.ShortDesc = ShortDesc;
    Desc.URI = URI;
 
-   QueueURI(Desc);
+   // If we got a hit for Release, we will get one for Release.gpg too (or obscure errors),
+   // so we skip the download step and go instantly to verification
+   if (TransactionManager->IMSHit == true && RealFileExists(GetFinalFilename()))
+   {
+      Complete = true;
+      Status = StatDone;
+      PartialFile = DestFile = GetFinalFilename();
+      MetaIndexFileSignature = DestFile;
+      MetaIndex->QueueForSignatureVerify(this, MetaIndex->DestFile, DestFile);
+   }
+   else
+      QueueURI(Desc);
 }
                                                                        /*}}}*/
 pkgAcqMetaSig::~pkgAcqMetaSig()                                                /*{{{*/
 {
+}
+                                                                       /*}}}*/
+// pkgAcqMetaSig::GetFinalFilename - Return the full final file path   /*{{{*/
+std::string pkgAcqMetaSig::GetFinalFilename() const
+{
+   return GetFinalFileNameFromURI(RealURI);
 }
                                                                        /*}}}*/
 // pkgAcqMetaSig::Done - The signature was downloaded/verified         /*{{{*/
@@ -1793,21 +1814,32 @@ void pkgAcqMetaSig::Done(string Message,unsigned long long Size,
                          HashStringList const &Hashes,
                         pkgAcquire::MethodConfig *Cfg)
 {
+   if (MetaIndexFileSignature.empty() == false)
+   {
+      DestFile = MetaIndexFileSignature;
+      MetaIndexFileSignature.clear();
+   }
    Item::Done(Message, Size, Hashes, Cfg);
 
-   if(AuthPass == false)
+   if(MetaIndex->AuthPass == false)
    {
-      if(CheckDownloadDone(Message, Hashes) == true)
+      if(MetaIndex->CheckDownloadDone(this, Message, Hashes) == true)
       {
-         // destfile will be modified to point to MetaIndexFile for the
-         // gpgv method, so we need to save it here
-         MetaIndexFileSignature = DestFile;
-         QueueForSignatureVerify(MetaIndexFile, MetaIndexFileSignature);
+        // destfile will be modified to point to MetaIndexFile for the
+        // gpgv method, so we need to save it here
+        MetaIndexFileSignature = DestFile;
+        MetaIndex->QueueForSignatureVerify(this, MetaIndex->DestFile, DestFile);
       }
       return;
    }
-   else if(CheckAuthDone(Message) == true)
-      TransactionManager->TransactionStageCopy(this, MetaIndexFileSignature, GetFinalFilename());
+   else if(MetaIndex->CheckAuthDone(Message) == true)
+   {
+      if (TransactionManager->IMSHit == false)
+      {
+        TransactionManager->TransactionStageCopy(this, DestFile, GetFinalFilename());
+        TransactionManager->TransactionStageCopy(MetaIndex, MetaIndex->DestFile, MetaIndex->GetFinalFilename());
+      }
+   }
 }
                                                                        /*}}}*/
 void pkgAcqMetaSig::Failed(string Message,pkgAcquire::MethodConfig *Cnf)/*{{{*/
@@ -1815,20 +1847,18 @@ void pkgAcqMetaSig::Failed(string Message,pkgAcquire::MethodConfig *Cnf)/*{{{*/
    Item::Failed(Message,Cnf);
 
    // check if we need to fail at this point 
-   if (AuthPass == true && CheckStopAuthentication(Message))
+   if (MetaIndex->AuthPass == true && MetaIndex->CheckStopAuthentication(this, Message))
          return;
 
-   // FIXME: meh, this is not really elegant
-   string const Final = GetFinalFileNameFromURI(RealURI);
-   string const InReleaseURI = RealURI.replace(RealURI.rfind("Release.gpg"), 12,
-                                         "InRelease");
-   string const FinalInRelease = GetFinalFileNameFromURI(InReleaseURI);
+   string const FinalRelease = MetaIndex->GetFinalFilename();
+   string const FinalReleasegpg = GetFinalFilename();
+   string const FinalInRelease = TransactionManager->GetFinalFilename();
 
-   if (RealFileExists(Final) || RealFileExists(FinalInRelease))
+   if (RealFileExists(FinalReleasegpg) || RealFileExists(FinalInRelease))
    {
       std::string downgrade_msg;
       strprintf(downgrade_msg, _("The repository '%s' is no longer signed."),
-                URIDesc.c_str());
+                MetaIndex->URIDesc.c_str());
       if(_config->FindB("Acquire::AllowDowngradeToInsecureRepositories"))
       {
          // meh, the users wants to take risks (we still mark the packages
@@ -1841,7 +1871,7 @@ void pkgAcqMetaSig::Failed(string Message,pkgAcquire::MethodConfig *Cnf)/*{{{*/
       } else {
          _error->Error("%s", downgrade_msg.c_str());
         if (TransactionManager->IMSHit == false)
-           Rename(MetaIndexFile, MetaIndexFile+".FAILED");
+           Rename(MetaIndex->DestFile, MetaIndex->DestFile + ".FAILED");
         Item::Failed("Message: " + downgrade_msg, Cnf);
          TransactionManager->AbortTransaction();
          return;
@@ -1850,23 +1880,44 @@ void pkgAcqMetaSig::Failed(string Message,pkgAcquire::MethodConfig *Cnf)/*{{{*/
    else
       _error->Warning(_("The data from '%s' is not signed. Packages "
               "from that repository can not be authenticated."),
-           URIDesc.c_str());
+           MetaIndex->URIDesc.c_str());
 
-   // this ensures that any file in the lists/ dir is removed by the
-   // transaction
-   DestFile = GetPartialFileNameFromURI(RealURI);
+   // ensures that a Release.gpg file in the lists/ is removed by the transaction
    TransactionManager->TransactionStageRemoval(this, DestFile);
 
    // only allow going further if the users explicitely wants it
-   if(AllowInsecureRepositories(MetaIndexParser, TransactionManager, this) == true)
+   if(AllowInsecureRepositories(MetaIndex->MetaIndexParser, TransactionManager, this) == true)
    {
+      if (RealFileExists(FinalReleasegpg) || RealFileExists(FinalInRelease))
+      {
+        // open the last Release if we have it
+        if (TransactionManager->IMSHit == false)
+        {
+           MetaIndex->LastMetaIndexParser = new indexRecords;
+           _error->PushToStack();
+           if (RealFileExists(FinalInRelease))
+              MetaIndex->LastMetaIndexParser->Load(FinalInRelease);
+           else
+              MetaIndex->LastMetaIndexParser->Load(FinalRelease);
+           // its unlikely to happen, but if what we have is bad ignore it
+           if (_error->PendingError())
+           {
+              delete MetaIndex->LastMetaIndexParser;
+              MetaIndex->LastMetaIndexParser = NULL;
+           }
+           _error->RevertToStack();
+        }
+      }
+
       // we parse the indexes here because at this point the user wanted
       // a repository that may potentially harm him
-      MetaIndexParser->Load(MetaIndexFile);
-      if (!VerifyVendor(Message))
+      MetaIndex->MetaIndexParser->Load(MetaIndex->DestFile);
+      if (MetaIndex->VerifyVendor(Message) == false)
         /* expired Release files are still a problem you need extra force for */;
       else
-        QueueIndexes(true);
+        MetaIndex->QueueIndexes(true);
+
+      TransactionManager->TransactionStageCopy(MetaIndex, MetaIndex->DestFile, MetaIndex->GetFinalFilename());
    }
 
    // FIXME: this is used often (e.g. in pkgAcqIndexTrans) so refactor
@@ -1926,18 +1977,14 @@ void pkgAcqMetaIndex::Done(string Message,unsigned long long Size,      /*{{{*/
 {
    Item::Done(Message,Size,Hashes,Cfg);
 
-   if(CheckDownloadDone(Message, Hashes))
+   if(CheckDownloadDone(this, Message, Hashes))
    {
       // we have a Release file, now download the Signature, all further
       // verify/queue for additional downloads will be done in the
       // pkgAcqMetaSig::Done() code
-      std::string const MetaIndexFile = DestFile;
-      new pkgAcqMetaSig(Owner, TransactionManager, 
+      new pkgAcqMetaSig(Owner, TransactionManager,
                         MetaIndexSigURI, MetaIndexSigURIDesc,
-                        MetaIndexSigShortDesc, MetaIndexFile, IndexTargets, 
-                        MetaIndexParser);
-
-      TransactionManager->TransactionStageCopy(this, DestFile, GetFinalFilename());
+                        MetaIndexSigShortDesc, this);
    }
 }
                                                                        /*}}}*/
@@ -1948,6 +1995,40 @@ bool pkgAcqMetaBase::CheckAuthDone(string Message)                       /*{{{*/
    // perform additional verification of its contents, and use them
    // to verify the indexes we are about to download
 
+   if (TransactionManager->IMSHit == false)
+   {
+      // open the last (In)Release if we have it
+      std::string const FinalFile = GetFinalFilename();
+      std::string FinalRelease;
+      std::string FinalInRelease;
+      if (APT::String::Endswith(FinalFile, "InRelease"))
+      {
+        FinalInRelease = FinalFile;
+        FinalRelease = FinalFile.substr(0, FinalFile.length() - strlen("InRelease")) + "Release";
+      }
+      else
+      {
+        FinalInRelease = FinalFile.substr(0, FinalFile.length() - strlen("Release")) + "InRelease";
+        FinalRelease = FinalFile;
+      }
+      if (RealFileExists(FinalInRelease) || RealFileExists(FinalRelease))
+      {
+        LastMetaIndexParser = new indexRecords;
+        _error->PushToStack();
+        if (RealFileExists(FinalInRelease))
+           LastMetaIndexParser->Load(FinalInRelease);
+        else
+           LastMetaIndexParser->Load(FinalRelease);
+        // its unlikely to happen, but if what we have is bad ignore it
+        if (_error->PendingError())
+        {
+           delete LastMetaIndexParser;
+           LastMetaIndexParser = NULL;
+        }
+        _error->RevertToStack();
+      }
+   }
+
    if (!MetaIndexParser->Load(DestFile))
    {
       Status = StatAuthError;
@@ -2001,48 +2082,47 @@ std::string pkgAcqMetaBase::GetFinalFilename() const
 }
                                                                        /*}}}*/
 // pkgAcqMetaBase::QueueForSignatureVerify                             /*{{{*/
-void pkgAcqMetaBase::QueueForSignatureVerify(const std::string &MetaIndexFile,
-                                    const std::string &MetaIndexFileSignature)
+void pkgAcqMetaBase::QueueForSignatureVerify(pkgAcquire::Item * const I, std::string const &File, std::string const &Signature)
 {
    AuthPass = true;
-   Desc.URI = "gpgv:" + MetaIndexFileSignature;
-   DestFile = MetaIndexFile;
-   QueueURI(Desc);
-   SetActiveSubprocess("gpgv");
+   I->Desc.URI = "gpgv:" + Signature;
+   I->DestFile = File;
+   QueueURI(I->Desc);
+   I->SetActiveSubprocess("gpgv");
 }
                                                                        /*}}}*/
 // pkgAcqMetaBase::CheckDownloadDone                                   /*{{{*/
-bool pkgAcqMetaBase::CheckDownloadDone(const std::string &Message, HashStringList const &Hashes)
+bool pkgAcqMetaBase::CheckDownloadDone(pkgAcquire::Item * const I, const std::string &Message, HashStringList const &Hashes) const
 {
    // We have just finished downloading a Release file (it is not
    // verified yet)
 
-   string FileName = LookupTag(Message,"Filename");
+   string const FileName = LookupTag(Message,"Filename");
    if (FileName.empty() == true)
    {
-      Status = StatError;
-      ErrorText = "Method gave a blank filename";
+      I->Status = StatError;
+      I->ErrorText = "Method gave a blank filename";
       return false;
    }
 
-   if (FileName != DestFile)
+   if (FileName != I->DestFile)
    {
-      Local = true;
-      Desc.URI = "copy:" + FileName;
-      QueueURI(Desc);
+      I->Local = true;
+      I->Desc.URI = "copy:" + FileName;
+      I->QueueURI(I->Desc);
       return false;
    }
 
    // make sure to verify against the right file on I-M-S hit
-   IMSHit = StringToBool(LookupTag(Message,"IMS-Hit"),false);
-   if (IMSHit == false)
+   bool IMSHit = StringToBool(LookupTag(Message,"IMS-Hit"), false);
+   if (IMSHit == false && Hashes.usable())
    {
       // detect IMS-Hits servers haven't detected by Hash comparison
-      std::string FinalFile = GetFinalFilename();
+      std::string const FinalFile = I->GetFinalFilename();
       if (RealFileExists(FinalFile) && Hashes.VerifyFile(FinalFile) == true)
       {
         IMSHit = true;
-        unlink(DestFile.c_str());
+        unlink(I->DestFile.c_str());
       }
    }
 
@@ -2052,11 +2132,11 @@ bool pkgAcqMetaBase::CheckDownloadDone(const std::string &Message, HashStringLis
       // even if it doesn't exist.
       if (TransactionManager != NULL)
         TransactionManager->IMSHit = true;
-      DestFile = GetFinalFilename();
+      I->PartialFile = I->DestFile = I->GetFinalFilename();
    }
 
    // set Item to complete as the remaining work is all local (verify etc)
-   Complete = true;
+   I->Complete = true;
 
    return true;
 }
@@ -2175,6 +2255,19 @@ bool pkgAcqMetaBase::VerifyVendor(string Message)                        /*{{{*/
       }
    }
 
+   /* Did we get a file older than what we have? This is a last minute IMS hit and doubles
+      as a prevention of downgrading us to older (still valid) files */
+   if (TransactionManager->IMSHit == false && LastMetaIndexParser != NULL &&
+        LastMetaIndexParser->GetDate() > MetaIndexParser->GetDate())
+   {
+      TransactionManager->IMSHit = true;
+      unlink(DestFile.c_str());
+      PartialFile = DestFile = GetFinalFilename();
+      delete MetaIndexParser;
+      MetaIndexParser = LastMetaIndexParser;
+      LastMetaIndexParser = NULL;
+   }
+
    if (_config->FindB("Debug::pkgAcquire::Auth", false)) 
    {
       std::cerr << "Got Codename: " << MetaIndexParser->GetDist() << std::endl;
@@ -2248,7 +2341,6 @@ pkgAcqMetaClearSig::pkgAcqMetaClearSig(pkgAcquire *Owner,         /*{{{*/
 {
    // index targets + (worst case:) Release/Release.gpg
    ExpectedAdditionalItems = IndexTargets->size() + 2;
-
 }
                                                                        /*}}}*/
 pkgAcqMetaClearSig::~pkgAcqMetaClearSig()                              /*{{{*/
@@ -2268,14 +2360,25 @@ string pkgAcqMetaClearSig::Custom600Headers()
 }
                                                                        /*}}}*/
 // pkgAcqMetaClearSig::Done - We got a file                            /*{{{*/
-// ---------------------------------------------------------------------
+class APT_HIDDEN DummyItem : public pkgAcquire::Item
+{
+   std::string URI;
+   public:
+   virtual std::string DescURI() {return URI;};
+
+   DummyItem(pkgAcquire *Owner, std::string const &URI) : pkgAcquire::Item(Owner), URI(URI)
+   {
+      Status = StatDone;
+      DestFile = GetFinalFileNameFromURI(URI);
+   }
+};
 void pkgAcqMetaClearSig::Done(std::string Message,unsigned long long Size,
                               HashStringList const &Hashes,
                               pkgAcquire::MethodConfig *Cnf)
 {
    Item::Done(Message, Size, Hashes, Cnf);
 
-   // if we expect a ClearTextSignature (InRelase), ensure that
+   // if we expect a ClearTextSignature (InRelease), ensure that
    // this is what we get and if not fail to queue a 
    // Release/Release.gpg, see #346386
    if (FileExists(DestFile) && !StartsWithGPGClearTextSignature(DestFile))
@@ -2288,12 +2391,23 @@ void pkgAcqMetaClearSig::Done(std::string Message,unsigned long long Size,
 
    if(AuthPass == false)
    {
-      if(CheckDownloadDone(Message, Hashes) == true)
-         QueueForSignatureVerify(DestFile, DestFile);
+      if(CheckDownloadDone(this, Message, Hashes) == true)
+         QueueForSignatureVerify(this, DestFile, DestFile);
       return;
    }
    else if(CheckAuthDone(Message) == true)
-      TransactionManager->TransactionStageCopy(this, DestFile, GetFinalFilename());
+   {
+      if (TransactionManager->IMSHit == false)
+        TransactionManager->TransactionStageCopy(this, DestFile, GetFinalFilename());
+      else if (RealFileExists(GetFinalFilename()) == false)
+      {
+        // We got an InRelease file IMSHit, but we haven't one, which means
+        // we had a valid Release/Release.gpg combo stepping in, which we have
+        // to 'acquire' now to ensure list cleanup isn't removing them
+        new DummyItem(Owner, MetaIndexURI);
+        new DummyItem(Owner, MetaSigURI);
+      }
+   }
 }
                                                                        /*}}}*/
 void pkgAcqMetaClearSig::Failed(string Message,pkgAcquire::MethodConfig *Cnf) /*{{{*/
@@ -2318,7 +2432,7 @@ void pkgAcqMetaClearSig::Failed(string Message,pkgAcquire::MethodConfig *Cnf) /*
    }
    else
    {
-      if(CheckStopAuthentication(Message))
+      if(CheckStopAuthentication(this, Message))
          return;
 
       _error->Warning(_("The data from '%s' is not signed. Packages "
index 646de84167a0b17ce60bc5440d41bd181cdfb104..07c86f31be51b7b2bb667c4edfc09a04e9814e89 100644 (file)
@@ -394,6 +394,7 @@ class pkgAcqMetaBase  : public pkgAcquire::Item                             /*{{{*/
 
    /** \brief A package-system-specific parser for the meta-index file. */
    indexRecords *MetaIndexParser;
+   indexRecords *LastMetaIndexParser;
 
    /** \brief The index files which should be looked up in the meta-index
     *  and then downloaded.
@@ -429,11 +430,10 @@ class pkgAcqMetaBase  : public pkgAcquire::Item                           /*{{{*/
     *  \param Message The message block received from the fetch
     *  subprocess.
     */
-   bool CheckDownloadDone(const std::string &Message, HashStringList const &Hashes);
+   bool CheckDownloadDone(pkgAcquire::Item * const I, const std::string &Message, HashStringList const &Hashes) const;
 
    /** \brief Queue the downloaded Signature for verification */
-   void QueueForSignatureVerify(const std::string &MetaIndexFile,
-                                const std::string &MetaIndexFileSignature);
+   void QueueForSignatureVerify(pkgAcquire::Item * const I, std::string const &File, std::string const &Signature);
 
 #if APT_PKG_ABI >= 413
    virtual std::string Custom600Headers() const;
@@ -453,7 +453,7 @@ class pkgAcqMetaBase  : public pkgAcquire::Item                             /*{{{*/
    bool CheckAuthDone(std::string Message);
 
    /** Check if the current item should fail at this point */
-   bool CheckStopAuthentication(const std::string &Message);
+   bool CheckStopAuthentication(pkgAcquire::Item * const I, const std::string &Message);
 
    /** \brief Check that the release file is a release file for the
     *  correct distribution.
@@ -462,8 +462,7 @@ class pkgAcqMetaBase  : public pkgAcquire::Item                             /*{{{*/
     */
    bool VerifyVendor(std::string Message);
 
-   /** \brief Get the full pathname of the final file for the current URI */
-   virtual std::string GetFinalFilename() const;
+   virtual bool TransactionState(TransactionStates const state);
 
  public:
    // This refers more to the Transaction-Manager than the actual file
@@ -487,6 +486,9 @@ class pkgAcqMetaBase  : public pkgAcquire::Item                             /*{{{*/
     */
    void TransactionStageRemoval(Item *I, const std::string &FinalFile);
 
+   /** \brief Get the full pathname of the final file for the current URI */
+   virtual std::string GetFinalFilename() const;
+
    pkgAcqMetaBase(pkgAcquire *Owner,
                   const std::vector<IndexTarget*>* IndexTargets,
                   indexRecords* MetaIndexParser,
@@ -495,50 +497,6 @@ class pkgAcqMetaBase  : public pkgAcquire::Item                            /*{{{*/
                   pkgAcqMetaBase *TransactionManager=NULL);
 };
                                                                        /*}}}*/
-/** \brief An acquire item that downloads the detached signature       {{{
- *  of a meta-index (Release) file, then queues up the release
- *  file itself.
- *
- *  \todo Why protected members?
- *
- *  \sa pkgAcqMetaIndex
- */
-class APT_HIDDEN pkgAcqMetaSig : public pkgAcqMetaBase
-{
-   void *d;
-
-   protected:
-
-   /** \brief The file we need to verify */
-   std::string MetaIndexFile;
-
-   /** \brief The file we use to verify the MetaIndexFile with */
-   std::string MetaIndexFileSignature;
-
-   /** \brief Long URI description used in the acquire system */
-   std::string URIDesc;
-
-   /** \brief Short URI description used in the acquire system */
-   std::string ShortDesc;
-
-   public:
-
-   // Specialized action members
-   virtual void Failed(std::string Message,pkgAcquire::MethodConfig *Cnf);
-   virtual void Done(std::string Message,unsigned long long Size,
-                     HashStringList const &Hashes,
-                    pkgAcquire::MethodConfig *Cnf);
-
-   /** \brief Create a new pkgAcqMetaSig. */
-   pkgAcqMetaSig(pkgAcquire *Owner,
-                 pkgAcqMetaBase *TransactionManager,
-                 std::string URI,std::string URIDesc, std::string ShortDesc,
-                 std::string MetaIndexFile,
-                const std::vector<IndexTarget*>* IndexTargets,
-                indexRecords* MetaIndexParser);
-   virtual ~pkgAcqMetaSig();
-};
-                                                                       /*}}}*/
 /** \brief An item that is responsible for downloading the meta-index  {{{
  *  file (i.e., Release) itself and verifying its signature.
  *
@@ -584,6 +542,53 @@ class APT_HIDDEN pkgAcqMetaIndex : public pkgAcqMetaBase
                    std::string MetaIndexSigURI, std::string MetaIndexSigURIDesc, std::string MetaIndexSigShortDesc,
                   const std::vector<IndexTarget*>* IndexTargets,
                   indexRecords* MetaIndexParser);
+
+   friend class pkgAcqMetaSig;
+};
+                                                                       /*}}}*/
+/** \brief An acquire item that downloads the detached signature       {{{
+ *  of a meta-index (Release) file, then queues up the release
+ *  file itself.
+ *
+ *  \todo Why protected members?
+ *
+ *  \sa pkgAcqMetaIndex
+ */
+class APT_HIDDEN pkgAcqMetaSig : public pkgAcquire::Item
+{
+   void *d;
+
+   pkgAcqMetaIndex * const MetaIndex;
+
+   /** \brief The file we use to verify the MetaIndexFile with (not always set!) */
+   std::string MetaIndexFileSignature;
+
+   protected:
+
+   /** \brief Long URI description used in the acquire system */
+   std::string URIDesc;
+
+   /** \brief URI used to get the file */
+   std::string RealURI;
+
+   /** \brief Get the full pathname of the final file for the current URI */
+   virtual std::string GetFinalFilename() const;
+
+   public:
+   virtual std::string DescURI() {return RealURI;};
+
+   // Specialized action members
+   virtual void Failed(std::string Message,pkgAcquire::MethodConfig *Cnf);
+   virtual void Done(std::string Message,unsigned long long Size,
+                     HashStringList const &Hashes,
+                    pkgAcquire::MethodConfig *Cnf);
+
+   /** \brief Create a new pkgAcqMetaSig. */
+   pkgAcqMetaSig(pkgAcquire *Owner,
+        pkgAcqMetaBase *TransactionManager,
+        std::string const &URI,std::string const &URIDesc,
+        std::string const &ShortDesc, pkgAcqMetaIndex * const MetaIndex);
+   virtual ~pkgAcqMetaSig();
 };
                                                                        /*}}}*/
 /** \brief An item repsonsible for downloading clearsigned metaindexes {{{*/
index d65266f649a229e843401e8f6ebd0762ab2bfd8c..de26178337b96e7344ddd4b57ac8bde711d684f7 100644 (file)
@@ -58,6 +58,11 @@ APT_PURE time_t indexRecords::GetValidUntil() const
    return this->ValidUntil;
 }
 
+APT_PURE time_t indexRecords::GetDate() const
+{
+   return this->Date;
+}
+
 APT_PURE indexRecords::checkSum *indexRecords::Lookup(const string MetaKey)
 {
    std::map<std::string, indexRecords::checkSum* >::const_iterator sum = Entries.find(MetaKey);
@@ -133,9 +138,15 @@ bool indexRecords::Load(const string Filename)                             /*{{{*/
       return false;
    }
 
-   string Label = Section.FindS("Label");
-   string StrDate = Section.FindS("Date");
-   string StrValidUntil = Section.FindS("Valid-Until");
+   string const StrDate = Section.FindS("Date");
+   if (RFC1123StrToTime(StrDate.c_str(), Date) == false)
+   {
+      strprintf(ErrorText, _("Invalid 'Date' entry in Release file %s"), Filename.c_str());
+      return false;
+   }
+
+   string const Label = Section.FindS("Label");
+   string const StrValidUntil = Section.FindS("Valid-Until");
 
    // if we have a Valid-Until header in the Release file, use it as default
    if (StrValidUntil.empty() == false)
@@ -158,20 +169,13 @@ bool indexRecords::Load(const string Filename)                            /*{{{*/
       (MinAge == 0 || ValidUntil == 0)) // No user settings, use the one from the Release file
       return true;
 
-   time_t date;
-   if (RFC1123StrToTime(StrDate.c_str(), date) == false)
-   {
-      strprintf(ErrorText, _("Invalid 'Date' entry in Release file %s"), Filename.c_str());
-      return false;
-   }
-
    if (MinAge != 0 && ValidUntil != 0) {
-      time_t const min_date = date + MinAge;
+      time_t const min_date = Date + MinAge;
       if (ValidUntil < min_date)
         ValidUntil = min_date;
    }
    if (MaxAge != 0) {
-      time_t const max_date = date + MaxAge;
+      time_t const max_date = Date + MaxAge;
       if (ValidUntil == 0 || ValidUntil > max_date)
         ValidUntil = max_date;
    }
index 35e534c12c0da9a6e582eb9d0d8dc8a9ab1e12fb..6ed5f0c2b1dadc7a32135400c6b82c0ad752f09f 100644 (file)
@@ -36,6 +36,7 @@ class indexRecords
    std::string Dist;
    std::string Suite;
    std::string ExpectedDist;
+   time_t Date;
    time_t ValidUntil;
    bool SupportsAcquireByHash;
 
@@ -62,6 +63,7 @@ class indexRecords
    std::string GetSuite() const;
    bool GetSupportsAcquireByHash() const;
    time_t GetValidUntil() const;
+   time_t GetDate() const;
    std::string GetExpectedDist() const;
 
    /** \brief check if source is marked as always trusted */
index 8c8936eaddf3a9ed01ede6f201f2e075de8a891f..b253deb915d5ee93a3eec3a8521ba691787da0da 100644 (file)
@@ -1164,9 +1164,9 @@ testfileequal() {
        shift
        msgtest "Test for correctness of file" "$FILE"
        if [ -z "$*" ]; then
-               echo -n "" | checkdiff $FILE - && msgpass || msgfail
+               echo -n "" | checkdiff - $FILE && msgpass || msgfail
        else
-               echo "$*" | checkdiff $FILE - && msgpass || msgfail
+               echo "$*" | checkdiff - $FILE && msgpass || msgfail
        fi
 }
 
@@ -1547,7 +1547,8 @@ aptautotest_aptcdrom_add() { aptautotest_aptget_update "$@"; }
 testaptautotestnodpkgwarning() {
        local TESTCALL="$1"
        while [ -n "$2" ]; do
-               if expr match "$2" '^-[a-z]*s' >/dev/null 2>&1; then return; fi
+               if expr match "$2" '^-[a-z]*s' >/dev/null 2>&1; then return; fi # simulation mode
+               if expr match "$2" '^-dy\?' >/dev/null 2>&1; then return; fi # download-only mode
                shift
        done
        testfailure grep '^dpkg: warning:.*ignor.*' "${TMPWORKINGDIRECTORY}/rootdir/tmp-before/${TESTCALL}.output"
index f091bffaa9168b69dc47115e54efe392b1ed4a00..7385e701a023b8abf38d88e55d2c1e1845006c6f 100755 (executable)
@@ -43,7 +43,7 @@ runtest() {
     testfileequal 'listsdir.lst' "$(listcurrentlistsdirectory)"
 
     # ensure that we still do a hash check for other files on ims hit of Release
-    if grep -q '^Hit .* \(InRelease\|Release.gpg\)$' expected.output ; then
+    if grep -q '^Hit .* InRelease$' expected.output || ! grep -q '^Ign .* Release\(\.gpg\)\?$' expected.output; then
            $TEST aptget update -o Debug::Acquire::gpgv=1
            cp rootdir/tmp/${TEST}.output goodsign.output
            testfileequal 'listsdir.lst' "$(listcurrentlistsdirectory)"
@@ -66,7 +66,6 @@ msgmsg 'Release/Release.gpg'
 EXPECT='Ign http://localhost:8080 unstable InRelease
   404  Not Found
 Hit http://localhost:8080 unstable Release
-Hit http://localhost:8080 unstable Release.gpg
 Reading package lists...'
 find aptarchive -name 'InRelease' -delete
 echo 'Acquire::GzipIndexes "0";' > rootdir/etc/apt/apt.conf.d/02compressindex
@@ -81,7 +80,7 @@ Hit http://localhost:8080 unstable Release
 Ign http://localhost:8080 unstable Release.gpg
   404  Not Found
 Reading package lists...
-W: The data from 'http://localhost:8080 unstable Release.gpg' is not signed. Packages from that repository can not be authenticated."
+W: The data from 'http://localhost:8080 unstable Release' is not signed. Packages from that repository can not be authenticated."
 find aptarchive -name 'Release.gpg' -delete
 echo 'Acquire::GzipIndexes "0";' > rootdir/etc/apt/apt.conf.d/02compressindex
 runtest 'warning'
@@ -108,8 +107,7 @@ msgmsg 'expired Release/Release.gpg'
 EXPECT='Ign http://localhost:8080 unstable InRelease
   404  Not Found
 Hit http://localhost:8080 unstable Release
-Hit http://localhost:8080 unstable Release.gpg
-E: Release file for http://localhost:8080/dists/unstable/Release.gpg is expired (invalid since). Updates for this repository will not be applied.'
+E: Release file for http://localhost:8080/dists/unstable/Release is expired (invalid since). Updates for this repository will not be applied.'
 find aptarchive -name 'InRelease' -delete
 echo 'Acquire::GzipIndexes "0";' > rootdir/etc/apt/apt.conf.d/02compressindex
 runtest 'failure'
@@ -122,8 +120,8 @@ EXPECT="Ign http://localhost:8080 unstable InRelease
 Hit http://localhost:8080 unstable Release
 Ign http://localhost:8080 unstable Release.gpg
   404  Not Found
-W: The data from 'http://localhost:8080 unstable Release.gpg' is not signed. Packages from that repository can not be authenticated.
-E: Release file for http://localhost:8080/dists/unstable/InRelease is expired (invalid since). Updates for this repository will not be applied."
+W: The data from 'http://localhost:8080 unstable Release' is not signed. Packages from that repository can not be authenticated.
+E: Release file for http://localhost:8080/dists/unstable/Release is expired (invalid since). Updates for this repository will not be applied."
 find aptarchive -name 'Release.gpg' -delete
 echo 'Acquire::GzipIndexes "0";' > rootdir/etc/apt/apt.conf.d/02compressindex
 runtest 'failure' 'warning'
index 71576de810fac80fa303afa2fb1b9c7539806d2c..db4430ea32fdf86943b115d1feb90968f4e61b89 100755 (executable)
@@ -8,6 +8,7 @@ set -e
 
 simulate_mitm_and_inject_evil_package()
 {
+    redatereleasefiles '+1 hour'
     rm -f $APTARCHIVE/dists/unstable/InRelease
     rm -f $APTARCHIVE/dists/unstable/Release.gpg
     inject_evil_package
@@ -31,7 +32,7 @@ EOF
 
 assert_update_is_refused_and_last_good_state_used()
 {
-    testfailureequal "E: The repository 'file: unstable Release.gpg' is no longer signed." aptget update -qq
+    testfailuremsg "E: The repository 'file: unstable Release' is no longer signed." aptget update
 
     assert_repo_is_intact
 }
@@ -193,7 +194,7 @@ test_release_gpg_to_invalid_release_release_gpg()
     echo "Some evil data" >>  $APTARCHIVE/dists/unstable/Release
     inject_evil_package
 
-    testwarningequal "W: An error occurred during the signature verification. The repository is not updated and the previous index files will be used. GPG error: file: unstable Release.gpg: The following signatures were invalid: BADSIG 5A90D141DBAC8DAE Joe Sixpack (APT Testcases Dummy) <joe@example.org>
+    testwarningequal "W: An error occurred during the signature verification. The repository is not updated and the previous index files will be used. GPG error: file: 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>
 
index a67ecb760e8ae20e7f6744e3d9cddfe2b5e59f96..b1d55c15615e39a9d3a98b628111add4856ea109 100755 (executable)
@@ -56,7 +56,6 @@ Reading package lists..." aptget update
        testsuccessequal "Ign $1 unstable InRelease
   404  Not Found
 Hit $1 unstable Release
-Hit $1 unstable Release.gpg
 Reading package lists..." aptget update
        testfileequal 'listsdir.lst' "$(listcurrentlistsdirectory)"
 
@@ -66,7 +65,6 @@ Reading package lists..." aptget update
        testsuccessequal "Ign $1 unstable InRelease
   404  Not Found
 Hit $1 unstable Release
-Hit $1 unstable Release.gpg
 Reading package lists..." aptget update
        testfileequal 'listsdir-without-amd64.lst' "$(listcurrentlistsdirectory)"
 
@@ -75,7 +73,6 @@ Reading package lists..." aptget update
        testsuccessequal "Ign $1 unstable InRelease
   404  Not Found
 Hit $1 unstable Release
-Hit $1 unstable Release.gpg
 Get:1 $1 unstable/main amd64 Packages [$(stat -c '%s' 'aptarchive/dists/unstable/main/binary-amd64/Packages.gz') B]
 Reading package lists..." aptget update
        testfileequal 'listsdir.lst' "$(listcurrentlistsdirectory)"
@@ -85,7 +82,6 @@ Reading package lists..." aptget update
        testsuccessequal "Ign $1 unstable InRelease
   404  Not Found
 Get:1 $1 unstable Release [$(stat -c '%s' 'aptarchive/dists/unstable/Release') B]
-Get:2 $1 unstable Release.gpg [$(stat -c '%s' 'aptarchive/dists/unstable/Release.gpg') B]
 Reading package lists..." aptget update
        webserverconfig 'aptwebserver::support::modified-since' 'true'
        webserverconfig 'aptwebserver::support::last-modified' 'true'
index 29fe1ab5627a89955c5b8116a78e01027da02364..b464a04a1a808b098d87ec2e3a085f701c7ea1be 100755 (executable)
@@ -78,7 +78,7 @@ test_inrelease_to_valid_release() {
     rm $APTARCHIVE/dists/unstable/Release.gpg
 
     # update fails
-    testfailureequal "E: The repository 'file: unstable Release.gpg' is no longer signed." aptget update -qq
+    testfailureequal "E: The repository 'file: unstable Release' is no longer signed." aptget update -qq
 
     # test that security downgrade was not successful
     testfileequal lists.before "$(listcurrentlistsdirectory)"
@@ -101,7 +101,7 @@ test_inrelease_to_release_reverts_all() {
     break_repository_sources_index '+1hour'
 
     # ensure error
-    testfailureequal "E: The repository 'file: unstable Release.gpg' is no longer signed." aptget update -qq # -o Debug::acquire::transaction=1
+    testfailureequal "E: The repository 'file: unstable Release' is no longer signed." aptget update -qq # -o Debug::acquire::transaction=1
 
     # ensure that the Packages file is also rolled back
     testfileequal lists.before "$(listcurrentlistsdirectory)"
diff --git a/test/integration/test-releasefile-date-older b/test/integration/test-releasefile-date-older
new file mode 100755 (executable)
index 0000000..5cdc34f
--- /dev/null
@@ -0,0 +1,62 @@
+#!/bin/sh
+set -e
+
+TESTDIR=$(readlink -f $(dirname $0))
+. $TESTDIR/framework
+setupenvironment
+configarchitecture 'i386'
+
+insertpackage 'wheezy' 'apt' 'all' '0.8.15'
+
+setupaptarchive --no-update
+
+# we don't complain as the server could have just sent a 'Hit' here and this
+# 'downgrade attack' is usually performed by out-of-sync mirrors. Valid-Until
+# catches the 'real' downgrade attacks (expect that it finds stale mirrors).
+# Scaring users with an error here serves hence no point.
+
+msgmsg 'InRelease file is silently rejected if' 'new Date is before old Date'
+rm -rf rootdir/var/lib/apt/lists
+generatereleasefiles 'now' 'now + 7 days'
+signreleasefiles
+testsuccess aptget update
+listcurrentlistsdirectory > listsdir.lst
+redatereleasefiles 'now - 2 days'
+testsuccess aptget update
+testfileequal 'listsdir.lst' "$(listcurrentlistsdirectory)"
+
+msgmsg 'Release.gpg file is silently rejected if' 'new Date is before old Date'
+rm -rf rootdir/var/lib/apt/lists
+generatereleasefiles 'now' 'now + 7 days'
+signreleasefiles
+find aptarchive -name 'InRelease' -delete
+testsuccess aptget update
+listcurrentlistsdirectory > listsdir.lst
+redatereleasefiles 'now - 2 days'
+find aptarchive -name 'InRelease' -delete
+testsuccess aptget update
+testfileequal 'listsdir.lst' "$(listcurrentlistsdirectory)"
+
+msgmsg 'Crisscross InRelease/Release.gpg file is silently rejected if' 'new Date is before old Date'
+rm -rf rootdir/var/lib/apt/lists
+generatereleasefiles 'now' 'now + 7 days'
+signreleasefiles
+find aptarchive -name 'Release.gpg' -delete
+testsuccess aptget update
+listcurrentlistsdirectory > listsdir.lst
+redatereleasefiles 'now - 2 days'
+find aptarchive -name 'InRelease' -delete
+testsuccess aptget update
+testfileequal 'listsdir.lst' "$(listcurrentlistsdirectory)"
+
+msgmsg 'Crisscross Release.gpg/InRelease file is silently rejected if' 'new Date is before old Date'
+rm -rf rootdir/var/lib/apt/lists
+generatereleasefiles 'now' 'now + 7 days'
+signreleasefiles
+find aptarchive -name 'InRelease' -delete
+testsuccess aptget update
+listcurrentlistsdirectory > listsdir.lst
+redatereleasefiles 'now - 2 days'
+find aptarchive -name 'Release.gpg' -delete
+testsuccess aptget update
+testfileequal 'listsdir.lst' "$(listcurrentlistsdirectory)"
index 0d9a9125476371bc440e662de4497954b0201517..e000abf5d3ebfc44b453dc14580000f652cb9fcf 100755 (executable)
@@ -16,13 +16,12 @@ setupaptarchive --no-update
 
 runtest() {
        local MSG="$1"
-       msgtest "$1" "$2"
+       msgtest "Release file is $MSG as it has" "$2"
        rm -rf rootdir/var/lib/apt/lists
-       aptget clean
        generatereleasefiles "$3" "$4"
        signreleasefiles
        shift 4
-       if expr match "$MSG" '.*accepted.*' >/dev/null; then
+       if [ "$MSG" = 'accepted' ]; then
                testsuccess --nomsg aptget update "$@"
                testfailure grep -q 'is expired' rootdir/tmp/testsuccess.output
        else
@@ -31,19 +30,19 @@ runtest() {
        fi
 }
 
-runtest 'Release file is accepted as it has' 'no Until' '' ''
-runtest 'Release file is accepted as it has' 'no Until and good Max-Valid' '' '' -o Acquire::Max-ValidTime=3600
-runtest 'Release file is rejected as it has' 'no Until, but bad Max-Valid' 'now - 2 days' '' -o Acquire::Max-ValidTime=3600
-runtest 'Release file is accepted as it has' 'good Until' 'now - 3 days' 'now + 1 day'
-runtest 'Release file is rejected as it has' 'bad Until' 'now - 7 days' 'now - 4 days'
-runtest 'Release file is rejected as it has' 'bad Until (ignore good Max-Valid)' 'now - 7 days' 'now - 4 days' -o Acquire::Max-ValidTime=1209600
-runtest 'Release file is rejected as it has' 'bad Max-Valid (bad Until)' 'now - 7 days' 'now - 4 days' -o Acquire::Max-ValidTime=86400
-runtest 'Release file is rejected as it has' 'bad Max-Valid (good Until)' 'now - 7 days' 'now + 4 days' -o Acquire::Max-ValidTime=86400
-runtest 'Release file is accepted as it has' 'good labeled Max-Valid' 'now - 7 days' 'now + 4 days' -o Acquire::Max-ValidTime=86400 -o Acquire::Max-ValidTime::Testcases=1209600
-runtest 'Release file is rejected as it has' 'bad labeled Max-Valid' 'now - 7 days' 'now + 4 days' -o Acquire::Max-ValidTime=1209600 -o Acquire::Max-ValidTime::Testcases=86400
-runtest 'Release file is accepted as it has' 'good Until (good Min-Valid, no Max-Valid)' 'now - 7 days' 'now + 1 days' -o Acquire::Min-ValidTime=1209600
-runtest 'Release file is accepted as it has' 'good Min-Valid (bad Until, no Max-Valid)' 'now - 7 days' 'now - 4 days' -o Acquire::Min-ValidTime=1209600
-runtest 'Release file is accepted as it has' 'good Min-Valid (bad Until, good Max-Valid) <' 'now - 7 days' 'now - 2 days' -o Acquire::Min-ValidTime=1209600 -o Acquire::Max-ValidTime=2419200
-runtest 'Release file is rejected as it has' 'bad Max-Valid (bad Until, good Min-Valid) >' 'now - 7 days' 'now - 2 days' -o Acquire::Max-ValidTime=12096 -o Acquire::Min-ValidTime=2419200
-runtest 'Release file is rejected as it has' 'bad Max-Valid (bad Until, bad Min-Valid) <' 'now - 7 days' 'now - 2 days' -o Acquire::Min-ValidTime=12096 -o Acquire::Max-ValidTime=241920
-runtest 'Release file is rejected as it has' 'bad Max-Valid (bad Until, bad Min-Valid) >' 'now - 7 days' 'now - 2 days' -o Acquire::Max-ValidTime=12096 -o Acquire::Min-ValidTime=241920
+runtest 'accepted' 'no Until' '' ''
+runtest 'accepted' 'no Until and good Max-Valid' '' '' -o Acquire::Max-ValidTime=3600
+runtest 'rejected' 'no Until, but bad Max-Valid' 'now - 2 days' '' -o Acquire::Max-ValidTime=3600
+runtest 'accepted' 'good Until' 'now - 3 days' 'now + 1 day'
+runtest 'rejected' 'bad Until' 'now - 7 days' 'now - 4 days'
+runtest 'rejected' 'bad Until (ignore good Max-Valid)' 'now - 7 days' 'now - 4 days' -o Acquire::Max-ValidTime=1209600
+runtest 'rejected' 'bad Max-Valid (bad Until)' 'now - 7 days' 'now - 4 days' -o Acquire::Max-ValidTime=86400
+runtest 'rejected' 'bad Max-Valid (good Until)' 'now - 7 days' 'now + 4 days' -o Acquire::Max-ValidTime=86400
+runtest 'accepted' 'good labeled Max-Valid' 'now - 7 days' 'now + 4 days' -o Acquire::Max-ValidTime=86400 -o Acquire::Max-ValidTime::Testcases=1209600
+runtest 'rejected' 'bad labeled Max-Valid' 'now - 7 days' 'now + 4 days' -o Acquire::Max-ValidTime=1209600 -o Acquire::Max-ValidTime::Testcases=86400
+runtest 'accepted' 'good Until (good Min-Valid, no Max-Valid)' 'now - 7 days' 'now + 1 days' -o Acquire::Min-ValidTime=1209600
+runtest 'accepted' 'good Min-Valid (bad Until, no Max-Valid)' 'now - 7 days' 'now - 4 days' -o Acquire::Min-ValidTime=1209600
+runtest 'accepted' 'good Min-Valid (bad Until, good Max-Valid) <' 'now - 7 days' 'now - 2 days' -o Acquire::Min-ValidTime=1209600 -o Acquire::Max-ValidTime=2419200
+runtest 'rejected' 'bad Max-Valid (bad Until, good Min-Valid) >' 'now - 7 days' 'now - 2 days' -o Acquire::Max-ValidTime=12096 -o Acquire::Min-ValidTime=2419200
+runtest 'rejected' 'bad Max-Valid (bad Until, bad Min-Valid) <' 'now - 7 days' 'now - 2 days' -o Acquire::Min-ValidTime=12096 -o Acquire::Max-ValidTime=241920
+runtest 'rejected' 'bad Max-Valid (bad Until, bad Min-Valid) >' 'now - 7 days' 'now - 2 days' -o Acquire::Max-ValidTime=12096 -o Acquire::Min-ValidTime=241920
index 363b7fe5bb73f569731a640e23fc4a74db379c18..469ed34d2137d7037c93cbb085c58d0e3acee8f4 100755 (executable)
@@ -91,25 +91,9 @@ touch aptarchive/apt.deb
 
 PKGFILE="${TESTDIR}/$(echo "$(basename $0)" | sed 's#^test-#Packages-#')"
 
-updatesuccess() {
-       local LOG='update.log'
-       if aptget update >$LOG 2>&1 || grep -q -E '^(W|E): ' $LOG; then
-               msgpass
-       else
-               cat $LOG
-               msgfail
-       fi
-}
-
-updatefailure() {
-       local LOG='update.log'
-       aptget update >$LOG 2>&1 || true
-       if grep -q -E "$1" $LOG; then
-               msgpass
-       else
-               cat $LOG
-               msgfail
-       fi
+updatewithwarnings() {
+       testwarning aptget update
+       testsuccess grep -E "$1" rootdir/tmp/testwarning.output
 }
 
 runtest() {
@@ -117,8 +101,8 @@ runtest() {
        rm -rf rootdir/var/lib/apt/lists
        signreleasefiles 'Joe Sixpack'
        find aptarchive/ -name "$DELETEFILE" -delete
-       msgtest 'Cold archive signed by' 'Joe Sixpack'
-       updatesuccess
+       msgmsg 'Cold archive signed by' 'Joe Sixpack'
+       testsuccess aptget update
        testsuccessequal "$(cat ${PKGFILE})
 " aptcache show apt
        installaptold
@@ -126,8 +110,8 @@ runtest() {
        prepare ${PKGFILE}-new
        signreleasefiles 'Joe Sixpack'
        find aptarchive/ -name "$DELETEFILE" -delete
-       msgtest 'Good warm archive signed by' 'Joe Sixpack'
-       updatesuccess
+       msgmsg 'Good warm archive signed by' 'Joe Sixpack'
+       testsuccess aptget update
        testsuccessequal "$(cat ${PKGFILE}-new)
 " aptcache show apt
        installaptnew
@@ -137,8 +121,8 @@ runtest() {
        cp keys/rexexpired.pub rootdir/etc/apt/trusted.gpg.d/rexexpired.gpg
        signreleasefiles 'Rex Expired'
        find aptarchive/ -name "$DELETEFILE" -delete
-       msgtest 'Cold archive signed by' 'Rex Expired'
-       updatefailure '^W: .* KEYEXPIRED'
+       msgmsg 'Cold archive signed by' 'Rex Expired'
+       updatewithwarnings '^W: .* KEYEXPIRED'
        testsuccessequal "$(cat ${PKGFILE})
 " aptcache show apt
        failaptold
@@ -148,8 +132,8 @@ runtest() {
        rm -rf rootdir/var/lib/apt/lists
        signreleasefiles 'Marvin Paranoid'
        find aptarchive/ -name "$DELETEFILE" -delete
-       msgtest 'Cold archive signed by' 'Marvin Paranoid'
-       updatefailure '^W: .* NO_PUBKEY'
+       msgmsg 'Cold archive signed by' 'Marvin Paranoid'
+       updatewithwarnings '^W: .* NO_PUBKEY'
        testsuccessequal "$(cat ${PKGFILE})
 " aptcache show apt
        failaptold
@@ -162,8 +146,8 @@ runtest() {
        done
        signreleasefiles 'Joe Sixpack'
        find aptarchive/ -name "$DELETEFILE" -delete
-       msgtest 'Bad warm archive signed by' 'Joe Sixpack'
-       updatesuccess
+       msgmsg 'Bad warm archive signed by' 'Joe Sixpack'
+       testsuccess aptget update
        testsuccessequal "$(cat ${PKGFILE}-new)
 " aptcache show apt
        installaptnew
@@ -173,8 +157,8 @@ runtest() {
        rm -rf rootdir/var/lib/apt/lists
        signreleasefiles 'Joe Sixpack'
        find aptarchive/ -name "$DELETEFILE" -delete
-       msgtest 'Cold archive signed by' 'Joe Sixpack'
-       updatesuccess
+       msgmsg 'Cold archive signed by' 'Joe Sixpack'
+       testsuccess aptget update
        testsuccessequal "$(cat ${PKGFILE})
 " aptcache show apt
        installaptold
@@ -182,8 +166,8 @@ runtest() {
        prepare ${PKGFILE}-new
        signreleasefiles 'Marvin Paranoid'
        find aptarchive/ -name "$DELETEFILE" -delete
-       msgtest 'Good warm archive signed by' 'Marvin Paranoid'
-       updatefailure '^W: .* NO_PUBKEY'
+       msgmsg 'Good warm archive signed by' 'Marvin Paranoid'
+       updatewithwarnings '^W: .* NO_PUBKEY'
        testsuccessequal "$(cat ${PKGFILE})
 " aptcache show apt
        installaptold
@@ -192,8 +176,8 @@ runtest() {
        cp keys/rexexpired.pub rootdir/etc/apt/trusted.gpg.d/rexexpired.gpg
        signreleasefiles 'Rex Expired'
        find aptarchive/ -name "$DELETEFILE" -delete
-       msgtest 'Good warm archive signed by' 'Rex Expired'
-       updatefailure '^W: .* KEYEXPIRED'
+       msgmsg 'Good warm archive signed by' 'Rex Expired'
+       updatewithwarnings '^W: .* KEYEXPIRED'
        testsuccessequal "$(cat ${PKGFILE})
 " aptcache show apt
        installaptold
@@ -202,8 +186,8 @@ runtest() {
        prepare ${PKGFILE}-new
        signreleasefiles
        find aptarchive/ -name "$DELETEFILE" -delete
-       msgtest 'Good warm archive signed by' 'Joe Sixpack'
-       updatesuccess
+       msgmsg 'Good warm archive signed by' 'Joe Sixpack'
+       testsuccess aptget update
        testsuccessequal "$(cat ${PKGFILE}-new)
 " aptcache show apt
        installaptnew
@@ -213,24 +197,24 @@ runtest2() {
        prepare ${PKGFILE}
        rm -rf rootdir/var/lib/apt/lists
        signreleasefiles 'Joe Sixpack'
-       msgtest 'Cold archive signed by' 'Joe Sixpack'
-       updatesuccess
+       msgmsg 'Cold archive signed by' 'Joe Sixpack'
+       testsuccess aptget update
 
        # New .deb but now an unsigned archive. For example MITM to circumvent
        # package verification.
        prepare ${PKGFILE}-new
        find aptarchive/ -name InRelease -delete
        find aptarchive/ -name Release.gpg -delete
-       msgtest 'Warm archive signed by' 'nobody'
-       updatesuccess
+       msgmsg 'Warm archive signed by' 'nobody'
+       updatewithwarnings 'W: .* no longer signed.'
        testsuccessequal "$(cat ${PKGFILE}-new)
 " aptcache show apt
        failaptnew
 
        # Unsigned archive from the beginning must also be detected.
        rm -rf rootdir/var/lib/apt/lists
-       msgtest 'Cold archive signed by' 'nobody'
-       updatesuccess
+       msgmsg 'Cold archive signed by' 'nobody'
+       updatewithwarnings 'W: .* is not signed.'
        testsuccessequal "$(cat ${PKGFILE}-new)
 " aptcache show apt
        failaptnew