diff options
author | David Kalnischkies <david@kalnischkies.de> | 2017-06-28 19:20:09 +0200 |
---|---|---|
committer | David Kalnischkies <david@kalnischkies.de> | 2017-06-28 19:20:09 +0200 |
commit | dfe0e754a808aabd55f9cd68201a7f1432070836 (patch) | |
tree | 868ec0ba429d4df96b09a45c2f782c7c49786db3 | |
parent | cbaf353ead58aa9eefe51542b6ad91e69b6289ce (diff) | |
parent | 24b5bc4e41ed527799a9fa01dec9c29294d0a3f2 (diff) |
Merge branch 'feature/releaseinfochange'
-rw-r--r-- | apt-pkg/acquire-item.cc | 76 | ||||
-rw-r--r-- | apt-pkg/acquire.cc | 31 | ||||
-rw-r--r-- | apt-pkg/acquire.h | 43 | ||||
-rw-r--r-- | apt-pkg/deb/debmetaindex.cc | 20 | ||||
-rw-r--r-- | apt-pkg/metaindex.cc | 35 | ||||
-rw-r--r-- | apt-pkg/metaindex.h | 15 | ||||
-rw-r--r-- | apt-private/acqprogress.cc | 22 | ||||
-rw-r--r-- | apt-private/acqprogress.h | 3 | ||||
-rw-r--r-- | apt-private/private-cmndline.cc | 12 | ||||
-rw-r--r-- | apt-private/private-output.cc | 26 | ||||
-rw-r--r-- | apt-private/private-output.h | 1 | ||||
-rw-r--r-- | doc/apt-get.8.xml | 17 | ||||
-rw-r--r-- | doc/apt-secure.8.xml | 42 | ||||
-rw-r--r-- | doc/examples/configure-index | 37 | ||||
-rwxr-xr-x | test/integration/test-apt-update-releaseinfo-changes | 80 | ||||
-rwxr-xr-x | test/integration/test-bug-841874-warning-for-mismatching-distribution | 12 | ||||
-rwxr-xr-x | test/integration/test-policy-pinning | 2 |
17 files changed, 416 insertions, 58 deletions
diff --git a/apt-pkg/acquire-item.cc b/apt-pkg/acquire-item.cc index 574ef4939..975116d1a 100644 --- a/apt-pkg/acquire-item.cc +++ b/apt-pkg/acquire-item.cc @@ -1605,13 +1605,79 @@ bool pkgAcqMetaBase::VerifyVendor(string const &) /*{{{*/ if (TransactionManager->MetaIndexParser->CheckDist(ExpectedDist) == false) _error->Warning(_("Conflicting distribution: %s (expected %s but got %s)"), Desc.Description.c_str(), ExpectedDist.c_str(), NowCodename.c_str()); - // might be okay, might be not + + // changed info potentially breaks user config like pinning if (TransactionManager->LastMetaIndexParser != nullptr) { - auto const LastCodename = TransactionManager->LastMetaIndexParser->GetCodename(); - if (LastCodename.empty() == false && NowCodename.empty() == false && LastCodename != NowCodename) - _error->Warning(_("Conflicting distribution: %s (expected %s but got %s)"), - Desc.Description.c_str(), LastCodename.c_str(), NowCodename.c_str()); + std::vector<pkgAcquireStatus::ReleaseInfoChange> Changes; + auto const AllowInfoChange = _config->FindB("Acquire::AllowReleaseInfoChange", false); + auto const quietInfoChange = _config->FindB("quiet::ReleaseInfoChange", false); + struct { + char const * const Type; + bool const Allowed; + decltype(&metaIndex::GetOrigin) const Getter; + } checkers[] = { + { "Origin", AllowInfoChange, &metaIndex::GetOrigin }, + { "Label", AllowInfoChange, &metaIndex::GetLabel }, + { "Version", true, &metaIndex::GetVersion }, // numbers change all the time, that is okay + { "Suite", AllowInfoChange, &metaIndex::GetSuite }, + { "Codename", AllowInfoChange, &metaIndex::GetCodename }, + { nullptr, false, nullptr } + }; + auto const CheckReleaseInfo = [&](char const * const Type, bool const AllowChange, decltype(checkers[0].Getter) const Getter) { + std::string const Last = (TransactionManager->LastMetaIndexParser->*Getter)(); + std::string const Now = (TransactionManager->MetaIndexParser->*Getter)(); + if (Last == Now) + return; + auto const Allow = _config->FindB(std::string("Acquire::AllowReleaseInfoChange::").append(Type), AllowChange); + if (Allow == true && _config->FindB(std::string("quiet::ReleaseInfoChange::").append(Type), quietInfoChange) == true) + return; + std::string msg; + strprintf(msg, _("Repository '%s' changed its '%s' value from '%s' to '%s'"), + Desc.Description.c_str(), Type, Last.c_str(), Now.c_str()); + Changes.push_back({Type, std::move(Last), std::move(Now), std::move(msg), Allow}); + }; + for (short i = 0; checkers[i].Type != nullptr; ++i) + CheckReleaseInfo(checkers[i].Type, checkers[i].Allowed, checkers[i].Getter); + + { + auto const Last = TransactionManager->LastMetaIndexParser->GetDefaultPin(); + auto const Now = TransactionManager->MetaIndexParser->GetDefaultPin(); + if (Last != Now) + { + auto const Allow = _config->FindB("Acquire::AllowReleaseInfoChange::DefaultPin", AllowInfoChange); + if (Allow == false || _config->FindB("quiet::ReleaseInfoChange::DefaultPin", quietInfoChange) == false) + { + std::string msg; + strprintf(msg, _("Repository '%s' changed its default priority for %s from %hi to %hi."), + Desc.Description.c_str(), "apt_preferences(5)", Last, Now); + Changes.push_back({"DefaultPin", std::to_string(Last), std::to_string(Now), std::move(msg), Allow}); + } + } + } + if (Changes.empty() == false) + { + auto const notes = TransactionManager->MetaIndexParser->GetReleaseNotes(); + if (notes.empty() == false) + { + std::string msg; + // TRANSLATOR: the "this" refers to changes in the repository like a new release or owner change + strprintf(msg, _("More information about this can be found online in the Release notes at: %s"), notes.c_str()); + Changes.push_back({"Release-Notes", "", std::move(notes), std::move(msg), true}); + } + if (std::any_of(Changes.begin(),Changes.end(),[](pkgAcquireStatus::ReleaseInfoChange const &c) { return c.DefaultAction == false; })) + { + std::string msg; + // TRANSLATOR: %s is the name of the manpage in question, e.g. apt-secure(8) + strprintf(msg, _("This must be accepted explicitly before updates for " + "this repository can be applied. See %s manpage for details."), "apt-secure(8)"); + Changes.push_back({"Confirmation", "", "", std::move(msg), true}); + } + + } + if (Owner->Log == nullptr) + return pkgAcquireStatus::ReleaseInfoChangesAsGlobalErrors(std::move(Changes)); + return Owner->Log->ReleaseInfoChanges(TransactionManager->LastMetaIndexParser, TransactionManager->MetaIndexParser, std::move(Changes)); } return true; } diff --git a/apt-pkg/acquire.cc b/apt-pkg/acquire.cc index 8a6928fb9..f1dbff9f1 100644 --- a/apt-pkg/acquire.cc +++ b/apt-pkg/acquire.cc @@ -1406,10 +1406,39 @@ void pkgAcquireStatus::Stop() // --------------------------------------------------------------------- /* This is used to get accurate final transfer rate reporting. */ void pkgAcquireStatus::Fetched(unsigned long long Size,unsigned long long Resume) -{ +{ FetchedBytes += Size - Resume; } /*}}}*/ +bool pkgAcquireStatus::ReleaseInfoChanges(metaIndex const * const LastRelease, metaIndex const * const CurrentRelease, std::vector<ReleaseInfoChange> &&Changes)/*{{{*/ +{ + auto const virt = dynamic_cast<pkgAcquireStatus2*>(this); + if (virt != nullptr) + return virt->ReleaseInfoChanges(LastRelease, CurrentRelease, std::move(Changes)); + return ReleaseInfoChangesAsGlobalErrors(std::move(Changes)); +} + /*}}}*/ +bool pkgAcquireStatus::ReleaseInfoChangesAsGlobalErrors(std::vector<ReleaseInfoChange> &&Changes)/*{{{*/ +{ + bool AllOkay = true; + for (auto const &c: Changes) + if (c.DefaultAction) + _error->Notice("%s", c.Message.c_str()); + else + { + _error->Error("%s", c.Message.c_str()); + AllOkay = false; + } + return AllOkay; +} + /*}}}*/ +bool pkgAcquireStatus2::ReleaseInfoChanges(metaIndex const * const, metaIndex const * const, std::vector<ReleaseInfoChange> &&Changes) +{ + return ReleaseInfoChangesAsGlobalErrors(std::move(Changes)); +} +pkgAcquireStatus2::pkgAcquireStatus2() : pkgAcquireStatus() {} +pkgAcquireStatus2::~pkgAcquireStatus2() {} + pkgAcquire::UriIterator::UriIterator(pkgAcquire::Queue *Q) : d(NULL), CurQ(Q), CurItem(0) { diff --git a/apt-pkg/acquire.h b/apt-pkg/acquire.h index 5f1212338..8331c56e9 100644 --- a/apt-pkg/acquire.h +++ b/apt-pkg/acquire.h @@ -86,6 +86,7 @@ using std::string; #endif class pkgAcquireStatus; +class metaIndex; /** \brief The core download scheduler. {{{ * @@ -794,7 +795,39 @@ class pkgAcquireStatus * with prejudice. */ virtual bool MediaChange(std::string Media,std::string Drive) = 0; - + + struct ReleaseInfoChange + { + std::string Type; /*!< Type of the change like "Origin", "Codename", "Version", … */ + std::string From; /*!< old value */ + std::string To; /*!< new value */ + std::string Message; /*!< translated message describing the change */ + bool DefaultAction; /*!< true if the change is informational, false if it must be explicitly confirmed */ + }; + /** \brief ask the user for confirmation of changes to infos about a repository + * + * This method should present the user with a choice of accepting the change + * or not and indicate the user opinion via the return value. If DefaultAction is true + * it is acceptable to only notify the user about the change, but to accept the change + * automatically on behalf of the user. + * + * The default implementation will fail if any Change has DefaultAction == false. Regardless of + * success it will print for each change the message attached to it via GlobalError either as an + * error (if DefaultAction == false) or as a notice otherwise. + * + * \b Note: To keep ABI compatibility for now this method isn't marked as + * virtual, but you can derive your class from #pkgAcquireStatus2 which has it + * marked as virtual. TODO on next ABI break: merge both classes. + * + * @param LastRelease can be used to extract further information from the previous Release file + * @param CurrentRelease can be used to extract further information from the current Release file + * @param Changes is an array of changes alongside explanatory messages + * which should be presented in some way to the user. + * @return \b true if all changes are accepted by user, otherwise or if user can't be asked \b false + */ + bool ReleaseInfoChanges(metaIndex const * const LastRelease, metaIndex const * const CurrentRelease, std::vector<ReleaseInfoChange> &&Changes); + APT_HIDDEN static bool ReleaseInfoChangesAsGlobalErrors(std::vector<ReleaseInfoChange> &&Changes); + /** \brief Invoked when an item is confirmed to be up-to-date. * For instance, when an HTTP download is informed that the file on @@ -835,6 +868,14 @@ class pkgAcquireStatus pkgAcquireStatus(); virtual ~pkgAcquireStatus(); }; +class pkgAcquireStatus2: public pkgAcquireStatus +{ +public: + virtual bool ReleaseInfoChanges(metaIndex const * const LastRelease, metaIndex const * const CurrentRelease, std::vector<ReleaseInfoChange> &&Changes); + + pkgAcquireStatus2(); + virtual ~pkgAcquireStatus2(); +}; /*}}}*/ /** @} */ diff --git a/apt-pkg/deb/debmetaindex.cc b/apt-pkg/deb/debmetaindex.cc index 8c82414cb..df7419ddd 100644 --- a/apt-pkg/deb/debmetaindex.cc +++ b/apt-pkg/deb/debmetaindex.cc @@ -393,8 +393,12 @@ bool debReleaseIndex::Load(std::string const &Filename, std::string * const Erro // FIXME: find better tag name SupportsAcquireByHash = Section.FindB("Acquire-By-Hash", false); + SetOrigin(Section.FindS("Origin")); + SetLabel(Section.FindS("Label")); + SetVersion(Section.FindS("Version")); Suite = Section.FindS("Suite"); Codename = Section.FindS("Codename"); + SetReleaseNotes(Section.FindS("Release-Notes")); { std::string const archs = Section.FindS("Architectures"); if (archs.empty() == false) @@ -415,6 +419,20 @@ bool debReleaseIndex::Load(std::string const &Filename, std::string * const Erro else // e.g. security.debian.org uses this style d->SupportedComponents.push_back(comp.substr(pos + 1)); } + { + decltype(pkgCache::ReleaseFile::Flags) flags = 0; + Section.FindFlag("NotAutomatic", flags, pkgCache::Flag::NotAutomatic); + signed short defaultpin = 500; + if ((flags & pkgCache::Flag::NotAutomatic) == pkgCache::Flag::NotAutomatic) + { + Section.FindFlag("ButAutomaticUpgrades", flags, pkgCache::Flag::ButAutomaticUpgrades); + if ((flags & pkgCache::Flag::ButAutomaticUpgrades) == pkgCache::Flag::ButAutomaticUpgrades) + defaultpin = 100; + else + defaultpin = 1; + } + SetDefaultPin(defaultpin); + } bool FoundHashSum = false; bool FoundStrongHashSum = false; @@ -472,7 +490,6 @@ bool debReleaseIndex::Load(std::string const &Filename, std::string * const Erro if (CheckValidUntil == true) { - std::string const Label = Section.FindS("Label"); std::string const StrValidUntil = Section.FindS("Valid-Until"); // if we have a Valid-Until header in the Release file, use it as default @@ -485,6 +502,7 @@ bool debReleaseIndex::Load(std::string const &Filename, std::string * const Erro return false; } } + auto const Label = GetLabel(); // get the user settings for this archive and use what expires earlier time_t MaxAge = d->ValidUntilMax; if (MaxAge == 0) diff --git a/apt-pkg/metaindex.cc b/apt-pkg/metaindex.cc index 624c7c9c7..695cf8804 100644 --- a/apt-pkg/metaindex.cc +++ b/apt-pkg/metaindex.cc @@ -9,6 +9,17 @@ #include <vector> /*}}}*/ +class metaIndexPrivate /*{{{*/ +{ + public: + std::string Origin; + std::string Label; + std::string Version; + signed short DefaultPin; + std::string ReleaseNotes; +}; + /*}}}*/ + std::string metaIndex::Describe() const { return "Release"; @@ -26,7 +37,7 @@ bool metaIndex::Merge(pkgCacheGenerator &Gen,OpProgress *) const metaIndex::metaIndex(std::string const &URI, std::string const &Dist, char const * const Type) -: d(NULL), Indexes(NULL), Type(Type), URI(URI), Dist(Dist), Trusted(TRI_UNSET), +: d(new metaIndexPrivate()), Indexes(NULL), Type(Type), URI(URI), Dist(Dist), Trusted(TRI_UNSET), Date(0), ValidUntil(0), SupportsAcquireByHash(false), LoadedSuccessfully(TRI_UNSET) { /* nothing */ @@ -43,6 +54,7 @@ metaIndex::~metaIndex() } for (auto const &E: Entries) delete E.second; + delete d; } // one line Getters for public fields /*{{{*/ @@ -51,8 +63,13 @@ APT_PURE std::string metaIndex::GetDist() const { return Dist; } APT_PURE const char* metaIndex::GetType() const { return Type; } APT_PURE metaIndex::TriState metaIndex::GetTrusted() const { return Trusted; } APT_PURE std::string metaIndex::GetSignedBy() const { return SignedBy; } +APT_PURE std::string metaIndex::GetOrigin() const { return d->Origin; } +APT_PURE std::string metaIndex::GetLabel() const { return d->Label; } +APT_PURE std::string metaIndex::GetVersion() const { return d->Version; } APT_PURE std::string metaIndex::GetCodename() const { return Codename; } APT_PURE std::string metaIndex::GetSuite() const { return Suite; } +APT_PURE std::string metaIndex::GetReleaseNotes() const { return d->ReleaseNotes; } +APT_PURE signed short metaIndex::GetDefaultPin() const { return d->DefaultPin; } APT_PURE bool metaIndex::GetSupportsAcquireByHash() const { return SupportsAcquireByHash; } APT_PURE time_t metaIndex::GetValidUntil() const { return ValidUntil; } APT_PURE time_t metaIndex::GetDate() const { return this->Date; } @@ -104,11 +121,19 @@ std::vector<std::string> metaIndex::MetaKeys() const /*{{{*/ /*}}}*/ void metaIndex::swapLoad(metaIndex * const OldMetaIndex) /*{{{*/ { - std::swap(Entries, OldMetaIndex->Entries); + std::swap(SignedBy, OldMetaIndex->SignedBy); + std::swap(Suite, OldMetaIndex->Suite); + std::swap(Codename, OldMetaIndex->Codename); std::swap(Date, OldMetaIndex->Date); std::swap(ValidUntil, OldMetaIndex->ValidUntil); std::swap(SupportsAcquireByHash, OldMetaIndex->SupportsAcquireByHash); + std::swap(Entries, OldMetaIndex->Entries); std::swap(LoadedSuccessfully, OldMetaIndex->LoadedSuccessfully); + + OldMetaIndex->SetOrigin(d->Origin); + OldMetaIndex->SetLabel(d->Label); + OldMetaIndex->SetVersion(d->Version); + OldMetaIndex->SetDefaultPin(d->DefaultPin); } /*}}}*/ @@ -136,3 +161,9 @@ bool metaIndex::HasSupportForComponent(std::string const &component) const/*{{{* return true; } /*}}}*/ + +void metaIndex::SetOrigin(std::string const &origin) { d->Origin = origin; } +void metaIndex::SetLabel(std::string const &label) { d->Label = label; } +void metaIndex::SetVersion(std::string const &version) { d->Version = version; } +void metaIndex::SetDefaultPin(signed short const defaultpin) { d->DefaultPin = defaultpin; } +void metaIndex::SetReleaseNotes(std::string const ¬es) { d->ReleaseNotes = notes; } diff --git a/apt-pkg/metaindex.h b/apt-pkg/metaindex.h index 531143bcb..1951f118f 100644 --- a/apt-pkg/metaindex.h +++ b/apt-pkg/metaindex.h @@ -25,6 +25,8 @@ class IndexTarget; class pkgCacheGenerator; class OpProgress; +class metaIndexPrivate; + class metaIndex { public: @@ -43,7 +45,7 @@ public: TRI_YES, TRI_DONTCARE, TRI_NO, TRI_UNSET }; private: - void * const d; + metaIndexPrivate * const d; protected: std::vector <pkgIndexFile *> *Indexes; // parsed from the sources.list @@ -70,8 +72,13 @@ public: TriState GetTrusted() const; std::string GetSignedBy() const; + std::string GetOrigin() const; + std::string GetLabel() const; + std::string GetVersion() const; std::string GetCodename() const; std::string GetSuite() const; + std::string GetReleaseNotes() const; + signed short GetDefaultPin() const; bool GetSupportsAcquireByHash() const; time_t GetValidUntil() const; time_t GetDate() const; @@ -112,6 +119,12 @@ public: bool IsArchitectureSupported(std::string const &arch) const; bool IsArchitectureAllSupportedFor(IndexTarget const &target) const; bool HasSupportForComponent(std::string const &component) const; + // FIXME: should be members of the class on abi break + APT_HIDDEN void SetOrigin(std::string const &origin); + APT_HIDDEN void SetLabel(std::string const &label); + APT_HIDDEN void SetVersion(std::string const &version); + APT_HIDDEN void SetDefaultPin(signed short const defaultpin); + APT_HIDDEN void SetReleaseNotes(std::string const ¬es); }; #endif diff --git a/apt-private/acqprogress.cc b/apt-private/acqprogress.cc index e4bfbd4e5..1f053cb9f 100644 --- a/apt-private/acqprogress.cc +++ b/apt-private/acqprogress.cc @@ -17,6 +17,7 @@ #include <apt-pkg/error.h> #include <apt-private/acqprogress.h> +#include <apt-private/private-output.h> #include <string.h> #include <stdio.h> @@ -32,7 +33,7 @@ // --------------------------------------------------------------------- /* */ AcqTextStatus::AcqTextStatus(std::ostream &out, unsigned int &ScreenWidth,unsigned int const Quiet) : - pkgAcquireStatus(), out(out), ScreenWidth(ScreenWidth), LastLineLength(0), ID(0), Quiet(Quiet) + pkgAcquireStatus2(), out(out), ScreenWidth(ScreenWidth), LastLineLength(0), ID(0), Quiet(Quiet) { // testcases use it to disable pulses without disabling other user messages if (Quiet == 0 && _config->FindB("quiet::NoUpdate", false) == true) @@ -330,6 +331,25 @@ bool AcqTextStatus::MediaChange(std::string Media, std::string Drive) return bStatus; } /*}}}*/ +bool AcqTextStatus::ReleaseInfoChanges(metaIndex const * const L, metaIndex const * const N, std::vector<ReleaseInfoChange> &&Changes)/*{{{*/ +{ + if (Quiet >= 2 || isatty(STDOUT_FILENO) != 1 || isatty(STDIN_FILENO) != 1 || + _config->FindB("APT::Get::Update::InteractiveReleaseInfoChanges", false) == false) + return pkgAcquireStatus2::ReleaseInfoChanges(nullptr, nullptr, std::move(Changes)); + + _error->PushToStack(); + auto const confirmed = pkgAcquireStatus2::ReleaseInfoChanges(L, N, std::move(Changes)); + if (confirmed == true) + { + _error->MergeWithStack(); + return true; + } + clearLastLine(); + _error->DumpErrors(out, GlobalError::NOTICE, false); + _error->RevertToStack(); + return YnPrompt(_("Do you want to accept these changes and continue updating from this repository?"), false, false, out, out); +} + /*}}}*/ void AcqTextStatus::clearLastLine() { /*{{{*/ if (Quiet > 0 || LastLineLength == 0) return; diff --git a/apt-private/acqprogress.h b/apt-private/acqprogress.h index 6b6d555b1..196995ac4 100644 --- a/apt-private/acqprogress.h +++ b/apt-private/acqprogress.h @@ -15,7 +15,7 @@ #include <string> #include <iostream> -class APT_PUBLIC AcqTextStatus : public pkgAcquireStatus +class APT_PUBLIC AcqTextStatus : public pkgAcquireStatus2 { std::ostream &out; unsigned int &ScreenWidth; @@ -28,6 +28,7 @@ class APT_PUBLIC AcqTextStatus : public pkgAcquireStatus public: + virtual bool ReleaseInfoChanges(metaIndex const * const LastRelease, metaIndex const * const CurrentRelease, std::vector<ReleaseInfoChange> &&Changes) APT_OVERRIDE; virtual bool MediaChange(std::string Media,std::string Drive) APT_OVERRIDE; virtual void IMSHit(pkgAcquire::ItemDesc &Itm) APT_OVERRIDE; virtual void Fetch(pkgAcquire::ItemDesc &Itm) APT_OVERRIDE; diff --git a/apt-private/private-cmndline.cc b/apt-private/private-cmndline.cc index 06683ae61..6b84324de 100644 --- a/apt-private/private-cmndline.cc +++ b/apt-private/private-cmndline.cc @@ -203,6 +203,15 @@ static bool addArgumentsAPTGet(std::vector<CommandLine::Args> &Args, char const else if (CmdMatches("update")) { addArg(0, "list-cleanup", "APT::Get::List-Cleanup", 0); + addArg(0, "allow-insecure-repositories", "Acquire::AllowInsecureRepositories", 0); + addArg(0, "allow-weak-repositories", "Acquire::AllowWeakRepositories", 0); + addArg(0, "allow-releaseinfo-change", "Acquire::AllowReleaseInfoChange", 0); + addArg(0, "allow-releaseinfo-change-origin", "Acquire::AllowReleaseInfoChange::Origin", 0); + addArg(0, "allow-releaseinfo-change-label", "Acquire::AllowReleaseInfoChange::Label", 0); + addArg(0, "allow-releaseinfo-change-version", "Acquire::AllowReleaseInfoChange::Version", 0); + addArg(0, "allow-releaseinfo-change-codename", "Acquire::AllowReleaseInfoChange::Codename", 0); + addArg(0, "allow-releaseinfo-change-suite", "Acquire::AllowReleaseInfoChange::Suite", 0); + addArg(0, "allow-releaseinfo-change-defaultpin", "Acquire::AllowReleaseInfoChange::DefaultPin", 0); } else if (CmdMatches("source")) { @@ -273,8 +282,6 @@ static bool addArgumentsAPTGet(std::vector<CommandLine::Args> &Args, char const addArg(0,"remove","APT::Get::Remove",0); addArg(0,"only-source","APT::Get::Only-Source",0); addArg(0,"allow-unauthenticated","APT::Get::AllowUnauthenticated",0); - addArg(0,"allow-insecure-repositories","Acquire::AllowInsecureRepositories",0); - addArg(0,"allow-weak-repositories","Acquire::AllowWeakRepositories",0); addArg(0,"install-recommends","APT::Install-Recommends",CommandLine::Boolean); addArg(0,"install-suggests","APT::Install-Suggests",CommandLine::Boolean); addArg(0,"fix-policy","APT::Get::Fix-Policy-Broken",0); @@ -462,6 +469,7 @@ static void BinarySpecificConfiguration(char const * const Binary) /*{{{*/ _config->CndSet("Binary::apt::APT::Cmd::Show-Update-Stats", true); _config->CndSet("Binary::apt::DPkg::Progress-Fancy", true); _config->CndSet("Binary::apt::APT::Keep-Downloaded-Packages", false); + _config->CndSet("Binary::apt::APT::Get::Update::InteractiveReleaseInfoChanges", true); } _config->Set("Binary", binary); diff --git a/apt-private/private-output.cc b/apt-private/private-output.cc index 9c25cda6d..3b8832c21 100644 --- a/apt-private/private-output.cc +++ b/apt-private/private-output.cc @@ -650,20 +650,20 @@ void Stats(ostream &out,pkgDepCache &Dep) // YnPrompt - Yes No Prompt. /*{{{*/ // --------------------------------------------------------------------- /* Returns true on a Yes.*/ -bool YnPrompt(char const * const Question, bool Default) +bool YnPrompt(char const * const Question, bool const Default, bool const ShowGlobalErrors, std::ostream &c1o, std::ostream &c2o) { auto const AssumeYes = _config->FindB("APT::Get::Assume-Yes",false); auto const AssumeNo = _config->FindB("APT::Get::Assume-No",false); // if we ask interactively, show warnings/notices before the question - if (AssumeYes == false && AssumeNo == false) + if (ShowGlobalErrors == true && AssumeYes == false && AssumeNo == false) { if (_config->FindI("quiet",0) > 0) - _error->DumpErrors(c2out); + _error->DumpErrors(c2o); else - _error->DumpErrors(c2out, GlobalError::DEBUG); + _error->DumpErrors(c2o, GlobalError::DEBUG); } - c2out << Question << std::flush; + c2o << Question << std::flush; /* nl_langinfo does not support LANGUAGE setting, so we unset it here to have the help-message (hopefully) match the expected characters */ @@ -678,13 +678,13 @@ bool YnPrompt(char const * const Question, bool Default) // e.g. "Do you want to continue? [Y/n] " // The user has to answer with an input matching the // YESEXPR/NOEXPR defined in your l10n. - c2out << " " << _("[Y/n]") << " " << std::flush; + c2o << " " << _("[Y/n]") << " " << std::flush; else // TRANSLATOR: Yes/No question help-text: defaulting to N[o] // e.g. "Should this file be removed? [y/N] " // The user has to answer with an input matching the // YESEXPR/NOEXPR defined in your l10n. - c2out << " " << _("[y/N]") << " " << std::flush; + c2o << " " << _("[y/N]") << " " << std::flush; if (language != NULL) { @@ -695,13 +695,13 @@ bool YnPrompt(char const * const Question, bool Default) if (AssumeYes) { // TRANSLATOR: "Yes" answer printed for a yes/no question if --assume-yes is set - c1out << _("Y") << std::endl; + c1o << _("Y") << std::endl; return true; } else if (AssumeNo) { // TRANSLATOR: "No" answer printed for a yes/no question if --assume-no is set - c1out << _("N") << std::endl; + c1o << _("N") << std::endl; return false; } @@ -721,16 +721,20 @@ bool YnPrompt(char const * const Question, bool Default) REG_EXTENDED|REG_ICASE|REG_NOSUB); if (Res != 0) { - char Error[300]; + char Error[300]; regerror(Res,&Pattern,Error,sizeof(Error)); return _error->Error(_("Regex compilation error - %s"),Error); } - + Res = regexec(&Pattern, response, 0, NULL, 0); if (Res == 0) return true; return false; } +bool YnPrompt(char const * const Question, bool const Default) +{ + return YnPrompt(Question, Default, true, c1out, c2out); +} /*}}}*/ // AnalPrompt - Annoying Yes No Prompt. /*{{{*/ // --------------------------------------------------------------------- diff --git a/apt-private/private-output.h b/apt-private/private-output.h index bb9428d7f..79da3d130 100644 --- a/apt-private/private-output.h +++ b/apt-private/private-output.h @@ -102,6 +102,7 @@ void Stats(std::ostream &out, pkgDepCache &Dep); // prompting bool YnPrompt(char const * const Question, bool Default=true); +bool YnPrompt(char const * const Question, bool const Default, bool const ShowGlobalErrors, std::ostream &c1o, std::ostream &c2o); bool AnalPrompt(std::string const &Question, const char *Text); std::string PrettyFullName(pkgCache::PkgIterator const &Pkg); diff --git a/doc/apt-get.8.xml b/doc/apt-get.8.xml index 931a4f313..a38a14e0c 100644 --- a/doc/apt-get.8.xml +++ b/doc/apt-get.8.xml @@ -575,6 +575,23 @@ Configuration Item: <literal>Acquire::AllowInsecureRepositories</literal>.</para></listitem> </varlistentry> + <varlistentry><term><option>--allow-releaseinfo-changes</option></term> + <listitem><para>Allow the update command to continue downloading + data from a repository which changed its information of the release + contained in the repository indicating e.g a new major release. + APT will fail at the update command for such repositories until the + change is confirmed to ensure the user is prepared for the change. + See also &apt-secure; for details on the concept and configuration. + </para><para> + Specialist options + (<literal>--allow-releaseinfo-changes-</literal><replaceable>field</replaceable>) + exist to allow changes only for certain fields like <literal>origin</literal>, + <literal>label</literal>, <literal>codename</literal>, <literal>suite</literal>, + <literal>version</literal> and <literal>defaultpin</literal>. See also &apt-preferences;. + + Configuration Item: <literal>Acquire::AllowReleaseInfoChanges</literal>.</para></listitem> + </varlistentry> + <varlistentry><term><option>--show-progress</option></term> <listitem><para>Show user friendly progress information in the terminal window when packages are installed, upgraded or diff --git a/doc/apt-secure.8.xml b/doc/apt-secure.8.xml index 8ad249d7c..4f5d491f3 100644 --- a/doc/apt-secure.8.xml +++ b/doc/apt-secure.8.xml @@ -13,7 +13,7 @@ &apt-email; &apt-product; <!-- The last update date --> - <date>2016-08-06T00:00:00Z</date> + <date>2017-04-12T00:00:00Z</date> </refentryinfo> <refmeta> @@ -50,10 +50,20 @@ that data like packages in the archive can't be modified by people who have no access to the Release file signing key. Starting with version 1.1 <command>APT</command> requires repositories to provide recent authentication - information for unimpeded usage of the repository. + information for unimpeded usage of the repository. Since version 1.5 changes + in the information contained in the Release file about the repository need to be + confirmed before APT continues to apply updates from this repository. </para> <para> + Note: All APT-based package management front-ends like &apt-get;, &aptitude; + and &synaptic; support this authentication feature, so this manpage uses + <literal>APT</literal> to refer to them all for simplicity only. + </para> +</refsect1> + + <refsect1><title>Unsigned Repositories</title> + <para> If an archive has an unsigned Release file or no Release file at all current APT versions will refuse to download data from them by default in <command>update</command> operations and even if forced to download @@ -83,16 +93,9 @@ to <literal>true</literal> or for Individual repositories with the &sources-list; option <literal>allow-downgrade-to-insecure=yes</literal>. </para> - - <para> - Note: All APT-based package management front-ends like &apt-get;, &aptitude; - and &synaptic; support this authentication feature, so this manpage uses - <literal>APT</literal> to refer to them all for simplicity only. - </para> </refsect1> - <refsect1><title>Trusted Repositories</title> - + <refsect1><title>Signed Repositories</title> <para> The chain of trust from an APT archive to the end user is made up of several steps. <command>apt-secure</command> is the last step in @@ -162,7 +165,22 @@ this mechanism can complement a per-package signature.</para> </refsect1> - <refsect1><title>User Configuration</title> +<refsect1><title>Information changes</title> + <para> + A Release file contains beside the checksums for the files in the repository + also general information about the repository like the origin, codename or + version number of the release. + </para><para> + This information is shown in various places so a repository owner should always + ensure correctness. Further more user configuration like &apt-preferences; + can depend and make use of this information. Since version 1.5 the user must + therefore explicitly confirm changes to signal that the user is sufficently + prepared e.g. for the new major release of the distribution shipped in the + repository (as e.g. indicated by the codename). + </para> +</refsect1> + +<refsect1><title>User Configuration</title> <para> <command>apt-key</command> is the program that manages the list of keys used by APT to trust repositories. It can be used to add or remove keys as well @@ -183,7 +201,7 @@ </para> </refsect1> -<refsect1><title>Archive Configuration</title> +<refsect1><title>Repository Configuration</title> <para> If you want to provide archive signatures in an archive under your maintenance you have to: diff --git a/doc/examples/configure-index b/doc/examples/configure-index index a48d4cb99..155dac84f 100644 --- a/doc/examples/configure-index +++ b/doc/examples/configure-index @@ -29,10 +29,20 @@ and the syntax of configuration files and commandline options! */ -quiet "<INT>"; -quiet::NoUpdate "<BOOL>"; // never update progress information - included in -q=1 -quiet::NoProgress "<BOOL>"; // disables the 0% → 100% progress on cache generation and stuff -quiet::NoStatistic "<BOOL>"; // no "42 kB downloaded" stats in update +quiet "<INT>" { + NoUpdate "<BOOL>"; // never update progress information - included in -q=1 + NoProgress "<BOOL>"; // disables the 0% → 100% progress on cache generation and stuff + NoStatistic "<BOOL>"; // no "42 kB downloaded" stats in update + ReleaseInfoChange "<BOOL>" // don't even print the notices if the info change is allowed + { + Origin "<BOOL>"; + Label "<BOOL>"; + Version "<BOOL>"; + Codename "<BOOL>"; + Suite "<BOOL>"; + DefaultPin "<BOOL>"; + }; +}; // Options for APT in general APT @@ -96,6 +106,8 @@ APT CallResolver "<BOOL>"; IndexTargets::ReleaseInfo "<BOOL>"; IndexTargets::format "<STRING>"; + + Update::InteractiveReleaseInfoChanges "<BOOL>"; }; Cache @@ -221,6 +233,20 @@ Acquire SameMirrorForAllIndexes "<BOOL>"; // use the mirror serving the Release file for Packages & co + AllowInsecureRepositories "<BOOL>"; + AllowWeakRepositories "<BOOL>"; + AllowDowngradeToInsecureRepositories "<BOOL>"; + // allow repositories to change information potentally breaking user config like pinning + AllowReleaseInfoChange "<BOOL>" + { + Origin "<BOOL>"; + Label "<BOOL>"; + Version "<BOOL>"; // allowed by default + Codename "<BOOL>"; + Suite "<BOOL>"; + DefaultPin "<BOOL>"; + }; + // HTTP method configuration http { @@ -686,9 +712,6 @@ acquire::cdrom::mount "<DIR>"; acquire::maxreleasefilesize "<INT>"; acquire::queuehost::limit "<INT>"; acquire::max-pipeline-depth "<INT>"; -acquire::allowinsecurerepositories "<BOOL>"; -acquire::allowweakrepositories "<BOOL>"; -acquire::allowdowngradetoinsecurerepositories "<BOOL>"; acquire::progress::diffpercent "<BOOL>"; acquire::gzipindexes "<BOOL>"; acquire::indextargets::randomized "<BOOL>"; diff --git a/test/integration/test-apt-update-releaseinfo-changes b/test/integration/test-apt-update-releaseinfo-changes new file mode 100755 index 000000000..e4bca3658 --- /dev/null +++ b/test/integration/test-apt-update-releaseinfo-changes @@ -0,0 +1,80 @@ +#!/bin/sh +set -e + +TESTDIR="$(readlink -f "$(dirname "$0")")" +. "$TESTDIR/framework" +setupenvironment +configarchitecture 'amd64' + +insertpackage 'earth' 'human' 'all' '1' + +getoriginfromsuite() { echo -n 'Earth'; } +getlabelfromsuite() { echo -n 'Blue Planet'; } +getcodenamefromsuite() { echo -n 'home'; } +getreleaseversionfromsuite() { echo -n '1.0'; } +getnotautomaticfromsuite() { echo -n 'yes'; } +getbutautomaticupgradesfromsuite() { echo -n 'yes'; } +setupaptarchive --no-update +testsuccess aptget update + +cp -a aptarchive/dists aptarchive/dists.bak +cp -a rootdir/var/lib/apt/lists rootdir/var/lib/apt/lists.bak +APTARCHIVE="$(readlink -f './aptarchive')" + +sed -i -e 's#^Origin: Earth#Origin: Mars#' $(find ./aptarchive -name 'Release') +signreleasefiles +testfailuremsg "E: Repository 'file:$APTARCHIVE earth InRelease' changed its 'Origin' value from 'Earth' to 'Mars' +N: This must be accepted explicitly before updates for this repository can be applied. See apt-secure(8) manpage for details." apt update +testfailure apt update --allow-releaseinfo-change-label +testsuccesswithnotice apt update --allow-releaseinfo-change +testequal "All packages are up to date. +N: Repository 'file:$APTARCHIVE earth InRelease' changed its 'Origin' value from 'Earth' to 'Mars'" tail -n 2 rootdir/tmp/testsuccesswithnotice.output + +rm -rf rootdir/var/lib/apt/lists +cp -a rootdir/var/lib/apt/lists.bak rootdir/var/lib/apt/lists +sed -i -e 's#^Label: Blue#Label: Red#' $(find ./aptarchive -name 'Release') +signreleasefiles +testfailuremsg "E: Repository 'file:$APTARCHIVE earth InRelease' changed its 'Origin' value from 'Earth' to 'Mars' +E: Repository 'file:$APTARCHIVE earth InRelease' changed its 'Label' value from 'Blue Planet' to 'Red Planet' +N: This must be accepted explicitly before updates for this repository can be applied. See apt-secure(8) manpage for details." apt update +testfailure apt update --allow-releaseinfo-change-label +testfailuremsg "N: Repository 'file:$APTARCHIVE earth InRelease' changed its 'Origin' value from 'Earth' to 'Mars' +E: Repository 'file:$APTARCHIVE earth InRelease' changed its 'Label' value from 'Blue Planet' to 'Red Planet' +N: This must be accepted explicitly before updates for this repository can be applied. See apt-secure(8) manpage for details." apt update --allow-releaseinfo-change-origin +testsuccess apt update --allow-releaseinfo-change-origin --allow-releaseinfo-change-label -o quiet::ReleaseInfoChange=true + +# version changes are allowed by default +sed -i -e 's#^Version: 1#Version: 2#' $(find ./aptarchive -name 'Release') +signreleasefiles +testfailuremsg "E: Repository 'file:$APTARCHIVE earth InRelease' changed its 'Version' value from '1.0' to '2.0' +N: This must be accepted explicitly before updates for this repository can be applied. See apt-secure(8) manpage for details." apt update --no-allow-releaseinfo-change-version +testsuccesswithnotice apt update +testequal "All packages are up to date. +N: Repository 'file:$APTARCHIVE earth InRelease' changed its 'Version' value from '1.0' to '2.0'" tail -n 2 rootdir/tmp/testsuccesswithnotice.output + +sed -i -e 's#^Codename: home#Codename: colony#' $(find ./aptarchive -name 'Release') +signreleasefiles +testfailuremsg "E: Repository 'file:$APTARCHIVE earth InRelease' changed its 'Codename' value from 'home' to 'colony' +N: This must be accepted explicitly before updates for this repository can be applied. See apt-secure(8) manpage for details." apt update --no-allow-releaseinfo-change-codename +testsuccesswithnotice apt update --allow-releaseinfo-change-codename +testequal "All packages are up to date. +N: Repository 'file:$APTARCHIVE earth InRelease' changed its 'Codename' value from 'home' to 'colony'" tail -n 2 rootdir/tmp/testsuccesswithnotice.output + +sed -i -e '/^ButAutomaticUpgrades: / d' $(find ./aptarchive -name 'Release') +signreleasefiles +testfailuremsg "E: Repository 'file:$APTARCHIVE earth InRelease' changed its default priority for apt_preferences(5) from 100 to 1. +N: This must be accepted explicitly before updates for this repository can be applied. See apt-secure(8) manpage for details." apt update +testsuccesswithnotice apt update --allow-releaseinfo-change +testequal "All packages are up to date. +N: Repository 'file:$APTARCHIVE earth InRelease' changed its default priority for apt_preferences(5) from 100 to 1." tail -n 2 rootdir/tmp/testsuccesswithnotice.output + +sed -i -e '/^NotAutomatic: / d' -e '/^Codename: / a\ +Release-Notes: https://example.org/mars/release-notes' $(find ./aptarchive -name 'Release') +signreleasefiles +testfailuremsg "E: Repository 'file:$APTARCHIVE earth InRelease' changed its default priority for apt_preferences(5) from 1 to 500. +N: More information about this can be found online in the Release notes at: https://example.org/mars/release-notes +N: This must be accepted explicitly before updates for this repository can be applied. See apt-secure(8) manpage for details." apt update +testsuccesswithnotice apt update --allow-releaseinfo-change-defaultpin +testequal "All packages are up to date. +N: Repository 'file:$APTARCHIVE earth InRelease' changed its default priority for apt_preferences(5) from 1 to 500. +N: More information about this can be found online in the Release notes at: https://example.org/mars/release-notes" tail -n 3 rootdir/tmp/testsuccesswithnotice.output diff --git a/test/integration/test-bug-841874-warning-for-mismatching-distribution b/test/integration/test-bug-841874-warning-for-mismatching-distribution index 6cc8e3173..7502eefc3 100755 --- a/test/integration/test-bug-841874-warning-for-mismatching-distribution +++ b/test/integration/test-bug-841874-warning-for-mismatching-distribution @@ -47,15 +47,3 @@ testfailure apt show foo ln -s "${APTARCHIVE}/dists/testing" "${APTARCHIVE}/dists/buster" testsuccess apt update testsuccess apt show foo - -# changing codenames gets a warning, too -rm -rf rootdir/var/lib/apt/lists -sed -i -e 's#buster#testing#g' rootdir/etc/apt/sources.list.d/* -testsuccess apt update -testsuccess apt show foo -sed -i -e 's#^Codename: buster#Codename: zurg#g' $(find ./aptarchive -name 'Release') -signreleasefiles -testwarningmsg "W: Conflicting distribution: file:$APTARCHIVE testing/updates InRelease (expected buster/updates but got zurg/updates)" apt update -testsuccess apt show foo -testsuccess apt update -testsuccess apt show foo diff --git a/test/integration/test-policy-pinning b/test/integration/test-policy-pinning index 30238bd87..5676d1457 100755 --- a/test/integration/test-policy-pinning +++ b/test/integration/test-policy-pinning @@ -238,7 +238,7 @@ testequalpolicycoolstuff "2.0~bpo1" "2.0~bpo1" 990 500 990 "" -o Test=ButAutomat rm incoming/backports.main.pkglist incoming/backports.main.srclist buildsimplenativepackage "coolstuff" "all" "2.0~bpo2" "backports" -setupaptarchive +setupaptarchive --no-update sed -i aptarchive/dists/backports/Release -e 1i"NotAutomatic: yes" signreleasefiles |