]> git.saurik.com Git - apt.git/blobdiff - apt-pkg/acquire-item.cc
add [weak] tag to hash errors to indicate insufficiency
[apt.git] / apt-pkg / acquire-item.cc
index 959627a01ed2a6111568767e05ff5b388f31ae70..63b3c9a1f9806cc95fe5bbb684842b2060876287 100644 (file)
@@ -175,41 +175,79 @@ static void ReportMirrorFailureToCentral(pkgAcquire::Item const &I, std::string
 }
                                                                        /*}}}*/
 
-static bool MessageInsecureRepository(bool const isError, std::string const &msg)/*{{{*/
+static bool MessageInsecureRepository(bool const isError, char const * const msg, std::string const &repo)/*{{{*/
 {
+   std::string m;
+   strprintf(m, msg, repo.c_str());
    if (isError)
    {
-      _error->Error("%s", msg.c_str());
+      _error->Error("%s", m.c_str());
       _error->Notice("%s", _("Updating from such a repository can't be done securely, and is therefore disabled by default."));
    }
    else
    {
-      _error->Warning("%s", msg.c_str());
+      _error->Warning("%s", m.c_str());
       _error->Notice("%s", _("Data from such a repository can't be authenticated and is therefore potentially dangerous to use."));
    }
    _error->Notice("%s", _("See apt-secure(8) manpage for repository creation and user configuration details."));
    return false;
-}
-static bool APT_NONNULL(2) MessageInsecureRepository(bool const isError, char const * const msg, std::string const &repo)
-{
-   std::string m;
-   strprintf(m, msg, repo.c_str());
-   return MessageInsecureRepository(isError, m);
 }
                                                                        /*}}}*/
-static bool APT_NONNULL(1, 3, 4, 5) AllowInsecureRepositories(char const * const msg, std::string const &repo,/*{{{*/
+// AllowInsecureRepositories                                           /*{{{*/
+enum class InsecureType { UNSIGNED, WEAK, NORELEASE };
+static bool APT_NONNULL(3, 4, 5) AllowInsecureRepositories(InsecureType msg, std::string const &repo,
       metaIndex const * const MetaIndexParser, pkgAcqMetaClearSig * const TransactionManager, pkgAcquire::Item * const I)
 {
+   // we skip weak downgrades as its unlikely that a repository gets really weaker –
+   // its more realistic that apt got pickier in a newer version
+   if (msg != InsecureType::WEAK)
+   {
+      std::string const FinalInRelease = TransactionManager->GetFinalFilename();
+      std::string const FinalReleasegpg = FinalInRelease.substr(0, FinalInRelease.length() - strlen("InRelease")) + "Release.gpg";
+      if (RealFileExists(FinalReleasegpg) || RealFileExists(FinalInRelease))
+      {
+        char const * msgstr = nullptr;
+        switch (msg)
+        {
+           case InsecureType::UNSIGNED: msgstr = _("The repository '%s' is no longer signed."); break;
+           case InsecureType::NORELEASE: msgstr = _("The repository '%s' does no longer have a Release file."); break;
+           case InsecureType::WEAK: /* unreachable */ break;
+        }
+        if (_config->FindB("Acquire::AllowDowngradeToInsecureRepositories"))
+        {
+           // meh, the users wants to take risks (we still mark the packages
+           // from this repository as unauthenticated)
+           _error->Warning(msgstr, repo.c_str());
+           _error->Warning(_("This is normally not allowed, but the option "
+                    "Acquire::AllowDowngradeToInsecureRepositories was "
+                    "given to override it."));
+        } else {
+           MessageInsecureRepository(true, msgstr, repo);
+           TransactionManager->AbortTransaction();
+           I->Status = pkgAcquire::Item::StatError;
+           return false;
+        }
+      }
+   }
+
    if(MetaIndexParser->GetTrusted() == metaIndex::TRI_YES)
       return true;
 
+   char const * msgstr = nullptr;
+   switch (msg)
+   {
+      case InsecureType::UNSIGNED: msgstr = _("The repository '%s' is not signed."); break;
+      case InsecureType::NORELEASE: msgstr = _("The repository '%s' does not have a Release file."); break;
+      case InsecureType::WEAK: msgstr = _("The repository '%s' provides only weak security information."); break;
+   }
+
    if (_config->FindB("Acquire::AllowInsecureRepositories") == true)
    {
-      MessageInsecureRepository(false, msg, repo);
+      MessageInsecureRepository(false, msgstr, repo);
       return true;
    }
 
-   MessageInsecureRepository(true, msg, repo);
+   MessageInsecureRepository(true, msgstr, repo);
    TransactionManager->AbortTransaction();
    I->Status = pkgAcquire::Item::StatError;
    return false;
@@ -693,9 +731,11 @@ void pkgAcquire::Item::Failed(string const &Message,pkgAcquire::MethodConfig con
    }
 
    string const FailReason = LookupTag(Message, "FailReason");
-   enum { MAXIMUM_SIZE_EXCEEDED, HASHSUM_MISMATCH, OTHER } failreason = OTHER;
+   enum { MAXIMUM_SIZE_EXCEEDED, HASHSUM_MISMATCH, WEAK_HASHSUMS, OTHER } failreason = OTHER;
    if ( FailReason == "MaximumSizeExceeded")
       failreason = MAXIMUM_SIZE_EXCEEDED;
+   else if ( FailReason == "WeakHashSums")
+      failreason = WEAK_HASHSUMS;
    else if (Status == StatAuthError)
       failreason = HASHSUM_MISMATCH;
 
@@ -709,6 +749,9 @@ void pkgAcquire::Item::Failed(string const &Message,pkgAcquire::MethodConfig con
            case HASHSUM_MISMATCH:
               out << _("Hash Sum mismatch") << std::endl;
               break;
+           case WEAK_HASHSUMS:
+              out << _("Insufficient information available to perform this download securely") << std::endl;
+              break;
            case MAXIMUM_SIZE_EXCEEDED:
            case OTHER:
               out << LookupTag(Message, "Message") << std::endl;
@@ -719,7 +762,12 @@ void pkgAcquire::Item::Failed(string const &Message,pkgAcquire::MethodConfig con
         {
            out << "Hashes of expected file:" << std::endl;
            for (auto const &hs: ExpectedHashes)
-              out << " - " << hs.toStr() << std::endl;
+           {
+              out << " - " << hs.toStr();
+              if (hs.usable() == false)
+                 out << " [weak]";
+              out << std::endl;
+           }
         }
         if (failreason == HASHSUM_MISMATCH)
         {
@@ -729,7 +777,13 @@ void pkgAcquire::Item::Failed(string const &Message,pkgAcquire::MethodConfig con
               std::string const tagname = std::string(*type) + "-Hash";
               std::string const hashsum = LookupTag(Message, tagname.c_str());
               if (hashsum.empty() == false)
-                 out << " - " << HashString(*type, hashsum).toStr() << std::endl;
+              {
+                 auto const hs = HashString(*type, hashsum);
+                 out << " - " << hs.toStr();
+                 if (hs.usable() == false)
+                    out << " [weak]";
+                 out << std::endl;
+              }
            }
            out << "Last modification reported: " << LookupTag(Message, "Last-Modified", "<none>") << std::endl;
         }
@@ -743,6 +797,7 @@ void pkgAcquire::Item::Failed(string const &Message,pkgAcquire::MethodConfig con
    {
       case MAXIMUM_SIZE_EXCEEDED: RenameOnError(MaximumSizeExceeded); break;
       case HASHSUM_MISMATCH: RenameOnError(HashSumMismatch); break;
+      case WEAK_HASHSUMS: break;
       case OTHER: break;
    }
 
@@ -956,7 +1011,7 @@ void pkgAcqMetaBase::AbortTransaction()
    {
       case TransactionStarted: break;
       case TransactionAbort: _error->Fatal("Transaction %s was already aborted and is aborted again", TransactionManager->Target.URI.c_str()); return;
-      case TransactionCommit: _error->Fatal("Transaction %s was already aborted and is now commited", TransactionManager->Target.URI.c_str()); return;
+      case TransactionCommit: _error->Fatal("Transaction %s was already aborted and is now committed", TransactionManager->Target.URI.c_str()); return;
    }
    TransactionManager->State = TransactionAbort;
 
@@ -998,8 +1053,8 @@ void pkgAcqMetaBase::CommitTransaction()
    switch (TransactionManager->State)
    {
       case TransactionStarted: break;
-      case TransactionAbort: _error->Fatal("Transaction %s was already commited and is now aborted", TransactionManager->Target.URI.c_str()); return;
-      case TransactionCommit: _error->Fatal("Transaction %s was already commited and is again commited", TransactionManager->Target.URI.c_str()); return;
+      case TransactionAbort: _error->Fatal("Transaction %s was already committed and is now aborted", TransactionManager->Target.URI.c_str()); return;
+      case TransactionCommit: _error->Fatal("Transaction %s was already committed and is again committed", TransactionManager->Target.URI.c_str()); return;
    }
    TransactionManager->State = TransactionCommit;
 
@@ -1149,6 +1204,8 @@ bool pkgAcqMetaBase::CheckAuthDone(string const &Message)         /*{{{*/
    // valid signature from a key in the trusted keyring.  We
    // perform additional verification of its contents, and use them
    // to verify the indexes we are about to download
+   if (_config->FindB("Debug::pkgAcquire::Auth", false))
+      std::cerr << "Signature verification succeeded: " << DestFile << std::endl;
 
    if (TransactionManager->IMSHit == false)
    {
@@ -1169,7 +1226,8 @@ bool pkgAcqMetaBase::CheckAuthDone(string const &Message)         /*{{{*/
       LoadLastMetaIndexParser(TransactionManager, FinalRelease, FinalInRelease);
    }
 
-   if (TransactionManager->MetaIndexParser->Load(DestFile, &ErrorText) == false)
+   bool const GoodAuth = TransactionManager->MetaIndexParser->Load(DestFile, &ErrorText);
+   if (GoodAuth == false && AllowInsecureRepositories(InsecureType::WEAK, Target.Description, TransactionManager->MetaIndexParser, TransactionManager, this) == false)
    {
       Status = StatAuthError;
       return false;
@@ -1181,14 +1239,10 @@ bool pkgAcqMetaBase::CheckAuthDone(string const &Message)               /*{{{*/
       return false;
    }
 
-   if (_config->FindB("Debug::pkgAcquire::Auth", false))
-      std::cerr << "Signature verification succeeded: "
-                << DestFile << std::endl;
-
    // Download further indexes with verification
-   TransactionManager->QueueIndexes(true);
+   TransactionManager->QueueIndexes(GoodAuth);
 
-   return true;
+   return GoodAuth;
 }
                                                                        /*}}}*/
 void pkgAcqMetaClearSig::QueueIndexes(bool const verify)                       /*{{{*/
@@ -1196,16 +1250,31 @@ void pkgAcqMetaClearSig::QueueIndexes(bool const verify)                        /*{{{*/
    // at this point the real Items are loaded in the fetcher
    ExpectedAdditionalItems = 0;
 
-   bool const metaBaseSupportsByHash = TransactionManager->MetaIndexParser->GetSupportsAcquireByHash();
-   for (auto Target : TransactionManager->MetaIndexParser->GetIndexTargets())
-   {
+   std::set<std::string> targetsSeen;
+   bool const hasReleaseFile = TransactionManager->MetaIndexParser != NULL;
+   bool const metaBaseSupportsByHash = hasReleaseFile && TransactionManager->MetaIndexParser->GetSupportsAcquireByHash();
+   bool hasHashes = true;
+   auto IndexTargets = TransactionManager->MetaIndexParser->GetIndexTargets();
+   if (hasReleaseFile && verify == false)
+      hasHashes = std::any_of(IndexTargets.begin(), IndexTargets.end(),
+           [&](IndexTarget const &Target) { return TransactionManager->MetaIndexParser->Exists(Target.MetaKey); });
+   for (auto&& Target: IndexTargets)
+   {
+      // if we have seen a target which is created-by a target this one here is declared a
+      // fallback to, we skip acquiring the fallback (but we make sure we clean up)
+      if (targetsSeen.find(Target.Option(IndexTarget::FALLBACK_OF)) != targetsSeen.end())
+      {
+        targetsSeen.emplace(Target.Option(IndexTarget::CREATED_BY));
+        new CleanupItem(Owner, TransactionManager, Target);
+        continue;
+      }
       // all is an implementation detail. Users shouldn't use this as arch
       // We need this support trickery here as e.g. Debian has binary-all files already,
       // but arch:all packages are still in the arch:any files, so we would waste precious
       // download time, bandwidth and diskspace for nothing, BUT Debian doesn't feature all
       // in the set of supported architectures, so we can filter based on this property rather
       // than invent an entirely new flag we would need to carry for all of eternity.
-      if (Target.Option(IndexTarget::ARCHITECTURE) == "all")
+      if (hasReleaseFile && Target.Option(IndexTarget::ARCHITECTURE) == "all")
       {
         if (TransactionManager->MetaIndexParser->IsArchitectureSupported("all") == false ||
               TransactionManager->MetaIndexParser->IsArchitectureAllSupportedFor(Target) == false)
@@ -1216,12 +1285,12 @@ void pkgAcqMetaClearSig::QueueIndexes(bool const verify)                        /*{{{*/
       }
 
       bool trypdiff = Target.OptionBool(IndexTarget::PDIFFS);
-      if (verify == true)
+      if (hasReleaseFile == true)
       {
         if (TransactionManager->MetaIndexParser->Exists(Target.MetaKey) == false)
         {
            // optional targets that we do not have in the Release file are skipped
-           if (Target.IsOptional)
+           if (hasHashes == true && Target.IsOptional)
            {
               new CleanupItem(Owner, TransactionManager, Target);
               continue;
@@ -1240,18 +1309,26 @@ void pkgAcqMetaClearSig::QueueIndexes(bool const verify)                        /*{{{*/
               // if the architecture is officially supported but currently no packages for it available,
               // ignore silently as this is pretty much the same as just shipping an empty file.
               // if we don't know which architectures are supported, we do NOT ignore it to notify user about this
-              if (TransactionManager->MetaIndexParser->IsArchitectureSupported("*undefined*") == false)
+              if (hasHashes == true && TransactionManager->MetaIndexParser->IsArchitectureSupported("*undefined*") == false)
               {
                  new CleanupItem(Owner, TransactionManager, Target);
                  continue;
               }
            }
 
-           Status = StatAuthError;
-           strprintf(ErrorText, _("Unable to find expected entry '%s' in Release file (Wrong sources.list entry or malformed file)"), Target.MetaKey.c_str());
-           return;
+           if (hasHashes == true)
+           {
+              Status = StatAuthError;
+              strprintf(ErrorText, _("Unable to find expected entry '%s' in Release file (Wrong sources.list entry or malformed file)"), Target.MetaKey.c_str());
+              return;
+           }
+           else
+           {
+              new pkgAcqIndex(Owner, TransactionManager, Target);
+              continue;
+           }
         }
-        else
+        else if (verify)
         {
            auto const hashes = GetExpectedHashesFor(Target.MetaKey);
            if (hashes.empty() == false)
@@ -1267,6 +1344,7 @@ void pkgAcqMetaClearSig::QueueIndexes(bool const verify)                  /*{{{*/
               else if (hashes.FileSize() == 0)
               {
                  new CleanupItem(Owner, TransactionManager, Target);
+                 targetsSeen.emplace(Target.Option(IndexTarget::CREATED_BY));
                  continue;
               }
            }
@@ -1327,6 +1405,7 @@ void pkgAcqMetaClearSig::QueueIndexes(bool const verify)                  /*{{{*/
            std::string const idxfilename = GetFinalFileNameFromURI(GetDiffIndexURI(Target));
            if (FileExists(idxfilename))
               new NoActionItem(Owner, Target, idxfilename);
+           targetsSeen.emplace(Target.Option(IndexTarget::CREATED_BY));
            continue;
         }
 
@@ -1348,6 +1427,7 @@ void pkgAcqMetaClearSig::QueueIndexes(bool const verify)                  /*{{{*/
       }
 
       // Queue the Index file (Packages, Sources, Translation-$foo, …)
+      targetsSeen.emplace(Target.Option(IndexTarget::CREATED_BY));
       if (trypdiff)
          new pkgAcqDiffIndex(Owner, TransactionManager, Target);
       else
@@ -1518,6 +1598,17 @@ void pkgAcqMetaClearSig::Done(std::string const &Message,
         new NoActionItem(Owner, DetachedSigTarget);
       }
    }
+   else if (Status != StatAuthError)
+   {
+      string const FinalFile = GetFinalFileNameFromURI(DetachedDataTarget.URI);
+      string const OldFile = GetFinalFilename();
+      if (TransactionManager->IMSHit == false)
+        TransactionManager->TransactionStageCopy(this, DestFile, FinalFile);
+      else if (RealFileExists(OldFile) == false)
+        new NoActionItem(Owner, DetachedDataTarget);
+      else
+        TransactionManager->TransactionStageCopy(this, OldFile, FinalFile);
+   }
 }
                                                                        /*}}}*/
 void pkgAcqMetaClearSig::Failed(string const &Message,pkgAcquire::MethodConfig const * const Cnf) /*{{{*/
@@ -1549,10 +1640,7 @@ void pkgAcqMetaClearSig::Failed(string const &Message,pkgAcquire::MethodConfig c
       if(CheckStopAuthentication(this, Message))
          return;
 
-      // No Release file was present, or verification failed, so fall
-      // back to queueing Packages files without verification
-      // only allow going further if the user explicitly wants it
-      if(AllowInsecureRepositories(_("The repository '%s' is not signed."), ClearsignedTarget.Description, TransactionManager->MetaIndexParser, TransactionManager, this) == true)
+      if(AllowInsecureRepositories(InsecureType::UNSIGNED, ClearsignedTarget.Description, TransactionManager->MetaIndexParser, TransactionManager, this) == true)
       {
         Status = StatDone;
 
@@ -1623,7 +1711,7 @@ void pkgAcqMetaIndex::Failed(string const &Message,
    // No Release file was present so fall
    // back to queueing Packages files without verification
    // only allow going further if the user explicitly wants it
-   if(AllowInsecureRepositories(_("The repository '%s' does not have a Release file."), Target.Description, TransactionManager->MetaIndexParser, TransactionManager, this) == true)
+   if(AllowInsecureRepositories(InsecureType::NORELEASE, Target.Description, TransactionManager->MetaIndexParser, TransactionManager, this) == true)
    {
       // ensure old Release files are removed
       TransactionManager->TransactionStageRemoval(this, GetFinalFilename());
@@ -1723,6 +1811,14 @@ void pkgAcqMetaSig::Done(string const &Message, HashStringList const &Hashes,
         TransactionManager->TransactionStageCopy(MetaIndex, MetaIndex->DestFile, MetaIndex->GetFinalFilename());
       }
    }
+   else if (MetaIndex->Status != StatAuthError)
+   {
+      std::string const FinalFile = MetaIndex->GetFinalFilename();
+      if (TransactionManager->IMSHit == false)
+        TransactionManager->TransactionStageCopy(MetaIndex, MetaIndex->DestFile, FinalFile);
+      else
+        TransactionManager->TransactionStageCopy(MetaIndex, FinalFile, FinalFile);
+   }
 }
                                                                        /*}}}*/
 void pkgAcqMetaSig::Failed(string const &Message,pkgAcquire::MethodConfig const * const Cnf)/*{{{*/
@@ -1733,40 +1829,14 @@ void pkgAcqMetaSig::Failed(string const &Message,pkgAcquire::MethodConfig const
    if (MetaIndex->AuthPass == true && MetaIndex->CheckStopAuthentication(this, Message))
          return;
 
-   string const FinalRelease = MetaIndex->GetFinalFilename();
-   string const FinalReleasegpg = GetFinalFilename();
-   string const FinalInRelease = TransactionManager->GetFinalFilename();
-
-   if (RealFileExists(FinalReleasegpg) || RealFileExists(FinalInRelease))
-   {
-      std::string downgrade_msg;
-      strprintf(downgrade_msg, _("The repository '%s' is no longer signed."),
-                MetaIndex->Target.Description.c_str());
-      if(_config->FindB("Acquire::AllowDowngradeToInsecureRepositories"))
-      {
-         // meh, the users wants to take risks (we still mark the packages
-         // from this repository as unauthenticated)
-         _error->Warning("%s", downgrade_msg.c_str());
-         _error->Warning(_("This is normally not allowed, but the option "
-                           "Acquire::AllowDowngradeToInsecureRepositories was "
-                           "given to override it."));
-         Status = StatDone;
-      } else {
-        MessageInsecureRepository(true, downgrade_msg);
-        if (TransactionManager->IMSHit == false)
-           Rename(MetaIndex->DestFile, MetaIndex->DestFile + ".FAILED");
-        Item::Failed("Message: " + downgrade_msg, Cnf);
-         TransactionManager->AbortTransaction();
-         return;
-      }
-   }
-
    // ensures that a Release.gpg file in the lists/ is removed by the transaction
    TransactionManager->TransactionStageRemoval(this, DestFile);
 
    // only allow going further if the user explicitly wants it
-   if (AllowInsecureRepositories(_("The repository '%s' is not signed."), MetaIndex->Target.Description, TransactionManager->MetaIndexParser, TransactionManager, this) == true)
+   if (AllowInsecureRepositories(InsecureType::UNSIGNED, MetaIndex->Target.Description, TransactionManager->MetaIndexParser, TransactionManager, this) == true)
    {
+      string const FinalRelease = MetaIndex->GetFinalFilename();
+      string const FinalInRelease = TransactionManager->GetFinalFilename();
       LoadLastMetaIndexParser(TransactionManager, FinalRelease, FinalInRelease);
 
       // we parse the indexes here because at this point the user wanted
@@ -1777,8 +1847,10 @@ void pkgAcqMetaSig::Failed(string const &Message,pkgAcquire::MethodConfig const
       else
         TransactionManager->QueueIndexes(GoodLoad);
 
-      TransactionManager->TransactionStageCopy(MetaIndex, MetaIndex->DestFile, MetaIndex->GetFinalFilename());
+      TransactionManager->TransactionStageCopy(MetaIndex, MetaIndex->DestFile, FinalRelease);
    }
+   else if (TransactionManager->IMSHit == false)
+      Rename(MetaIndex->DestFile, MetaIndex->DestFile + ".FAILED");
 
    // FIXME: this is used often (e.g. in pkgAcqIndexTrans) so refactor
    if (Cnf->LocalOnly == true ||