Index: net/base/transport_security_state.cc |
=================================================================== |
--- net/base/transport_security_state.cc (revision 134551) |
+++ net/base/transport_security_state.cc (working copy) |
@@ -19,8 +19,6 @@ |
#include <utility> |
#include "base/base64.h" |
-#include "base/json/json_reader.h" |
-#include "base/json/json_writer.h" |
#include "base/logging.h" |
#include "base/memory/scoped_ptr.h" |
#include "base/metrics/histogram.h" |
@@ -33,7 +31,6 @@ |
#include "base/values.h" |
#include "crypto/sha2.h" |
#include "googleurl/src/gurl.h" |
-#include "net/base/asn1_util.h" |
#include "net/base/dns_util.h" |
#include "net/base/ssl_info.h" |
#include "net/base/x509_certificate.h" |
@@ -47,20 +44,16 @@ |
const long int TransportSecurityState::kMaxHSTSAgeSecs = 86400 * 365; // 1 year |
-TransportSecurityState::TransportSecurityState(const std::string& hsts_hosts) |
- : delegate_(NULL) { |
- if (!hsts_hosts.empty()) { |
- bool dirty; |
- Deserialise(hsts_hosts, &dirty, &forced_hosts_); |
- } |
-} |
- |
static std::string HashHost(const std::string& canonicalized_host) { |
char hashed[crypto::kSHA256Length]; |
crypto::SHA256HashString(canonicalized_host, hashed, sizeof(hashed)); |
return std::string(hashed, sizeof(hashed)); |
} |
+TransportSecurityState::TransportSecurityState() |
+ : delegate_(NULL) { |
+} |
+ |
void TransportSecurityState::SetDelegate( |
TransportSecurityState::Delegate* delegate) { |
delegate_ = delegate; |
@@ -74,24 +67,19 @@ |
if (canonicalized_host.empty()) |
return; |
- // Only override a preloaded state if the new state describes a more strict |
- // policy. TODO(palmer): Reconsider this? |
DomainState existing_state; |
- if (IsPreloadedSTS(canonicalized_host, true, &existing_state) && |
- canonicalized_host == CanonicalizeHost(existing_state.domain) && |
- existing_state.IsMoreStrict(state)) { |
- return; |
- } |
- // Use the original creation date if we already have this host. |
+ // Use the original creation date if we already have this host. (But note |
+ // that statically-defined states have no |created| date. Therefore, we do |
+ // not bother to search the SNI-only static states.) |
DomainState state_copy(state); |
- if (GetDomainState(&existing_state, host, true) && |
+ if (GetDomainState(host, false /* sni_enabled */, &existing_state) && |
!existing_state.created.is_null()) { |
state_copy.created = existing_state.created; |
} |
- // We don't store these values. |
- state_copy.preloaded = false; |
+ // No need to store this value since it is redundant. (|canonicalized_host| |
+ // is the map key.) |
state_copy.domain.clear(); |
enabled_hosts_[HashHost(canonicalized_host)] = state_copy; |
@@ -115,35 +103,18 @@ |
return false; |
} |
-bool TransportSecurityState::HasPinsForHost(DomainState* result, |
- const std::string& host, |
- bool sni_available) { |
+bool TransportSecurityState::GetDomainState(const std::string& host, |
+ bool sni_enabled, |
+ DomainState* result) { |
DCHECK(CalledOnValidThread()); |
- return HasMetadata(result, host, sni_available) && |
- (!result->dynamic_spki_hashes.empty() || |
- !result->preloaded_spki_hashes.empty()); |
-} |
- |
-bool TransportSecurityState::GetDomainState(DomainState* result, |
- const std::string& host, |
- bool sni_available) { |
- DCHECK(CalledOnValidThread()); |
- |
- return HasMetadata(result, host, sni_available); |
-} |
- |
-bool TransportSecurityState::HasMetadata(DomainState* result, |
- const std::string& host, |
- bool sni_available) { |
- DCHECK(CalledOnValidThread()); |
- |
DomainState state; |
const std::string canonicalized_host = CanonicalizeHost(host); |
if (canonicalized_host.empty()) |
return false; |
- bool has_preload = IsPreloadedSTS(canonicalized_host, sni_available, &state); |
+ bool has_preload = GetStaticDomainState(canonicalized_host, sni_enabled, |
+ &state); |
std::string canonicalized_preload = CanonicalizeHost(state.domain); |
base::Time current_time(base::Time::Now()); |
@@ -162,7 +133,7 @@ |
if (j == enabled_hosts_.end()) |
continue; |
- if (current_time > j->second.expiry && |
+ if (current_time > j->second.upgrade_expiry && |
current_time > j->second.dynamic_spki_hashes_expiry) { |
enabled_hosts_.erase(j); |
DirtyNotify(); |
@@ -290,23 +261,6 @@ |
return true; |
} |
-// static |
-bool TransportSecurityState::GetPublicKeyHash( |
- const X509Certificate& cert, SHA1Fingerprint* spki_hash) { |
- std::string der_bytes; |
- if (!X509Certificate::GetDEREncoded(cert.os_cert_handle(), &der_bytes)) |
- return false; |
- |
- base::StringPiece spki; |
- if (!asn1::ExtractSPKIFromDERCert(der_bytes, &spki)) |
- return false; |
- |
- base::SHA1HashBytes(reinterpret_cast<const unsigned char*>(spki.data()), |
- spki.size(), spki_hash->data); |
- |
- return true; |
-} |
- |
struct FingerprintsEqualPredicate { |
explicit FingerprintsEqualPredicate(const SHA1Fingerprint& fingerprint) : |
fingerprint_(fingerprint) {} |
@@ -368,13 +322,12 @@ |
// "Public-Key-Pins" ":" |
// "max-age" "=" delta-seconds ";" |
// "pin-" algo "=" base64 [ ";" ... ] |
-// |
-// static |
-bool TransportSecurityState::ParsePinsHeader(const std::string& value, |
- const SSLInfo& ssl_info, |
- DomainState* state) { |
+bool TransportSecurityState::DomainState::ParsePinsHeader( |
+ const base::Time& now, |
+ const std::string& value, |
+ const SSLInfo& ssl_info) { |
bool parsed_max_age = false; |
- int max_age = 0; |
+ int max_age_candidate = 0; |
FingerprintVector pins; |
std::string source = value; |
@@ -389,11 +342,12 @@ |
if (LowerCaseEqualsASCII(equals.first, "max-age")) { |
if (equals.second.empty() || |
- !MaxAgeToInt(equals.second.begin(), equals.second.end(), &max_age)) { |
+ !MaxAgeToInt(equals.second.begin(), equals.second.end(), |
+ &max_age_candidate)) { |
return false; |
} |
- if (max_age > kMaxHSTSAgeSecs) |
- max_age = kMaxHSTSAgeSecs; |
+ if (max_age_candidate > kMaxHSTSAgeSecs) |
+ max_age_candidate = kMaxHSTSAgeSecs; |
parsed_max_age = true; |
} else if (LowerCaseEqualsASCII(equals.first, "pin-sha1")) { |
if (!ParseAndAppendPin(equals.second, &pins)) |
@@ -410,15 +364,14 @@ |
if (!parsed_max_age || !IsPinListValid(pins, ssl_info)) |
return false; |
- state->max_age = max_age; |
- state->dynamic_spki_hashes_expiry = |
- base::Time::Now() + base::TimeDelta::FromSeconds(max_age); |
+ dynamic_spki_hashes_expiry = |
+ now + base::TimeDelta::FromSeconds(max_age_candidate); |
- state->dynamic_spki_hashes.clear(); |
- if (max_age > 0) { |
+ dynamic_spki_hashes.clear(); |
+ if (max_age_candidate > 0) { |
for (FingerprintVector::const_iterator i = pins.begin(); |
- i != pins.end(); i++) { |
- state->dynamic_spki_hashes.push_back(*i); |
+ i != pins.end(); ++i) { |
+ dynamic_spki_hashes.push_back(*i); |
} |
} |
@@ -427,14 +380,9 @@ |
// "Strict-Transport-Security" ":" |
// "max-age" "=" delta-seconds [ ";" "includeSubDomains" ] |
-// |
-// static |
-bool TransportSecurityState::ParseHeader(const std::string& value, |
- int* max_age, |
- bool* include_subdomains) { |
- DCHECK(max_age); |
- DCHECK(include_subdomains); |
- |
+bool TransportSecurityState::DomainState::ParseSTSHeader( |
+ const base::Time& now, |
+ const std::string& value) { |
int max_age_candidate = 0; |
enum ParserState { |
@@ -511,14 +459,18 @@ |
case AFTER_MAX_AGE_EQUALS: |
return false; |
case AFTER_MAX_AGE: |
- *max_age = max_age_candidate; |
- *include_subdomains = false; |
+ upgrade_expiry = |
+ now + base::TimeDelta::FromSeconds(max_age_candidate); |
+ include_subdomains = false; |
+ upgrade_mode = MODE_FORCE_HTTPS; |
return true; |
case AFTER_MAX_AGE_INCLUDE_SUB_DOMAINS_DELIMITER: |
return false; |
case AFTER_INCLUDE_SUBDOMAINS: |
- *max_age = max_age_candidate; |
- *include_subdomains = true; |
+ upgrade_expiry = |
+ now + base::TimeDelta::FromSeconds(max_age_candidate); |
+ include_subdomains = true; |
+ upgrade_mode = MODE_FORCE_HTTPS; |
return true; |
default: |
NOTREACHED(); |
@@ -526,370 +478,6 @@ |
} |
} |
-// Side pinning and superfluous certificates: |
-// |
-// In SSLClientSocketNSS::DoVerifyCertComplete we look for certificates with a |
-// Subject of CN=meta. When we find one we'll currently try and parse side |
-// pinned key from it. |
-// |
-// A side pin is a key which can be pinned to, but also can be kept offline and |
-// still held by the site owner. The CN=meta certificate is just a backwards |
-// compatiable method of carrying a lump of bytes to the client. (We could use |
-// a TLS extension just as well, but it's a lot easier for admins to add extra |
-// certificates to the chain.) |
- |
-// A TagMap represents the simple key-value structure that we use. Keys are |
-// 32-bit ints. Values are byte strings. |
-typedef std::map<uint32, base::StringPiece> TagMap; |
- |
-// ParseTags parses a list of key-value pairs from |in| to |out| and advances |
-// |in| past the data. The key-value pair data is: |
-// u16le num_tags |
-// u32le tag[num_tags] |
-// u16le lengths[num_tags] |
-// ...data... |
-static bool ParseTags(base::StringPiece* in, TagMap *out) { |
- // Many part of Chrome already assume little-endian. This is just to help |
- // anyone who should try to port it in the future. |
-#if defined(__BYTE_ORDER) |
- // Linux check |
- COMPILE_ASSERT(__BYTE_ORDER == __LITTLE_ENDIAN, assumes_little_endian); |
-#elif defined(__BIG_ENDIAN__) |
- // Mac check |
- #error assumes little endian |
-#endif |
- |
- uint16 num_tags_16; |
- if (in->size() < sizeof(num_tags_16)) |
- return false; |
- |
- memcpy(&num_tags_16, in->data(), sizeof(num_tags_16)); |
- in->remove_prefix(sizeof(num_tags_16)); |
- unsigned num_tags = num_tags_16; |
- |
- if (in->size() < 6 * num_tags) |
- return false; |
- |
- const uint32* tags = reinterpret_cast<const uint32*>(in->data()); |
- const uint16* lens = reinterpret_cast<const uint16*>( |
- in->data() + 4*num_tags); |
- in->remove_prefix(6*num_tags); |
- |
- uint32 prev_tag = 0; |
- for (unsigned i = 0; i < num_tags; i++) { |
- size_t len = lens[i]; |
- uint32 tag = tags[i]; |
- |
- if (in->size() < len) |
- return false; |
- // tags must be in ascending order. |
- if (i > 0 && prev_tag >= tag) |
- return false; |
- (*out)[tag] = base::StringPiece(in->data(), len); |
- in->remove_prefix(len); |
- prev_tag = tag; |
- } |
- |
- return true; |
-} |
- |
-// GetTag extracts the data associated with |tag| in |tags|. |
-static bool GetTag(uint32 tag, const TagMap& tags, base::StringPiece* out) { |
- TagMap::const_iterator i = tags.find(tag); |
- if (i == tags.end()) |
- return false; |
- |
- *out = i->second; |
- return true; |
-} |
- |
-// kP256SubjectPublicKeyInfoPrefix can be prepended onto a P256 elliptic curve |
-// point in X9.62 format in order to make a valid SubjectPublicKeyInfo. The |
-// ASN.1 interpretation of these bytes is: |
-// |
-// 0:d=0 hl=2 l= 89 cons: SEQUENCE |
-// 2:d=1 hl=2 l= 19 cons: SEQUENCE |
-// 4:d=2 hl=2 l= 7 prim: OBJECT :id-ecPublicKey |
-// 13:d=2 hl=2 l= 8 prim: OBJECT :prime256v1 |
-// 23:d=1 hl=2 l= 66 prim: BIT STRING |
-static const uint8 kP256SubjectPublicKeyInfoPrefix[] = { |
- 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, |
- 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a, |
- 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03, |
- 0x42, 0x00, |
-}; |
- |
-// VerifySignature returns true iff |sig| is a valid signature of |
-// |hash| by |pubkey|. The actual implementation is crypto library |
-// specific. |
-static bool VerifySignature(const base::StringPiece& pubkey, |
- const base::StringPiece& sig, |
- const base::StringPiece& hash); |
- |
-#if defined(USE_OPENSSL) |
- |
-static EVP_PKEY* DecodeX962P256PublicKey( |
- const base::StringPiece& pubkey_bytes) { |
- // The public key is an X9.62 encoded P256 point. |
- if (pubkey_bytes.size() != 1 + 2*32) |
- return NULL; |
- |
- std::string pubkey_spki( |
- reinterpret_cast<const char*>(kP256SubjectPublicKeyInfoPrefix), |
- sizeof(kP256SubjectPublicKeyInfoPrefix)); |
- pubkey_spki += pubkey_bytes.as_string(); |
- |
- EVP_PKEY* ret = NULL; |
- const unsigned char* der_pubkey = |
- reinterpret_cast<const unsigned char*>(pubkey_spki.data()); |
- d2i_PUBKEY(&ret, &der_pubkey, pubkey_spki.size()); |
- return ret; |
-} |
- |
-static bool VerifySignature(const base::StringPiece& pubkey, |
- const base::StringPiece& sig, |
- const base::StringPiece& hash) { |
- crypto::ScopedOpenSSL<EVP_PKEY, EVP_PKEY_free> secpubkey( |
- DecodeX962P256PublicKey(pubkey)); |
- if (!secpubkey.get()) |
- return false; |
- |
- |
- crypto::ScopedOpenSSL<EC_KEY, EC_KEY_free> ec_key( |
- EVP_PKEY_get1_EC_KEY(secpubkey.get())); |
- if (!ec_key.get()) |
- return false; |
- |
- return ECDSA_verify(0, reinterpret_cast<const unsigned char*>(hash.data()), |
- hash.size(), |
- reinterpret_cast<const unsigned char*>(sig.data()), |
- sig.size(), ec_key.get()) == 1; |
-} |
- |
-#else |
- |
-// DecodeX962P256PublicKey parses an uncompressed, X9.62 format, P256 elliptic |
-// curve point from |pubkey_bytes| and returns it as a SECKEYPublicKey. |
-static SECKEYPublicKey* DecodeX962P256PublicKey( |
- const base::StringPiece& pubkey_bytes) { |
- // The public key is an X9.62 encoded P256 point. |
- if (pubkey_bytes.size() != 1 + 2*32) |
- return NULL; |
- |
- std::string pubkey_spki( |
- reinterpret_cast<const char*>(kP256SubjectPublicKeyInfoPrefix), |
- sizeof(kP256SubjectPublicKeyInfoPrefix)); |
- pubkey_spki += pubkey_bytes.as_string(); |
- |
- SECItem der; |
- memset(&der, 0, sizeof(der)); |
- der.data = reinterpret_cast<uint8*>(const_cast<char*>(pubkey_spki.data())); |
- der.len = pubkey_spki.size(); |
- |
- CERTSubjectPublicKeyInfo* spki = SECKEY_DecodeDERSubjectPublicKeyInfo(&der); |
- if (!spki) |
- return NULL; |
- SECKEYPublicKey* public_key = SECKEY_ExtractPublicKey(spki); |
- SECKEY_DestroySubjectPublicKeyInfo(spki); |
- |
- return public_key; |
-} |
- |
-static bool VerifySignature(const base::StringPiece& pubkey, |
- const base::StringPiece& sig, |
- const base::StringPiece& hash) { |
- SECKEYPublicKey* secpubkey = DecodeX962P256PublicKey(pubkey); |
- if (!secpubkey) |
- return false; |
- |
- SECItem sigitem; |
- memset(&sigitem, 0, sizeof(sigitem)); |
- sigitem.data = reinterpret_cast<uint8*>(const_cast<char*>(sig.data())); |
- sigitem.len = sig.size(); |
- |
- // |decoded_sigitem| is newly allocated, as is the data that it points to. |
- SECItem* decoded_sigitem = DSAU_DecodeDerSigToLen( |
- &sigitem, SECKEY_SignatureLen(secpubkey)); |
- |
- if (!decoded_sigitem) { |
- SECKEY_DestroyPublicKey(secpubkey); |
- return false; |
- } |
- |
- SECItem hashitem; |
- memset(&hashitem, 0, sizeof(hashitem)); |
- hashitem.data = reinterpret_cast<unsigned char*>( |
- const_cast<char*>(hash.data())); |
- hashitem.len = hash.size(); |
- |
- SECStatus rv = PK11_Verify(secpubkey, decoded_sigitem, &hashitem, NULL); |
- SECKEY_DestroyPublicKey(secpubkey); |
- SECITEM_FreeItem(decoded_sigitem, PR_TRUE); |
- return rv == SECSuccess; |
-} |
- |
-#endif // !defined(USE_OPENSSL) |
- |
-// These are the tag values that we use. Tags are little-endian on the wire and |
-// these values correspond to the ASCII of the name. |
-static const uint32 kTagALGO = 0x4f474c41; |
-static const uint32 kTagP256 = 0x36353250; |
-static const uint32 kTagPUBK = 0x4b425550; |
-static const uint32 kTagSIG = 0x474953; |
-static const uint32 kTagSPIN = 0x4e495053; |
- |
-// static |
-bool TransportSecurityState::ParseSidePin( |
- const base::StringPiece& leaf_spki, |
- const base::StringPiece& in_side_info, |
- FingerprintVector* out_pub_key_hash) { |
- base::StringPiece side_info(in_side_info); |
- |
- TagMap outer; |
- if (!ParseTags(&side_info, &outer)) |
- return false; |
- // trailing data is not allowed |
- if (side_info.size()) |
- return false; |
- |
- base::StringPiece side_pin_bytes; |
- if (!GetTag(kTagSPIN, outer, &side_pin_bytes)) |
- return false; |
- |
- bool have_parsed_a_key = false; |
- uint8 leaf_spki_hash[crypto::kSHA256Length]; |
- bool have_leaf_spki_hash = false; |
- |
- while (side_pin_bytes.size() > 0) { |
- TagMap side_pin; |
- if (!ParseTags(&side_pin_bytes, &side_pin)) |
- return false; |
- |
- base::StringPiece algo, pubkey, sig; |
- if (!GetTag(kTagALGO, side_pin, &algo) || |
- !GetTag(kTagPUBK, side_pin, &pubkey) || |
- !GetTag(kTagSIG, side_pin, &sig)) { |
- return false; |
- } |
- |
- if (algo.size() != sizeof(kTagP256) || |
- 0 != memcmp(algo.data(), &kTagP256, sizeof(kTagP256))) { |
- // We don't support anything but P256 at the moment. |
- continue; |
- } |
- |
- if (!have_leaf_spki_hash) { |
- crypto::SHA256HashString( |
- leaf_spki.as_string(), leaf_spki_hash, sizeof(leaf_spki_hash)); |
- have_leaf_spki_hash = true; |
- } |
- |
- if (VerifySignature(pubkey, sig, base::StringPiece( |
- reinterpret_cast<const char*>(leaf_spki_hash), |
- sizeof(leaf_spki_hash)))) { |
- SHA1Fingerprint fpr; |
- base::SHA1HashBytes( |
- reinterpret_cast<const uint8*>(pubkey.data()), |
- pubkey.size(), |
- fpr.data); |
- out_pub_key_hash->push_back(fpr); |
- have_parsed_a_key = true; |
- } |
- } |
- |
- return have_parsed_a_key; |
-} |
- |
-// 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::string HashedDomainToExternalString(const std::string& hashed) { |
- std::string out; |
- CHECK(base::Base64Encode(hashed, &out)); |
- return 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::string& external) { |
- std::string out; |
- if (!base::Base64Decode(external, &out) || |
- out.size() != crypto::kSHA256Length) { |
- return std::string(); |
- } |
- |
- return out; |
-} |
- |
-static ListValue* SPKIHashesToListValue(const FingerprintVector& hashes) { |
- ListValue* pins = new ListValue; |
- |
- for (FingerprintVector::const_iterator i = hashes.begin(); |
- i != hashes.end(); ++i) { |
- std::string hash_str(reinterpret_cast<const char*>(i->data), |
- sizeof(i->data)); |
- std::string b64; |
- base::Base64Encode(hash_str, &b64); |
- pins->Append(new StringValue("sha1/" + b64)); |
- } |
- |
- return pins; |
-} |
- |
-bool TransportSecurityState::Serialise(std::string* output) { |
- DCHECK(CalledOnValidThread()); |
- |
- DictionaryValue toplevel; |
- base::Time now = base::Time::Now(); |
- for (std::map<std::string, DomainState>::const_iterator |
- i = enabled_hosts_.begin(); i != enabled_hosts_.end(); ++i) { |
- DictionaryValue* state = new DictionaryValue; |
- state->SetBoolean("include_subdomains", i->second.include_subdomains); |
- state->SetDouble("created", i->second.created.ToDoubleT()); |
- state->SetDouble("expiry", i->second.expiry.ToDoubleT()); |
- state->SetDouble("dynamic_spki_hashes_expiry", |
- i->second.dynamic_spki_hashes_expiry.ToDoubleT()); |
- |
- switch (i->second.mode) { |
- case DomainState::MODE_STRICT: |
- state->SetString("mode", "strict"); |
- break; |
- case DomainState::MODE_SPDY_ONLY: |
- state->SetString("mode", "spdy-only"); |
- break; |
- case DomainState::MODE_PINNING_ONLY: |
- state->SetString("mode", "pinning-only"); |
- break; |
- default: |
- NOTREACHED() << "DomainState with unknown mode"; |
- delete state; |
- continue; |
- } |
- |
- state->Set("preloaded_spki_hashes", |
- SPKIHashesToListValue(i->second.preloaded_spki_hashes)); |
- |
- if (now < i->second.dynamic_spki_hashes_expiry) { |
- state->Set("dynamic_spki_hashes", |
- SPKIHashesToListValue(i->second.dynamic_spki_hashes)); |
- } |
- |
- toplevel.Set(HashedDomainToExternalString(i->first), state); |
- } |
- |
- base::JSONWriter::WriteWithOptions(&toplevel, |
- base::JSONWriter::OPTIONS_PRETTY_PRINT, |
- output); |
- return true; |
-} |
- |
-bool TransportSecurityState::LoadEntries(const std::string& input, |
- bool* dirty) { |
- DCHECK(CalledOnValidThread()); |
- |
- enabled_hosts_.clear(); |
- return Deserialise(input, dirty, &enabled_hosts_); |
-} |
- |
static bool AddHash(const std::string& type_and_base64, |
FingerprintVector* out) { |
SHA1Fingerprint hash; |
@@ -901,117 +489,8 @@ |
return true; |
} |
-static void SPKIHashesFromListValue(FingerprintVector* hashes, |
- const ListValue& pins) { |
- size_t num_pins = pins.GetSize(); |
- for (size_t i = 0; i < num_pins; ++i) { |
- std::string type_and_base64; |
- if (pins.GetString(i, &type_and_base64)) |
- AddHash(type_and_base64, hashes); |
- } |
-} |
+TransportSecurityState::~TransportSecurityState() {} |
-// static |
-bool TransportSecurityState::Deserialise( |
- const std::string& input, |
- bool* dirty, |
- std::map<std::string, DomainState>* out) { |
- scoped_ptr<Value> value(base::JSONReader::Read(input)); |
- if (!value.get() || !value->IsType(Value::TYPE_DICTIONARY)) |
- return false; |
- |
- DictionaryValue* dict_value = reinterpret_cast<DictionaryValue*>(value.get()); |
- const base::Time current_time(base::Time::Now()); |
- bool dirtied = false; |
- |
- for (DictionaryValue::key_iterator i = dict_value->begin_keys(); |
- i != dict_value->end_keys(); ++i) { |
- DictionaryValue* state; |
- if (!dict_value->GetDictionaryWithoutPathExpansion(*i, &state)) |
- continue; |
- |
- bool include_subdomains; |
- std::string mode_string; |
- double created; |
- double expiry; |
- double dynamic_spki_hashes_expiry = 0.0; |
- |
- if (!state->GetBoolean("include_subdomains", &include_subdomains) || |
- !state->GetString("mode", &mode_string) || |
- !state->GetDouble("expiry", &expiry)) { |
- continue; |
- } |
- |
- // Don't fail if this key is not present. |
- (void) state->GetDouble("dynamic_spki_hashes_expiry", |
- &dynamic_spki_hashes_expiry); |
- |
- ListValue* pins_list = NULL; |
- FingerprintVector preloaded_spki_hashes; |
- if (state->GetList("preloaded_spki_hashes", &pins_list)) |
- SPKIHashesFromListValue(&preloaded_spki_hashes, *pins_list); |
- |
- FingerprintVector dynamic_spki_hashes; |
- if (state->GetList("dynamic_spki_hashes", &pins_list)) |
- SPKIHashesFromListValue(&dynamic_spki_hashes, *pins_list); |
- |
- DomainState::Mode mode; |
- if (mode_string == "strict") { |
- mode = DomainState::MODE_STRICT; |
- } else if (mode_string == "spdy-only") { |
- mode = DomainState::MODE_SPDY_ONLY; |
- } else if (mode_string == "pinning-only") { |
- mode = DomainState::MODE_PINNING_ONLY; |
- } else { |
- LOG(WARNING) << "Unknown TransportSecurityState mode string found: " |
- << mode_string; |
- continue; |
- } |
- |
- base::Time expiry_time = base::Time::FromDoubleT(expiry); |
- base::Time dynamic_spki_hashes_expiry_time = |
- base::Time::FromDoubleT(dynamic_spki_hashes_expiry); |
- base::Time created_time; |
- if (state->GetDouble("created", &created)) { |
- created_time = base::Time::FromDoubleT(created); |
- } else { |
- // We're migrating an old entry with no creation date. Make sure we |
- // write the new date back in a reasonable time frame. |
- dirtied = true; |
- created_time = base::Time::Now(); |
- } |
- |
- if (expiry_time <= current_time && |
- dynamic_spki_hashes_expiry_time <= current_time) { |
- // Make sure we dirty the state if we drop an entry. |
- dirtied = true; |
- continue; |
- } |
- |
- std::string hashed = ExternalStringToHashedDomain(*i); |
- if (hashed.empty()) { |
- dirtied = true; |
- continue; |
- } |
- |
- DomainState new_state; |
- new_state.mode = mode; |
- new_state.created = created_time; |
- new_state.expiry = expiry_time; |
- new_state.include_subdomains = include_subdomains; |
- new_state.preloaded_spki_hashes = preloaded_spki_hashes; |
- new_state.dynamic_spki_hashes = dynamic_spki_hashes; |
- new_state.dynamic_spki_hashes_expiry = dynamic_spki_hashes_expiry_time; |
- (*out)[hashed] = new_state; |
- } |
- |
- *dirty = dirtied; |
- return true; |
-} |
- |
-TransportSecurityState::~TransportSecurityState() { |
-} |
- |
void TransportSecurityState::DirtyNotify() { |
DCHECK(CalledOnValidThread()); |
@@ -1122,11 +601,11 @@ |
out->include_subdomains = entries[j].include_subdomains; |
*ret = true; |
if (!entries[j].https_required) |
- out->mode = TransportSecurityState::DomainState::MODE_PINNING_ONLY; |
+ out->upgrade_mode = TransportSecurityState::DomainState::MODE_DEFAULT; |
if (entries[j].pins.required_hashes) { |
const char* const* hash = entries[j].pins.required_hashes; |
while (*hash) { |
- bool ok = AddHash(*hash, &out->preloaded_spki_hashes); |
+ bool ok = AddHash(*hash, &out->static_spki_hashes); |
DCHECK(ok) << " failed to parse " << *hash; |
hash++; |
} |
@@ -1134,7 +613,7 @@ |
if (entries[j].pins.excluded_hashes) { |
const char* const* hash = entries[j].pins.excluded_hashes; |
while (*hash) { |
- bool ok = AddHash(*hash, &out->bad_preloaded_spki_hashes); |
+ bool ok = AddHash(*hash, &out->bad_static_spki_hashes); |
DCHECK(ok) << " failed to parse " << *hash; |
hash++; |
} |
@@ -1177,7 +656,7 @@ |
// static |
bool TransportSecurityState::IsGooglePinnedProperty(const std::string& host, |
- bool sni_available) { |
+ bool sni_enabled) { |
std::string canonicalized_host = CanonicalizeHost(host); |
const struct HSTSPreload* entry = |
GetHSTSPreload(canonicalized_host, kPreloadedSTS, kNumPreloadedSTS); |
@@ -1185,7 +664,7 @@ |
if (entry && entry->pins.required_hashes == kGoogleAcceptableCerts) |
return true; |
- if (sni_available) { |
+ if (sni_enabled) { |
entry = GetHSTSPreload(canonicalized_host, kPreloadedSNISTS, |
kNumPreloadedSNISTS); |
if (entry && entry->pins.required_hashes == kGoogleAcceptableCerts) |
@@ -1215,16 +694,13 @@ |
entry->second_level_domain_name, DOMAIN_NUM_EVENTS); |
} |
-// IsPreloadedSTS returns true if the canonicalized hostname should always be |
-// considered to have STS enabled. |
-bool TransportSecurityState::IsPreloadedSTS( |
+bool TransportSecurityState::GetStaticDomainState( |
const std::string& canonicalized_host, |
- bool sni_available, |
+ bool sni_enabled, |
DomainState* out) { |
DCHECK(CalledOnValidThread()); |
- out->preloaded = true; |
- out->mode = DomainState::MODE_STRICT; |
+ out->upgrade_mode = DomainState::MODE_FORCE_HTTPS; |
out->include_subdomains = false; |
for (size_t i = 0; canonicalized_host[i]; i += canonicalized_host[i] + 1) { |
@@ -1235,7 +711,6 @@ |
if (forced_hosts_.find(hashed_host) != forced_hosts_.end()) { |
*out = forced_hosts_[hashed_host]; |
out->domain = DNSDomainToString(host_sub_chunk); |
- out->preloaded = true; |
return true; |
} |
bool ret; |
@@ -1243,7 +718,7 @@ |
&ret)) { |
return ret; |
} |
- if (sni_available && |
+ if (sni_enabled && |
HasPreload(kPreloadedSNISTS, kNumPreloadedSNISTS, canonicalized_host, i, |
out, &ret)) { |
return ret; |
@@ -1253,6 +728,16 @@ |
return false; |
} |
+void TransportSecurityState::AddOrUpdateEnabledHosts(std::string hashed_host, |
+ const DomainState& state) { |
+ enabled_hosts_[hashed_host] = state; |
+} |
+ |
+void TransportSecurityState::AddOrUpdateForcedHosts(std::string hashed_host, |
+ const DomainState& state) { |
+ forced_hosts_[hashed_host] = state; |
+} |
+ |
static std::string HashesToBase64String( |
const FingerprintVector& hashes) { |
std::vector<std::string> hashes_strs; |
@@ -1269,33 +754,31 @@ |
} |
TransportSecurityState::DomainState::DomainState() |
- : mode(MODE_STRICT), |
+ : upgrade_mode(MODE_FORCE_HTTPS), |
created(base::Time::Now()), |
- include_subdomains(false), |
- preloaded(false) { |
+ include_subdomains(false) { |
} |
TransportSecurityState::DomainState::~DomainState() { |
} |
bool TransportSecurityState::DomainState::IsChainOfPublicKeysPermitted( |
- const FingerprintVector& hashes) { |
- |
- if (HashesIntersect(bad_preloaded_spki_hashes, hashes)) { |
+ const FingerprintVector& hashes) const { |
+ if (HashesIntersect(bad_static_spki_hashes, hashes)) { |
LOG(ERROR) << "Rejecting public key chain for domain " << domain |
<< ". Validated chain: " << HashesToBase64String(hashes) |
<< ", matches one or more bad hashes: " |
- << HashesToBase64String(bad_preloaded_spki_hashes); |
+ << HashesToBase64String(bad_static_spki_hashes); |
return false; |
} |
- if (!(dynamic_spki_hashes.empty() && preloaded_spki_hashes.empty()) && |
+ if (!(dynamic_spki_hashes.empty() && static_spki_hashes.empty()) && |
!HashesIntersect(dynamic_spki_hashes, hashes) && |
- !HashesIntersect(preloaded_spki_hashes, hashes)) { |
+ !HashesIntersect(static_spki_hashes, hashes)) { |
LOG(ERROR) << "Rejecting public key chain for domain " << domain |
<< ". Validated chain: " << HashesToBase64String(hashes) |
<< ", expected: " << HashesToBase64String(dynamic_spki_hashes) |
- << " or: " << HashesToBase64String(preloaded_spki_hashes); |
+ << " or: " << HashesToBase64String(static_spki_hashes); |
return false; |
} |
@@ -1303,20 +786,21 @@ |
return true; |
} |
-bool TransportSecurityState::DomainState::IsMoreStrict( |
- const TransportSecurityState::DomainState& other) { |
- if (this->dynamic_spki_hashes.empty() && !other.dynamic_spki_hashes.empty()) |
- return false; |
+bool TransportSecurityState::DomainState::ShouldRedirectHTTPToHTTPS() const { |
+ return upgrade_mode == MODE_FORCE_HTTPS; |
+} |
- if (!this->include_subdomains && other.include_subdomains) |
- return false; |
- |
+bool TransportSecurityState::DomainState::Equals( |
+ const DomainState& other) const { |
+ // TODO(palmer): Implement this |
+ (void) other; |
return true; |
} |
-bool TransportSecurityState::DomainState::ShouldRedirectHTTPToHTTPS() |
- const { |
- return mode == MODE_STRICT; |
+bool TransportSecurityState::DomainState::HasPins() const { |
+ return static_spki_hashes.size() > 0 || |
+ bad_static_spki_hashes.size() > 0 || |
+ dynamic_spki_hashes.size() > 0; |
} |
} // namespace |