Browse Source

- 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)
* 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
tags/debian/0.8.0
David Kalnischkies 11 years ago
parent
commit
308b793694
16 changed files with 186 additions and 33 deletions
  1. +13
    -1
      apt-pkg/acquire-item.cc
  2. +1
    -1
      apt-pkg/acquire-method.cc
  3. +52
    -20
      apt-pkg/contrib/strutl.cc
  4. +3
    -1
      apt-pkg/contrib/strutl.h
  5. +42
    -3
      apt-pkg/indexrecords.cc
  6. +4
    -0
      apt-pkg/indexrecords.h
  7. +13
    -1
      debian/changelog
  8. +2
    -1
      doc/apt-ftparchive.1.xml
  9. +24
    -0
      doc/apt.conf.5.xml
  10. +4
    -0
      doc/examples/configure-index
  11. +10
    -0
      ftparchive/writer.cc
  12. +1
    -2
      methods/ftp.cc
  13. +1
    -1
      methods/http.cc
  14. +1
    -2
      methods/rsh.cc
  15. +14
    -0
      test/pre-upload-check.py
  16. +1
    -0
      test/testsources.list/sources.list.all-validuntil-broken

+ 13
- 1
apt-pkg/acquire-item.cc View File

@@ -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());


+ 1
- 1
apt-pkg/acquire-method.cc View File

@@ -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;


+ 52
- 20
apt-pkg/contrib/strutl.cc View File

@@ -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


+ 3
- 1
apt-pkg/contrib/strutl.h View File

@@ -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);


+ 42
- 3
apt-pkg/indexrecords.cc View File

@@ -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)
{
}

+ 4
- 0
apt-pkg/indexrecords.h View File

@@ -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(){};


+ 13
- 1
debian/changelog View File

@@ -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



+ 2
- 1
doc/apt-ftparchive.1.xml View File

@@ -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>



+ 24
- 0
doc/apt.conf.5.xml View File

@@ -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


+ 4
- 0
doc/examples/configure-index View File

@@ -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
{


+ 10
- 0
ftparchive/writer.cc View File

@@ -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"] = "";


+ 1
- 2
methods/ftp.cc View File

@@ -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 /*{{{*/


+ 1
- 1
methods/http.cc View File

@@ -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;
}


+ 1
- 2
methods/rsh.cc View File

@@ -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 /*{{{*/


+ 14
- 0
test/pre-upload-check.py View File

@@ -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 "


+ 1
- 0
test/testsources.list/sources.list.all-validuntil-broken View File

@@ -0,0 +1 @@
deb http://people.ubuntu.com/~mvo/apt/auth-test-suit/all-validuntil-broken/ /

Loading…
Cancel
Save