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, ...@@ -115,8 +115,10 @@ bool ParseCookieLifetime(net::ParsedCookie* cookie,
} }
Time parsed_expiry_time; Time parsed_expiry_time;
if (cookie->HasExpires()) if (cookie->HasExpires()) {
parsed_expiry_time = net::cookie_util::ParseCookieTime(cookie->Expires()); parsed_expiry_time =
net::cookie_util::ParseCookieExpirationTime(cookie->Expires());
}
if (!parsed_expiry_time.is_null()) { if (!parsed_expiry_time.is_null()) {
*seconds_till_expiry = *seconds_till_expiry =
......
...@@ -157,7 +157,8 @@ Time CanonicalCookie::CanonExpiration(const ParsedCookie& pc, ...@@ -157,7 +157,8 @@ Time CanonicalCookie::CanonExpiration(const ParsedCookie& pc,
// Try the Expires attribute. // Try the Expires attribute.
if (pc.HasExpires() && !pc.Expires().empty()) { if (pc.HasExpires() && !pc.Expires().empty()) {
// Adjust for clock skew between server and host. // 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()) if (!parsed_expiry.is_null())
return parsed_expiry + (current - server_time); return parsed_expiry + (current - server_time);
} }
......
...@@ -139,6 +139,8 @@ class NET_EXPORT CanonicalCookie { ...@@ -139,6 +139,8 @@ class NET_EXPORT CanonicalCookie {
std::string DebugString() const; std::string DebugString() const;
static std::string CanonPath(const GURL& url, const ParsedCookie& pc); 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, static base::Time CanonExpiration(const ParsedCookie& pc,
const base::Time& current, const base::Time& current,
const base::Time& server_time); const base::Time& server_time);
......
...@@ -125,8 +125,8 @@ std::unique_ptr<CanonicalCookie> BuildCanonicalCookie( ...@@ -125,8 +125,8 @@ std::unique_ptr<CanonicalCookie> BuildCanonicalCookie(
// functions. Would be nice to export them, and re-use here. // functions. Would be nice to export them, and re-use here.
EXPECT_FALSE(pc.HasMaxAge()); EXPECT_FALSE(pc.HasMaxAge());
EXPECT_TRUE(pc.HasPath()); EXPECT_TRUE(pc.HasPath());
base::Time cookie_expires = pc.HasExpires() base::Time cookie_expires =
? cookie_util::ParseCookieTime(pc.Expires()) pc.HasExpires() ? cookie_util::ParseCookieExpirationTime(pc.Expires())
: base::Time(); : base::Time();
std::string cookie_path = pc.Path(); std::string cookie_path = pc.Path();
......
...@@ -18,6 +18,77 @@ ...@@ -18,6 +18,77 @@
namespace net { namespace net {
namespace cookie_util { 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) { bool DomainIsHostOnly(const std::string& domain_string) {
return (domain_string.empty() || domain_string[0] != '.'); return (domain_string.empty() || domain_string[0] != '.');
} }
...@@ -103,7 +174,7 @@ bool GetCookieDomainWithString(const GURL& url, ...@@ -103,7 +174,7 @@ bool GetCookieDomainWithString(const GURL& url,
// - The time must be of the format hh:mm:ss. // - The time must be of the format hh:mm:ss.
// An average cookie expiration will look something like this: // An average cookie expiration will look something like this:
// Sat, 15-Apr-17 21:01:22 GMT // 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[] = { static const char* const kMonths[] = {
"jan", "feb", "mar", "apr", "may", "jun", "jan", "feb", "mar", "apr", "may", "jun",
"jul", "aug", "sep", "oct", "nov", "dec" }; "jul", "aug", "sep", "oct", "nov", "dec" };
...@@ -200,13 +271,11 @@ base::Time ParseCookieTime(const std::string& time_string) { ...@@ -200,13 +271,11 @@ base::Time ParseCookieTime(const std::string& time_string) {
if (exploded.year >= 0 && exploded.year <= 68) if (exploded.year >= 0 && exploded.year <= 68)
exploded.year += 2000; exploded.year += 2000;
// If our values are within their correct ranges, we got our time. // Note that clipping the date if it is outside of a platform-specific range
if (exploded.day_of_month >= 1 && exploded.day_of_month <= 31 && // is permitted by: https://tools.ietf.org/html/rfc6265#section-5.2.1
exploded.month >= 1 && exploded.month <= 12 && base::Time result;
exploded.year >= 1601 && exploded.year <= 30827 && if (SaturatedTimeFromUTCExploded(exploded, &result))
exploded.hour <= 23 && exploded.minute <= 59 && exploded.second <= 59) { return result;
return base::Time::FromUTCExploded(exploded);
}
// One of our values was out of expected range. For well-formed input, // One of our values was out of expected range. For well-formed input,
// the following check would be reasonable: // the following check would be reasonable:
......
...@@ -37,8 +37,13 @@ NET_EXPORT bool GetCookieDomainWithString(const GURL& url, ...@@ -37,8 +37,13 @@ NET_EXPORT bool GetCookieDomainWithString(const GURL& url,
// i.e. it doesn't begin with a leading '.' character. // i.e. it doesn't begin with a leading '.' character.
NET_EXPORT bool DomainIsHostOnly(const std::string& domain_string); NET_EXPORT bool DomainIsHostOnly(const std::string& domain_string);
// Parses the string with the cookie time (very forgivingly). // Parses the string with the cookie expiration time (very forgivingly).
NET_EXPORT base::Time ParseCookieTime(const std::string& time_string); // 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. // Convenience for converting a cookie origin (domain and https pair) to a URL.
NET_EXPORT GURL CookieOriginToURL(const std::string& domain, bool is_https); 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