summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apt-pkg/acquire-item.cc14
-rw-r--r--apt-pkg/acquire-method.cc2
-rw-r--r--apt-pkg/contrib/strutl.cc72
-rw-r--r--apt-pkg/contrib/strutl.h4
-rw-r--r--apt-pkg/indexrecords.cc45
-rw-r--r--apt-pkg/indexrecords.h4
-rw-r--r--debian/changelog14
-rw-r--r--doc/apt-ftparchive.1.xml3
-rw-r--r--doc/apt.conf.5.xml24
-rw-r--r--doc/examples/configure-index4
-rw-r--r--ftparchive/writer.cc10
-rw-r--r--methods/ftp.cc3
-rw-r--r--methods/http.cc2
-rw-r--r--methods/rsh.cc3
-rwxr-xr-xtest/pre-upload-check.py14
-rw-r--r--test/testsources.list/sources.list.all-validuntil-broken1
16 files changed, 186 insertions, 33 deletions
diff --git a/apt-pkg/acquire-item.cc b/apt-pkg/acquire-item.cc
index 9e29f8189..629d572a4 100644
--- a/apt-pkg/acquire-item.cc
+++ b/apt-pkg/acquire-item.cc
@@ -33,6 +33,7 @@
#include <string>
#include <sstream>
#include <stdio.h>
+#include <ctime>
/*}}}*/
using namespace std;
@@ -1180,6 +1181,17 @@ bool pkgAcqMetaIndex::VerifyVendor(string Message) /*{{{*/
Transformed = "";
}
+ if (_config->FindB("Acquire::Check-Valid-Until", true) == true &&
+ MetaIndexParser->GetValidUntil() > 0) {
+ time_t const invalid_since = time(NULL) - MetaIndexParser->GetValidUntil();
+ if (invalid_since > 0)
+ // TRANSLATOR: The first %s is the URL of the bad Release file, the second is
+ // the time since then the file is invalid - formated in the same way as in
+ // the download progress display (e.g. 7d 3h 42min 1s)
+ return _error->Error(_("Release file expired, ignoring %s (invalid since %s)"),
+ RealURI.c_str(), TimeToStr(invalid_since).c_str());
+ }
+
if (_config->FindB("Debug::pkgAcquire::Auth", false))
{
std::cerr << "Got Codename: " << MetaIndexParser->GetDist() << std::endl;
@@ -1197,7 +1209,7 @@ bool pkgAcqMetaIndex::VerifyVendor(string Message) /*{{{*/
// return false;
if (!Transformed.empty())
{
- _error->Warning("Conflicting distribution: %s (expected %s but got %s)",
+ _error->Warning(_("Conflicting distribution: %s (expected %s but got %s)"),
Desc.Description.c_str(),
Transformed.c_str(),
MetaIndexParser->GetDist().c_str());
diff --git a/apt-pkg/acquire-method.cc b/apt-pkg/acquire-method.cc
index fe066741c..b82dceecb 100644
--- a/apt-pkg/acquire-method.cc
+++ b/apt-pkg/acquire-method.cc
@@ -373,7 +373,7 @@ int pkgAcqMethod::Run(bool Single)
Tmp->Uri = LookupTag(Message,"URI");
Tmp->DestFile = LookupTag(Message,"FileName");
- if (StrToTime(LookupTag(Message,"Last-Modified"),Tmp->LastModified) == false)
+ if (RFC1123StrToTime(LookupTag(Message,"Last-Modified").c_str(),Tmp->LastModified) == false)
Tmp->LastModified = 0;
Tmp->IndexFile = StringToBool(LookupTag(Message,"Index-File"),false);
Tmp->Next = 0;
diff --git a/apt-pkg/contrib/strutl.cc b/apt-pkg/contrib/strutl.cc
index c7d63ce8a..160450366 100644
--- a/apt-pkg/contrib/strutl.cc
+++ b/apt-pkg/contrib/strutl.cc
@@ -827,34 +827,66 @@ static int MonthConv(char *Month)
}
}
/*}}}*/
-// timegm - Internal timegm function if gnu is not available /*{{{*/
+// timegm - Internal timegm if the gnu version is not available /*{{{*/
// ---------------------------------------------------------------------
-/* Ripped this evil little function from wget - I prefer the use of
- GNU timegm if possible as this technique will have interesting problems
- with leap seconds, timezones and other.
-
- Converts struct tm to time_t, assuming the data in tm is UTC rather
+/* Converts struct tm to time_t, assuming the data in tm is UTC rather
than local timezone (mktime assumes the latter).
-
- Contributed by Roger Beeman <beeman@cisco.com>, with the help of
- Mark Baushke <mdb@cisco.com> and the rest of the Gurus at CISCO. */
-
-/* Turned it into an autoconf check, because GNU is not the only thing which
- can provide timegm. -- 2002-09-22, Joel Baker */
-#ifndef HAVE_TIMEGM // Now with autoconf!
+ This function is a nonstandard GNU extension that is also present on
+ the BSDs and maybe other systems. For others we follow the advice of
+ the manpage of timegm and use his portable replacement. */
+#ifndef HAVE_TIMEGM
static time_t timegm(struct tm *t)
{
- time_t tl, tb;
-
- tl = mktime (t);
- if (tl == -1)
- return -1;
- tb = mktime (gmtime (&tl));
- return (tl <= tb ? (tl + (tl - tb)) : (tl - (tb - tl)));
+ char *tz = getenv("TZ");
+ setenv("TZ", "", 1);
+ tzset();
+ time_t ret = mktime(t);
+ if (tz)
+ setenv("TZ", tz, 1);
+ else
+ unsetenv("TZ");
+ tzset();
+ return ret;
}
#endif
/*}}}*/
+// FullDateToTime - Converts a HTTP1.1 full date strings into a time_t /*{{{*/
+// ---------------------------------------------------------------------
+/* tries to parses a full date as specified in RFC2616 Section 3.3.1
+ with one exception: All timezones (%Z) are accepted but the protocol
+ says that it MUST be GMT, but this one is equal to UTC which we will
+ encounter from time to time (e.g. in Release files) so we accept all
+ here and just assume it is GMT (or UTC) later on */
+bool RFC1123StrToTime(const char* const str,time_t &time)
+{
+ struct tm Tm;
+ // Sun, 06 Nov 1994 08:49:37 GMT ; RFC 822, updated by RFC 1123
+ if (strptime(str, "%a, %d %b %Y %H:%M:%S %Z", &Tm) == NULL &&
+ // Sunday, 06-Nov-94 08:49:37 GMT ; RFC 850, obsoleted by RFC 1036
+ strptime(str, "%A, %d-%b-%y %H:%M:%S %Z", &Tm) == NULL &&
+ // Sun Nov 6 08:49:37 1994 ; ANSI C's asctime() format
+ strptime(str, "%a %b %d %H:%M:%S %Y", &Tm) == NULL)
+ return false;
+
+ time = timegm(&Tm);
+ return true;
+}
+ /*}}}*/
+// FTPMDTMStrToTime - Converts a ftp modification date into a time_t /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+bool FTPMDTMStrToTime(const char* const str,time_t &time)
+{
+ struct tm Tm;
+ // MDTM includes no whitespaces but recommend and ignored by strptime
+ if (strptime(str, "%Y %m %d %H %M %S", &Tm) == NULL)
+ return false;
+
+ time = timegm(&Tm);
+ return true;
+}
+ /*}}}*/
// StrToTime - Converts a string into a time_t /*{{{*/
// ---------------------------------------------------------------------
/* This handles all 3 populare time formats including RFC 1123, RFC 1036
diff --git a/apt-pkg/contrib/strutl.h b/apt-pkg/contrib/strutl.h
index e509145f9..a457ff047 100644
--- a/apt-pkg/contrib/strutl.h
+++ b/apt-pkg/contrib/strutl.h
@@ -45,7 +45,9 @@ string Base64Encode(const string &Str);
string OutputInDepth(const unsigned long Depth, const char* Separator=" ");
string URItoFileName(const string &URI);
string TimeRFC1123(time_t Date);
-bool StrToTime(const string &Val,time_t &Result);
+bool RFC1123StrToTime(const char* const str,time_t &time) __must_check;
+bool FTPMDTMStrToTime(const char* const str,time_t &time) __must_check;
+__deprecated bool StrToTime(const string &Val,time_t &Result);
string LookupTag(const string &Message,const char *Tag,const char *Default = 0);
int StringToBool(const string &Text,int Default = -1);
bool ReadMessages(int Fd, vector<string> &List);
diff --git a/apt-pkg/indexrecords.cc b/apt-pkg/indexrecords.cc
index 9a9600531..eb9a36866 100644
--- a/apt-pkg/indexrecords.cc
+++ b/apt-pkg/indexrecords.cc
@@ -7,8 +7,11 @@
#include <apt-pkg/tagfile.h>
#include <apt-pkg/error.h>
#include <apt-pkg/strutl.h>
+#include <apt-pkg/configuration.h>
#include <apti18n.h>
#include <sys/stat.h>
+#include <clocale>
+
/*}}}*/
string indexRecords::GetDist() const
{
@@ -26,6 +29,11 @@ string indexRecords::GetExpectedDist() const
return this->ExpectedDist;
}
+time_t indexRecords::GetValidUntil() const
+{
+ return this->ValidUntil;
+}
+
const indexRecords::checkSum *indexRecords::Lookup(const string MetaKey)
{
return Entries[MetaKey];
@@ -85,9 +93,40 @@ bool indexRecords::Load(const string Filename) /*{{{*/
{
strprintf(ErrorText, _("No Hash entry in Release file %s"), Filename.c_str());
return false;
- }
+ }
+
+ string Label = Section.FindS("Label");
+ string StrDate = Section.FindS("Date");
+ string 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)
+ {
+ strprintf(ErrorText, _("Invalid 'Valid-Until' entry in Release file %s"), Filename.c_str());
+ return false;
+ }
+ }
+ // get the user settings for this archive and use what expires earlier
+ int MaxAge = _config->FindI("Acquire::Max-ValidTime", 0);
+ if (Label.empty() == true)
+ MaxAge = _config->FindI(string("Acquire::Max-ValidTime::" + Label).c_str(), MaxAge);
+
+ if(MaxAge == 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;
+ }
+ date += 24*60*60*MaxAge;
+
+ if (ValidUntil == 0 || ValidUntil > date)
+ ValidUntil = date;
- string Strdate = Section.FindS("Date"); // FIXME: verify this somehow?
return true;
}
/*}}}*/
@@ -165,6 +204,6 @@ indexRecords::indexRecords()
}
indexRecords::indexRecords(const string ExpectedDist) :
- ExpectedDist(ExpectedDist)
+ ExpectedDist(ExpectedDist), ValidUntil(0)
{
}
diff --git a/apt-pkg/indexrecords.h b/apt-pkg/indexrecords.h
index 2e3103b70..5b532c1a5 100644
--- a/apt-pkg/indexrecords.h
+++ b/apt-pkg/indexrecords.h
@@ -12,6 +12,7 @@
#include <map>
#include <vector>
+#include <ctime>
class indexRecords
{
@@ -25,6 +26,8 @@ class indexRecords
string Dist;
string Suite;
string ExpectedDist;
+ time_t ValidUntil;
+
std::map<string,checkSum *> Entries;
public:
@@ -40,6 +43,7 @@ class indexRecords
virtual bool Load(string Filename);
string GetDist() const;
+ time_t GetValidUntil() const;
virtual bool CheckDist(const string MaybeDist) const;
string GetExpectedDist() const;
virtual ~indexRecords(){};
diff --git a/debian/changelog b/debian/changelog
index 142d8359a..56a17eb38 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -20,6 +20,9 @@ apt (0.7.26~exp6) UNRELEASED; urgency=low
* apt-pkg/aptconfiguration.cc:
- remove duplicate architectures in getArchitectures()
* apt-pkg/indexrecords.{cc,h}:
+ - backport forgotten Valid-Until patch from the obsolete experimental
+ branch to prevent replay attacks better, thanks to Thomas Viehmann
+ for the initial patch! (Closes: #499897)
- add a constant Exists check for MetaKeys
* apt-pkg/acquire-item.cc:
- do not try PDiff if it is not listed in the Meta file
@@ -48,8 +51,17 @@ apt (0.7.26~exp6) UNRELEASED; urgency=low
- split Open() into submethods to be able to build only parts
- make the OpProgress optional in the Cache buildprocess
- store also the SourceList we use internally for export
+ * doc/apt.conf.5.xml:
+ - document the new Valid-Until related options
+ * apt-pkg/contrib/strutl.cc:
+ - split StrToTime() into HTTP1.1 and FTP date parser methods and
+ use strptime() instead of some self-made scanf mangling
+ - use the portable timegm shown in his manpage instead of a strange
+ looking code copycat from wget
+ * ftparchive/writer.cc:
+ - add ValidTime option to generate a Valid-Until header in Release file
- -- David Kalnischkies <kalnischkies@gmail.com> Wed, 09 Jun 2010 10:50:12 +0200
+ -- David Kalnischkies <kalnischkies@gmail.com> Wed, 09 Jun 2010 10:52:31 +0200
apt (0.7.26~exp5) experimental; urgency=low
diff --git a/doc/apt-ftparchive.1.xml b/doc/apt-ftparchive.1.xml
index a3ac45bd3..549aa6a34 100644
--- a/doc/apt-ftparchive.1.xml
+++ b/doc/apt-ftparchive.1.xml
@@ -122,7 +122,8 @@
e.g. <literal>APT::FTPArchive::Release::Origin</literal>. The supported fields
are: <literal>Origin</literal>, <literal>Label</literal>, <literal>Suite</literal>,
<literal>Version</literal>, <literal>Codename</literal>, <literal>Date</literal>,
- <literal>Architectures</literal>, <literal>Components</literal>, <literal>Description</literal>.</para></listitem>
+ <literal>Valid-Until</literal>, <literal>Architectures</literal>,
+ <literal>Components</literal>, <literal>Description</literal>.</para></listitem>
</varlistentry>
diff --git a/doc/apt.conf.5.xml b/doc/apt.conf.5.xml
index fe005e0f1..0cf4bb663 100644
--- a/doc/apt.conf.5.xml
+++ b/doc/apt.conf.5.xml
@@ -230,6 +230,30 @@ DPkg::Pre-Install-Pkgs {"/usr/sbin/dpkg-preconfigure --apt";};
and the URI handlers.
<variablelist>
+ <varlistentry><term>Check-Valid-Until</term>
+ <listitem><para>Security related option defaulting to true as an
+ expiring validation for a Release file prevents longtime replay attacks
+ and can e.g. also help users to identify no longer updated mirrors -
+ but the feature depends on the correctness of the time on the user system.
+ Archive maintainers are encouraged to create Release files with the
+ <literal>Valid-Until</literal> header, but if they don't or a stricter value
+ is volitional the following <literal>Max-ValidTime</literal> option can be used.
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry><term>Max-ValidTime</term>
+ <listitem><para>Seconds the Release file should be considered valid after
+ it was created. The default is "for ever" (0) if the Release file of the
+ archive doesn't include a <literal>Valid-Until</literal> header.
+ If it does then this date is the default. The date from the Release file or
+ the date specified by the creation time of the Release file
+ (<literal>Date</literal> header) plus the seconds specified with this
+ options are used to check if the validation of a file has expired by using
+ the earlier date of the two. Archive specific settings can be made by
+ appending the label of the archive to the option name.
+ </para></listitem>
+ </varlistentry>
+
<varlistentry><term>PDiffs</term>
<listitem><para>Try to download deltas called <literal>PDiffs</literal> for
Packages or Sources files instead of downloading whole ones. True
diff --git a/doc/examples/configure-index b/doc/examples/configure-index
index 487c09acb..fdec32c2c 100644
--- a/doc/examples/configure-index
+++ b/doc/examples/configure-index
@@ -176,6 +176,10 @@ Acquire
PDiffs::SizeLimit "50"; // don't use diffs if size of all patches excess
// 50% of the size of the original file
+ Check-Valid-Until "true";
+ Max-ValidTime "864000"; // 10 days
+ Max-ValidTime::Debian-Security "604800"; // 7 days, label specific configuration
+
// HTTP method configuration
http
{
diff --git a/ftparchive/writer.cc b/ftparchive/writer.cc
index 6cda29b21..650eec57c 100644
--- a/ftparchive/writer.cc
+++ b/ftparchive/writer.cc
@@ -924,6 +924,15 @@ ReleaseWriter::ReleaseWriter(string const &DB)
datestr[0] = '\0';
}
+ time_t const validuntil = now + _config->FindI("APT::FTPArchive::Release::ValidTime", 0);
+ char validstr[128];
+ if (now == validuntil ||
+ strftime(validstr, sizeof(validstr), "%a, %d %b %Y %H:%M:%S UTC",
+ gmtime(&validuntil)) == 0)
+ {
+ datestr[0] = '\0';
+ }
+
map<string,string> Fields;
Fields["Origin"] = "";
Fields["Label"] = "";
@@ -931,6 +940,7 @@ ReleaseWriter::ReleaseWriter(string const &DB)
Fields["Version"] = "";
Fields["Codename"] = "";
Fields["Date"] = datestr;
+ Fields["Valid-Until"] = validstr;
Fields["Architectures"] = "";
Fields["Components"] = "";
Fields["Description"] = "";
diff --git a/methods/ftp.cc b/methods/ftp.cc
index 3e1725823..97248f900 100644
--- a/methods/ftp.cc
+++ b/methods/ftp.cc
@@ -661,8 +661,7 @@ bool FTPConn::ModTime(const char *Path, time_t &Time)
return true;
// Parse it
- StrToTime(Msg,Time);
- return true;
+ return FTPMDTMStrToTime(Msg.c_str(), Time);
}
/*}}}*/
// FTPConn::CreateDataFd - Get a data connection /*{{{*/
diff --git a/methods/http.cc b/methods/http.cc
index d43dd14c8..5fdc62696 100644
--- a/methods/http.cc
+++ b/methods/http.cc
@@ -631,7 +631,7 @@ bool ServerState::HeaderLine(string Line)
if (stringcasecmp(Tag,"Last-Modified:") == 0)
{
- if (StrToTime(Val,Date) == false)
+ if (RFC1123StrToTime(Val.c_str(), Date) == false)
return _error->Error(_("Unknown date format"));
return true;
}
diff --git a/methods/rsh.cc b/methods/rsh.cc
index f0ccfc42d..97b4ef151 100644
--- a/methods/rsh.cc
+++ b/methods/rsh.cc
@@ -278,8 +278,7 @@ bool RSHConn::ModTime(const char *Path, time_t &Time)
return false;
// Parse it
- StrToTime(Msg,Time);
- return true;
+ return FTPMDTMStrToTime(Msg.c_str(), Time);
}
/*}}}*/
// RSHConn::Get - Get a file /*{{{*/
diff --git a/test/pre-upload-check.py b/test/pre-upload-check.py
index 268b3d672..e2dfecbd3 100755
--- a/test/pre-upload-check.py
+++ b/test/pre-upload-check.py
@@ -95,6 +95,20 @@ class testAuthentication(unittest.TestCase):
self.assert_(len(glob.glob("/var/lib/apt/lists/partial/*")) == 0,
"partial/ dir has leftover files: %s" % glob.glob("/var/lib/apt/lists/partial/*"))
+ def testValid(self):
+ for f in glob.glob("testsources.list/sources.list*validuntil*"):
+ self._cleanup()
+ (prefix, testtype, result) = f.split("-")
+ expected_res = self._expectedRes(result)
+ cmd = ["update"]
+ res = call([self.apt,"-o","Dir::Etc::sourcelist=./%s" % f]+cmd+apt_args,
+ stdout=stdout, stderr=stderr)
+ self.assert_(res == expected_res,
+ "test '%s' failed (got %s expected %s" % (f,res,expected_res))
+ if expected_res == 0:
+ self.assert_(len(glob.glob("/var/lib/apt/lists/partial/*")) == 0,
+ "partial/ dir has leftover files: %s" % glob.glob("/var/lib/apt/lists/partial/*"))
+
class testLocalRepositories(unittest.TestCase):
" test local repository regressions "
diff --git a/test/testsources.list/sources.list.all-validuntil-broken b/test/testsources.list/sources.list.all-validuntil-broken
new file mode 100644
index 000000000..bab59bb81
--- /dev/null
+++ b/test/testsources.list/sources.list.all-validuntil-broken
@@ -0,0 +1 @@
+deb http://people.ubuntu.com/~mvo/apt/auth-test-suit/all-validuntil-broken/ /