Index: net/http/transport_security_state.cc |
diff --git a/net/http/transport_security_state.cc b/net/http/transport_security_state.cc |
index 2e829ad4a9e101fbc669934991b8bfe82a194849..c5fb02e4163499cf44e7df2ff24c0ad71b6afffd 100644 |
--- a/net/http/transport_security_state.cc |
+++ b/net/http/transport_security_state.cc |
@@ -56,6 +56,11 @@ const TransportSecurityStateSource* g_hsts_source = &kHSTSSource; |
// 1: Unless a delegate says otherwise, require CT. |
int g_ct_required_for_testing = 0; |
+bool IsDynamicExpectCTEnabled() { |
+ return base::FeatureList::IsEnabled( |
+ TransportSecurityState::kDynamicExpectCTFeature); |
+} |
+ |
// LessThan comparator for use with std::binary_search() in determining |
// whether a SHA-256 HashValue appears within a sorted array of |
// SHA256HashValues. |
@@ -726,6 +731,10 @@ bool SerializeExpectStapleReport(const HostPortPair& host_port_pair, |
} // namespace |
+// static |
+const base::Feature TransportSecurityState::kDynamicExpectCTFeature{ |
+ "DynamicExpectCT", base::FEATURE_DISABLED_BY_DEFAULT}; |
+ |
void SetTransportSecurityStateSourceForTesting( |
const TransportSecurityStateSource* source) { |
g_hsts_source = source ? source : &kHSTSSource; |
@@ -988,6 +997,23 @@ void TransportSecurityState::AddHPKPInternal(const std::string& host, |
EnablePKPHost(host, pkp_state); |
} |
+void TransportSecurityState::AddExpectCTInternal( |
+ const std::string& host, |
+ const base::Time& last_observed, |
+ const base::Time& expiry, |
+ bool enforce, |
+ const GURL& report_uri) { |
+ DCHECK(CalledOnValidThread()); |
+ |
+ ExpectCTState expect_ct_state; |
+ expect_ct_state.last_observed = last_observed; |
+ expect_ct_state.expiry = expiry; |
+ expect_ct_state.enforce = enforce; |
+ expect_ct_state.report_uri = report_uri; |
+ |
+ EnableExpectCTHost(host, expect_ct_state); |
+} |
+ |
void TransportSecurityState:: |
SetEnablePublicKeyPinningBypassForLocalTrustAnchors(bool value) { |
enable_pkp_bypass_for_local_trust_anchors_ = value; |
@@ -1043,6 +1069,33 @@ void TransportSecurityState::EnablePKPHost(const std::string& host, |
DirtyNotify(); |
} |
+void TransportSecurityState::EnableExpectCTHost(const std::string& host, |
+ const ExpectCTState& state) { |
+ DCHECK(CalledOnValidThread()); |
+ if (!IsDynamicExpectCTEnabled()) |
+ return; |
+ |
+ const std::string canonicalized_host = CanonicalizeHost(host); |
+ if (canonicalized_host.empty()) |
+ return; |
+ |
+ // Only store new state when Expect-CT is explicitly enabled. If it is |
+ // disabled, remove the state from the enabled hosts. |
+ if (state.enforce || !state.report_uri.is_empty()) { |
+ ExpectCTState expect_ct_state(state); |
+ // No need to store this value since it is redundant. (|canonicalized_host| |
+ // is the map key.) |
+ expect_ct_state.domain.clear(); |
+ |
+ enabled_expect_ct_hosts_[HashHost(canonicalized_host)] = expect_ct_state; |
+ } else { |
+ const std::string hashed_host = HashHost(canonicalized_host); |
+ enabled_expect_ct_hosts_.erase(hashed_host); |
+ } |
+ |
+ DirtyNotify(); |
+} |
+ |
TransportSecurityState::PKPStatus |
TransportSecurityState::CheckPinsAndMaybeSendReport( |
const HostPortPair& host_port_pair, |
@@ -1164,6 +1217,13 @@ bool TransportSecurityState::DeleteDynamicDataForHost(const std::string& host) { |
deleted = true; |
} |
+ ExpectCTStateMap::iterator expect_ct_iterator = |
+ enabled_expect_ct_hosts_.find(hashed_host); |
+ if (expect_ct_iterator != enabled_expect_ct_hosts_.end()) { |
+ enabled_expect_ct_hosts_.erase(expect_ct_iterator); |
+ deleted = true; |
+ } |
+ |
if (deleted) |
DirtyNotify(); |
return deleted; |
@@ -1173,6 +1233,7 @@ void TransportSecurityState::ClearDynamicData() { |
DCHECK(CalledOnValidThread()); |
enabled_sts_hosts_.clear(); |
enabled_pkp_hosts_.clear(); |
+ enabled_expect_ct_hosts_.clear(); |
} |
void TransportSecurityState::DeleteAllDynamicDataSince(const base::Time& time) { |
@@ -1201,6 +1262,18 @@ void TransportSecurityState::DeleteAllDynamicDataSince(const base::Time& time) { |
++pkp_iterator; |
} |
+ ExpectCTStateMap::iterator expect_ct_iterator = |
+ enabled_expect_ct_hosts_.begin(); |
+ while (expect_ct_iterator != enabled_expect_ct_hosts_.end()) { |
+ if (expect_ct_iterator->second.last_observed >= time) { |
+ dirtied = true; |
+ enabled_expect_ct_hosts_.erase(expect_ct_iterator++); |
+ continue; |
+ } |
+ |
+ ++expect_ct_iterator; |
+ } |
+ |
if (dirtied) |
DirtyNotify(); |
} |
@@ -1279,6 +1352,14 @@ void TransportSecurityState::AddHPKP(const std::string& host, |
report_uri); |
} |
+void TransportSecurityState::AddExpectCT(const std::string& host, |
+ const base::Time& expiry, |
+ bool enforce, |
+ const GURL& report_uri) { |
+ DCHECK(CalledOnValidThread()); |
+ AddExpectCTInternal(host, base::Time::Now(), expiry, enforce, report_uri); |
+} |
+ |
bool TransportSecurityState::ProcessHPKPReportOnlyHeader( |
const std::string& value, |
const HostPortPair& host_port_pair, |
@@ -1571,6 +1652,30 @@ bool TransportSecurityState::GetDynamicPKPState(const std::string& host, |
return false; |
} |
+bool TransportSecurityState::GetDynamicExpectCTState(const std::string& host, |
+ ExpectCTState* result) { |
+ DCHECK(CalledOnValidThread()); |
+ |
+ const std::string canonicalized_host = CanonicalizeHost(host); |
+ if (canonicalized_host.empty()) |
+ return false; |
+ |
+ base::Time current_time(base::Time::Now()); |
+ ExpectCTStateMap::iterator j = |
+ enabled_expect_ct_hosts_.find(HashHost(canonicalized_host)); |
+ if (j == enabled_expect_ct_hosts_.end()) |
+ return false; |
+ // If the entry is invalid, drop it. |
+ if (current_time > j->second.expiry) { |
+ enabled_expect_ct_hosts_.erase(j); |
+ DirtyNotify(); |
+ return false; |
+ } |
+ |
+ *result = j->second; |
+ return true; |
+} |
+ |
void TransportSecurityState::AddOrUpdateEnabledSTSHosts( |
const std::string& hashed_host, |
const STSState& state) { |
@@ -1587,6 +1692,14 @@ void TransportSecurityState::AddOrUpdateEnabledPKPHosts( |
enabled_pkp_hosts_[hashed_host] = state; |
} |
+void TransportSecurityState::AddOrUpdateEnabledExpectCTHosts( |
+ const std::string& hashed_host, |
+ const ExpectCTState& state) { |
+ DCHECK(CalledOnValidThread()); |
+ DCHECK(state.enforce || !state.report_uri.is_empty()); |
+ enabled_expect_ct_hosts_[hashed_host] = state; |
+} |
+ |
TransportSecurityState::STSState::STSState() |
: upgrade_mode(MODE_DEFAULT), include_subdomains(false) { |
} |
@@ -1615,10 +1728,19 @@ TransportSecurityState::PKPState::PKPState(const PKPState& other) = default; |
TransportSecurityState::PKPState::~PKPState() { |
} |
-TransportSecurityState::ExpectCTState::ExpectCTState() {} |
+TransportSecurityState::ExpectCTState::ExpectCTState() : enforce(false) {} |
TransportSecurityState::ExpectCTState::~ExpectCTState() {} |
+TransportSecurityState::ExpectCTStateIterator::ExpectCTStateIterator( |
+ const TransportSecurityState& state) |
+ : iterator_(state.enabled_expect_ct_hosts_.begin()), |
+ end_(state.enabled_expect_ct_hosts_.end()) { |
+ DCHECK(state.CalledOnValidThread()); |
+} |
+ |
+TransportSecurityState::ExpectCTStateIterator::~ExpectCTStateIterator() {} |
+ |
TransportSecurityState::ExpectStapleState::ExpectStapleState() |
: include_subdomains(false) {} |