1 // Copyright 2017 The Chromium Authors
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 "net/network_error_logging/network_error_logging_service.h"
6
7 #include <memory>
8 #include <optional>
9 #include <string>
10 #include <utility>
11 #include <vector>
12
13 #include "base/feature_list.h"
14 #include "base/functional/bind.h"
15 #include "base/json/json_reader.h"
16 #include "base/logging.h"
17 #include "base/memory/raw_ptr.h"
18 #include "base/metrics/histogram_macros.h"
19 #include "base/rand_util.h"
20 #include "base/time/clock.h"
21 #include "base/time/default_clock.h"
22 #include "base/time/time.h"
23 #include "base/values.h"
24 #include "net/base/features.h"
25 #include "net/base/ip_address.h"
26 #include "net/base/net_errors.h"
27 #include "net/base/network_anonymization_key.h"
28 #include "net/base/registry_controlled_domains/registry_controlled_domain.h"
29 #include "net/base/url_util.h"
30 #include "net/log/net_log.h"
31 #include "net/reporting/reporting_service.h"
32 #include "url/gurl.h"
33 #include "url/origin.h"
34
35 namespace net {
36
37 namespace {
38
39 const int kMaxJsonSize = 16 * 1024;
40 const int kMaxJsonDepth = 4;
41
42 const char kReportToKey[] = "report_to";
43 const char kMaxAgeKey[] = "max_age";
44 const char kIncludeSubdomainsKey[] = "include_subdomains";
45 const char kSuccessFractionKey[] = "success_fraction";
46 const char kFailureFractionKey[] = "failure_fraction";
47
48 const char kApplicationPhase[] = "application";
49 const char kConnectionPhase[] = "connection";
50 const char kDnsPhase[] = "dns";
51
52 const char kDnsAddressChangedType[] = "dns.address_changed";
53 const char kHttpErrorType[] = "http.error";
54
55 const struct {
56 Error error;
57 const char* phase = nullptr;
58 const char* type = nullptr;
59 } kErrorTypes[] = {
60 {OK, kApplicationPhase, "ok"},
61
62 // dns.unreachable?
63 {ERR_NAME_NOT_RESOLVED, kDnsPhase, "dns.name_not_resolved"},
64 {ERR_NAME_RESOLUTION_FAILED, kDnsPhase, "dns.failed"},
65 {ERR_DNS_TIMED_OUT, kDnsPhase, "dns.timed_out"},
66 {ERR_DNS_MALFORMED_RESPONSE, kDnsPhase, "dns.protocol"},
67 {ERR_DNS_SERVER_FAILED, kDnsPhase, "dns.server"},
68
69 {ERR_TIMED_OUT, kConnectionPhase, "tcp.timed_out"},
70 {ERR_CONNECTION_TIMED_OUT, kConnectionPhase, "tcp.timed_out"},
71 {ERR_CONNECTION_CLOSED, kConnectionPhase, "tcp.closed"},
72 {ERR_CONNECTION_RESET, kConnectionPhase, "tcp.reset"},
73 {ERR_CONNECTION_REFUSED, kConnectionPhase, "tcp.refused"},
74 {ERR_CONNECTION_ABORTED, kConnectionPhase, "tcp.aborted"},
75 {ERR_ADDRESS_INVALID, kConnectionPhase, "tcp.address_invalid"},
76 {ERR_ADDRESS_UNREACHABLE, kConnectionPhase, "tcp.address_unreachable"},
77 {ERR_CONNECTION_FAILED, kConnectionPhase, "tcp.failed"},
78
79 {ERR_SSL_VERSION_OR_CIPHER_MISMATCH, kConnectionPhase,
80 "tls.version_or_cipher_mismatch"},
81 {ERR_BAD_SSL_CLIENT_AUTH_CERT, kConnectionPhase,
82 "tls.bad_client_auth_cert"},
83 {ERR_CERT_INVALID, kConnectionPhase, "tls.cert.invalid"},
84 {ERR_CERT_COMMON_NAME_INVALID, kConnectionPhase, "tls.cert.name_invalid"},
85 {ERR_CERT_DATE_INVALID, kConnectionPhase, "tls.cert.date_invalid"},
86 {ERR_CERT_AUTHORITY_INVALID, kConnectionPhase,
87 "tls.cert.authority_invalid"},
88 {ERR_CERT_REVOKED, kConnectionPhase, "tls.cert.revoked"},
89 {ERR_SSL_PINNED_KEY_NOT_IN_CERT_CHAIN, kConnectionPhase,
90 "tls.cert.pinned_key_not_in_cert_chain"},
91 {ERR_SSL_PROTOCOL_ERROR, kConnectionPhase, "tls.protocol.error"},
92 {ERR_INSECURE_RESPONSE, kConnectionPhase, "tls.failed"},
93 {ERR_SSL_UNRECOGNIZED_NAME_ALERT, kConnectionPhase,
94 "tls.unrecognized_name_alert"},
95 // tls.failed?
96
97 {ERR_HTTP2_PING_FAILED, kApplicationPhase, "h2.ping_failed"},
98 {ERR_HTTP2_PROTOCOL_ERROR, kConnectionPhase, "h2.protocol.error"},
99
100 {ERR_QUIC_PROTOCOL_ERROR, kConnectionPhase, "h3.protocol.error"},
101
102 // http.protocol.error?
103 {ERR_TOO_MANY_REDIRECTS, kApplicationPhase, "http.response.redirect_loop"},
104 {ERR_INVALID_RESPONSE, kApplicationPhase, "http.response.invalid"},
105 {ERR_INVALID_HTTP_RESPONSE, kApplicationPhase, "http.response.invalid"},
106 {ERR_EMPTY_RESPONSE, kApplicationPhase, "http.response.invalid.empty"},
107 {ERR_CONTENT_LENGTH_MISMATCH, kApplicationPhase,
108 "http.response.invalid.content_length_mismatch"},
109 {ERR_INCOMPLETE_CHUNKED_ENCODING, kApplicationPhase,
110 "http.response.invalid.incomplete_chunked_encoding"},
111 {ERR_INVALID_CHUNKED_ENCODING, kApplicationPhase,
112 "http.response.invalid.invalid_chunked_encoding"},
113 {ERR_REQUEST_RANGE_NOT_SATISFIABLE, kApplicationPhase,
114 "http.request.range_not_satisfiable"},
115 {ERR_RESPONSE_HEADERS_TRUNCATED, kApplicationPhase,
116 "http.response.headers.truncated"},
117 {ERR_RESPONSE_HEADERS_MULTIPLE_CONTENT_DISPOSITION, kApplicationPhase,
118 "http.response.headers.multiple_content_disposition"},
119 {ERR_RESPONSE_HEADERS_MULTIPLE_CONTENT_LENGTH, kApplicationPhase,
120 "http.response.headers.multiple_content_length"},
121 // http.failed?
122
123 {ERR_ABORTED, kApplicationPhase, "abandoned"},
124 // unknown?
125
126 // TODO(juliatuttle): Surely there are more errors we want here.
127 };
128
GetPhaseAndTypeFromNetError(Error error,std::string * phase_out,std::string * type_out)129 void GetPhaseAndTypeFromNetError(Error error,
130 std::string* phase_out,
131 std::string* type_out) {
132 for (const auto& error_type : kErrorTypes) {
133 DCHECK(error_type.phase != nullptr);
134 DCHECK(error_type.type != nullptr);
135 if (error_type.error == error) {
136 *phase_out = error_type.phase;
137 *type_out = error_type.type;
138 return;
139 }
140 }
141 *phase_out = IsCertificateError(error) ? kConnectionPhase : kApplicationPhase;
142 *type_out = "unknown";
143 }
144
IsHttpError(const NetworkErrorLoggingService::RequestDetails & request)145 bool IsHttpError(const NetworkErrorLoggingService::RequestDetails& request) {
146 return request.status_code >= 400 && request.status_code < 600;
147 }
148
RecordSignedExchangeRequestOutcome(NetworkErrorLoggingService::RequestOutcome outcome)149 void RecordSignedExchangeRequestOutcome(
150 NetworkErrorLoggingService::RequestOutcome outcome) {
151 UMA_HISTOGRAM_ENUMERATION(
152 NetworkErrorLoggingService::kSignedExchangeRequestOutcomeHistogram,
153 outcome);
154 }
155
156 class NetworkErrorLoggingServiceImpl : public NetworkErrorLoggingService {
157 public:
NetworkErrorLoggingServiceImpl(PersistentNelStore * store)158 explicit NetworkErrorLoggingServiceImpl(PersistentNelStore* store)
159 : store_(store) {
160 if (!PoliciesArePersisted())
161 initialized_ = true;
162 }
163
~NetworkErrorLoggingServiceImpl()164 ~NetworkErrorLoggingServiceImpl() override {
165 if (PoliciesArePersisted() && initialized_)
166 store_->Flush();
167 }
168
169 // NetworkErrorLoggingService implementation:
170
OnHeader(const NetworkAnonymizationKey & network_anonymization_key,const url::Origin & origin,const IPAddress & received_ip_address,const std::string & value)171 void OnHeader(const NetworkAnonymizationKey& network_anonymization_key,
172 const url::Origin& origin,
173 const IPAddress& received_ip_address,
174 const std::string& value) override {
175 // NEL is only available to secure origins, so don't permit insecure origins
176 // to set policies.
177 if (!origin.GetURL().SchemeIsCryptographic())
178 return;
179
180 base::Time header_received_time = clock_->Now();
181 // base::Unretained is safe because the callback gets stored in
182 // task_backlog_, so the callback will not outlive |*this|.
183 DoOrBacklogTask(base::BindOnce(
184 &NetworkErrorLoggingServiceImpl::DoOnHeader, base::Unretained(this),
185 respect_network_anonymization_key_ ? network_anonymization_key
186 : NetworkAnonymizationKey(),
187 origin, received_ip_address, value, header_received_time));
188 }
189
OnRequest(RequestDetails details)190 void OnRequest(RequestDetails details) override {
191 // This method is only called on secure requests.
192 DCHECK(details.uri.SchemeIsCryptographic());
193
194 if (!reporting_service_)
195 return;
196
197 if (!respect_network_anonymization_key_)
198 details.network_anonymization_key = NetworkAnonymizationKey();
199
200 base::Time request_received_time = clock_->Now();
201 // base::Unretained is safe because the callback gets stored in
202 // task_backlog_, so the callback will not outlive |*this|.
203 DoOrBacklogTask(base::BindOnce(&NetworkErrorLoggingServiceImpl::DoOnRequest,
204 base::Unretained(this), std::move(details),
205 request_received_time));
206 }
207
QueueSignedExchangeReport(SignedExchangeReportDetails details)208 void QueueSignedExchangeReport(SignedExchangeReportDetails details) override {
209 if (!reporting_service_) {
210 RecordSignedExchangeRequestOutcome(
211 RequestOutcome::kDiscardedNoReportingService);
212 return;
213 }
214 if (!details.outer_url.SchemeIsCryptographic()) {
215 RecordSignedExchangeRequestOutcome(
216 RequestOutcome::kDiscardedInsecureOrigin);
217 return;
218 }
219
220 if (!respect_network_anonymization_key_)
221 details.network_anonymization_key = NetworkAnonymizationKey();
222
223 base::Time request_received_time = clock_->Now();
224 // base::Unretained is safe because the callback gets stored in
225 // task_backlog_, so the callback will not outlive |*this|.
226 DoOrBacklogTask(base::BindOnce(
227 &NetworkErrorLoggingServiceImpl::DoQueueSignedExchangeReport,
228 base::Unretained(this), std::move(details), request_received_time));
229 }
230
RemoveBrowsingData(const base::RepeatingCallback<bool (const url::Origin &)> & origin_filter)231 void RemoveBrowsingData(
232 const base::RepeatingCallback<bool(const url::Origin&)>& origin_filter)
233 override {
234 // base::Unretained is safe because the callback gets stored in
235 // task_backlog_, so the callback will not outlive |*this|.
236 DoOrBacklogTask(
237 base::BindOnce(&NetworkErrorLoggingServiceImpl::DoRemoveBrowsingData,
238 base::Unretained(this), origin_filter));
239 }
240
RemoveAllBrowsingData()241 void RemoveAllBrowsingData() override {
242 // base::Unretained is safe because the callback gets stored in
243 // task_backlog_, so the callback will not outlive |*this|.
244 DoOrBacklogTask(
245 base::BindOnce(&NetworkErrorLoggingServiceImpl::DoRemoveAllBrowsingData,
246 base::Unretained(this)));
247 }
248
StatusAsValue() const249 base::Value StatusAsValue() const override {
250 base::Value::Dict dict;
251 base::Value::List policy_list;
252 // We wanted sorted (or at least reproducible) output; luckily, policies_ is
253 // a std::map, and therefore already sorted.
254 for (const auto& key_and_policy : policies_) {
255 const NelPolicyKey& key = key_and_policy.first;
256 const NelPolicy& policy = key_and_policy.second;
257 base::Value::Dict policy_dict;
258 policy_dict.Set("NetworkAnonymizationKey",
259 key.network_anonymization_key.ToDebugString());
260 policy_dict.Set("origin", key.origin.Serialize());
261 policy_dict.Set("includeSubdomains", policy.include_subdomains);
262 policy_dict.Set("reportTo", policy.report_to);
263 policy_dict.Set("expires", NetLog::TimeToString(policy.expires));
264 policy_dict.Set("successFraction", policy.success_fraction);
265 policy_dict.Set("failureFraction", policy.failure_fraction);
266 policy_list.Append(std::move(policy_dict));
267 }
268 dict.Set("originPolicies", std::move(policy_list));
269 return base::Value(std::move(dict));
270 }
271
GetPolicyKeysForTesting()272 std::set<NelPolicyKey> GetPolicyKeysForTesting() override {
273 std::set<NelPolicyKey> keys;
274 for (const auto& entry : policies_) {
275 keys.insert(entry.first);
276 }
277 return keys;
278 }
279
280 NetworkErrorLoggingService::PersistentNelStore*
GetPersistentNelStoreForTesting()281 GetPersistentNelStoreForTesting() override {
282 return store_;
283 }
284
GetReportingServiceForTesting()285 ReportingService* GetReportingServiceForTesting() override {
286 return reporting_service_;
287 }
288
289 private:
290 // Map from (NAK, origin) to owned policy.
291 using PolicyMap = std::map<NelPolicyKey, NelPolicy>;
292
293 // Wildcard policies are policies for which the include_subdomains flag is
294 // true.
295 //
296 // Wildcard policies are accessed by domain name, not full origin. The key
297 // consists of the NetworkAnonymizationKey of the policy, plus a string which
298 // is the host part of the policy's origin.
299 //
300 // Looking up a wildcard policy for a domain yields the wildcard policy with
301 // the longest host part (most specific subdomain) that is a substring of the
302 // domain.
303 //
304 // When multiple policies with the same (NAK, origin.host()) are present, they
305 // are all stored, the policy returned is not well defined.
306 //
307 // Policies in the map are unowned; they are pointers to the original in
308 // the PolicyMap.
309 using WildcardPolicyMap =
310 std::map<WildcardNelPolicyKey,
311 std::set<raw_ptr<const NelPolicy, SetExperimental>>>;
312
313 PolicyMap policies_;
314 WildcardPolicyMap wildcard_policies_;
315
316 // The persistent store in which NEL policies will be stored to disk, if not
317 // null. If |store_| is null, then NEL policies will be in-memory only.
318 // The store is owned by the URLRequestContext because Reporting also needs
319 // access to it.
320 raw_ptr<PersistentNelStore> store_;
321
322 // Set to true when we have told the store to load NEL policies. This is to
323 // make sure we don't try to load policies multiple times.
324 bool started_loading_policies_ = false;
325
326 // Set to true when the NEL service has been initialized. Before
327 // initialization is complete, commands to the NEL service (i.e. public
328 // method calls) are stashed away in |task_backlog_|, to be executed once
329 // initialization is complete. Initialization is complete automatically if
330 // there is no PersistentNelStore. If there is a store, then initialization is
331 // complete when the NEL policies have finished being loaded from the store
332 // (either successfully or unsuccessfully).
333 bool initialized_ = false;
334
335 // Backlog of tasks waiting on initialization.
336 std::vector<base::OnceClosure> task_backlog_;
337
338 // Set based on network state partitioning status on construction.
339 bool respect_network_anonymization_key_ =
340 NetworkAnonymizationKey::IsPartitioningEnabled();
341
342 base::WeakPtrFactory<NetworkErrorLoggingServiceImpl> weak_factory_{this};
343
PoliciesArePersisted() const344 bool PoliciesArePersisted() const { return store_ != nullptr; }
345
DoOrBacklogTask(base::OnceClosure task)346 void DoOrBacklogTask(base::OnceClosure task) {
347 if (shut_down_)
348 return;
349
350 FetchAllPoliciesFromStoreIfNecessary();
351
352 if (!initialized_) {
353 task_backlog_.push_back(std::move(task));
354 return;
355 }
356
357 std::move(task).Run();
358 }
359
ExecuteBacklog()360 void ExecuteBacklog() {
361 DCHECK(initialized_);
362
363 if (shut_down_)
364 return;
365
366 for (base::OnceClosure& task : task_backlog_) {
367 std::move(task).Run();
368 }
369 task_backlog_.clear();
370 }
371
DoOnHeader(const NetworkAnonymizationKey & network_anonymization_key,const url::Origin & origin,const IPAddress & received_ip_address,const std::string & value,base::Time header_received_time)372 void DoOnHeader(const NetworkAnonymizationKey& network_anonymization_key,
373 const url::Origin& origin,
374 const IPAddress& received_ip_address,
375 const std::string& value,
376 base::Time header_received_time) {
377 DCHECK(initialized_);
378
379 NelPolicy policy;
380 policy.key = NelPolicyKey(network_anonymization_key, origin);
381 policy.received_ip_address = received_ip_address;
382 policy.last_used = header_received_time;
383
384 if (!ParseHeader(value, clock_->Now(), &policy))
385 return;
386
387 // Disallow eTLDs from setting include_subdomains policies.
388 if (policy.include_subdomains &&
389 registry_controlled_domains::GetRegistryLength(
390 policy.key.origin.GetURL(),
391 registry_controlled_domains::INCLUDE_UNKNOWN_REGISTRIES,
392 registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES) == 0) {
393 return;
394 }
395
396 // If a policy for this NelPolicyKey already existed, remove the old policy.
397 auto it = policies_.find(policy.key);
398 if (it != policies_.end())
399 RemovePolicy(it);
400
401 // A policy's |expires| field is set to a null time if the max_age was 0.
402 // Having a max_age of 0 means that the policy should be removed, so return
403 // here instead of continuing on to inserting the policy.
404 if (policy.expires.is_null())
405 return;
406
407 AddPolicy(std::move(policy));
408
409 // Evict policies if the policy limit is exceeded.
410 if (policies_.size() > kMaxPolicies) {
411 RemoveAllExpiredPolicies();
412 while (policies_.size() > kMaxPolicies) {
413 EvictStalestPolicy();
414 }
415 }
416 }
417
DoOnRequest(RequestDetails details,base::Time request_received_time)418 void DoOnRequest(RequestDetails details, base::Time request_received_time) {
419 DCHECK(reporting_service_);
420 DCHECK(initialized_);
421
422 if (!respect_network_anonymization_key_)
423 details.network_anonymization_key = NetworkAnonymizationKey();
424
425 auto report_origin = url::Origin::Create(details.uri);
426 const NelPolicy* policy =
427 FindPolicyForReport(details.network_anonymization_key, report_origin);
428 if (!policy)
429 return;
430
431 MarkPolicyUsed(policy, request_received_time);
432
433 Error type = details.type;
434 // It is expected for Reporting uploads to terminate with ERR_ABORTED, since
435 // the ReportingUploader cancels them after receiving the response code and
436 // headers.
437 if (details.reporting_upload_depth > 0 && type == ERR_ABORTED) {
438 // TODO(juliatuttle): Modify ReportingUploader to drain successful uploads
439 // instead of aborting them, so NEL can properly report on aborted
440 // requests.
441 type = OK;
442 }
443
444 std::string phase_string;
445 std::string type_string;
446 GetPhaseAndTypeFromNetError(type, &phase_string, &type_string);
447
448 if (IsHttpError(details)) {
449 phase_string = kApplicationPhase;
450 type_string = kHttpErrorType;
451 }
452
453 // This check would go earlier, but the histogram bucket will be more
454 // meaningful if it only includes reports that otherwise could have been
455 // uploaded.
456 if (details.reporting_upload_depth > kMaxNestedReportDepth)
457 return;
458
459 // include_subdomains policies are only allowed to report on DNS resolution
460 // errors.
461 if (phase_string != kDnsPhase &&
462 IsMismatchingSubdomainReport(*policy, report_origin)) {
463 return;
464 }
465
466 // If the server that handled the request is different than the server that
467 // delivered the NEL policy (as determined by their IP address), then we
468 // have to "downgrade" the NEL report, so that it only includes information
469 // about DNS resolution.
470 if (phase_string != kDnsPhase && details.server_ip.IsValid() &&
471 details.server_ip != policy->received_ip_address) {
472 phase_string = kDnsPhase;
473 type_string = kDnsAddressChangedType;
474 details.elapsed_time = base::TimeDelta();
475 details.status_code = 0;
476 }
477
478 bool success = (type == OK) && !IsHttpError(details);
479 const std::optional<double> sampling_fraction =
480 SampleAndReturnFraction(*policy, success);
481 if (!sampling_fraction.has_value())
482 return;
483
484 DVLOG(1) << "Created NEL report (" << type_string
485 << ", status=" << details.status_code
486 << ", depth=" << details.reporting_upload_depth << ") for "
487 << details.uri;
488
489 // A null reporting source token is used since this report is not associated
490 // with any particular document.
491 reporting_service_->QueueReport(
492 details.uri, std::nullopt, details.network_anonymization_key,
493 details.user_agent, policy->report_to, kReportType,
494 CreateReportBody(phase_string, type_string, sampling_fraction.value(),
495 details),
496 details.reporting_upload_depth);
497 }
498
DoQueueSignedExchangeReport(SignedExchangeReportDetails details,base::Time request_received_time)499 void DoQueueSignedExchangeReport(SignedExchangeReportDetails details,
500 base::Time request_received_time) {
501 DCHECK(reporting_service_);
502
503 const auto report_origin = url::Origin::Create(details.outer_url);
504 const NelPolicy* policy =
505 FindPolicyForReport(details.network_anonymization_key, report_origin);
506 if (!policy) {
507 RecordSignedExchangeRequestOutcome(
508 RequestOutcome::kDiscardedNoOriginPolicy);
509 return;
510 }
511
512 MarkPolicyUsed(policy, request_received_time);
513
514 if (IsMismatchingSubdomainReport(*policy, report_origin)) {
515 RecordSignedExchangeRequestOutcome(
516 RequestOutcome::kDiscardedNonDNSSubdomainReport);
517 return;
518 }
519 // Don't send the report when the IP addresses of the server and the policy
520 // don’t match. This case is coverd by OnRequest() while processing the HTTP
521 // response.
522 // This happens if the server has set the NEL policy previously, but doesn't
523 // set the NEL policy for the signed exchange response, and the IP address
524 // has changed due to DNS round robin.
525 if (details.server_ip_address != policy->received_ip_address) {
526 RecordSignedExchangeRequestOutcome(
527 RequestOutcome::kDiscardedIPAddressMismatch);
528 return;
529 }
530 const std::optional<double> sampling_fraction =
531 SampleAndReturnFraction(*policy, details.success);
532 if (!sampling_fraction.has_value()) {
533 RecordSignedExchangeRequestOutcome(
534 details.success ? RequestOutcome::kDiscardedUnsampledSuccess
535 : RequestOutcome::kDiscardedUnsampledFailure);
536 return;
537 }
538
539 // A null reporting source token is used since this report is not associated
540 // with any particular document.
541 reporting_service_->QueueReport(
542 details.outer_url, std::nullopt, details.network_anonymization_key,
543 details.user_agent, policy->report_to, kReportType,
544 CreateSignedExchangeReportBody(details, sampling_fraction.value()),
545 0 /* depth */);
546 RecordSignedExchangeRequestOutcome(RequestOutcome::kQueued);
547 }
548
DoRemoveBrowsingData(const base::RepeatingCallback<bool (const url::Origin &)> & origin_filter)549 void DoRemoveBrowsingData(
550 const base::RepeatingCallback<bool(const url::Origin&)>& origin_filter) {
551 DCHECK(initialized_);
552 for (auto it = policies_.begin(); it != policies_.end();) {
553 const NelPolicyKey& key = it->first;
554 // Remove policies matching the filter.
555 if (origin_filter.Run(key.origin)) {
556 it = RemovePolicy(it);
557 } else {
558 ++it;
559 }
560 }
561 if (PoliciesArePersisted())
562 store_->Flush();
563 }
564
DoRemoveAllBrowsingData()565 void DoRemoveAllBrowsingData() {
566 DCHECK(initialized_);
567 if (PoliciesArePersisted()) {
568 // TODO(chlily): Add a DeleteAllNelPolicies command to PersistentNelStore.
569 for (auto origin_and_policy : policies_) {
570 store_->DeleteNelPolicy(origin_and_policy.second);
571 }
572 store_->Flush();
573 }
574
575 wildcard_policies_.clear();
576 policies_.clear();
577 }
578
579 // Returns whether the |json_value| was parsed as a valid header that either
580 // sets a NEL policy (max age > 0) or removes an existing one (max age == 0).
ParseHeader(const std::string & json_value,base::Time now,NelPolicy * policy_out) const581 bool ParseHeader(const std::string& json_value,
582 base::Time now,
583 NelPolicy* policy_out) const {
584 DCHECK(policy_out);
585
586 // JSON is malformed (too large, syntax error, not a dictionary).
587 if (json_value.size() > kMaxJsonSize)
588 return false;
589
590 std::optional<base::Value> value =
591 base::JSONReader::Read(json_value, base::JSON_PARSE_RFC, kMaxJsonDepth);
592 if (!value)
593 return false;
594
595 base::Value::Dict* dict = value->GetIfDict();
596 if (!dict)
597 return false;
598
599 // Max-Age property is missing or malformed.
600 int max_age_sec = dict->FindInt(kMaxAgeKey).value_or(-1);
601 if (max_age_sec < 0)
602 return false;
603
604 // Report-To property is missing or malformed.
605 std::string report_to;
606 if (max_age_sec > 0) {
607 std::string* maybe_report_to = dict->FindString(kReportToKey);
608 if (!maybe_report_to)
609 return false;
610 report_to = *maybe_report_to;
611 }
612
613 // include_subdomains is optional and defaults to false, so it's okay if
614 // GetBoolean fails.
615 bool include_subdomains =
616 dict->FindBool(kIncludeSubdomainsKey).value_or(false);
617
618 // TODO(chlily): According to the spec we should restrict these sampling
619 // fractions to [0.0, 1.0].
620 // success_fraction is optional and defaults to 0.0, so it's okay if
621 // GetDouble fails.
622 double success_fraction =
623 dict->FindDouble(kSuccessFractionKey).value_or(0.0);
624
625 // failure_fraction is optional and defaults to 1.0, so it's okay if
626 // GetDouble fails.
627 double failure_fraction =
628 dict->FindDouble(kFailureFractionKey).value_or(1.0);
629
630 policy_out->report_to = report_to;
631 policy_out->include_subdomains = include_subdomains;
632 policy_out->success_fraction = success_fraction;
633 policy_out->failure_fraction = failure_fraction;
634 policy_out->expires =
635 max_age_sec > 0 ? now + base::Seconds(max_age_sec) : base::Time();
636 return true;
637 }
638
FindPolicyForReport(const NetworkAnonymizationKey & network_anonymization_key,const url::Origin & report_origin) const639 const NelPolicy* FindPolicyForReport(
640 const NetworkAnonymizationKey& network_anonymization_key,
641 const url::Origin& report_origin) const {
642 DCHECK(initialized_);
643
644 auto it =
645 policies_.find(NelPolicyKey(network_anonymization_key, report_origin));
646 if (it != policies_.end() && clock_->Now() < it->second.expires)
647 return &it->second;
648
649 std::string domain = report_origin.host();
650 const NelPolicy* wildcard_policy = nullptr;
651 while (!wildcard_policy && !domain.empty()) {
652 wildcard_policy = FindWildcardPolicy(network_anonymization_key, domain);
653 domain = GetSuperdomain(domain);
654 }
655
656 return wildcard_policy;
657 }
658
FindWildcardPolicy(const NetworkAnonymizationKey & network_anonymization_key,const std::string & domain) const659 const NelPolicy* FindWildcardPolicy(
660 const NetworkAnonymizationKey& network_anonymization_key,
661 const std::string& domain) const {
662 DCHECK(!domain.empty());
663
664 auto it = wildcard_policies_.find(
665 WildcardNelPolicyKey(network_anonymization_key, domain));
666 if (it == wildcard_policies_.end())
667 return nullptr;
668
669 DCHECK(!it->second.empty());
670
671 for (const NelPolicy* policy : it->second) {
672 if (clock_->Now() < policy->expires)
673 return policy;
674 }
675
676 return nullptr;
677 }
678
679 // There must be no pre-existing policy for |policy.key|. Returns iterator
680 // to the inserted policy.
AddPolicy(NelPolicy policy)681 PolicyMap::iterator AddPolicy(NelPolicy policy) {
682 // If |initialized_| is false, then we are calling this from
683 // OnPoliciesLoaded(), which means we don't want to add the given policy to
684 // the store because we have just loaded it from there.
685 if (PoliciesArePersisted() && initialized_)
686 store_->AddNelPolicy(policy);
687
688 auto iter_and_result = policies_.emplace(policy.key, std::move(policy));
689 // TODO(crbug.com/1326282): Change this to a DCHECK when we're sure the bug
690 // is fixed.
691 CHECK(iter_and_result.second);
692
693 const NelPolicy& inserted_policy = iter_and_result.first->second;
694 MaybeAddWildcardPolicy(inserted_policy.key, &inserted_policy);
695
696 return iter_and_result.first;
697 }
698
MaybeAddWildcardPolicy(const NelPolicyKey & origin_key,const NelPolicy * policy)699 void MaybeAddWildcardPolicy(const NelPolicyKey& origin_key,
700 const NelPolicy* policy) {
701 DCHECK(policy);
702 DCHECK_EQ(policy, &policies_[origin_key]);
703
704 if (!policy->include_subdomains)
705 return;
706
707 WildcardNelPolicyKey wildcard_key(origin_key);
708 auto inserted = wildcard_policies_[wildcard_key].insert(policy);
709 DCHECK(inserted.second);
710 }
711
712 // Removes the policy pointed to by |policy_it|. Invalidates |policy_it|.
713 // Returns the iterator to the next element.
RemovePolicy(PolicyMap::iterator policy_it)714 PolicyMap::iterator RemovePolicy(PolicyMap::iterator policy_it) {
715 DCHECK(policy_it != policies_.end());
716 NelPolicy* policy = &policy_it->second;
717 MaybeRemoveWildcardPolicy(policy);
718
719 if (PoliciesArePersisted() && initialized_)
720 store_->DeleteNelPolicy(*policy);
721
722 return policies_.erase(policy_it);
723 }
724
MaybeRemoveWildcardPolicy(const NelPolicy * policy)725 void MaybeRemoveWildcardPolicy(const NelPolicy* policy) {
726 DCHECK(policy);
727
728 if (!policy->include_subdomains)
729 return;
730
731 const NelPolicyKey& origin_key = policy->key;
732 DCHECK_EQ(policy, &policies_[origin_key]);
733
734 auto wildcard_it =
735 wildcard_policies_.find(WildcardNelPolicyKey(origin_key));
736 DCHECK(wildcard_it != wildcard_policies_.end());
737
738 size_t erased = wildcard_it->second.erase(policy);
739 DCHECK_EQ(1u, erased);
740 if (wildcard_it->second.empty())
741 wildcard_policies_.erase(wildcard_it);
742 }
743
MarkPolicyUsed(const NelPolicy * policy,base::Time time_used) const744 void MarkPolicyUsed(const NelPolicy* policy, base::Time time_used) const {
745 policy->last_used = time_used;
746 if (PoliciesArePersisted() && initialized_)
747 store_->UpdateNelPolicyAccessTime(*policy);
748 }
749
RemoveAllExpiredPolicies()750 void RemoveAllExpiredPolicies() {
751 for (auto it = policies_.begin(); it != policies_.end();) {
752 if (it->second.expires < clock_->Now()) {
753 it = RemovePolicy(it);
754 } else {
755 ++it;
756 }
757 }
758 }
759
EvictStalestPolicy()760 void EvictStalestPolicy() {
761 PolicyMap::iterator stalest_it = policies_.begin();
762 for (auto it = policies_.begin(); it != policies_.end(); ++it) {
763 if (it->second.last_used < stalest_it->second.last_used)
764 stalest_it = it;
765 }
766
767 // This should only be called if we have hit the max policy limit, so there
768 // should be at least one policy.
769 DCHECK(stalest_it != policies_.end());
770
771 RemovePolicy(stalest_it);
772 }
773
CreateReportBody(const std::string & phase,const std::string & type,double sampling_fraction,const RequestDetails & details)774 static base::Value::Dict CreateReportBody(const std::string& phase,
775 const std::string& type,
776 double sampling_fraction,
777 const RequestDetails& details) {
778 base::Value::Dict body;
779
780 body.Set(kReferrerKey, details.referrer.spec());
781 body.Set(kSamplingFractionKey, sampling_fraction);
782 body.Set(kServerIpKey, details.server_ip.ToString());
783 body.Set(kProtocolKey, details.protocol);
784 body.Set(kMethodKey, details.method);
785 body.Set(kStatusCodeKey, details.status_code);
786 body.Set(kElapsedTimeKey,
787 static_cast<int>(details.elapsed_time.InMilliseconds()));
788 body.Set(kPhaseKey, phase);
789 body.Set(kTypeKey, type);
790
791 return body;
792 }
793
CreateSignedExchangeReportBody(const SignedExchangeReportDetails & details,double sampling_fraction)794 static base::Value::Dict CreateSignedExchangeReportBody(
795 const SignedExchangeReportDetails& details,
796 double sampling_fraction) {
797 base::Value::Dict body;
798 body.Set(kPhaseKey, kSignedExchangePhaseValue);
799 body.Set(kTypeKey, details.type);
800 body.Set(kSamplingFractionKey, sampling_fraction);
801 body.Set(kReferrerKey, details.referrer);
802 body.Set(kServerIpKey, details.server_ip_address.ToString());
803 body.Set(kProtocolKey, details.protocol);
804 body.Set(kMethodKey, details.method);
805 body.Set(kStatusCodeKey, details.status_code);
806 body.Set(kElapsedTimeKey,
807 static_cast<int>(details.elapsed_time.InMilliseconds()));
808
809 base::Value::Dict sxg_body;
810 sxg_body.Set(kOuterUrlKey, details.outer_url.spec());
811 if (details.inner_url.is_valid())
812 sxg_body.Set(kInnerUrlKey, details.inner_url.spec());
813
814 base::Value::List cert_url_list;
815 if (details.cert_url.is_valid())
816 cert_url_list.Append(details.cert_url.spec());
817 sxg_body.Set(kCertUrlKey, std::move(cert_url_list));
818 body.Set(kSignedExchangeBodyKey, std::move(sxg_body));
819
820 return body;
821 }
822
IsMismatchingSubdomainReport(const NelPolicy & policy,const url::Origin & report_origin) const823 bool IsMismatchingSubdomainReport(const NelPolicy& policy,
824 const url::Origin& report_origin) const {
825 return policy.include_subdomains && (policy.key.origin != report_origin);
826 }
827
828 // Returns a valid value of matching fraction iff the event should be sampled.
SampleAndReturnFraction(const NelPolicy & policy,bool success) const829 std::optional<double> SampleAndReturnFraction(const NelPolicy& policy,
830 bool success) const {
831 const double sampling_fraction =
832 success ? policy.success_fraction : policy.failure_fraction;
833
834 // Sampling fractions are often either 0.0 or 1.0, so in those cases we
835 // can avoid having to call RandDouble().
836 if (sampling_fraction <= 0.0)
837 return std::nullopt;
838 if (sampling_fraction >= 1.0)
839 return sampling_fraction;
840
841 if (base::RandDouble() >= sampling_fraction)
842 return std::nullopt;
843 return sampling_fraction;
844 }
845
FetchAllPoliciesFromStoreIfNecessary()846 void FetchAllPoliciesFromStoreIfNecessary() {
847 if (!PoliciesArePersisted() || started_loading_policies_)
848 return;
849
850 started_loading_policies_ = true;
851 FetchAllPoliciesFromStore();
852 }
853
FetchAllPoliciesFromStore()854 void FetchAllPoliciesFromStore() {
855 DCHECK(PoliciesArePersisted());
856 DCHECK(!initialized_);
857
858 store_->LoadNelPolicies(
859 base::BindOnce(&NetworkErrorLoggingServiceImpl::OnPoliciesLoaded,
860 weak_factory_.GetWeakPtr()));
861 }
862
863 // This is called when loading from the store is complete, regardless of
864 // success or failure.
865 // DB initialization may have failed, in which case we will receive an empty
866 // vector from the PersistentNelStore. This is indistinguishable from a
867 // successful load that happens to not yield any policies, but in
868 // either case we still want to go through the task backlog.
OnPoliciesLoaded(std::vector<NelPolicy> loaded_policies)869 void OnPoliciesLoaded(std::vector<NelPolicy> loaded_policies) {
870 DCHECK(PoliciesArePersisted());
871 DCHECK(!initialized_);
872
873 // TODO(chlily): Toss any expired policies we encounter.
874 for (NelPolicy& policy : loaded_policies) {
875 if (policies_.find(policy.key) == policies_.end()) {
876 AddPolicy(std::move(policy));
877 }
878 }
879 initialized_ = true;
880 ExecuteBacklog();
881 }
882 };
883
884 } // namespace
885
886 NetworkErrorLoggingService::NelPolicyKey::NelPolicyKey() = default;
887
NelPolicyKey(const NetworkAnonymizationKey & network_anonymization_key,const url::Origin & origin)888 NetworkErrorLoggingService::NelPolicyKey::NelPolicyKey(
889 const NetworkAnonymizationKey& network_anonymization_key,
890 const url::Origin& origin)
891 : network_anonymization_key(network_anonymization_key), origin(origin) {}
892
893 NetworkErrorLoggingService::NelPolicyKey::NelPolicyKey(
894 const NelPolicyKey& other) = default;
895
operator <(const NelPolicyKey & other) const896 bool NetworkErrorLoggingService::NelPolicyKey::operator<(
897 const NelPolicyKey& other) const {
898 return std::tie(network_anonymization_key, origin) <
899 std::tie(other.network_anonymization_key, other.origin);
900 }
901
operator ==(const NelPolicyKey & other) const902 bool NetworkErrorLoggingService::NelPolicyKey::operator==(
903 const NelPolicyKey& other) const {
904 return std::tie(network_anonymization_key, origin) ==
905 std::tie(other.network_anonymization_key, other.origin);
906 }
907
operator !=(const NelPolicyKey & other) const908 bool NetworkErrorLoggingService::NelPolicyKey::operator!=(
909 const NelPolicyKey& other) const {
910 return !(*this == other);
911 }
912
913 NetworkErrorLoggingService::NelPolicyKey::~NelPolicyKey() = default;
914
915 NetworkErrorLoggingService::WildcardNelPolicyKey::WildcardNelPolicyKey() =
916 default;
917
WildcardNelPolicyKey(const NetworkAnonymizationKey & network_anonymization_key,const std::string & domain)918 NetworkErrorLoggingService::WildcardNelPolicyKey::WildcardNelPolicyKey(
919 const NetworkAnonymizationKey& network_anonymization_key,
920 const std::string& domain)
921 : network_anonymization_key(network_anonymization_key), domain(domain) {}
922
WildcardNelPolicyKey(const NelPolicyKey & origin_key)923 NetworkErrorLoggingService::WildcardNelPolicyKey::WildcardNelPolicyKey(
924 const NelPolicyKey& origin_key)
925 : WildcardNelPolicyKey(origin_key.network_anonymization_key,
926 origin_key.origin.host()) {}
927
928 NetworkErrorLoggingService::WildcardNelPolicyKey::WildcardNelPolicyKey(
929 const WildcardNelPolicyKey& other) = default;
930
operator <(const WildcardNelPolicyKey & other) const931 bool NetworkErrorLoggingService::WildcardNelPolicyKey::operator<(
932 const WildcardNelPolicyKey& other) const {
933 return std::tie(network_anonymization_key, domain) <
934 std::tie(other.network_anonymization_key, other.domain);
935 }
936
937 NetworkErrorLoggingService::WildcardNelPolicyKey::~WildcardNelPolicyKey() =
938 default;
939
940 NetworkErrorLoggingService::NelPolicy::NelPolicy() = default;
941
942 NetworkErrorLoggingService::NelPolicy::NelPolicy(const NelPolicy& other) =
943 default;
944
945 NetworkErrorLoggingService::NelPolicy::~NelPolicy() = default;
946
947 NetworkErrorLoggingService::RequestDetails::RequestDetails() = default;
948
949 NetworkErrorLoggingService::RequestDetails::RequestDetails(
950 const RequestDetails& other) = default;
951
952 NetworkErrorLoggingService::RequestDetails::~RequestDetails() = default;
953
954 NetworkErrorLoggingService::SignedExchangeReportDetails::
955 SignedExchangeReportDetails() = default;
956
957 NetworkErrorLoggingService::SignedExchangeReportDetails::
958 SignedExchangeReportDetails(const SignedExchangeReportDetails& other) =
959 default;
960
961 NetworkErrorLoggingService::SignedExchangeReportDetails::
962 ~SignedExchangeReportDetails() = default;
963
964 const char NetworkErrorLoggingService::kHeaderName[] = "NEL";
965
966 const char NetworkErrorLoggingService::kReportType[] = "network-error";
967
968 const char
969 NetworkErrorLoggingService::kSignedExchangeRequestOutcomeHistogram[] =
970 "Net.NetworkErrorLogging.SignedExchangeRequestOutcome";
971
972 // Allow NEL reports on regular requests, plus NEL reports on Reporting uploads
973 // containing only regular requests, but do not allow NEL reports on Reporting
974 // uploads containing Reporting uploads.
975 //
976 // This prevents origins from building purposefully-broken Reporting endpoints
977 // that generate new NEL reports to bypass the age limit on Reporting reports.
978 const int NetworkErrorLoggingService::kMaxNestedReportDepth = 1;
979
980 const char NetworkErrorLoggingService::kReferrerKey[] = "referrer";
981 const char NetworkErrorLoggingService::kSamplingFractionKey[] =
982 "sampling_fraction";
983 const char NetworkErrorLoggingService::kServerIpKey[] = "server_ip";
984 const char NetworkErrorLoggingService::kProtocolKey[] = "protocol";
985 const char NetworkErrorLoggingService::kMethodKey[] = "method";
986 const char NetworkErrorLoggingService::kStatusCodeKey[] = "status_code";
987 const char NetworkErrorLoggingService::kElapsedTimeKey[] = "elapsed_time";
988 const char NetworkErrorLoggingService::kPhaseKey[] = "phase";
989 const char NetworkErrorLoggingService::kTypeKey[] = "type";
990
991 const char NetworkErrorLoggingService::kSignedExchangePhaseValue[] = "sxg";
992 const char NetworkErrorLoggingService::kSignedExchangeBodyKey[] = "sxg";
993 const char NetworkErrorLoggingService::kOuterUrlKey[] = "outer_url";
994 const char NetworkErrorLoggingService::kInnerUrlKey[] = "inner_url";
995 const char NetworkErrorLoggingService::kCertUrlKey[] = "cert_url";
996
997 // See also: max number of Reporting endpoints specified in ReportingPolicy.
998 const size_t NetworkErrorLoggingService::kMaxPolicies = 1000u;
999
1000 // static
Create(PersistentNelStore * store)1001 std::unique_ptr<NetworkErrorLoggingService> NetworkErrorLoggingService::Create(
1002 PersistentNelStore* store) {
1003 return std::make_unique<NetworkErrorLoggingServiceImpl>(store);
1004 }
1005
1006 NetworkErrorLoggingService::~NetworkErrorLoggingService() = default;
1007
SetReportingService(ReportingService * reporting_service)1008 void NetworkErrorLoggingService::SetReportingService(
1009 ReportingService* reporting_service) {
1010 DCHECK(!reporting_service_);
1011 reporting_service_ = reporting_service;
1012 }
1013
OnShutdown()1014 void NetworkErrorLoggingService::OnShutdown() {
1015 shut_down_ = true;
1016 reporting_service_ = nullptr;
1017 }
1018
SetClockForTesting(const base::Clock * clock)1019 void NetworkErrorLoggingService::SetClockForTesting(const base::Clock* clock) {
1020 clock_ = clock;
1021 }
1022
StatusAsValue() const1023 base::Value NetworkErrorLoggingService::StatusAsValue() const {
1024 NOTIMPLEMENTED();
1025 return base::Value();
1026 }
1027
1028 std::set<NetworkErrorLoggingService::NelPolicyKey>
GetPolicyKeysForTesting()1029 NetworkErrorLoggingService::GetPolicyKeysForTesting() {
1030 NOTIMPLEMENTED();
1031 return std::set<NelPolicyKey>();
1032 }
1033
1034 NetworkErrorLoggingService::PersistentNelStore*
GetPersistentNelStoreForTesting()1035 NetworkErrorLoggingService::GetPersistentNelStoreForTesting() {
1036 NOTIMPLEMENTED();
1037 return nullptr;
1038 }
1039
GetReportingServiceForTesting()1040 ReportingService* NetworkErrorLoggingService::GetReportingServiceForTesting() {
1041 NOTIMPLEMENTED();
1042 return nullptr;
1043 }
1044
NetworkErrorLoggingService()1045 NetworkErrorLoggingService::NetworkErrorLoggingService()
1046 : clock_(base::DefaultClock::GetInstance()) {}
1047
1048 } // namespace net
1049