xref: /aosp_15_r20/external/cronet/net/network_error_logging/network_error_logging_service.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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