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

Side by Side Diff: components/certificate_transparency/ct_policy_manager.cc

Issue 2102783003: Add enterprise policy to exempt hosts from Certificate Transparency (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@enterprise_ct
Patch Set: Combine with https://codereview.chromium.org/2087743002 Created 4 years, 5 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 unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "components/certificate_transparency/ct_policy_manager.h"
6
7 #include <map>
8 #include <set>
9 #include <string>
10
11 #include "base/bind.h"
12 #include "base/callback.h"
13 #include "base/location.h"
14 #include "base/sequenced_task_runner.h"
15 #include "base/strings/string_util.h"
16 #include "base/threading/sequenced_task_runner_handle.h"
17 #include "base/values.h"
18 #include "components/certificate_transparency/pref_names.h"
19 #include "components/prefs/pref_registry_simple.h"
20 #include "components/prefs/pref_service.h"
21 #include "components/url_formatter/url_fixer.h"
22 #include "components/url_matcher/url_matcher.h"
23
24 namespace certificate_transparency {
25
26 class CTPolicyManager::CTDelegate
27 : public net::TransportSecurityState::RequireCTDelegate {
28 public:
29 explicit CTDelegate(
30 scoped_refptr<base::SequencedTaskRunner> network_task_runner);
31 ~CTDelegate() override = default;
32
33 // Called on the prefs task runner. Updates the CTDelegate to require CT
34 // for |required_hosts|, and exclude |excluded_hosts| from CT policies.
35 void UpdateFromPrefs(const base::ListValue* required_hosts,
36 const base::ListValue* excluded_hosts);
37
38 // RequireCTDelegate implementation
battre 2016/06/28 08:33:13 add: to be called on the |network_task_runner_|?
39 CTRequirementLevel IsCTRequiredForHost(const std::string& hostname) override;
40
41 private:
42 struct Filter {
43 bool ct_required = false;
44 bool match_subdomains = false;
45 size_t host_length = 0;
46 };
47
48 // Called on the |network_task_runner_|, updates the |url_matcher_| to
49 // require CT for |required_hosts| and exclude |excluded_hosts|, both
50 // of which are Lists of Strings which are URLBlacklist filters.
51 void Update(base::ListValue* required_hosts, base::ListValue* excluded_hosts);
52
53 // Parses the filters from |host_patterns|, adding them as filters to
54 // |filters_| (with |ct_required| indicating whether or not CT is required
55 // for that host), and updating |*conditions| with the corresponding
56 // URLMatcher::Conditions to match the host.
57 void AddFilters(bool ct_required,
58 base::ListValue* host_patterns,
59 url_matcher::URLMatcherConditionSet::Vector* conditions);
60
61 // Returns true if |lhs| has greater precedence than |rhs|.
62 bool FilterTakesPrecedence(const Filter& lhs, const Filter& rhs) const;
63
64 scoped_refptr<base::SequencedTaskRunner> network_task_runner_;
65 std::unique_ptr<url_matcher::URLMatcher> url_matcher_;
66 url_matcher::URLMatcherConditionSet::ID next_id_;
67 std::map<url_matcher::URLMatcherConditionSet::ID, Filter> filters_;
68
69 DISALLOW_COPY_AND_ASSIGN(CTDelegate);
70 };
71
72 CTPolicyManager::CTDelegate::CTDelegate(
73 scoped_refptr<base::SequencedTaskRunner> network_task_runner)
74 : network_task_runner_(std::move(network_task_runner)),
75 url_matcher_(new url_matcher::URLMatcher),
76 next_id_(0) {}
77
78 void CTPolicyManager::CTDelegate::UpdateFromPrefs(
79 const base::ListValue* required_hosts,
80 const base::ListValue* excluded_hosts) {
81 network_task_runner_->PostTask(
82 FROM_HERE,
83 base::Bind(&CTDelegate::Update, base::Unretained(this),
84 base::Owned(required_hosts->CreateDeepCopy().release()),
85 base::Owned(excluded_hosts->CreateDeepCopy().release())));
86 }
87
88 net::TransportSecurityState::RequireCTDelegate::CTRequirementLevel
89 CTPolicyManager::CTDelegate::IsCTRequiredForHost(const std::string& hostname) {
90 DCHECK(network_task_runner_->RunsTasksOnCurrentThread());
91
92 // The scheme and port are ignored in the policy, and the contract of a
93 // RequireCTDelegate is that the host is always in URL form.
94 // TODO(rsleevi): Guarantee this
95 std::set<url_matcher::URLMatcherConditionSet::ID> matching_ids =
96 url_matcher_->MatchURL(GURL("https://" + hostname));
97 if (matching_ids.empty())
98 return CTRequirementLevel::DEFAULT;
99
100 // Determine the overall policy by determining the most specific policy.
101 std::map<url_matcher::URLMatcherConditionSet::ID, Filter>::const_iterator it =
102 filters_.begin();
103 const Filter* active_filter = nullptr;
104 for (const auto& match : matching_ids) {
105 // Because both |filters_| and |matching_ids| are sorted on the ID,
106 // treat both as forward-only iterators.
107 while (it != filters_.end() && it->first < match)
108 ++it;
109 if (it == filters_.end()) {
110 NOTREACHED();
111 break;
112 }
battre 2016/06/28 08:33:13 Do you expect this to be faster than a simple look
Ryan Sleevi 2016/06/28 16:41:27 Yes, lookups would be O(matching_ids.size() log fi
battre 2016/06/29 09:14:43 I think that it does not make any practical differ
113
114 if (!active_filter || FilterTakesPrecedence(it->second, *active_filter))
115 active_filter = &it->second;
116 }
117 CHECK(active_filter);
118
119 return active_filter->ct_required ? CTRequirementLevel::REQUIRED
120 : CTRequirementLevel::NOT_REQUIRED;
121 }
122
123 void CTPolicyManager::CTDelegate::Update(base::ListValue* required_hosts,
124 base::ListValue* excluded_hosts) {
125 DCHECK(network_task_runner_->RunsTasksOnCurrentThread());
126
127 url_matcher_.reset(new url_matcher::URLMatcher);
128 filters_.clear();
129 next_id_ = 0;
130
131 url_matcher::URLMatcherConditionSet::Vector all_conditions;
132 AddFilters(true, required_hosts, &all_conditions);
133 AddFilters(false, excluded_hosts, &all_conditions);
134
135 url_matcher_->AddConditionSets(all_conditions);
136 }
137
138 void CTPolicyManager::CTDelegate::AddFilters(
139 bool ct_required,
140 base::ListValue* hosts,
141 url_matcher::URLMatcherConditionSet::Vector* conditions) {
142 for (size_t i = 0; i < hosts->GetSize(); ++i) {
143 std::string pattern;
144 if (!hosts->GetString(i, &pattern))
145 continue;
146
147 Filter filter;
148 filter.ct_required = ct_required;
149
150 // Parse the pattern just to the hostname, ignoring all other portions of
151 // the URL.
152 url::Parsed parsed;
153 std::string ignored_scheme = url_formatter::SegmentURL(pattern, &parsed);
154 if (!parsed.host.is_nonempty())
155 continue; // If there is no host to match, can't apply the filter.
156
157 std::string lc_host = base::ToLowerASCII(
158 base::StringPiece(pattern).substr(parsed.host.begin, parsed.host.len));
159 if (lc_host == "*") {
160 // Wildcard hosts are not allowed and ignored.
161 continue;
162 } else if (lc_host[0] == '.') {
163 // A leading dot means exact match and to not match subdomains.
164 lc_host.erase(0, 1);
165 filter.match_subdomains = false;
166 } else {
167 // Canonicalize the host to make sure it's not an IP address, as
168 // matching subdomains is not desirable for those.
169 url::RawCanonOutputT<char> output;
170 url::CanonHostInfo host_info;
171 url::CanonicalizeHostVerbose(pattern.c_str(), parsed.host, &output,
172 &host_info);
173 // TODO(rsleevi): Use canonicalized form?
174 if (host_info.family == url::CanonHostInfo::NEUTRAL) {
175 // Match subdomains (implicit by the omission of '.'). Add in a
176 // leading dot to make sure matches only happen at the domain
177 // component boundary.
178 lc_host.insert(lc_host.begin(), '.');
179 filter.match_subdomains = true;
180 } else {
181 filter.match_subdomains = false;
182 }
183 }
184 filter.host_length = lc_host.size();
185
186 // Create a condition for the URLMatcher that matches the hostname (and/or
187 // subdomains).
188 url_matcher::URLMatcherConditionFactory* condition_factory =
189 url_matcher_->condition_factory();
190 std::set<url_matcher::URLMatcherCondition> condition_set;
191 condition_set.insert(
192 filter.match_subdomains
193 ? condition_factory->CreateHostSuffixCondition(lc_host)
194 : condition_factory->CreateHostEqualsCondition(lc_host));
195 conditions->push_back(
196 new url_matcher::URLMatcherConditionSet(next_id_, condition_set));
197 filters_[next_id_] = filter;
198 ++next_id_;
199 }
200 }
201
202 bool CTPolicyManager::CTDelegate::FilterTakesPrecedence(
203 const Filter& lhs,
204 const Filter& rhs) const {
205 if (lhs.match_subdomains != rhs.match_subdomains)
206 return !lhs.match_subdomains; // Prefer the more explicit policy.
207
208 if (lhs.host_length != rhs.host_length)
209 return lhs.host_length > rhs.host_length; // Prefer the longer host match.
210
211 if (lhs.ct_required != rhs.ct_required)
212 return lhs.ct_required; // Prefer the policy that requires CT.
213
214 return false;
215 }
216
217 // static
218 void CTPolicyManager::RegisterPrefs(PrefRegistrySimple* registry) {
219 registry->RegisterListPref(prefs::kCTRequiredHosts);
220 registry->RegisterListPref(prefs::kCTExcludedHosts);
221 }
222
223 CTPolicyManager::CTPolicyManager(
224 PrefService* pref_service,
225 scoped_refptr<base::SequencedTaskRunner> network_task_runner)
226 : delegate_(new CTDelegate(std::move(network_task_runner))),
227 weak_factory_(this) {
228 pref_change_registrar_.Init(pref_service);
229 pref_change_registrar_.Add(
230 prefs::kCTRequiredHosts,
231 base::Bind(&CTPolicyManager::ScheduleUpdate, base::Unretained(this)));
232 pref_change_registrar_.Add(
233 prefs::kCTExcludedHosts,
234 base::Bind(&CTPolicyManager::ScheduleUpdate, base::Unretained(this)));
235
236 ScheduleUpdate();
237 }
238
239 CTPolicyManager::~CTPolicyManager() {}
240
241 void CTPolicyManager::Shutdown() {
242 pref_change_registrar_.RemoveAll();
243 }
244
245 net::TransportSecurityState::RequireCTDelegate* CTPolicyManager::GetDelegate() {
246 return delegate_.get();
247 }
248
249 void CTPolicyManager::ScheduleUpdate() {
250 // Cancel any pending updates, and schedule a new update. If this method
251 // is called again, this pending update will be cancelled because the weak
252 // pointer is invalidated, and the new update will take precedence.
253 weak_factory_.InvalidateWeakPtrs();
254 base::SequencedTaskRunnerHandle::Get()->PostTask(
255 FROM_HERE,
256 base::Bind(&CTPolicyManager::Update, weak_factory_.GetWeakPtr()));
257 }
258
259 void CTPolicyManager::Update() {
260 delegate_->UpdateFromPrefs(
261 pref_change_registrar_.prefs()->GetList(prefs::kCTRequiredHosts),
262 pref_change_registrar_.prefs()->GetList(prefs::kCTExcludedHosts));
263 }
264
265 } // namespace certificate_transparency
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698