Commit b6e73773 authored by mmenke's avatar mmenke Committed by Commit bot

When parsing cookie expiration times, saturate out of range dates

rather than reject them.

Roughly this means cookie expiration times prior to 1970 on (non-OSX)
POSIX, or after 2038 on 32-bit (non-OSX) POSIX, will now be interpreted
as either very small or very large base::Time values.

This is a reland of https://codereview.chromium.org/2424443002/, which
ran into issues due to base::Time::FromTimeT() returning Time::Max() when
passed the maximum time_t value. As a workaround, ParseCookieExpirationTime
now returns the min/max base::Time() values for times outside the supported
range, instad of the min/max base::Time() values supported by
Time::FromUTCExploded() on the current platform.

BUG=649416

Review-Url: https://chromiumcodereview.appspot.com/2438513003
Cr-Commit-Position: refs/heads/master@{#426539}
parent 88eb893d
......@@ -115,8 +115,10 @@ bool ParseCookieLifetime(net::ParsedCookie* cookie,
}
Time parsed_expiry_time;
if (cookie->HasExpires())
parsed_expiry_time = net::cookie_util::ParseCookieTime(cookie->Expires());
if (cookie->HasExpires()) {
parsed_expiry_time =
net::cookie_util::ParseCookieExpirationTime(cookie->Expires());
}
if (!parsed_expiry_time.is_null()) {
*seconds_till_expiry =
......
......@@ -157,7 +157,8 @@ Time CanonicalCookie::CanonExpiration(const ParsedCookie& pc,
// Try the Expires attribute.
if (pc.HasExpires() && !pc.Expires().empty()) {
// Adjust for clock skew between server and host.
base::Time parsed_expiry = cookie_util::ParseCookieTime(pc.Expires());
base::Time parsed_expiry =
cookie_util::ParseCookieExpirationTime(pc.Expires());
if (!parsed_expiry.is_null())
return parsed_expiry + (current - server_time);
}
......
......@@ -139,6 +139,8 @@ class NET_EXPORT CanonicalCookie {
std::string DebugString() const;
static std::string CanonPath(const GURL& url, const ParsedCookie& pc);
// Returns a "null" time if expiration was unspecified or invalid.
static base::Time CanonExpiration(const ParsedCookie& pc,
const base::Time& current,
const base::Time& server_time);
......
......@@ -125,9 +125,9 @@ std::unique_ptr<CanonicalCookie> BuildCanonicalCookie(
// functions. Would be nice to export them, and re-use here.
EXPECT_FALSE(pc.HasMaxAge());
EXPECT_TRUE(pc.HasPath());
base::Time cookie_expires = pc.HasExpires()
? cookie_util::ParseCookieTime(pc.Expires())
: base::Time();
base::Time cookie_expires =
pc.HasExpires() ? cookie_util::ParseCookieExpirationTime(pc.Expires())
: base::Time();
std::string cookie_path = pc.Path();
return CanonicalCookie::Create(url, pc.Name(), pc.Value(), url.host(),
......
......@@ -18,6 +18,77 @@
namespace net {
namespace cookie_util {
namespace {
base::Time MinNonNullTime() {
return base::Time::FromInternalValue(1);
}
// Tries to assemble a base::Time given a base::Time::Exploded representing a
// UTC calendar date.
//
// If the date falls outside of the range supported internally by
// FromUTCExploded() on the current platform, then the result is:
//
// * Time(1) if it's below the range FromUTCExploded() supports.
// * Time::Max() if it's above the range FromUTCExploded() supports.
bool SaturatedTimeFromUTCExploded(const base::Time::Exploded& exploded,
base::Time* out) {
// Try to calculate the base::Time in the normal fashion.
if (base::Time::FromUTCExploded(exploded, out)) {
// Don't return Time(0) on success.
if (out->is_null())
*out = MinNonNullTime();
return true;
}
// base::Time::FromUTCExploded() has platform-specific limits:
//
// * Windows: Years 1601 - 30827
// * 32-bit POSIX: Years 1970 - 2038
//
// Work around this by returning min/max valid times for times outside those
// ranges when imploding the time is doomed to fail.
//
// Note that the following implementation is NOT perfect. It will accept
// some invalid calendar dates in the out-of-range case.
if (!exploded.HasValidValues())
return false;
#if defined(OS_POSIX) && !defined(OS_MACOSX)
// Allow dates prior to unix epoch (which fail on non-Mac/iOS POSIX).
if (exploded.year < 1970) {
*out = MinNonNullTime();
return true;
}
// On 32-bit non-Mac/iOS POSIX systems, the time_t value that FromExploded()
// returns overflows in the middle of year 2038. In that case, return
// Time::Max().
if (sizeof(time_t) == 4u && exploded.year >= 2038) {
*out = base::Time::Max();
return true;
}
#endif // defined(OS_POSIX) && !defined(OS_MACOSX)
#if defined(OS_WIN)
// Allow dates prior to Windows epoch.
if (exploded.year < 1601) {
*out = MinNonNullTime();
return true;
}
// Allow dates after the Windows epoch.
if (exploded.year >= 30827) {
*out = base::Time::Max();
return true;
}
#endif // defined(OS_WIN)
return false;
}
} // namespace
bool DomainIsHostOnly(const std::string& domain_string) {
return (domain_string.empty() || domain_string[0] != '.');
}
......@@ -103,7 +174,7 @@ bool GetCookieDomainWithString(const GURL& url,
// - The time must be of the format hh:mm:ss.
// An average cookie expiration will look something like this:
// Sat, 15-Apr-17 21:01:22 GMT
base::Time ParseCookieTime(const std::string& time_string) {
base::Time ParseCookieExpirationTime(const std::string& time_string) {
static const char* const kMonths[] = {
"jan", "feb", "mar", "apr", "may", "jun",
"jul", "aug", "sep", "oct", "nov", "dec" };
......@@ -200,13 +271,11 @@ base::Time ParseCookieTime(const std::string& time_string) {
if (exploded.year >= 0 && exploded.year <= 68)
exploded.year += 2000;
// If our values are within their correct ranges, we got our time.
if (exploded.day_of_month >= 1 && exploded.day_of_month <= 31 &&
exploded.month >= 1 && exploded.month <= 12 &&
exploded.year >= 1601 && exploded.year <= 30827 &&
exploded.hour <= 23 && exploded.minute <= 59 && exploded.second <= 59) {
return base::Time::FromUTCExploded(exploded);
}
// Note that clipping the date if it is outside of a platform-specific range
// is permitted by: https://tools.ietf.org/html/rfc6265#section-5.2.1
base::Time result;
if (SaturatedTimeFromUTCExploded(exploded, &result))
return result;
// One of our values was out of expected range. For well-formed input,
// the following check would be reasonable:
......
......@@ -37,8 +37,13 @@ NET_EXPORT bool GetCookieDomainWithString(const GURL& url,
// i.e. it doesn't begin with a leading '.' character.
NET_EXPORT bool DomainIsHostOnly(const std::string& domain_string);
// Parses the string with the cookie time (very forgivingly).
NET_EXPORT base::Time ParseCookieTime(const std::string& time_string);
// Parses the string with the cookie expiration time (very forgivingly).
// Returns the "null" time on failure.
//
// If the expiration date is below or above the platform-specific range
// supported by Time::FromUTCExplodeded(), then this will return Time(1) or
// Time::Max(), respectively.
NET_EXPORT base::Time ParseCookieExpirationTime(const std::string& time_string);
// Convenience for converting a cookie origin (domain and https pair) to a URL.
NET_EXPORT GURL CookieOriginToURL(const std::string& domain, bool is_https);
......
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment