1 // Copyright 2017 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 #ifndef COMPONENTS_SAFE_BROWSING_CONTENT_PASSWORD_PROTECTION_PASSWORD_PROTECTION_SERVICE_H_
6 #define COMPONENTS_SAFE_BROWSING_CONTENT_PASSWORD_PROTECTION_PASSWORD_PROTECTION_SERVICE_H_
7 
8 #include <set>
9 #include <unordered_map>
10 
11 #include "base/callback.h"
12 #include "base/feature_list.h"
13 #include "base/gtest_prod_util.h"
14 #include "base/macros.h"
15 #include "base/memory/ref_counted.h"
16 #include "base/memory/weak_ptr.h"
17 #include "base/scoped_observer.h"
18 #include "base/task/cancelable_task_tracker.h"
19 #include "base/values.h"
20 #include "build/build_config.h"
21 #include "components/history/core/browser/history_service.h"
22 #include "components/history/core/browser/history_service_observer.h"
23 #include "components/password_manager/core/browser/password_manager_metrics_util.h"
24 #include "components/password_manager/core/browser/password_reuse_detector.h"
25 #include "components/safe_browsing/buildflags.h"
26 #include "components/safe_browsing/content/common/safe_browsing.mojom.h"
27 #include "components/safe_browsing/content/password_protection/metrics_util.h"
28 #include "components/safe_browsing/core/browser/referrer_chain_provider.h"
29 #include "components/safe_browsing/core/common/safe_browsing_prefs.h"
30 #include "components/safe_browsing/core/db/v4_protocol_manager_util.h"
31 #include "components/safe_browsing/core/proto/csd.pb.h"
32 #include "components/sessions/core/session_id.h"
33 #include "components/signin/public/identity_manager/account_info.h"
34 #include "mojo/public/cpp/bindings/remote.h"
35 #include "services/network/public/cpp/shared_url_loader_factory.h"
36 #include "services/service_manager/public/cpp/interface_provider.h"
37 #include "third_party/protobuf/src/google/protobuf/repeated_field.h"
38 
39 namespace content {
40 class WebContents;
41 class NavigationHandle;
42 }
43 
44 namespace policy {
45 class BrowserPolicyConnector;
46 }
47 
48 class GURL;
49 class HostContentSettingsMap;
50 
51 namespace safe_browsing {
52 
53 class PasswordProtectionNavigationThrottle;
54 class PasswordProtectionRequest;
55 class SafeBrowsingDatabaseManager;
56 
57 using ReusedPasswordAccountType =
58     LoginReputationClientRequest::PasswordReuseEvent::ReusedPasswordAccountType;
59 using ReusedPasswordType =
60     LoginReputationClientRequest::PasswordReuseEvent::ReusedPasswordType;
61 using password_manager::metrics_util::PasswordType;
62 
63 // Manage password protection pings and verdicts. There is one instance of this
64 // class per profile. Therefore, every PasswordProtectionService instance is
65 // associated with a unique HistoryService instance and a unique
66 // HostContentSettingsMap instance.
67 class PasswordProtectionService : public history::HistoryServiceObserver {
68  public:
69   PasswordProtectionService(
70       const scoped_refptr<SafeBrowsingDatabaseManager>& database_manager,
71       scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
72       history::HistoryService* history_service);
73 
74   ~PasswordProtectionService() override;
75 
GetWeakPtr()76   base::WeakPtr<PasswordProtectionService> GetWeakPtr() {
77     return weak_factory_.GetWeakPtr();
78   }
79 
80   // Looks up |settings| to find the cached verdict response. If verdict is not
81   // available or is expired, return VERDICT_TYPE_UNSPECIFIED. Can be called on
82   // any thread.
83   virtual LoginReputationClientResponse::VerdictType GetCachedVerdict(
84       const GURL& url,
85       LoginReputationClientRequest::TriggerType trigger_type,
86       ReusedPasswordAccountType password_type,
87       LoginReputationClientResponse* out_response);
88 
89   // Stores |verdict| in |settings| based on its |trigger_type|, |url|,
90   // reused |password_type|, |verdict| and |receive_time|.
91   virtual void CacheVerdict(
92       const GURL& url,
93       LoginReputationClientRequest::TriggerType trigger_type,
94       ReusedPasswordAccountType password_type,
95       const LoginReputationClientResponse& verdict,
96       const base::Time& receive_time);
97 
98   // Creates an instance of PasswordProtectionRequest and call Start() on that
99   // instance. This function also insert this request object in |requests_| for
100   // record keeping.
101   void StartRequest(
102       content::WebContents* web_contents,
103       const GURL& main_frame_url,
104       const GURL& password_form_action,
105       const GURL& password_form_frame_url,
106       const std::string& username,
107       PasswordType password_type,
108       const std::vector<password_manager::MatchingReusedCredential>&
109           matching_reused_credentials,
110       LoginReputationClientRequest::TriggerType trigger_type,
111       bool password_field_exists);
112 
113 #if defined(ON_FOCUS_PING_ENABLED)
114   virtual void MaybeStartPasswordFieldOnFocusRequest(
115       content::WebContents* web_contents,
116       const GURL& main_frame_url,
117       const GURL& password_form_action,
118       const GURL& password_form_frame_url,
119       const std::string& hosted_domain);
120 #endif
121 
122 #if defined(SYNC_PASSWORD_REUSE_DETECTION_ENABLED)
123   virtual void MaybeStartProtectedPasswordEntryRequest(
124       content::WebContents* web_contents,
125       const GURL& main_frame_url,
126       const std::string& username,
127       PasswordType password_type,
128       const std::vector<password_manager::MatchingReusedCredential>&
129           matching_reused_credentials,
130       bool password_field_exists);
131 #endif
132 
133 #if defined(SYNC_PASSWORD_REUSE_WARNING_ENABLED)
134   // Records a Chrome Sync event that sync password reuse was detected.
135   virtual void MaybeLogPasswordReuseDetectedEvent(
136       content::WebContents* web_contents) = 0;
137 
138   // If we want to show password reuse modal warning.
139   bool ShouldShowModalWarning(
140       LoginReputationClientRequest::TriggerType trigger_type,
141       ReusedPasswordAccountType password_type,
142       LoginReputationClientResponse::VerdictType verdict_type);
143 
144   // Shows modal warning dialog on the current |web_contents| and pass the
145   // |verdict_token| to callback of this dialog.
146   virtual void ShowModalWarning(
147       content::WebContents* web_contents,
148       RequestOutcome outcome,
149       LoginReputationClientResponse::VerdictType verdict_type,
150       const std::string& verdict_token,
151       ReusedPasswordAccountType password_type) = 0;
152 
153   // Shows chrome://reset-password interstitial.
154   virtual void ShowInterstitial(content::WebContents* web_contens,
155                                 ReusedPasswordAccountType password_type) = 0;
156 #endif
157 
158 // The following functions are disabled on Android, because enterprise reporting
159 // extension is not supported.
160 #if !defined(OS_ANDROID)
161   // Triggers the safeBrowsingPrivate.OnPolicySpecifiedPasswordReuseDetected.
162   virtual void MaybeReportPasswordReuseDetected(
163       content::WebContents* web_contents,
164       const std::string& username,
165       PasswordType password_type,
166       bool is_phishing_url) = 0;
167 
168   // Called when a protected password change is detected. Must be called on
169   // UI thread.
170   virtual void ReportPasswordChanged() = 0;
171 #endif
172 
173 #if defined(SYNC_PASSWORD_REUSE_WARNING_ENABLED)
174   virtual void UpdateSecurityState(safe_browsing::SBThreatType threat_type,
175                                    ReusedPasswordAccountType password_type,
176                                    content::WebContents* web_contents) = 0;
177 #endif
178 
179   scoped_refptr<SafeBrowsingDatabaseManager> database_manager();
180 
181   // Safe Browsing backend cannot get a reliable reputation of a URL if
182   // (1) URL is not valid
183   // (2) URL doesn't have http or https scheme
184   // (3) It maps to a local host.
185   // (4) Its hostname is an IP Address in an IANA-reserved range.
186   // (5) Its hostname is a not-yet-assigned by ICANN gTLD.
187   // (6) Its hostname is a dotless domain.
188   static bool CanGetReputationOfURL(const GURL& url);
189 
190   // If user has clicked through any Safe Browsing interstitial on this given
191   // |web_contents|.
192   virtual bool UserClickedThroughSBInterstitial(
193       content::WebContents* web_contents) = 0;
194 
195   // Called when a new navigation is starting. Create throttle if there is a
196   // pending sync password reuse ping or if there is a modal warning dialog
197   // showing in the corresponding web contents.
198   std::unique_ptr<PasswordProtectionNavigationThrottle>
199   MaybeCreateNavigationThrottle(content::NavigationHandle* navigation_handle);
200 
201   // Returns if the warning UI is enabled.
202   bool IsWarningEnabled(ReusedPasswordAccountType password_type);
203 
204   // Returns the pref value of password protection warning trigger.
205   virtual PasswordProtectionTrigger GetPasswordProtectionWarningTriggerPref(
206       ReusedPasswordAccountType password_type) const = 0;
207 
208   // If |url| matches Safe Browsing whitelist domains, password protection
209   // change password URL, or password protection login URLs in the enterprise
210   // policy.
211   virtual bool IsURLWhitelistedForPasswordEntry(
212       const GURL& url,
213       RequestOutcome* reason) const = 0;
214 
215   // Persist the phished saved password credential in the "compromised
216   // credentials" table. Calls the password store to add a row for each
217   // MatchingReusedCredential where the phished saved password is used on.
218   virtual void PersistPhishedSavedPasswordCredential(
219       const std::vector<password_manager::MatchingReusedCredential>&
220           matching_reused_credentials) = 0;
221 
222   // Converts from password::metrics_util::PasswordType to
223   // LoginReputationClientRequest::PasswordReuseEvent::ReusedPasswordType.
224   static ReusedPasswordType GetPasswordProtectionReusedPasswordType(
225       PasswordType password_type);
226 
227   // Converts from password_manager::metrics_util::PasswordType
228   // to PasswordReuseEvent::ReusedPasswordAccountType. |username| is only
229   // used if |password_type| is OTHER_GAIA_PASSWORD because it needs to be
230   // compared to the list of signed in accounts.
231   ReusedPasswordAccountType GetPasswordProtectionReusedPasswordAccountType(
232       PasswordType password_type,
233       const std::string& username) const;
234 
235   // Converts from ReusedPasswordAccountType to
236   // password_manager::metrics_util::PasswordType.
237   static PasswordType ConvertReusedPasswordAccountTypeToPasswordType(
238       ReusedPasswordAccountType password_type);
239 
240   // If we can send ping for this type of reused password.
241   bool IsSupportedPasswordTypeForPinging(PasswordType password_type) const;
242 
243   // If we can show modal warning for this type of reused password.
244   bool IsSupportedPasswordTypeForModalWarning(
245       ReusedPasswordAccountType password_type) const;
246 
247   const ReusedPasswordAccountType&
reused_password_account_type_for_last_shown_warning()248   reused_password_account_type_for_last_shown_warning() const {
249     return reused_password_account_type_for_last_shown_warning_;
250   }
251 #if defined(UNIT_TEST)
set_reused_password_account_type_for_last_shown_warning(ReusedPasswordAccountType reused_password_account_type_for_last_shown_warning)252   void set_reused_password_account_type_for_last_shown_warning(
253       ReusedPasswordAccountType
254           reused_password_account_type_for_last_shown_warning) {
255     reused_password_account_type_for_last_shown_warning_ =
256         reused_password_account_type_for_last_shown_warning;
257   }
258 #endif
259 
username_for_last_shown_warning()260   const std::string& username_for_last_shown_warning() const {
261     return username_for_last_shown_warning_;
262   }
263 #if defined(UNIT_TEST)
set_username_for_last_shown_warning(const std::string & username)264   void set_username_for_last_shown_warning(const std::string& username) {
265     username_for_last_shown_warning_ = username;
266   }
267 #endif
268 
saved_passwords_matching_domains()269   const std::vector<std::string>& saved_passwords_matching_domains() const {
270     return saved_passwords_matching_domains_;
271   }
272 #if defined(UNIT_TEST)
set_saved_passwords_matching_domains(const std::vector<std::string> & matching_domains)273   void set_saved_passwords_matching_domains(
274       const std::vector<std::string>& matching_domains) {
275     saved_passwords_matching_domains_ = matching_domains;
276   }
277 #endif
278 
279   virtual AccountInfo GetAccountInfo() const = 0;
280 
281  protected:
282   friend class PasswordProtectionRequest;
283 
284   // Chrome can send password protection ping if it is allowed by for the
285   // |trigger_type| and if Safe Browsing can compute reputation of
286   // |main_frame_url| (e.g. Safe Browsing is not able to compute reputation of a
287   // private IP or a local host). Update |reason| if sending ping is not
288   // allowed. |password_type| is used for UMA metric recording.
289   bool CanSendPing(LoginReputationClientRequest::TriggerType trigger_type,
290                    const GURL& main_frame_url,
291                    ReusedPasswordAccountType password_type,
292                    RequestOutcome* reason);
293 
294   // Called by a PasswordProtectionRequest instance when it finishes to remove
295   // itself from |requests_|.
296   virtual void RequestFinished(
297       PasswordProtectionRequest* request,
298       RequestOutcome outcome,
299       std::unique_ptr<LoginReputationClientResponse> response);
300 
301   // Called by a PasswordProtectionRequest instance to check if a sample ping
302   // can be sent to Safe Browsing.
303   virtual bool CanSendSamplePing() = 0;
304 
305   // Sanitize referrer chain by only keeping origin information of all URLs.
306   virtual void SanitizeReferrerChain(ReferrerChain* referrer_chain) = 0;
307 
308   // Cancels all requests in |requests_|, empties it, and releases references to
309   // the requests.
310   void CancelPendingRequests();
311 
312   // Gets the total number of verdicts of the specified |trigger_type| we cached
313   // for this profile. This counts both expired and active verdicts.
314   virtual int GetStoredVerdictCount(
315       LoginReputationClientRequest::TriggerType trigger_type);
316 
317   // Gets an unowned |BrowserPolicyConnector| for the current platform.
318   virtual const policy::BrowserPolicyConnector* GetBrowserPolicyConnector()
319       const = 0;
320 
url_loader_factory()321   scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory() {
322     return url_loader_factory_;
323   }
324 
325   // Returns the URL where PasswordProtectionRequest instances send requests.
326   static GURL GetPasswordProtectionRequestUrl();
327 
328   // Gets the request timeout in milliseconds.
329   static int GetRequestTimeoutInMS();
330 
331   // Obtains referrer chain of |event_url| and |event_tab_id| and adds this
332   // info into |frame|.
333   virtual void FillReferrerChain(
334       const GURL& event_url,
335       SessionID
336           event_tab_id,  // SessionID::InvalidValue() if tab not available.
337       LoginReputationClientRequest::Frame* frame) = 0;
338 
339   void FillUserPopulation(
340       LoginReputationClientRequest::TriggerType trigger_type,
341       LoginReputationClientRequest* request_proto);
342 
343   virtual bool IsExtendedReporting() = 0;
344 
345   virtual bool IsEnhancedProtection() = 0;
346 
347   virtual bool IsIncognito() = 0;
348 
349   virtual bool IsPingingEnabled(
350       LoginReputationClientRequest::TriggerType trigger_type,
351       ReusedPasswordAccountType password_type,
352       RequestOutcome* reason) = 0;
353 
354   virtual bool IsHistorySyncEnabled() = 0;
355 
356   // If primary account is syncing.
357   virtual bool IsPrimaryAccountSyncing() const = 0;
358 
359   // If primary account is signed in.
360   virtual bool IsPrimaryAccountSignedIn() const = 0;
361 
362   // If a domain is not defined for the primary account. This means the primary
363   // account is a Gmail account.
364   virtual bool IsPrimaryAccountGmail() const = 0;
365 
366   // If the domain for the non sync account is equal to |kNoHostedDomainFound|,
367   // this means that the account is a Gmail account.
368   virtual bool IsOtherGaiaAccountGmail(const std::string& username) const = 0;
369 
370   // Gets the account based off of the username from a list of signed in
371   // accounts.
372   virtual AccountInfo GetSignedInNonSyncAccount(
373       const std::string& username) const = 0;
374 
375 #if BUILDFLAG(FULL_SAFE_BROWSING)
376   virtual bool IsUnderAdvancedProtection() = 0;
377 #endif
378 
379 #if defined(SYNC_PASSWORD_REUSE_WARNING_ENABLED)
380   // Records a Chrome Sync event for the result of the URL reputation lookup
381   // if the user enters their sync password on a website.
382   virtual void MaybeLogPasswordReuseLookupEvent(
383       content::WebContents* web_contents,
384       RequestOutcome,
385       PasswordType password_type,
386       const LoginReputationClientResponse*) = 0;
387 
388   void RemoveWarningRequestsByWebContents(content::WebContents* web_contents);
389 
390   bool IsModalWarningShowingInWebContents(content::WebContents* web_contents);
391 
392   // Determines if we should show chrome://reset-password interstitial based on
393   // previous request outcome, the reused |password_type| and the
394   // |main_frame_url|.
395   virtual bool CanShowInterstitial(RequestOutcome reason,
396                                    ReusedPasswordAccountType password_type,
397                                    const GURL& main_frame_url) = 0;
398 #endif
399 
400   void CheckCsdWhitelistOnIOThread(const GURL& url, bool* check_result);
401 
402   // Gets the type of sync account associated with current profile or
403   // |NOT_SIGNED_IN|.
404   virtual LoginReputationClientRequest::PasswordReuseEvent::SyncAccountType
405   GetSyncAccountType() const = 0;
406 
common_spoofed_domains()407   const std::list<std::string>& common_spoofed_domains() const {
408     return common_spoofed_domains_;
409   }
410 
411  private:
412   friend class PasswordProtectionServiceTest;
413   friend class TestPasswordProtectionService;
414   friend class ChromePasswordProtectionServiceTest;
415   friend class ChromePasswordProtectionServiceBrowserTest;
416   FRIEND_TEST_ALL_PREFIXES(PasswordProtectionServiceTest,
417                            TestParseInvalidVerdictEntry);
418   FRIEND_TEST_ALL_PREFIXES(PasswordProtectionServiceTest,
419                            TestParseValidVerdictEntry);
420   FRIEND_TEST_ALL_PREFIXES(PasswordProtectionServiceTest,
421                            TestPathVariantsMatchCacheExpression);
422   FRIEND_TEST_ALL_PREFIXES(PasswordProtectionServiceTest,
423                            TestRemoveCachedVerdictOnURLsDeleted);
424   FRIEND_TEST_ALL_PREFIXES(PasswordProtectionServiceTest,
425                            TestCleanUpExpiredVerdict);
426 
427   // Overridden from history::HistoryServiceObserver.
428   void OnURLsDeleted(history::HistoryService* history_service,
429                      const history::DeletionInfo& deletion_info) override;
430 
431   void HistoryServiceBeingDeleted(
432       history::HistoryService* history_service) override;
433 
434   // Posted to UI thread by OnURLsDeleted(...). This function remove the related
435   // entries in kSafeBrowsingUnhandledSyncPasswordReuses.
436   virtual void RemoveUnhandledSyncPasswordReuseOnURLsDeleted(
437       bool all_history,
438       const history::URLRows& deleted_rows) = 0;
439 
440   static bool PathVariantsMatchCacheExpression(
441       const std::vector<std::string>& generated_paths,
442       const std::string& cache_expression_path);
443 
444   void RecordNoPingingReason(
445       LoginReputationClientRequest::TriggerType trigger_type,
446       RequestOutcome reason,
447       PasswordType password_type);
448 
449 #if BUILDFLAG(FULL_SAFE_BROWSING)
450   // Get the content area size of current browsing window.
451   virtual gfx::Size GetCurrentContentAreaSize() const = 0;
452 
453   // Binds the |phishing_detector| to the appropriate interface, as provided by
454   // |provider|.
455   virtual void GetPhishingDetector(
456       service_manager::InterfaceProvider* provider,
457       mojo::Remote<mojom::PhishingDetector>* phishing_detector);
458 #endif
459 
460   // The username of the account which password has been reused on. It is only
461   // set once a modal warning or interstitial is verified to be shown.
462   std::string username_for_last_shown_warning_ = "";
463 
464   // The last ReusedPasswordAccountType that was shown a warning or
465   // interstitial.
466   ReusedPasswordAccountType
467       reused_password_account_type_for_last_shown_warning_;
468 
469   std::vector<std::string> saved_passwords_matching_domains_;
470 
471   scoped_refptr<SafeBrowsingDatabaseManager> database_manager_;
472 
473   // The context we use to issue network requests. This request_context_getter
474   // is obtained from SafeBrowsingService so that we can use the Safe Browsing
475   // cookie store.
476   scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
477 
478   // Set of pending PasswordProtectionRequests that are still waiting for
479   // verdict.
480   std::set<scoped_refptr<PasswordProtectionRequest>> pending_requests_;
481 
482   // Set of PasswordProtectionRequests that are triggering modal warnings.
483   std::set<scoped_refptr<PasswordProtectionRequest>> warning_requests_;
484 
485   // List of most commonly spoofed domains to default to on the password warning
486   // dialog.
487   std::list<std::string> common_spoofed_domains_;
488 
489   ScopedObserver<history::HistoryService, history::HistoryServiceObserver>
490       history_service_observer_{this};
491 
492   // Weakptr can only cancel task if it is posted to the same thread. Therefore,
493   // we need CancelableTaskTracker to cancel tasks posted to IO thread.
494   base::CancelableTaskTracker tracker_;
495 
496   base::WeakPtrFactory<PasswordProtectionService> weak_factory_{this};
497   DISALLOW_COPY_AND_ASSIGN(PasswordProtectionService);
498 };
499 
500 }  // namespace safe_browsing
501 
502 #endif  // COMPONENTS_SAFE_BROWSING_CONTENT_PASSWORD_PROTECTION_PASSWORD_PROTECTION_SERVICE_H_
503