OLD | NEW |
(Empty) | |
| 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 #include "chrome/browser/password_manager/password_manager_util.h" |
| 6 |
| 7 #include <algorithm> |
| 8 #include <iterator> |
| 9 #include <memory> |
| 10 #include <tuple> |
| 11 #include <vector> |
| 12 |
| 13 #include "base/bind.h" |
| 14 #include "base/bind_helpers.h" |
| 15 #include "base/logging.h" |
| 16 #include "base/macros.h" |
| 17 #include "base/stl_util.h" |
| 18 #include "chrome/browser/password_manager/password_store_factory.h" |
| 19 #include "chrome/browser/profiles/profile.h" |
| 20 #include "components/autofill/core/common/password_form.h" |
| 21 #include "components/keyed_service/core/service_access_type.h" |
| 22 #include "components/password_manager/core/browser/password_store.h" |
| 23 #include "components/password_manager/core/browser/password_store_consumer.h" |
| 24 #include "components/password_manager/core/browser/statistics_table.h" |
| 25 #include "components/password_manager/core/common/password_manager_pref_names.h" |
| 26 #include "components/prefs/pref_service.h" |
| 27 #include "content/public/browser/browser_thread.h" |
| 28 #include "net/http/transport_security_state.h" |
| 29 #include "net/url_request/url_request_context.h" |
| 30 #include "net/url_request/url_request_context_getter.h" |
| 31 #include "url/gurl.h" |
| 32 |
| 33 using autofill::PasswordForm; |
| 34 using password_manager::InteractionsStats; |
| 35 using password_manager::PasswordStore; |
| 36 |
| 37 namespace password_manager_util { |
| 38 |
| 39 namespace { |
| 40 |
| 41 // Utility function that checks whether HSTS is enabled for a given host. |
| 42 // This requires to be passed a URLRequestContextGetter and needs to be called |
| 43 // on the IO thread. |
| 44 bool IsHSTSActiveForHostAndRequestContext( |
| 45 const GURL& origin, |
| 46 const scoped_refptr<net::URLRequestContextGetter>& request_context) { |
| 47 DCHECK_CURRENTLY_ON(content::BrowserThread::IO); |
| 48 if (!origin.is_valid()) |
| 49 return false; |
| 50 |
| 51 net::TransportSecurityState* security_state = |
| 52 request_context->GetURLRequestContext()->transport_security_state(); |
| 53 |
| 54 if (!security_state) |
| 55 return false; |
| 56 |
| 57 return security_state->ShouldUpgradeToSSL(origin.host()); |
| 58 } |
| 59 |
| 60 // Utility function that moves all elements from a specified iterator into a new |
| 61 // vector and returns it. The moved elements are deleted from the original |
| 62 // vector. |
| 63 std::vector<std::unique_ptr<PasswordForm>> SplitFormsFrom( |
| 64 std::vector<std::unique_ptr<PasswordForm>>::iterator from, |
| 65 std::vector<std::unique_ptr<PasswordForm>>* forms) { |
| 66 const auto end_forms = std::end(*forms); |
| 67 std::vector<std::unique_ptr<PasswordForm>> result; |
| 68 result.reserve(std::distance(from, end_forms)); |
| 69 std::move(from, end_forms, std::back_inserter(result)); |
| 70 forms->erase(from, end_forms); |
| 71 return result; |
| 72 } |
| 73 |
| 74 void RemoveLoginIfHSTS(const scoped_refptr<PasswordStore>& store, |
| 75 const PasswordForm& form, |
| 76 bool is_hsts) { |
| 77 if (is_hsts) |
| 78 store->RemoveLogin(form); |
| 79 } |
| 80 |
| 81 void RemoveSiteStatsIfHSTS(const scoped_refptr<PasswordStore>& store, |
| 82 const InteractionsStats& stats, |
| 83 bool is_hsts) { |
| 84 if (is_hsts) |
| 85 store->RemoveSiteStats(stats.origin_domain); |
| 86 } |
| 87 |
| 88 // This class removes obsolete HTTP data from a password store. HTTP data is |
| 89 // obsolete, if the corresponding host migrated to HTTPS and has HSTS enabled. |
| 90 class ObsoleteHttpCleaner : public password_manager::PasswordStoreConsumer { |
| 91 public: |
| 92 // Constructing a ObsoleteHttpCleaner will result in issuing the clean up |
| 93 // tasks already. |
| 94 explicit ObsoleteHttpCleaner(Profile* profile); |
| 95 ~ObsoleteHttpCleaner() override; |
| 96 |
| 97 // PasswordStoreConsumer: |
| 98 // This will be called for both autofillable logins as well as blacklisted |
| 99 // logins. Blacklisted logins are removed iff the scheme is HTTP and HSTS is |
| 100 // enabled for the host. |
| 101 // Autofillable logins are removed iff the scheme is HTTP and there exists |
| 102 // another HTTPS login with active HSTS that has the same host as well as the |
| 103 // same username and password. |
| 104 void OnGetPasswordStoreResults( |
| 105 std::vector<std::unique_ptr<PasswordForm>> results) override; |
| 106 |
| 107 // This will remove all stats for HTTP sites for which HSTS is active. |
| 108 void OnGetSiteStatistics(std::vector<InteractionsStats> stats) override; |
| 109 |
| 110 Profile* profile() { return profile_; } |
| 111 bool finished_cleaning() const { return remaining_cleaning_tasks_ == 0; } |
| 112 |
| 113 // Returns the PasswordStore associated with |profile_|. |
| 114 PasswordStore* GetPasswordStore() const; |
| 115 |
| 116 private: |
| 117 Profile* profile_; |
| 118 // There are 3 cleaning tasks initiated in the constructor. |
| 119 int remaining_cleaning_tasks_ = 3; |
| 120 |
| 121 DISALLOW_COPY_AND_ASSIGN(ObsoleteHttpCleaner); |
| 122 }; |
| 123 |
| 124 ObsoleteHttpCleaner::ObsoleteHttpCleaner(Profile* profile) : profile_(profile) { |
| 125 DCHECK(profile_); |
| 126 GetPasswordStore()->GetBlacklistLogins(this); |
| 127 GetPasswordStore()->GetAutofillableLogins(this); |
| 128 GetPasswordStore()->GetAllSiteStats(this); |
| 129 } |
| 130 |
| 131 ObsoleteHttpCleaner::~ObsoleteHttpCleaner() = default; |
| 132 |
| 133 void ObsoleteHttpCleaner::OnGetPasswordStoreResults( |
| 134 std::vector<std::unique_ptr<PasswordForm>> results) { |
| 135 --remaining_cleaning_tasks_; |
| 136 // Non HTTP or HTTPS credentials are ignored. |
| 137 base::EraseIf(results, [](const std::unique_ptr<PasswordForm>& form) { |
| 138 return !form->origin.SchemeIsHTTPOrHTTPS(); |
| 139 }); |
| 140 |
| 141 // Move HTTPS forms into their own container. |
| 142 auto https_forms = SplitFormsFrom( |
| 143 std::partition(std::begin(results), std::end(results), |
| 144 [](const std::unique_ptr<PasswordForm>& form) { |
| 145 return form->origin.SchemeIs(url::kHttpScheme); |
| 146 }), |
| 147 &results); |
| 148 |
| 149 // Move blacklisted HTTP forms into their own container. |
| 150 const auto blacklisted_http_forms = SplitFormsFrom( |
| 151 std::partition(std::begin(results), std::end(results), |
| 152 [](const std::unique_ptr<PasswordForm>& form) { |
| 153 return !form->blacklisted_by_user; |
| 154 }), |
| 155 &results); |
| 156 |
| 157 // Remove blacklisted HTTP forms from the password store when HSTS is active |
| 158 // for the given host. |
| 159 for (const auto& form : blacklisted_http_forms) { |
| 160 PostHSTSQueryForHostAndProfile( |
| 161 form->origin, profile(), |
| 162 base::Bind(&RemoveLoginIfHSTS, make_scoped_refptr(GetPasswordStore()), |
| 163 *form)); |
| 164 } |
| 165 |
| 166 // Return early if there are no non-blacklisted HTTP forms. |
| 167 if (results.empty()) |
| 168 return; |
| 169 |
| 170 // Sort HTTPS forms according to custom comparison function. Consider two |
| 171 // forms equivalent if they have the same host, as well as the same username |
| 172 // and password. |
| 173 const auto form_cmp = [](const std::unique_ptr<PasswordForm>& lhs, |
| 174 const std::unique_ptr<PasswordForm>& rhs) { |
| 175 return std::forward_as_tuple(lhs->origin.host_piece(), lhs->username_value, |
| 176 lhs->password_value) < |
| 177 std::forward_as_tuple(rhs->origin.host_piece(), rhs->username_value, |
| 178 rhs->password_value); |
| 179 }; |
| 180 |
| 181 std::sort(std::begin(https_forms), std::end(https_forms), form_cmp); |
| 182 |
| 183 // Iterate through HTTP forms and remove them from the password store if there |
| 184 // exists an equivalent HTTPS form that has HSTS enabled. |
| 185 for (const auto& form : results) { |
| 186 if (std::binary_search(std::begin(https_forms), std::end(https_forms), form, |
| 187 form_cmp)) { |
| 188 PostHSTSQueryForHostAndProfile( |
| 189 form->origin, profile(), |
| 190 base::Bind(&RemoveLoginIfHSTS, make_scoped_refptr(GetPasswordStore()), |
| 191 *form)); |
| 192 } |
| 193 } |
| 194 } |
| 195 |
| 196 void ObsoleteHttpCleaner::OnGetSiteStatistics( |
| 197 std::vector<InteractionsStats> stats) { |
| 198 --remaining_cleaning_tasks_; |
| 199 for (const auto& stat : stats) { |
| 200 if (stat.origin_domain.SchemeIs(url::kHttpScheme)) { |
| 201 PostHSTSQueryForHostAndProfile( |
| 202 stat.origin_domain, profile(), |
| 203 base::Bind(&RemoveSiteStatsIfHSTS, |
| 204 make_scoped_refptr(GetPasswordStore()), stat)); |
| 205 } |
| 206 } |
| 207 } |
| 208 |
| 209 PasswordStore* ObsoleteHttpCleaner::GetPasswordStore() const { |
| 210 return PasswordStoreFactory::GetForProfile(profile_, |
| 211 ServiceAccessType::IMPLICIT_ACCESS) |
| 212 .get(); |
| 213 } |
| 214 |
| 215 void WaitUntilCleaningIsDone(std::unique_ptr<ObsoleteHttpCleaner> cleaner) { |
| 216 // Given the async nature of the cleaning tasks it is non-trivial to determine |
| 217 // when they are all done. In this method we make use of the fact that as long |
| 218 // the cleaning is not completed, weak pointers to the cleaner object exist |
| 219 // (the scheduled, but not yet executed tasks hold them). If the cleaning is |
| 220 // not done yet, this method schedules a task on the password store, which |
| 221 // will be behind the cleaning tasks in the task queue. When the scheduled |
| 222 // task gets executed, this method is called again. Now it is guaranteed that |
| 223 // the initial scheduled cleaning tasks will have been executed, but it might |
| 224 // be the case that they wait for the result of other scheduled tasks (e.g. on |
| 225 // Windows there could be async calls to GetIE7Login). In this case another |
| 226 // round trip of tasks will be scheduled. Finally, when no weak ptrs remain, |
| 227 // this method sets a boolean preference flag and returns. |
| 228 if (cleaner->HasWeakPtrs()) { |
| 229 const auto post_to_ui_thread = |
| 230 [](std::unique_ptr<ObsoleteHttpCleaner> cleaner) { |
| 231 content::BrowserThread::PostTask( |
| 232 content::BrowserThread::UI, FROM_HERE, |
| 233 base::Bind(&WaitUntilCleaningIsDone, |
| 234 base::Passed(std::move(cleaner)))); |
| 235 }; |
| 236 |
| 237 cleaner->GetPasswordStore()->ScheduleTask( |
| 238 base::Bind(post_to_ui_thread, base::Passed(std::move(cleaner)))); |
| 239 return; |
| 240 } |
| 241 |
| 242 DCHECK(cleaner->finished_cleaning()); |
| 243 cleaner->profile()->GetPrefs()->SetBoolean( |
| 244 password_manager::prefs::kWasObsoleteHttpDataCleaned, true); |
| 245 } |
| 246 |
| 247 void CleanObsoleteHttpDataForProfile(Profile* profile) { |
| 248 if (!profile->GetPrefs()->GetBoolean( |
| 249 password_manager::prefs::kWasObsoleteHttpDataCleaned)) { |
| 250 WaitUntilCleaningIsDone(base::MakeUnique<ObsoleteHttpCleaner>(profile)); |
| 251 } |
| 252 } |
| 253 |
| 254 } // namespace |
| 255 |
| 256 void PostHSTSQueryForHostAndProfile(const GURL& origin, |
| 257 Profile* profile, |
| 258 const HSTSCallback& callback) { |
| 259 content::BrowserThread::PostTaskAndReplyWithResult( |
| 260 content::BrowserThread::IO, FROM_HERE, |
| 261 base::Bind(&IsHSTSActiveForHostAndRequestContext, origin, |
| 262 make_scoped_refptr(profile->GetRequestContext())), |
| 263 callback); |
| 264 } |
| 265 |
| 266 void DelayCleanObsoleteHttpDataForProfile(Profile* profile, |
| 267 int delay_in_seconds) { |
| 268 content::BrowserThread::PostDelayedTask( |
| 269 content::BrowserThread::UI, FROM_HERE, |
| 270 base::Bind(&CleanObsoleteHttpDataForProfile, profile), |
| 271 base::TimeDelta::FromSeconds(delay_in_seconds)); |
| 272 } |
| 273 |
| 274 } // namespace password_manager_util |
OLD | NEW |