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

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

Powered by Google App Engine
This is Rietveld 408576698