blob: b8faf552390601da1e31dfe13d229ab60cded96e [file] [log] [blame]
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "net/base/sdch_dictionary.h"
#include "base/time/clock.h"
#include "base/time/default_clock.h"
#include "base/values.h"
#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
namespace {
bool DomainMatch(const GURL& gurl, const std::string& restriction) {
// TODO(jar): This is not precisely a domain match definition.
return gurl.DomainIs(restriction);
}
} // namespace
namespace net {
SdchDictionary::SdchDictionary(const std::string& dictionary_text,
size_t offset,
const std::string& client_hash,
const std::string& server_hash,
const GURL& gurl,
const std::string& domain,
const std::string& path,
const base::Time& expiration,
const std::set<int>& ports)
: text_(dictionary_text, offset),
client_hash_(client_hash),
server_hash_(server_hash),
url_(gurl),
domain_(domain),
path_(path),
expiration_(expiration),
ports_(ports) {
}
SdchDictionary::SdchDictionary(const SdchDictionary& rhs)
: text_(rhs.text_),
client_hash_(rhs.client_hash_),
server_hash_(rhs.server_hash_),
url_(rhs.url_),
domain_(rhs.domain_),
path_(rhs.path_),
expiration_(rhs.expiration_),
ports_(rhs.ports_) {
}
SdchDictionary::~SdchDictionary() {
}
// Security functions restricting loads and use of dictionaries.
// static
SdchProblemCode SdchDictionary::CanSet(const std::string& domain,
const std::string& path,
const std::set<int>& ports,
const GURL& dictionary_url) {
/*
* A dictionary is invalid and must not be stored if any of the following are
* true:
* 1. The dictionary has no Domain attribute.
* 2. The effective host name that derives from the referer URL host name does
* not domain-match the Domain attribute.
* 3. The Domain attribute is a top level domain.
* 4. The referer URL host is a host domain name (not IP address) and has the
* form HD, where D is the value of the Domain attribute, and H is a string
* that contains one or more dots.
* 5. If the dictionary has a Port attribute and the referer URL's port
* was not in the list.
*/
// TODO(jar): Redirects in dictionary fetches might plausibly be problematic,
// and hence the conservative approach is to not allow any redirects (if there
// were any... then don't allow the dictionary to be set).
if (domain.empty())
return SDCH_DICTIONARY_MISSING_DOMAIN_SPECIFIER; // Domain is required.
if (registry_controlled_domains::GetDomainAndRegistry(
domain, registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES)
.empty()) {
return SDCH_DICTIONARY_SPECIFIES_TOP_LEVEL_DOMAIN; // domain was a TLD.
}
if (!DomainMatch(dictionary_url, domain))
return SDCH_DICTIONARY_DOMAIN_NOT_MATCHING_SOURCE_URL;
std::string referrer_url_host = dictionary_url.host();
size_t postfix_domain_index = referrer_url_host.rfind(domain);
// See if it is indeed a postfix, or just an internal string.
if (referrer_url_host.size() == postfix_domain_index + domain.size()) {
// It is a postfix... so check to see if there's a dot in the prefix.
size_t end_of_host_index = referrer_url_host.find_first_of('.');
if (referrer_url_host.npos != end_of_host_index &&
end_of_host_index < postfix_domain_index) {
return SDCH_DICTIONARY_REFERER_URL_HAS_DOT_IN_PREFIX;
}
}
if (!ports.empty() && 0 == ports.count(dictionary_url.EffectiveIntPort()))
return SDCH_DICTIONARY_PORT_NOT_MATCHING_SOURCE_URL;
return SDCH_OK;
}
SdchProblemCode SdchDictionary::CanUse(const GURL& target_url) const {
/*
* 1. The request URL's host name domain-matches the Domain attribute of the
* dictionary.
* 2. If the dictionary has a Port attribute, the request port is one of the
* ports listed in the Port attribute.
* 3. The request URL path-matches the path attribute of the dictionary.
* We can override (ignore) item (4) only when we have explicitly enabled
* HTTPS support AND the dictionary acquisition scheme matches the target
* url scheme.
*/
if (!DomainMatch(target_url, domain_))
return SDCH_DICTIONARY_FOUND_HAS_WRONG_DOMAIN;
if (!ports_.empty() && 0 == ports_.count(target_url.EffectiveIntPort()))
return SDCH_DICTIONARY_FOUND_HAS_WRONG_PORT_LIST;
if (path_.size() && !PathMatch(target_url.path(), path_))
return SDCH_DICTIONARY_FOUND_HAS_WRONG_PATH;
if (target_url.SchemeIsCryptographic() != url_.SchemeIsCryptographic())
return SDCH_DICTIONARY_FOUND_HAS_WRONG_SCHEME;
// TODO(jar): Remove overly restrictive failsafe test (added per security
// review) when we have a need to be more general.
if (!target_url.SchemeIsHTTPOrHTTPS())
return SDCH_ATTEMPT_TO_DECODE_NON_HTTP_DATA;
return SDCH_OK;
}
// static
bool SdchDictionary::PathMatch(const std::string& path,
const std::string& restriction) {
/* Must be either:
* 1. P2 is equal to P1
* 2. P2 is a prefix of P1 and either the final character in P2 is "/"
* or the character following P2 in P1 is "/".
*/
if (path == restriction)
return true;
size_t prefix_length = restriction.size();
if (prefix_length > path.size())
return false; // Can't be a prefix.
if (0 != path.compare(0, prefix_length, restriction))
return false;
return restriction[prefix_length - 1] == '/' || path[prefix_length] == '/';
}
bool SdchDictionary::Expired() const {
return base::Time::Now() > expiration_;
}
} // namespace net