Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(133)

Unified Diff: net/base/strict_transport_security_state.cc

Issue 201033: ForceTLS: hash hostnames, handle subdomains, canonicalise. (Closed)
Patch Set: ... Created 11 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: net/base/strict_transport_security_state.cc
diff --git a/net/base/strict_transport_security_state.cc b/net/base/strict_transport_security_state.cc
index 58f9f25bd4e4286701d5cfeb583fc384fa8f3be2..59b8949463651f660b7bd87c53cdef5f4289142a 100644
--- a/net/base/strict_transport_security_state.cc
+++ b/net/base/strict_transport_security_state.cc
@@ -8,11 +8,13 @@
#include "base/json_writer.h"
#include "base/logging.h"
#include "base/scoped_ptr.h"
+#include "base/sha2.h"
#include "base/string_tokenizer.h"
#include "base/string_util.h"
#include "base/values.h"
#include "googleurl/src/gurl.h"
-#include "net/base/registry_controlled_domain.h"
+#include "net/base/base64.h"
+#include "net/base/dns_util.h"
namespace net {
@@ -36,33 +38,54 @@ void StrictTransportSecurityState::DidReceiveHeader(const GURL& url,
}
void StrictTransportSecurityState::EnableHost(const std::string& host,
- base::Time expiry,
- bool include_subdomains) {
- // TODO(abarth): Canonicalize host.
+ base::Time expiry,
+ bool include_subdomains) {
+ const std::string canonicalised_host = CanonicaliseHost(host);
+ if (canonicalised_host.empty())
+ return;
+ char hashed[base::SHA256_LENGTH];
+ base::SHA256HashString(canonicalised_host, hashed, sizeof(hashed));
+
AutoLock lock(lock_);
State state = {expiry, include_subdomains};
- enabled_hosts_[host] = state;
+ enabled_hosts_[std::string(hashed, sizeof(hashed))] = state;
DirtyNotify();
}
bool StrictTransportSecurityState::IsEnabledForHost(const std::string& host) {
- // TODO(abarth): Canonicalize host.
- // TODO: check for subdomains too.
-
- AutoLock lock(lock_);
- std::map<std::string, State>::iterator i = enabled_hosts_.find(host);
- if (i == enabled_hosts_.end())
+ const std::string canonicalised_host = CanonicaliseHost(host);
+ if (canonicalised_host.empty())
return false;
base::Time current_time(base::Time::Now());
- if (current_time > i->second.expiry) {
- enabled_hosts_.erase(i);
- DirtyNotify();
- return false;
+ AutoLock lock(lock_);
+
+ for (size_t i = 0; canonicalised_host[i]; i += canonicalised_host[i] + 1) {
+ char hashed_domain[base::SHA256_LENGTH];
+
+ base::SHA256HashString(&canonicalised_host[i], &hashed_domain,
+ sizeof(hashed_domain));
+ std::map<std::string, State>::iterator j =
+ enabled_hosts_.find(std::string(hashed_domain, sizeof(hashed_domain)));
+ if (j == enabled_hosts_.end())
+ continue;
+
+ if (current_time > j->second.expiry) {
+ enabled_hosts_.erase(j);
+ DirtyNotify();
+ continue;
+ }
+
+ // If we matched the domain exactly, it doesn't matter what the value of
+ // include_subdomains is.
+ if (i == 0)
+ return true;
+
+ return j->second.include_subdomains;
}
- return true;
+ return false;
}
// "X-Force-TLS" ":" "max-age" "=" delta-seconds *1INCLUDESUBDOMAINS
@@ -169,6 +192,27 @@ void StrictTransportSecurityState::SetDelegate(
delegate_ = delegate;
}
+// This function converts the binary hashes, which we store in
+// |enabled_hosts_|, to a base64 string which we can include in a JSON file.
+static std::wstring HashedDomainToExternalString(const std::string& hashed) {
+ std::string out;
+ CHECK(Base64Encode(hashed, &out));
+ return ASCIIToWide(out);
+}
+
+// This inverts |HashedDomainToExternalString|, above. It turns an external
+// string (from a JSON file) into an internal (binary) string.
+static std::string ExternalStringToHashedDomain(const std::wstring& external) {
+ std::string external_ascii = WideToASCII(external);
+ std::string out;
+ if (!Base64Decode(external_ascii, &out) ||
+ out.size() != base::SHA256_LENGTH) {
+ return std::string();
+ }
+
+ return out;
+}
+
bool StrictTransportSecurityState::Serialise(std::string* output) {
AutoLock lock(lock_);
@@ -179,7 +223,7 @@ bool StrictTransportSecurityState::Serialise(std::string* output) {
state->SetBoolean(L"include_subdomains", i->second.include_subdomains);
state->SetReal(L"expiry", i->second.expiry.ToDoubleT());
- toplevel.Set(ASCIIToWide(i->first), state);
+ toplevel.Set(HashedDomainToExternalString(i->first), state);
}
JSONWriter::Write(&toplevel, true /* pretty print */, output);
@@ -205,7 +249,6 @@ bool StrictTransportSecurityState::Deserialise(const std::string& input) {
if (!dict_value->GetDictionary(*i, &state))
continue;
- const std::string host = WideToASCII(*i);
bool include_subdomains;
double expiry;
@@ -218,11 +261,15 @@ bool StrictTransportSecurityState::Deserialise(const std::string& input) {
if (expiry_time <= current_time)
continue;
+ std::string hashed = ExternalStringToHashedDomain(*i);
+ if (hashed.empty())
+ continue;
+
State new_state = { expiry_time, include_subdomains };
- enabled_hosts_[host] = new_state;
+ enabled_hosts_[hashed] = new_state;
}
- return enabled_hosts_.size() > 0;
+ return true;
}
void StrictTransportSecurityState::DirtyNotify() {
@@ -230,4 +277,40 @@ void StrictTransportSecurityState::DirtyNotify() {
delegate_->StateIsDirty(this);
}
+// static
+std::string StrictTransportSecurityState::CanonicaliseHost(
+ const std::string& host) {
+ // We cannot perform the operations as detailed in the spec here as |host|
+ // has already undergone IDN processing before it reached us. Thus, we check
+ // that there are no invalid characters in the host and lowercase the result.
+
+ std::string new_host;
+ if (!DNSDomainFromDot(host, &new_host)) {
+ NOTREACHED();
+ return std::string();
+ }
+
+ for (size_t i = 0; new_host[i]; i += new_host[i] + 1) {
+ const unsigned label_length = static_cast<unsigned>(new_host[i]);
+ if (!label_length)
+ break;
+
+ for (size_t j = 0; j < label_length; ++j) {
+ // RFC 3490, 4.1, step 3
+ if (!IsSTD3ASCIIValidCharacter(new_host[i + 1 + j]))
+ return std::string();
+
+ new_host[i + 1 + j] = tolower(new_host[i + 1 + j]);
+ }
+
+ // step 3(b)
+ if (new_host[i + 1] == '-' ||
+ new_host[i + label_length] == '-') {
+ return std::string();
+ }
+ }
+
+ return new_host;
+}
+
} // namespace

Powered by Google App Engine
This is Rietveld 408576698