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

Side by Side Diff: chrome/browser/password_manager/password_manager_util.cc

Issue 2714543006: Clean Obsolete HTTP Data from the Password Store (Closed)
Patch Set: Add Unittest File Created 3 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 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
112 // Returns the PasswordStore associated with |profile_|.
113 PasswordStore* GetPasswordStore() const;
114
115 private:
116 Profile* profile_;
vabr (Chromium) 2017/03/24 07:02:41 The Profile and the content tools for threading ar
vasilii 2017/03/24 15:14:30 I think that what you've suggested is doable. Post
vabr (Chromium) 2017/03/27 05:53:59 I agree that the migrator code should not compile
117
118 DISALLOW_COPY_AND_ASSIGN(ObsoleteHttpCleaner);
119 };
120
121 ObsoleteHttpCleaner::ObsoleteHttpCleaner(Profile* profile) : profile_(profile) {
122 DCHECK(profile_);
123 GetPasswordStore()->GetBlacklistLogins(this);
124 GetPasswordStore()->GetAutofillableLogins(this);
125 GetPasswordStore()->GetAllSiteStats(this);
126 }
127
128 ObsoleteHttpCleaner::~ObsoleteHttpCleaner() = default;
129
130 void ObsoleteHttpCleaner::OnGetPasswordStoreResults(
131 std::vector<std::unique_ptr<PasswordForm>> results) {
132 // Non HTTP or HTTPS credentials are ignored.
133 base::EraseIf(results, [](const std::unique_ptr<PasswordForm>& form) {
134 return !form->origin.SchemeIsHTTPOrHTTPS();
135 });
136
137 // Move HTTPS forms into their own container.
138 auto https_forms = SplitFormsFrom(
139 std::partition(std::begin(results), std::end(results),
140 [](const std::unique_ptr<PasswordForm>& form) {
141 return form->origin.SchemeIs(url::kHttpScheme);
142 }),
143 &results);
144
145 // Move blacklisted HTTP forms into their own container.
146 const auto blacklisted_http_forms = SplitFormsFrom(
147 std::partition(std::begin(results), std::end(results),
148 [](const std::unique_ptr<PasswordForm>& form) {
149 return !form->blacklisted_by_user;
150 }),
151 &results);
152
153 // Remove blacklisted HTTP forms from the password store when HSTS is active
154 // for the given host.
155 for (const auto& form : blacklisted_http_forms) {
156 PostHSTSQueryForHostAndProfile(
157 form->origin, profile(),
158 base::Bind(&RemoveLoginIfHSTS, make_scoped_refptr(GetPasswordStore()),
159 *form));
160 }
161
162 // Return early if there are no non-blacklisted HTTP forms.
163 if (results.empty())
164 return;
165
166 // Sort HTTPS forms according to custom comparison function. Consider two
167 // forms equivalent if they have the same host, as well as the same username
168 // and password.
169 const auto form_cmp = [](const std::unique_ptr<PasswordForm>& lhs,
170 const std::unique_ptr<PasswordForm>& rhs) {
171 return std::forward_as_tuple(lhs->origin.host_piece(), lhs->username_value,
172 lhs->password_value) <
173 std::forward_as_tuple(rhs->origin.host_piece(), rhs->username_value,
174 rhs->password_value);
175 };
176
177 std::sort(std::begin(https_forms), std::end(https_forms), form_cmp);
178
179 // Iterate through HTTP forms and remove them from the password store if there
180 // exists an equivalent HTTPS form that has HSTS enabled.
181 for (const auto& form : results) {
182 if (std::binary_search(std::begin(https_forms), std::end(https_forms), form,
183 form_cmp)) {
184 PostHSTSQueryForHostAndProfile(
185 form->origin, profile(),
186 base::Bind(&RemoveLoginIfHSTS, make_scoped_refptr(GetPasswordStore()),
187 *form));
188 }
189 }
190 }
191
192 void ObsoleteHttpCleaner::OnGetSiteStatistics(
193 std::vector<InteractionsStats> stats) {
194 for (const auto& stat : stats) {
195 if (stat.origin_domain.SchemeIs(url::kHttpScheme)) {
196 PostHSTSQueryForHostAndProfile(
197 stat.origin_domain, profile(),
198 base::Bind(&RemoveSiteStatsIfHSTS,
199 make_scoped_refptr(GetPasswordStore()), stat));
200 }
201 }
202 }
203
204 PasswordStore* ObsoleteHttpCleaner::GetPasswordStore() const {
205 return PasswordStoreFactory::GetForProfile(profile_,
206 ServiceAccessType::EXPLICIT_ACCESS)
vasilii 2017/03/23 17:39:26 It's definitely implicit access here (not tied to
jdoerrie 2017/03/24 14:08:31 Done.
207 .get();
208 }
209
210 void WaitUntilCleaningIsDone(std::unique_ptr<ObsoleteHttpCleaner> cleaner) {
211 // Given the async nature of the cleaning tasks it is non-trivial to determine
212 // when they are all done. In this method we make use of the fact that as long
213 // the cleaning is not completed, weak pointers to the cleaner object exist
214 // (the scheduled, but not yet executed tasks hold them). If the cleaning is
215 // not done yet, this method schedules a task on the password store, which
216 // will be behind the cleaning tasks in the task queue. When the scheduled
217 // task gets executed, this method is called again. Now it is guaranteed that
218 // the initial scheduled cleaning tasks will have been executed, but it might
219 // be the case that they wait for the result of other scheduled tasks (e.g. on
220 // Windows there could be async calls to GetIE7Login). In this case another
221 // round trip of tasks will be scheduled. Finally, when no weak ptrs remain,
222 // this method sets a boolean preference flag and returns.
223 if (cleaner->HasWeakPtrs()) {
224 const auto post_to_ui_thread =
225 [](std::unique_ptr<ObsoleteHttpCleaner> cleaner) {
226 content::BrowserThread::PostTask(
227 content::BrowserThread::UI, FROM_HERE,
228 base::Bind(&WaitUntilCleaningIsDone,
229 base::Passed(std::move(cleaner))));
230 };
231
232 cleaner->GetPasswordStore()->ScheduleTask(
233 base::Bind(post_to_ui_thread, base::Passed(std::move(cleaner))));
234 return;
235 }
236
237 cleaner->profile()->GetPrefs()->SetBoolean(
238 password_manager::prefs::kWasObsoleteHttpDataCleaned, true);
239 }
240
241 void CleanObsoleteHttpDataForProfile(Profile* profile) {
242 if (!profile->GetPrefs()->GetBoolean(
243 password_manager::prefs::kWasObsoleteHttpDataCleaned)) {
244 WaitUntilCleaningIsDone(base::MakeUnique<ObsoleteHttpCleaner>(profile));
245 }
246 }
247
248 } // namespace
249
250 void PostHSTSQueryForHostAndProfile(const GURL& origin,
251 Profile* profile,
252 const HSTSCallback& callback) {
253 content::BrowserThread::PostTaskAndReplyWithResult(
254 content::BrowserThread::IO, FROM_HERE,
255 base::Bind(&IsHSTSActiveForHostAndRequestContext, origin,
256 make_scoped_refptr(profile->GetRequestContext())),
257 callback);
258 }
259
260 void DelayCleanObsoleteHttpDataForProfile(Profile* profile,
261 int delay_in_seconds) {
262 content::BrowserThread::PostDelayedTask(
263 content::BrowserThread::UI, FROM_HERE,
264 base::Bind(&CleanObsoleteHttpDataForProfile, profile),
265 base::TimeDelta::FromSeconds(delay_in_seconds));
266 }
267
268 } // namespace password_manager_util
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698