diff options
-rw-r--r-- | apt-pkg/acquire-item.cc | 19 | ||||
-rw-r--r-- | apt-pkg/deb/debmetaindex.cc | 136 | ||||
-rw-r--r-- | apt-pkg/deb/debmetaindex.h | 4 | ||||
-rw-r--r-- | apt-pkg/metaindex.cc | 7 | ||||
-rw-r--r-- | apt-pkg/metaindex.h | 1 | ||||
-rw-r--r-- | apt-pkg/sourcelist.cc | 2 | ||||
-rw-r--r-- | debian/NEWS | 16 | ||||
-rw-r--r-- | doc/apt.conf.5.xml | 21 | ||||
-rw-r--r-- | doc/examples/configure-index | 3 | ||||
-rw-r--r-- | doc/sources.list.5.xml | 17 | ||||
-rw-r--r-- | test/integration/framework | 2 | ||||
-rwxr-xr-x | test/integration/test-releasefile-date | 46 |
12 files changed, 231 insertions, 43 deletions
diff --git a/apt-pkg/acquire-item.cc b/apt-pkg/acquire-item.cc index 792465b90..ff4036f4a 100644 --- a/apt-pkg/acquire-item.cc +++ b/apt-pkg/acquire-item.cc @@ -1661,6 +1661,25 @@ bool pkgAcqMetaBase::VerifyVendor(string const &) /*{{{*/ } } + if (TransactionManager->MetaIndexParser->GetNotBefore() > 0) + { + time_t const invalid_for = TransactionManager->MetaIndexParser->GetNotBefore() - time(nullptr); + if (invalid_for > 0) + { + std::string errmsg; + strprintf(errmsg, + // TRANSLATOR: The first %s is the URL of the bad Release file, the second is + // the time until the file will be valid - formatted in the same way as in + // the download progress display (e.g. 7d 3h 42min 1s) + _("Release file for %s is not valid yet (invalid for another %s). " + "Updates for this repository will not be applied."), + Target.URI.c_str(), TimeToStr(invalid_for).c_str()); + if (ErrorText.empty()) + ErrorText = errmsg; + return _error->Error("%s", errmsg.c_str()); + } + } + /* 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 && TransactionManager->LastMetaIndexParser != NULL && diff --git a/apt-pkg/deb/debmetaindex.cc b/apt-pkg/deb/debmetaindex.cc index 59a26390e..6cbed85a7 100644 --- a/apt-pkg/deb/debmetaindex.cc +++ b/apt-pkg/deb/debmetaindex.cc @@ -49,12 +49,16 @@ class APT_HIDDEN debReleaseIndexPrivate /*{{{*/ time_t ValidUntilMin; time_t ValidUntilMax; + metaIndex::TriState CheckDate; + time_t DateMaxFuture; + time_t NotBefore; + std::vector<std::string> Architectures; std::vector<std::string> NoSupportForAll; std::vector<std::string> SupportedComponents; std::map<std::string, std::string> const ReleaseOptions; - debReleaseIndexPrivate(std::map<std::string, std::string> const &Options) : CheckValidUntil(metaIndex::TRI_UNSET), ValidUntilMin(0), ValidUntilMax(0), ReleaseOptions(Options) {} + debReleaseIndexPrivate(std::map<std::string, std::string> const &Options) : CheckValidUntil(metaIndex::TRI_UNSET), ValidUntilMin(0), ValidUntilMax(0), CheckDate(metaIndex::TRI_UNSET), DateMaxFuture(0), NotBefore(0), ReleaseOptions(Options) {} }; /*}}}*/ // ReleaseIndex::MetaIndex* - display helpers /*{{{*/ @@ -482,54 +486,77 @@ bool debReleaseIndex::Load(std::string const &Filename, std::string * const Erro Date = 0; } - bool CheckValidUntil = _config->FindB("Acquire::Check-Valid-Until", true); - if (d->CheckValidUntil == metaIndex::TRI_NO) - CheckValidUntil = false; - else if (d->CheckValidUntil == metaIndex::TRI_YES) - CheckValidUntil = true; + bool CheckDate = _config->FindB("Acquire::Check-Date", true); + if (d->CheckDate == metaIndex::TRI_NO) + CheckDate = false; + else if (d->CheckDate == metaIndex::TRI_YES) + CheckDate = true; - if (CheckValidUntil == true) + if (CheckDate) { - std::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) - { - if(RFC1123StrToTime(StrValidUntil.c_str(), ValidUntil) == false) - { - if (ErrorText != NULL) - strprintf(*ErrorText, _("Invalid '%s' entry in Release file %s"), "Valid-Until", Filename.c_str()); - 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) - { - MaxAge = _config->FindI("Acquire::Max-ValidTime", 0); - if (Label.empty() == false) - MaxAge = _config->FindI(("Acquire::Max-ValidTime::" + Label).c_str(), MaxAge); - } - time_t MinAge = d->ValidUntilMin; - if (MinAge == 0) + // get the user settings for this archive + time_t MaxFuture = d->DateMaxFuture; + if (MaxFuture == 0) { - MinAge = _config->FindI("Acquire::Min-ValidTime", 0); + MaxFuture = _config->FindI("Acquire::Max-FutureTime", 10); if (Label.empty() == false) - MinAge = _config->FindI(("Acquire::Min-ValidTime::" + Label).c_str(), MinAge); + MaxFuture = _config->FindI(("Acquire::Max-FutureTime::" + Label).c_str(), MaxFuture); } - if (MinAge != 0 || ValidUntil != 0 || MaxAge != 0) + d->NotBefore = Date - MaxFuture; + + bool CheckValidUntil = _config->FindB("Acquire::Check-Valid-Until", true); + if (d->CheckValidUntil == metaIndex::TRI_NO) + CheckValidUntil = false; + else if (d->CheckValidUntil == metaIndex::TRI_YES) + CheckValidUntil = true; + + if (CheckValidUntil == true) { - if (MinAge != 0 && ValidUntil != 0) { - time_t const min_date = Date + MinAge; - if (ValidUntil < min_date) - ValidUntil = min_date; + std::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) + { + if (RFC1123StrToTime(StrValidUntil.c_str(), ValidUntil) == false) + { + if (ErrorText != NULL) + strprintf(*ErrorText, _("Invalid '%s' entry in Release file %s"), "Valid-Until", Filename.c_str()); + 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) + { + MaxAge = _config->FindI("Acquire::Max-ValidTime", 0); + if (Label.empty() == false) + MaxAge = _config->FindI(("Acquire::Max-ValidTime::" + Label).c_str(), MaxAge); + } + time_t MinAge = d->ValidUntilMin; + if (MinAge == 0) + { + MinAge = _config->FindI("Acquire::Min-ValidTime", 0); + if (Label.empty() == false) + MinAge = _config->FindI(("Acquire::Min-ValidTime::" + Label).c_str(), MinAge); } - if (MaxAge != 0 && Date != 0) { - time_t const max_date = Date + MaxAge; - if (ValidUntil == 0 || ValidUntil > max_date) - ValidUntil = max_date; + + if (MinAge != 0 || ValidUntil != 0 || MaxAge != 0) + { + if (MinAge != 0 && ValidUntil != 0) + { + time_t const min_date = Date + MinAge; + if (ValidUntil < min_date) + ValidUntil = min_date; + } + if (MaxAge != 0 && Date != 0) + { + time_t const max_date = Date + MaxAge; + if (ValidUntil == 0 || ValidUntil > max_date) + ValidUntil = max_date; + } } } } @@ -566,6 +593,11 @@ bool debReleaseIndex::Load(std::string const &Filename, std::string * const Erro return AuthPossible; } /*}}}*/ +time_t debReleaseIndex::GetNotBefore() const /*{{{*/ +{ + return d->NotBefore; +} + /*}}}*/ metaIndex * debReleaseIndex::UnloadedClone() const /*{{{*/ { if (Trusted == TRI_NO) @@ -685,6 +717,22 @@ bool debReleaseIndex::SetValidUntilMax(time_t const Valid) return _error->Error(_("Conflicting values set for option %s regarding source %s %s"), "Max-ValidTime", URI.c_str(), Dist.c_str()); return true; } +bool debReleaseIndex::SetCheckDate(TriState const pCheckDate) +{ + if (d->CheckDate == TRI_UNSET) + d->CheckDate = pCheckDate; + else if (d->CheckDate != pCheckDate) + return _error->Error(_("Conflicting values set for option %s regarding source %s %s"), "Check-Date", URI.c_str(), Dist.c_str()); + return true; +} +bool debReleaseIndex::SetDateMaxFuture(time_t const DateMaxFuture) +{ + if (d->DateMaxFuture == 0) + d->DateMaxFuture = DateMaxFuture; + else if (d->DateMaxFuture != DateMaxFuture) + return _error->Error(_("Conflicting values set for option %s regarding source %s %s"), "Date-Max-Future", URI.c_str(), Dist.c_str()); + return true; +} bool debReleaseIndex::SetSignedBy(std::string const &pSignedBy) { if (SignedBy.empty() == true && pSignedBy.empty() == false) @@ -1168,9 +1216,11 @@ class APT_HIDDEN debSLTypeDebian : public pkgSourceList::Type /*{{{*/ ); if (Deb->SetTrusted(GetTriStateOption(Options, "trusted")) == false || - Deb->SetCheckValidUntil(GetTriStateOption(Options, "check-valid-until")) == false || - Deb->SetValidUntilMax(GetTimeOption(Options, "valid-until-max")) == false || - Deb->SetValidUntilMin(GetTimeOption(Options, "valid-until-min")) == false) + Deb->SetCheckValidUntil(GetTriStateOption(Options, "check-valid-until")) == false || + Deb->SetValidUntilMax(GetTimeOption(Options, "valid-until-max")) == false || + Deb->SetValidUntilMin(GetTimeOption(Options, "valid-until-min")) == false || + Deb->SetCheckDate(GetTriStateOption(Options, "check-date")) == false || + Deb->SetDateMaxFuture(GetTimeOption(Options, "date-max-future")) == false) return false; std::map<std::string, std::string>::const_iterator const signedby = Options.find("signed-by"); diff --git a/apt-pkg/deb/debmetaindex.h b/apt-pkg/deb/debmetaindex.h index 5a97cfc78..864ac3eba 100644 --- a/apt-pkg/deb/debmetaindex.h +++ b/apt-pkg/deb/debmetaindex.h @@ -55,6 +55,8 @@ class APT_HIDDEN debReleaseIndex : public metaIndex bool SetCheckValidUntil(TriState const Trusted); bool SetValidUntilMin(time_t const Valid); bool SetValidUntilMax(time_t const Valid); + bool SetCheckDate(TriState const CheckDate); + bool SetDateMaxFuture(time_t const DateMaxFuture); bool SetSignedBy(std::string const &SignedBy); std::map<std::string, std::string> GetReleaseOptions(); @@ -63,6 +65,8 @@ class APT_HIDDEN debReleaseIndex : public metaIndex bool IsArchitectureAllSupportedFor(IndexTarget const &target) const; bool HasSupportForComponent(std::string const &component) const; + APT_PURE time_t GetNotBefore() const; + void AddComponent(std::string const &sourcesEntry, bool const isSrc, std::string const &Name, std::vector<std::string> const &Targets, diff --git a/apt-pkg/metaindex.cc b/apt-pkg/metaindex.cc index bdae6dcc9..8cbdb5e01 100644 --- a/apt-pkg/metaindex.cc +++ b/apt-pkg/metaindex.cc @@ -72,6 +72,13 @@ 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::GetNotBefore() const +{ + debReleaseIndex const *const deb = dynamic_cast<debReleaseIndex const *>(this); + if (deb != nullptr) + return deb->GetNotBefore(); + return 0; +} APT_PURE time_t metaIndex::GetDate() const { return this->Date; } APT_PURE metaIndex::TriState metaIndex::GetLoadedSuccessfully() const { return LoadedSuccessfully; } APT_PURE std::string metaIndex::GetExpectedDist() const { return Dist; } diff --git a/apt-pkg/metaindex.h b/apt-pkg/metaindex.h index 91cfce59b..08664305e 100644 --- a/apt-pkg/metaindex.h +++ b/apt-pkg/metaindex.h @@ -82,6 +82,7 @@ public: bool GetSupportsAcquireByHash() const; time_t GetValidUntil() const; time_t GetDate() const; + APT_HIDDEN time_t GetNotBefore() const; // FIXME make virtual std::string GetExpectedDist() const; bool CheckDist(std::string const &MaybeDist) const; diff --git a/apt-pkg/sourcelist.cc b/apt-pkg/sourcelist.cc index fd0264ff1..1fdc92dcb 100644 --- a/apt-pkg/sourcelist.cc +++ b/apt-pkg/sourcelist.cc @@ -119,6 +119,8 @@ bool pkgSourceList::Type::ParseStanza(vector<metaIndex *> &List, /*{{{*/ mapping.insert(std::make_pair("Check-Valid-Until", std::make_pair("check-valid-until", false))); mapping.insert(std::make_pair("Valid-Until-Min", std::make_pair("valid-until-min", false))); mapping.insert(std::make_pair("Valid-Until-Max", std::make_pair("valid-until-max", false))); + mapping.insert(std::make_pair("Check-Date", std::make_pair("check-date", false))); + mapping.insert(std::make_pair("Date-Max-Future", std::make_pair("date-max-future", false))); mapping.insert(std::make_pair("Signed-By", std::make_pair("signed-by", false))); mapping.insert(std::make_pair("PDiffs", std::make_pair("pdiffs", false))); mapping.insert(std::make_pair("By-Hash", std::make_pair("by-hash", false))); diff --git a/debian/NEWS b/debian/NEWS index 132920b5d..a8cd8f7ad 100644 --- a/debian/NEWS +++ b/debian/NEWS @@ -1,3 +1,19 @@ +apt (1.6~alpha8) UNRELEASED; urgency=medium + + APT now verifies that the date of Release files is not in the future. By + default, it may be 10 seconds in the future to allow for some clock drift. + + Two new configuration options can be used to tweak the behavior: + Acquire::Check-Date + Acquire::Max-DateFuture + + These can be overridden in sources.list entries using the check-date + and date-future-max options. Note that disabling check-date also + disables checks on valid-until: It is considered to mean that your + machine's time is not reliable. + + -- Julian Andres Klode <juliank@ubuntu.com> Mon, 12 Feb 2018 12:53:18 +0100 + apt (1.6~alpha1) unstable; urgency=medium All methods provided by apt except for cdrom, gpgv, and rsh now diff --git a/doc/apt.conf.5.xml b/doc/apt.conf.5.xml index fdcd99425..e285b3130 100644 --- a/doc/apt.conf.5.xml +++ b/doc/apt.conf.5.xml @@ -315,6 +315,27 @@ APT::Compressor::rev { for the download itself (see also &sources-list;).</para> <variablelist> + <varlistentry><term><option>Check-Date</option></term> + <listitem><para> + Security related option defaulting to true, enabling time-related + checks. Disabling it means that the machine's time cannot be + trusted, and APT will hence disable all time-related checks, + such as <option>Check-Valid-Until</option> and verifying that + the Date field of a release file is not in the future. + </para></listitem> + </varlistentry> + + <varlistentry><term><option>Max-FutureTime</option></term> + <listitem><para>Maximum time (in seconds) before its creation (as indicated + by the <literal>Date</literal> header) that the <filename>Release</filename> + file should be considered valid. + + The default value is <literal>10</literal>. + Archive specific settings can be made by appending the label of the archive + to the option name. Preferably, the same can be achieved for specific + &sources-list; entries by using the <option>Date-Max-Future</option> option there. + </para></listitem> + </varlistentry> <varlistentry><term><option>Check-Valid-Until</option></term> <listitem><para> Security related option defaulting to true, as giving a Release file's diff --git a/doc/examples/configure-index b/doc/examples/configure-index index 9088bd844..8a74fde2d 100644 --- a/doc/examples/configure-index +++ b/doc/examples/configure-index @@ -234,6 +234,9 @@ Acquire Max-ValidTime::* "<INT>"; // repository label specific configuration Min-ValidTime "<INT>"; // time in seconds Min-ValidTime::* "<INT>"; // repository label specific configuration + Check-Date "<BOOL>"; // whether to check the "Date" field + Max-FutureTime "<INT>"; // seconds to allow release file's Date field to be in the future (default 10) + Max-FutureTime::* "<INT>"; // repository label specific configuration SameMirrorForAllIndexes "<BOOL>"; // use the mirror serving the Release file for Packages & co diff --git a/doc/sources.list.5.xml b/doc/sources.list.5.xml index 2a047c83b..592227dd8 100644 --- a/doc/sources.list.5.xml +++ b/doc/sources.list.5.xml @@ -343,6 +343,23 @@ deb-src [ option1=value1 option2=value2 ] uri suite [component1] [component2] [. default. </para></listitem> + <listitem><para><option>Check-Date</option> (<option>check-date</option>) + is a yes/no value which controls if APT should consider + the machine's time correct and hence perform time related + checks, such as verifying that a Release file is not + from the future. Disabling it also disables the + <option>Check-Valid-Until</option> option + mentioned above. + </para></listitem> + + <listitem><para><option>Date-Max-Future</option> + (<option>date-max-future</option>) controls how far + from the future a repository may be. + Default to the value of the configuration option + <option>Acquire::Max-FutureTime</option> which is + 10 seconds by default. + </para></listitem> + <listitem><para><option>InRelease-Path</option> (<option>inrelease-path</option>) determines the path to the InRelease file, relative to the normal position of an <filename>InRelease</filename> file. diff --git a/test/integration/framework b/test/integration/framework index ecce46d2e..bc84de184 100644 --- a/test/integration/framework +++ b/test/integration/framework @@ -460,6 +460,8 @@ EOF echo "Apt::Cmd::Disable-Script-Warning \"1\";" > rootdir/etc/apt/apt.conf.d/apt-binary export APT_KEY_DONT_WARN_ON_DANGEROUS_USAGE=no echo 'Acquire::Connect::AddrConfig "false";' > rootdir/etc/apt/apt.conf.d/connect-addrconfig + # Allow release files to be 10 hours in the future, rather than 10 seconds + echo 'Acquire::Max-FutureTime "'$((10 * 60 * 60))'";' > rootdir/etc/apt/apt.conf.d/future-time configcompression '.' 'gz' #'bz2' 'lzma' 'xz' confighashes 'SHA256' # these are tests, not security best-practices diff --git a/test/integration/test-releasefile-date b/test/integration/test-releasefile-date new file mode 100755 index 000000000..a98507408 --- /dev/null +++ b/test/integration/test-releasefile-date @@ -0,0 +1,46 @@ +#!/bin/sh +set -e + +TESTDIR="$(readlink -f "$(dirname "$0")")" +. "$TESTDIR/framework" +setupenvironment +configarchitecture 'i386' + +insertpackage 'wheezy' 'apt' 'all' '0.8.15' + +getlabelfromsuite() { + echo -n 'Testcases' +} + +setupaptarchive --no-update + +runtest() { + local MSG="$1" + msgtest "Release file is $MSG as it has" "$2" + rm -rf rootdir/var/lib/apt/lists + generatereleasefiles "$3" + signreleasefiles + shift 3 + if [ "$MSG" = 'accepted' ]; then + testsuccess --nomsg aptget update "$@" + testfailure grep -q 'is not valid yet' rootdir/tmp/testsuccess.output + else + testfailure --nomsg aptget update "$@" + testsuccess grep -q 'is not valid yet' rootdir/tmp/testfailure.output + fi +} + + +runtest 'accepted' 'no date' '' +runtest 'accepted' 'ok date' 'now + 1 hour' +runtest 'rejected' 'date to far in the future' 'now + 12 hours' +runtest 'accepted' 'date to far in the future, but accepted via option' 'now + 12 hours' -o Acquire::Max-FutureTime=86400 + +sed -i -e 's#\(deb\(-src\)\?\) #\1 [check-date=no] #' rootdir/etc/apt/sources.list.d/* +runtest 'accepted' 'bad Date but overridden by sources option' 'now + 1 day' + +sed -i -e 's#\(deb\(-src\)\?\) \[.*\] #\1 [date-max-future=86400] #' rootdir/etc/apt/sources.list.d/* +runtest 'accepted' 'Date allowed via sources list option via sources option' 'now + 12 hours' + +sed -i -e 's#\(deb\(-src\)\?\) \[.*\] #\1 [date-max-future=86405] #' rootdir/etc/apt/sources.list.d/* +runtest 'rejected' 'Date further in the future than allowed by sources.list option' 'now + 2 day' |