OLD | NEW |
---|---|
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/password_manager/native_backend_kwallet_x.h" | 5 #include "chrome/browser/password_manager/native_backend_kwallet_x.h" |
6 | 6 |
7 #include <vector> | 7 #include <vector> |
8 | 8 |
9 #include "base/bind.h" | 9 #include "base/bind.h" |
10 #include "base/logging.h" | 10 #include "base/logging.h" |
11 #include "base/pickle.h" | 11 #include "base/pickle.h" |
12 #include "base/stl_util.h" | 12 #include "base/stl_util.h" |
13 #include "base/strings/stringprintf.h" | 13 #include "base/strings/stringprintf.h" |
14 #include "base/synchronization/waitable_event.h" | 14 #include "base/synchronization/waitable_event.h" |
15 #include "base/threading/thread_restrictions.h" | 15 #include "base/threading/thread_restrictions.h" |
16 #include "chrome/grit/chromium_strings.h" | 16 #include "chrome/grit/chromium_strings.h" |
17 #include "components/autofill/core/common/password_form.h" | 17 #include "components/autofill/core/common/password_form.h" |
18 #include "content/public/browser/browser_thread.h" | 18 #include "content/public/browser/browser_thread.h" |
19 #include "dbus/bus.h" | 19 #include "dbus/bus.h" |
20 #include "dbus/message.h" | 20 #include "dbus/message.h" |
21 #include "dbus/object_path.h" | 21 #include "dbus/object_path.h" |
22 #include "dbus/object_proxy.h" | 22 #include "dbus/object_proxy.h" |
23 #include "ui/base/l10n/l10n_util.h" | 23 #include "ui/base/l10n/l10n_util.h" |
24 | 24 |
25 using autofill::PasswordForm; | 25 using autofill::PasswordForm; |
26 using content::BrowserThread; | 26 using content::BrowserThread; |
27 | 27 |
28 namespace { | 28 namespace { |
29 | 29 |
30 // In case the fields in the pickle ever change, version them so we can try to | |
31 // read old pickles. (Note: do not eat old pickles past the expiration date.) | |
32 const int kPickleVersion = 6; | |
33 | |
30 // We could localize this string, but then changing your locale would cause | 34 // We could localize this string, but then changing your locale would cause |
31 // you to lose access to all your stored passwords. Maybe best not to do that. | 35 // you to lose access to all your stored passwords. Maybe best not to do that. |
32 // Name of the folder to store passwords in. | 36 // Name of the folder to store passwords in. |
33 const char kKWalletFolder[] = "Chrome Form Data"; | 37 const char kKWalletFolder[] = "Chrome Form Data"; |
34 | 38 |
35 // DBus service, path, and interface names for klauncher and kwalletd. | 39 // DBus service, path, and interface names for klauncher and kwalletd. |
36 const char kKWalletServiceName[] = "org.kde.kwalletd"; | 40 const char kKWalletServiceName[] = "org.kde.kwalletd"; |
37 const char kKWalletPath[] = "/modules/kwalletd"; | 41 const char kKWalletPath[] = "/modules/kwalletd"; |
38 const char kKWalletInterface[] = "org.kde.KWallet"; | 42 const char kKWalletInterface[] = "org.kde.KWallet"; |
39 const char kKLauncherServiceName[] = "org.kde.klauncher"; | 43 const char kKLauncherServiceName[] = "org.kde.klauncher"; |
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
93 LOG(WARNING) << "Failed to deserialize version " << version | 97 LOG(WARNING) << "Failed to deserialize version " << version |
94 << " KWallet entry (realm: " << signon_realm | 98 << " KWallet entry (realm: " << signon_realm |
95 << ") with native architecture size; will try alternate " | 99 << ") with native architecture size; will try alternate " |
96 << "size."; | 100 << "size."; |
97 } else { | 101 } else { |
98 LOG(ERROR) << "Failed to deserialize version " << version | 102 LOG(ERROR) << "Failed to deserialize version " << version |
99 << " KWallet entry (realm: " << signon_realm << ")"; | 103 << " KWallet entry (realm: " << signon_realm << ")"; |
100 } | 104 } |
101 } | 105 } |
102 | 106 |
107 // Deserializes a list of credentials from the wallet to |forms| (replacing | |
108 // the contents of |forms|). |size_32| controls reading the size field within | |
109 // the pickle as 32 bits. We used to use Pickle::WriteSize() to write the number | |
110 // of password forms, but that has a different size on 32- and 64-bit systems. | |
111 // So, now we always write a 64-bit quantity, but we support trying to read it | |
112 // as either size when reading old pickles that fail to deserialize using the | |
113 // native size. Returns true on success. | |
114 bool DeserializeValueSize(const std::string& signon_realm, | |
115 const PickleIterator& init_iter, | |
116 int version, | |
117 bool size_32, | |
118 bool warn_only, | |
119 ScopedVector<autofill::PasswordForm>* forms) { | |
120 forms->clear(); | |
121 PickleIterator iter = init_iter; | |
122 | |
123 size_t count = 0; | |
124 if (size_32) { | |
125 uint32_t count_32 = 0; | |
126 if (!iter.ReadUInt32(&count_32)) { | |
127 LOG(ERROR) << "Failed to deserialize KWallet entry " | |
128 << "(realm: " << signon_realm << ")"; | |
129 return false; | |
130 } | |
131 count = count_32; | |
132 } else { | |
133 if (!iter.ReadSizeT(&count)) { | |
134 LOG(ERROR) << "Failed to deserialize KWallet entry " | |
135 << "(realm: " << signon_realm << ")"; | |
136 return false; | |
137 } | |
138 } | |
139 | |
140 if (count > 0xFFFF) { | |
141 // Trying to pin down the cause of http://crbug.com/80728 (or fix it). | |
142 // This is a very large number of passwords to be saved for a single realm. | |
143 // It is almost certainly a corrupt pickle and not real data. Ignore it. | |
144 // This very well might actually be http://crbug.com/107701, so if we're | |
145 // reading an old pickle, we don't even log this the first time we try to | |
146 // read it. (That is, when we're reading the native architecture size.) | |
147 if (!warn_only) { | |
148 LOG(ERROR) << "Suspiciously large number of entries in KWallet entry " | |
149 << "(" << count << "; realm: " << signon_realm << ")"; | |
150 } | |
151 return false; | |
152 } | |
153 | |
154 // We'll swap |converted_forms| with |*forms| on success, to make sure we | |
155 // don't return partial results on failure. | |
156 ScopedVector<autofill::PasswordForm> converted_forms; | |
157 converted_forms.reserve(count); | |
158 for (size_t i = 0; i < count; ++i) { | |
159 scoped_ptr<PasswordForm> form(new PasswordForm()); | |
160 form->signon_realm.assign(signon_realm); | |
161 | |
162 int scheme = 0; | |
163 int64 date_created = 0; | |
164 int type = 0; | |
165 int generation_upload_status = 0; | |
166 // Note that these will be read back in the order listed due to | |
167 // short-circuit evaluation. This is important. | |
168 if (!iter.ReadInt(&scheme) || | |
169 !ReadGURL(&iter, warn_only, &form->origin) || | |
170 !ReadGURL(&iter, warn_only, &form->action) || | |
171 !iter.ReadString16(&form->username_element) || | |
172 !iter.ReadString16(&form->username_value) || | |
173 !iter.ReadString16(&form->password_element) || | |
174 !iter.ReadString16(&form->password_value) || | |
175 !iter.ReadString16(&form->submit_element) || | |
176 !iter.ReadBool(&form->ssl_valid) || | |
177 !iter.ReadBool(&form->preferred) || | |
178 !iter.ReadBool(&form->blacklisted_by_user) || | |
179 !iter.ReadInt64(&date_created)) { | |
180 LogDeserializationWarning(version, signon_realm, warn_only); | |
181 return false; | |
182 } | |
183 form->scheme = static_cast<PasswordForm::Scheme>(scheme); | |
184 | |
185 if (version > 1) { | |
186 if (!iter.ReadInt(&type) || | |
187 !iter.ReadInt(&form->times_used) || | |
188 !autofill::DeserializeFormData(&iter, &form->form_data)) { | |
189 LogDeserializationWarning(version, signon_realm, false); | |
190 return false; | |
191 } | |
192 form->type = static_cast<PasswordForm::Type>(type); | |
193 } | |
194 | |
195 if (version > 2) { | |
196 int64 date_synced = 0; | |
197 if (!iter.ReadInt64(&date_synced)) { | |
198 LogDeserializationWarning(version, signon_realm, false); | |
199 return false; | |
200 } | |
201 form->date_synced = base::Time::FromInternalValue(date_synced); | |
202 } | |
203 | |
204 if (version > 3) { | |
205 if (!iter.ReadString16(&form->display_name) || | |
206 !ReadGURL(&iter, warn_only, &form->avatar_url) || | |
207 !ReadGURL(&iter, warn_only, &form->federation_url) || | |
208 !iter.ReadBool(&form->skip_zero_click)) { | |
209 LogDeserializationWarning(version, signon_realm, false); | |
210 return false; | |
211 } | |
212 } | |
213 | |
214 if (version > 4) { | |
215 form->date_created = base::Time::FromInternalValue(date_created); | |
216 } else { | |
217 form->date_created = base::Time::FromTimeT(date_created); | |
218 } | |
219 | |
220 if (version > 5) { | |
221 if (!iter.ReadInt(&generation_upload_status)) { | |
222 LogDeserializationWarning(version, signon_realm, false); | |
223 } | |
224 form->generation_upload_status = | |
225 static_cast<PasswordForm::GenerationUploadStatus>( | |
226 generation_upload_status); | |
227 } | |
228 | |
229 converted_forms.push_back(form.release()); | |
230 } | |
231 | |
232 forms->swap(converted_forms); | |
233 return true; | |
234 } | |
235 | |
236 // Serializes a list of PasswordForms to be stored in the wallet. | |
237 void SerializeValue(const std::vector<autofill::PasswordForm*>& forms, | |
238 Pickle* pickle) { | |
239 pickle->WriteInt(kPickleVersion); | |
240 pickle->WriteSizeT(forms.size()); | |
241 for (autofill::PasswordForm* form : forms) { | |
242 pickle->WriteInt(form->scheme); | |
243 pickle->WriteString(form->origin.spec()); | |
244 pickle->WriteString(form->action.spec()); | |
245 pickle->WriteString16(form->username_element); | |
246 pickle->WriteString16(form->username_value); | |
247 pickle->WriteString16(form->password_element); | |
248 pickle->WriteString16(form->password_value); | |
249 pickle->WriteString16(form->submit_element); | |
250 pickle->WriteBool(form->ssl_valid); | |
251 pickle->WriteBool(form->preferred); | |
252 pickle->WriteBool(form->blacklisted_by_user); | |
253 pickle->WriteInt64(form->date_created.ToInternalValue()); | |
254 pickle->WriteInt(form->type); | |
255 pickle->WriteInt(form->times_used); | |
256 autofill::SerializeFormData(form->form_data, pickle); | |
257 pickle->WriteInt64(form->date_synced.ToInternalValue()); | |
258 pickle->WriteString16(form->display_name); | |
259 pickle->WriteString(form->avatar_url.spec()); | |
260 pickle->WriteString(form->federation_url.spec()); | |
261 pickle->WriteBool(form->skip_zero_click); | |
262 } | |
263 } | |
264 | |
265 // Moves the content of |second| to the end of |first|. | |
266 void AppendSecondToFirst(ScopedVector<autofill::PasswordForm>* first, | |
267 ScopedVector<autofill::PasswordForm> second) { | |
268 first->reserve(first->size() + second.size()); | |
269 first->insert(first->end(), second.begin(), second.end()); | |
270 second.weak_clear(); | |
271 } | |
272 | |
103 } // namespace | 273 } // namespace |
104 | 274 |
105 NativeBackendKWallet::NativeBackendKWallet(LocalProfileId id) | 275 NativeBackendKWallet::NativeBackendKWallet(LocalProfileId id) |
106 : profile_id_(id), | 276 : profile_id_(id), |
107 kwallet_proxy_(nullptr), | 277 kwallet_proxy_(nullptr), |
108 app_name_(l10n_util::GetStringUTF8(IDS_PRODUCT_NAME)) { | 278 app_name_(l10n_util::GetStringUTF8(IDS_PRODUCT_NAME)) { |
109 folder_name_ = GetProfileSpecificFolderName(); | 279 folder_name_ = GetProfileSpecificFolderName(); |
110 } | 280 } |
111 | 281 |
112 NativeBackendKWallet::~NativeBackendKWallet() { | 282 NativeBackendKWallet::~NativeBackendKWallet() { |
(...skipping 152 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
265 return INIT_SUCCESS; | 435 return INIT_SUCCESS; |
266 } | 436 } |
267 | 437 |
268 password_manager::PasswordStoreChangeList NativeBackendKWallet::AddLogin( | 438 password_manager::PasswordStoreChangeList NativeBackendKWallet::AddLogin( |
269 const PasswordForm& form) { | 439 const PasswordForm& form) { |
270 int wallet_handle = WalletHandle(); | 440 int wallet_handle = WalletHandle(); |
271 if (wallet_handle == kInvalidKWalletHandle) | 441 if (wallet_handle == kInvalidKWalletHandle) |
272 return password_manager::PasswordStoreChangeList(); | 442 return password_manager::PasswordStoreChangeList(); |
273 | 443 |
274 ScopedVector<autofill::PasswordForm> forms; | 444 ScopedVector<autofill::PasswordForm> forms; |
275 GetLoginsList(form.signon_realm, wallet_handle, &forms); | 445 if (!GetLoginsList(form.signon_realm, wallet_handle, &forms)) |
446 return password_manager::PasswordStoreChangeList(); | |
276 | 447 |
277 // We search for a login to update, rather than unconditionally appending the | 448 // We search for a login to update, rather than unconditionally appending the |
278 // login, because in some cases (especially involving sync) we can be asked to | 449 // login, because in some cases (especially involving sync) we can be asked to |
279 // add a login that already exists. In these cases we want to just update. | 450 // add a login that already exists. In these cases we want to just update. |
280 bool updated = false; | 451 bool updated = false; |
281 password_manager::PasswordStoreChangeList changes; | 452 password_manager::PasswordStoreChangeList changes; |
282 for (size_t i = 0; i < forms.size(); ++i) { | 453 for (size_t i = 0; i < forms.size(); ++i) { |
283 // Use the more restrictive removal comparison, so that we never have | 454 // Use the more restrictive removal comparison, so that we never have |
284 // duplicate logins that would all be removed together by RemoveLogin(). | 455 // duplicate logins that would all be removed together by RemoveLogin(). |
285 if (CompareForms(form, *forms[i], false)) { | 456 if (CompareForms(form, *forms[i], false)) { |
(...skipping 18 matching lines...) Expand all Loading... | |
304 bool NativeBackendKWallet::UpdateLogin( | 475 bool NativeBackendKWallet::UpdateLogin( |
305 const PasswordForm& form, | 476 const PasswordForm& form, |
306 password_manager::PasswordStoreChangeList* changes) { | 477 password_manager::PasswordStoreChangeList* changes) { |
307 DCHECK(changes); | 478 DCHECK(changes); |
308 changes->clear(); | 479 changes->clear(); |
309 int wallet_handle = WalletHandle(); | 480 int wallet_handle = WalletHandle(); |
310 if (wallet_handle == kInvalidKWalletHandle) | 481 if (wallet_handle == kInvalidKWalletHandle) |
311 return false; | 482 return false; |
312 | 483 |
313 ScopedVector<autofill::PasswordForm> forms; | 484 ScopedVector<autofill::PasswordForm> forms; |
314 GetLoginsList(form.signon_realm, wallet_handle, &forms); | 485 if (!GetLoginsList(form.signon_realm, wallet_handle, &forms)) |
486 return false; | |
315 | 487 |
316 bool updated = false; | 488 bool updated = false; |
317 for (size_t i = 0; i < forms.size(); ++i) { | 489 for (size_t i = 0; i < forms.size(); ++i) { |
318 if (CompareForms(form, *forms[i], true)) { | 490 if (CompareForms(form, *forms[i], true)) { |
319 *forms[i] = form; | 491 *forms[i] = form; |
320 updated = true; | 492 updated = true; |
321 } | 493 } |
322 } | 494 } |
323 if (!updated) | 495 if (!updated) |
324 return true; | 496 return true; |
325 | 497 |
326 if (SetLoginsList(forms.get(), form.signon_realm, wallet_handle)) { | 498 if (SetLoginsList(forms.get(), form.signon_realm, wallet_handle)) { |
327 changes->push_back(password_manager::PasswordStoreChange( | 499 changes->push_back(password_manager::PasswordStoreChange( |
328 password_manager::PasswordStoreChange::UPDATE, form)); | 500 password_manager::PasswordStoreChange::UPDATE, form)); |
329 return true; | 501 return true; |
330 } | 502 } |
331 | 503 |
332 return false; | 504 return false; |
333 } | 505 } |
334 | 506 |
335 bool NativeBackendKWallet::RemoveLogin(const PasswordForm& form) { | 507 bool NativeBackendKWallet::RemoveLogin(const PasswordForm& form) { |
336 int wallet_handle = WalletHandle(); | 508 int wallet_handle = WalletHandle(); |
337 if (wallet_handle == kInvalidKWalletHandle) | 509 if (wallet_handle == kInvalidKWalletHandle) |
338 return false; | 510 return false; |
339 | 511 |
340 ScopedVector<autofill::PasswordForm> all_forms; | 512 ScopedVector<autofill::PasswordForm> all_forms; |
341 GetLoginsList(form.signon_realm, wallet_handle, &all_forms); | 513 if (!GetLoginsList(form.signon_realm, wallet_handle, &all_forms)) |
514 return false; | |
342 | 515 |
343 ScopedVector<autofill::PasswordForm> kept_forms; | 516 ScopedVector<autofill::PasswordForm> kept_forms; |
344 kept_forms.reserve(all_forms.size()); | 517 kept_forms.reserve(all_forms.size()); |
345 for (auto& saved_form : all_forms) { | 518 for (auto& saved_form : all_forms) { |
346 if (!CompareForms(form, *saved_form, false)) { | 519 if (!CompareForms(form, *saved_form, false)) { |
347 kept_forms.push_back(saved_form); | 520 kept_forms.push_back(saved_form); |
348 saved_form = nullptr; | 521 saved_form = nullptr; |
349 } | 522 } |
350 } | 523 } |
351 | 524 |
(...skipping 13 matching lines...) Expand all Loading... | |
365 base::Time delete_begin, | 538 base::Time delete_begin, |
366 base::Time delete_end, | 539 base::Time delete_end, |
367 password_manager::PasswordStoreChangeList* changes) { | 540 password_manager::PasswordStoreChangeList* changes) { |
368 return RemoveLoginsBetween(delete_begin, delete_end, SYNC_TIMESTAMP, changes); | 541 return RemoveLoginsBetween(delete_begin, delete_end, SYNC_TIMESTAMP, changes); |
369 } | 542 } |
370 | 543 |
371 bool NativeBackendKWallet::GetLogins( | 544 bool NativeBackendKWallet::GetLogins( |
372 const PasswordForm& form, | 545 const PasswordForm& form, |
373 ScopedVector<autofill::PasswordForm>* forms) { | 546 ScopedVector<autofill::PasswordForm>* forms) { |
374 int wallet_handle = WalletHandle(); | 547 int wallet_handle = WalletHandle(); |
548 forms->clear(); | |
375 if (wallet_handle == kInvalidKWalletHandle) | 549 if (wallet_handle == kInvalidKWalletHandle) |
376 return false; | 550 return false; |
377 return GetLoginsList(form.signon_realm, wallet_handle, forms); | 551 return GetLoginsList(form.signon_realm, wallet_handle, forms); |
378 } | 552 } |
379 | 553 |
380 bool NativeBackendKWallet::GetAutofillableLogins( | 554 bool NativeBackendKWallet::GetAutofillableLogins( |
381 ScopedVector<autofill::PasswordForm>* forms) { | 555 ScopedVector<autofill::PasswordForm>* forms) { |
382 int wallet_handle = WalletHandle(); | 556 int wallet_handle = WalletHandle(); |
557 forms->clear(); | |
383 if (wallet_handle == kInvalidKWalletHandle) | 558 if (wallet_handle == kInvalidKWalletHandle) |
384 return false; | 559 return false; |
385 return GetLoginsList(true, wallet_handle, forms); | 560 return GetLoginsList(BlacklistOptions::AUTOFILLABLE, wallet_handle, forms); |
386 } | 561 } |
387 | 562 |
388 bool NativeBackendKWallet::GetBlacklistLogins( | 563 bool NativeBackendKWallet::GetBlacklistLogins( |
389 ScopedVector<autofill::PasswordForm>* forms) { | 564 ScopedVector<autofill::PasswordForm>* forms) { |
390 int wallet_handle = WalletHandle(); | 565 int wallet_handle = WalletHandle(); |
566 forms->clear(); | |
391 if (wallet_handle == kInvalidKWalletHandle) | 567 if (wallet_handle == kInvalidKWalletHandle) |
392 return false; | 568 return false; |
393 return GetLoginsList(false, wallet_handle, forms); | 569 return GetLoginsList(BlacklistOptions::BLACKLISTED, wallet_handle, forms); |
394 } | 570 } |
395 | 571 |
396 bool NativeBackendKWallet::GetLoginsList( | 572 bool NativeBackendKWallet::GetLoginsList( |
397 const std::string& signon_realm, | 573 const std::string& signon_realm, |
398 int wallet_handle, | 574 int wallet_handle, |
399 ScopedVector<autofill::PasswordForm>* forms) { | 575 ScopedVector<autofill::PasswordForm>* forms) { |
576 forms->clear(); | |
400 // Is there an entry in the wallet? | 577 // Is there an entry in the wallet? |
401 { | 578 { |
402 dbus::MethodCall method_call(kKWalletInterface, "hasEntry"); | 579 dbus::MethodCall method_call(kKWalletInterface, "hasEntry"); |
403 dbus::MessageWriter builder(&method_call); | 580 dbus::MessageWriter builder(&method_call); |
404 builder.AppendInt32(wallet_handle); // handle | 581 builder.AppendInt32(wallet_handle); // handle |
405 builder.AppendString(folder_name_); // folder | 582 builder.AppendString(folder_name_); // folder |
406 builder.AppendString(signon_realm); // key | 583 builder.AppendString(signon_realm); // key |
407 builder.AppendString(app_name_); // appid | 584 builder.AppendString(app_name_); // appid |
408 scoped_ptr<dbus::Response> response( | 585 scoped_ptr<dbus::Response> response( |
409 kwallet_proxy_->CallMethodAndBlock( | 586 kwallet_proxy_->CallMethodAndBlock( |
410 &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT)); | 587 &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT)); |
411 if (!response.get()) { | 588 if (!response.get()) { |
412 LOG(ERROR) << "Error contacting kwalletd (hasEntry)"; | 589 LOG(ERROR) << "Error contacting kwalletd (hasEntry)"; |
413 return false; | 590 return false; |
414 } | 591 } |
415 dbus::MessageReader reader(response.get()); | 592 dbus::MessageReader reader(response.get()); |
416 bool has_entry = false; | 593 bool has_entry = false; |
417 if (!reader.PopBool(&has_entry)) { | 594 if (!reader.PopBool(&has_entry)) { |
418 LOG(ERROR) << "Error reading response from kwalletd (hasEntry): " | 595 LOG(ERROR) << "Error reading response from kwalletd (hasEntry): " |
419 << response->ToString(); | 596 << response->ToString(); |
420 return false; | 597 return false; |
421 } | 598 } |
422 if (!has_entry) { | 599 if (!has_entry) { |
423 // This is not an error. There just isn't a matching entry. | 600 // This is not an error. There just isn't a matching entry. |
engedy
2015/03/11 19:25:35
I think in this case |forms| should be cleared. (T
vabr (Chromium)
2015/03/12 15:30:51
Done.
| |
424 return true; | 601 return true; |
425 } | 602 } |
426 } | 603 } |
427 | 604 |
428 { | 605 { |
429 dbus::MethodCall method_call(kKWalletInterface, "readEntry"); | 606 dbus::MethodCall method_call(kKWalletInterface, "readEntry"); |
430 dbus::MessageWriter builder(&method_call); | 607 dbus::MessageWriter builder(&method_call); |
431 builder.AppendInt32(wallet_handle); // handle | 608 builder.AppendInt32(wallet_handle); // handle |
432 builder.AppendString(folder_name_); // folder | 609 builder.AppendString(folder_name_); // folder |
433 builder.AppendString(signon_realm); // key | 610 builder.AppendString(signon_realm); // key |
(...skipping 13 matching lines...) Expand all Loading... | |
447 << response->ToString(); | 624 << response->ToString(); |
448 return false; | 625 return false; |
449 } | 626 } |
450 if (!bytes) | 627 if (!bytes) |
451 return false; | 628 return false; |
452 if (!CheckSerializedValue(bytes, length, signon_realm)) { | 629 if (!CheckSerializedValue(bytes, length, signon_realm)) { |
453 // This is weird, but we choose not to call it an error. There is an | 630 // This is weird, but we choose not to call it an error. There is an |
454 // invalid entry somehow, but by just ignoring it, we make it easier to | 631 // invalid entry somehow, but by just ignoring it, we make it easier to |
455 // repair without having to delete it using kwalletmanager (that is, by | 632 // repair without having to delete it using kwalletmanager (that is, by |
456 // just saving a new password within this realm to overwrite it). | 633 // just saving a new password within this realm to overwrite it). |
457 return true; | 634 return true; |
engedy
2015/03/11 19:25:35
Same here. Perhaps it is just easiest to keep the
vabr (Chromium)
2015/03/12 15:30:51
Agreed. Done.
| |
458 } | 635 } |
459 | 636 |
460 // Can't we all just agree on whether bytes are signed or not? Please? | 637 // Can't we all just agree on whether bytes are signed or not? Please? |
461 Pickle pickle(reinterpret_cast<const char*>(bytes), length); | 638 Pickle pickle(reinterpret_cast<const char*>(bytes), length); |
462 DeserializeValue(signon_realm, pickle, forms); | 639 *forms = DeserializeValue(signon_realm, pickle); |
463 } | 640 } |
464 | 641 |
465 return true; | 642 return true; |
466 } | 643 } |
467 | 644 |
468 bool NativeBackendKWallet::GetLoginsList( | 645 bool NativeBackendKWallet::GetLoginsList( |
469 bool autofillable, | 646 BlacklistOptions options, |
470 int wallet_handle, | 647 int wallet_handle, |
471 ScopedVector<autofill::PasswordForm>* forms) { | 648 ScopedVector<autofill::PasswordForm>* forms) { |
649 forms->clear(); | |
472 ScopedVector<autofill::PasswordForm> all_forms; | 650 ScopedVector<autofill::PasswordForm> all_forms; |
473 if (!GetAllLogins(wallet_handle, &all_forms)) | 651 if (!GetAllLogins(wallet_handle, &all_forms)) |
474 return false; | 652 return false; |
475 | 653 |
476 // We have to read all the entries, and then filter them here. | 654 // We have to read all the entries, and then filter them here. |
477 forms->reserve(forms->size() + all_forms.size()); | 655 forms->reserve(all_forms.size()); |
478 for (auto& saved_form : all_forms) { | 656 for (auto& saved_form : all_forms) { |
479 if (saved_form->blacklisted_by_user == !autofillable) { | 657 if (saved_form->blacklisted_by_user == |
658 (options == BlacklistOptions::BLACKLISTED)) { | |
480 forms->push_back(saved_form); | 659 forms->push_back(saved_form); |
481 saved_form = nullptr; | 660 saved_form = nullptr; |
482 } | 661 } |
483 } | 662 } |
484 | 663 |
485 return true; | 664 return true; |
486 } | 665 } |
487 | 666 |
488 bool NativeBackendKWallet::GetAllLogins( | 667 bool NativeBackendKWallet::GetAllLogins( |
489 int wallet_handle, | 668 int wallet_handle, |
490 ScopedVector<autofill::PasswordForm>* forms) { | 669 ScopedVector<autofill::PasswordForm>* forms) { |
670 forms->clear(); | |
491 // We could probably also use readEntryList here. | 671 // We could probably also use readEntryList here. |
492 std::vector<std::string> realm_list; | 672 std::vector<std::string> realm_list; |
493 { | 673 { |
494 dbus::MethodCall method_call(kKWalletInterface, "entryList"); | 674 dbus::MethodCall method_call(kKWalletInterface, "entryList"); |
495 dbus::MessageWriter builder(&method_call); | 675 dbus::MessageWriter builder(&method_call); |
496 builder.AppendInt32(wallet_handle); // handle | 676 builder.AppendInt32(wallet_handle); // handle |
497 builder.AppendString(folder_name_); // folder | 677 builder.AppendString(folder_name_); // folder |
498 builder.AppendString(app_name_); // appid | 678 builder.AppendString(app_name_); // appid |
499 scoped_ptr<dbus::Response> response( | 679 scoped_ptr<dbus::Response> response( |
500 kwallet_proxy_->CallMethodAndBlock( | 680 kwallet_proxy_->CallMethodAndBlock( |
501 &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT)); | 681 &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT)); |
502 if (!response.get()) { | 682 if (!response.get()) { |
503 LOG(ERROR) << "Error contacting kwalletd (entryList)"; | 683 LOG(ERROR) << "Error contacting kwalletd (entryList)"; |
504 return false; | 684 return false; |
505 } | 685 } |
506 dbus::MessageReader reader(response.get()); | 686 dbus::MessageReader reader(response.get()); |
507 if (!reader.PopArrayOfStrings(&realm_list)) { | 687 if (!reader.PopArrayOfStrings(&realm_list)) { |
508 LOG(ERROR) << "Error reading response from kwalletd (entryList): " | 688 LOG(ERROR) << "Error reading response from kwalletd (entryList): " |
509 << response->ToString(); | 689 << response->ToString(); |
510 return false; | 690 return false; |
511 } | 691 } |
512 } | 692 } |
513 | 693 |
514 for (size_t i = 0; i < realm_list.size(); ++i) { | 694 // Swapping |collected_forms| with |*forms| just before "return true" makes |
515 const std::string& signon_realm = realm_list[i]; | 695 // sure partial results are not returned on failure. |
696 ScopedVector<autofill::PasswordForm> collected_forms; | |
697 for (const std::string& signon_realm : realm_list) { | |
516 dbus::MethodCall method_call(kKWalletInterface, "readEntry"); | 698 dbus::MethodCall method_call(kKWalletInterface, "readEntry"); |
517 dbus::MessageWriter builder(&method_call); | 699 dbus::MessageWriter builder(&method_call); |
518 builder.AppendInt32(wallet_handle); // handle | 700 builder.AppendInt32(wallet_handle); // handle |
519 builder.AppendString(folder_name_); // folder | 701 builder.AppendString(folder_name_); // folder |
520 builder.AppendString(signon_realm); // key | 702 builder.AppendString(signon_realm); // key |
521 builder.AppendString(app_name_); // appid | 703 builder.AppendString(app_name_); // appid |
522 scoped_ptr<dbus::Response> response( | 704 scoped_ptr<dbus::Response> response( |
523 kwallet_proxy_->CallMethodAndBlock( | 705 kwallet_proxy_->CallMethodAndBlock( |
524 &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT)); | 706 &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT)); |
525 if (!response.get()) { | 707 if (!response.get()) { |
526 LOG(ERROR) << "Error contacting kwalletd (readEntry)"; | 708 LOG(ERROR) << "Error contacting kwalletd (readEntry)"; |
527 return false; | 709 return false; |
528 } | 710 } |
529 dbus::MessageReader reader(response.get()); | 711 dbus::MessageReader reader(response.get()); |
530 const uint8_t* bytes = nullptr; | 712 const uint8_t* bytes = nullptr; |
531 size_t length = 0; | 713 size_t length = 0; |
532 if (!reader.PopArrayOfBytes(&bytes, &length)) { | 714 if (!reader.PopArrayOfBytes(&bytes, &length)) { |
533 LOG(ERROR) << "Error reading response from kwalletd (readEntry): " | 715 LOG(ERROR) << "Error reading response from kwalletd (readEntry): " |
534 << response->ToString(); | 716 << response->ToString(); |
535 return false; | 717 return false; |
536 } | 718 } |
537 if (!bytes || !CheckSerializedValue(bytes, length, signon_realm)) | 719 if (!bytes || !CheckSerializedValue(bytes, length, signon_realm)) |
538 continue; | 720 continue; |
539 | 721 |
540 // Can't we all just agree on whether bytes are signed or not? Please? | 722 // Can't we all just agree on whether bytes are signed or not? Please? |
541 Pickle pickle(reinterpret_cast<const char*>(bytes), length); | 723 Pickle pickle(reinterpret_cast<const char*>(bytes), length); |
542 DeserializeValue(signon_realm, pickle, forms); | 724 AppendSecondToFirst(&collected_forms, |
725 DeserializeValue(signon_realm, pickle)); | |
543 } | 726 } |
727 forms->swap(collected_forms); | |
544 return true; | 728 return true; |
545 } | 729 } |
546 | 730 |
547 bool NativeBackendKWallet::SetLoginsList( | 731 bool NativeBackendKWallet::SetLoginsList( |
548 const std::vector<autofill::PasswordForm*>& forms, | 732 const std::vector<autofill::PasswordForm*>& forms, |
549 const std::string& signon_realm, | 733 const std::string& signon_realm, |
550 int wallet_handle) { | 734 int wallet_handle) { |
551 if (forms.empty()) { | 735 if (forms.empty()) { |
552 // No items left? Remove the entry from the wallet. | 736 // No items left? Remove the entry from the wallet. |
553 dbus::MethodCall method_call(kKWalletInterface, "removeEntry"); | 737 dbus::MethodCall method_call(kKWalletInterface, "removeEntry"); |
(...skipping 115 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
669 if (!reader.PopArrayOfBytes(&bytes, &length)) { | 853 if (!reader.PopArrayOfBytes(&bytes, &length)) { |
670 LOG(ERROR) << "Error reading response from kwalletd (readEntry): " | 854 LOG(ERROR) << "Error reading response from kwalletd (readEntry): " |
671 << response->ToString(); | 855 << response->ToString(); |
672 continue; | 856 continue; |
673 } | 857 } |
674 if (!bytes || !CheckSerializedValue(bytes, length, signon_realm)) | 858 if (!bytes || !CheckSerializedValue(bytes, length, signon_realm)) |
675 continue; | 859 continue; |
676 | 860 |
677 // Can't we all just agree on whether bytes are signed or not? Please? | 861 // Can't we all just agree on whether bytes are signed or not? Please? |
678 Pickle pickle(reinterpret_cast<const char*>(bytes), length); | 862 Pickle pickle(reinterpret_cast<const char*>(bytes), length); |
679 ScopedVector<autofill::PasswordForm> all_forms; | 863 ScopedVector<autofill::PasswordForm> all_forms = |
680 DeserializeValue(signon_realm, pickle, &all_forms); | 864 DeserializeValue(signon_realm, pickle); |
681 | 865 |
682 ScopedVector<autofill::PasswordForm> kept_forms; | 866 ScopedVector<autofill::PasswordForm> kept_forms; |
683 kept_forms.reserve(all_forms.size()); | 867 kept_forms.reserve(all_forms.size()); |
684 base::Time autofill::PasswordForm::*date_member = | 868 base::Time autofill::PasswordForm::*date_member = |
685 date_to_compare == CREATION_TIMESTAMP | 869 date_to_compare == CREATION_TIMESTAMP |
686 ? &autofill::PasswordForm::date_created | 870 ? &autofill::PasswordForm::date_created |
687 : &autofill::PasswordForm::date_synced; | 871 : &autofill::PasswordForm::date_synced; |
688 for (auto& saved_form : all_forms) { | 872 for (auto& saved_form : all_forms) { |
689 if (delete_begin <= saved_form->*date_member && | 873 if (delete_begin <= saved_form->*date_member && |
690 (delete_end.is_null() || saved_form->*date_member < delete_end)) { | 874 (delete_end.is_null() || saved_form->*date_member < delete_end)) { |
691 changes->push_back(password_manager::PasswordStoreChange( | 875 changes->push_back(password_manager::PasswordStoreChange( |
692 password_manager::PasswordStoreChange::REMOVE, *saved_form)); | 876 password_manager::PasswordStoreChange::REMOVE, *saved_form)); |
693 } else { | 877 } else { |
694 kept_forms.push_back(saved_form); | 878 kept_forms.push_back(saved_form); |
695 saved_form = nullptr; | 879 saved_form = nullptr; |
696 } | 880 } |
697 } | 881 } |
698 | 882 |
699 if (!SetLoginsList(kept_forms.get(), signon_realm, wallet_handle)) { | 883 if (!SetLoginsList(kept_forms.get(), signon_realm, wallet_handle)) { |
700 ok = false; | 884 ok = false; |
701 changes->clear(); | 885 changes->clear(); |
702 } | 886 } |
703 } | 887 } |
704 return ok; | 888 return ok; |
705 } | 889 } |
706 | 890 |
707 // static | 891 // static |
708 void NativeBackendKWallet::SerializeValue( | 892 ScopedVector<autofill::PasswordForm> NativeBackendKWallet::DeserializeValue( |
709 const std::vector<autofill::PasswordForm*>& forms, | |
710 Pickle* pickle) { | |
711 pickle->WriteInt(kPickleVersion); | |
712 pickle->WriteSizeT(forms.size()); | |
713 for (autofill::PasswordForm* form : forms) { | |
714 pickle->WriteInt(form->scheme); | |
715 pickle->WriteString(form->origin.spec()); | |
716 pickle->WriteString(form->action.spec()); | |
717 pickle->WriteString16(form->username_element); | |
718 pickle->WriteString16(form->username_value); | |
719 pickle->WriteString16(form->password_element); | |
720 pickle->WriteString16(form->password_value); | |
721 pickle->WriteString16(form->submit_element); | |
722 pickle->WriteBool(form->ssl_valid); | |
723 pickle->WriteBool(form->preferred); | |
724 pickle->WriteBool(form->blacklisted_by_user); | |
725 pickle->WriteInt64(form->date_created.ToInternalValue()); | |
726 pickle->WriteInt(form->type); | |
727 pickle->WriteInt(form->times_used); | |
728 autofill::SerializeFormData(form->form_data, pickle); | |
729 pickle->WriteInt64(form->date_synced.ToInternalValue()); | |
730 pickle->WriteString16(form->display_name); | |
731 pickle->WriteString(form->avatar_url.spec()); | |
732 pickle->WriteString(form->federation_url.spec()); | |
733 pickle->WriteBool(form->skip_zero_click); | |
734 pickle->WriteInt(form->generation_upload_status); | |
735 } | |
736 } | |
737 | |
738 // static | |
739 bool NativeBackendKWallet::DeserializeValueSize( | |
740 const std::string& signon_realm, | 893 const std::string& signon_realm, |
741 const PickleIterator& init_iter, | 894 const Pickle& pickle) { |
742 int version, | |
743 bool size_32, | |
744 bool warn_only, | |
745 ScopedVector<autofill::PasswordForm>* forms) { | |
746 PickleIterator iter = init_iter; | |
747 | |
748 size_t count = 0; | |
749 if (size_32) { | |
750 uint32_t count_32 = 0; | |
751 if (!iter.ReadUInt32(&count_32)) { | |
752 LOG(ERROR) << "Failed to deserialize KWallet entry " | |
753 << "(realm: " << signon_realm << ")"; | |
754 return false; | |
755 } | |
756 count = count_32; | |
757 } else { | |
758 if (!iter.ReadSizeT(&count)) { | |
759 LOG(ERROR) << "Failed to deserialize KWallet entry " | |
760 << "(realm: " << signon_realm << ")"; | |
761 return false; | |
762 } | |
763 } | |
764 | |
765 if (count > 0xFFFF) { | |
766 // Trying to pin down the cause of http://crbug.com/80728 (or fix it). | |
767 // This is a very large number of passwords to be saved for a single realm. | |
768 // It is almost certainly a corrupt pickle and not real data. Ignore it. | |
769 // This very well might actually be http://crbug.com/107701, so if we're | |
770 // reading an old pickle, we don't even log this the first time we try to | |
771 // read it. (That is, when we're reading the native architecture size.) | |
772 if (!warn_only) { | |
773 LOG(ERROR) << "Suspiciously large number of entries in KWallet entry " | |
774 << "(" << count << "; realm: " << signon_realm << ")"; | |
775 } | |
776 return false; | |
777 } | |
778 | |
779 forms->reserve(forms->size() + count); | |
780 for (size_t i = 0; i < count; ++i) { | |
781 scoped_ptr<PasswordForm> form(new PasswordForm()); | |
782 form->signon_realm.assign(signon_realm); | |
783 | |
784 int scheme = 0; | |
785 int64 date_created = 0; | |
786 int type = 0; | |
787 int generation_upload_status = 0; | |
788 // Note that these will be read back in the order listed due to | |
789 // short-circuit evaluation. This is important. | |
790 if (!iter.ReadInt(&scheme) || | |
791 !ReadGURL(&iter, warn_only, &form->origin) || | |
792 !ReadGURL(&iter, warn_only, &form->action) || | |
793 !iter.ReadString16(&form->username_element) || | |
794 !iter.ReadString16(&form->username_value) || | |
795 !iter.ReadString16(&form->password_element) || | |
796 !iter.ReadString16(&form->password_value) || | |
797 !iter.ReadString16(&form->submit_element) || | |
798 !iter.ReadBool(&form->ssl_valid) || | |
799 !iter.ReadBool(&form->preferred) || | |
800 !iter.ReadBool(&form->blacklisted_by_user) || | |
801 !iter.ReadInt64(&date_created)) { | |
802 LogDeserializationWarning(version, signon_realm, warn_only); | |
803 return false; | |
804 } | |
805 form->scheme = static_cast<PasswordForm::Scheme>(scheme); | |
806 | |
807 if (version > 1) { | |
808 if (!iter.ReadInt(&type) || | |
809 !iter.ReadInt(&form->times_used) || | |
810 !autofill::DeserializeFormData(&iter, &form->form_data)) { | |
811 LogDeserializationWarning(version, signon_realm, false); | |
812 return false; | |
813 } | |
814 form->type = static_cast<PasswordForm::Type>(type); | |
815 } | |
816 | |
817 if (version > 2) { | |
818 int64 date_synced = 0; | |
819 if (!iter.ReadInt64(&date_synced)) { | |
820 LogDeserializationWarning(version, signon_realm, false); | |
821 return false; | |
822 } | |
823 form->date_synced = base::Time::FromInternalValue(date_synced); | |
824 } | |
825 | |
826 if (version > 3) { | |
827 if (!iter.ReadString16(&form->display_name) || | |
828 !ReadGURL(&iter, warn_only, &form->avatar_url) || | |
829 !ReadGURL(&iter, warn_only, &form->federation_url) || | |
830 !iter.ReadBool(&form->skip_zero_click)) { | |
831 LogDeserializationWarning(version, signon_realm, false); | |
832 return false; | |
833 } | |
834 } | |
835 | |
836 if (version > 4) { | |
837 form->date_created = base::Time::FromInternalValue(date_created); | |
838 } else { | |
839 form->date_created = base::Time::FromTimeT(date_created); | |
840 } | |
841 | |
842 if (version > 5) { | |
843 if (!iter.ReadInt(&generation_upload_status)) { | |
844 LogDeserializationWarning(version, signon_realm, false); | |
845 } | |
846 form->generation_upload_status = | |
847 static_cast<PasswordForm::GenerationUploadStatus>( | |
848 generation_upload_status); | |
849 } | |
850 | |
851 forms->push_back(form.release()); | |
852 } | |
853 | |
854 return true; | |
855 } | |
856 | |
857 // static | |
858 void NativeBackendKWallet::DeserializeValue( | |
859 const std::string& signon_realm, | |
860 const Pickle& pickle, | |
861 ScopedVector<autofill::PasswordForm>* forms) { | |
862 PickleIterator iter(pickle); | 895 PickleIterator iter(pickle); |
863 | 896 |
864 int version = -1; | 897 int version = -1; |
865 if (!iter.ReadInt(&version) || | 898 if (!iter.ReadInt(&version) || |
866 version < 0 || version > kPickleVersion) { | 899 version < 0 || version > kPickleVersion) { |
867 LOG(ERROR) << "Failed to deserialize KWallet entry " | 900 LOG(ERROR) << "Failed to deserialize KWallet entry " |
868 << "(realm: " << signon_realm << ")"; | 901 << "(realm: " << signon_realm << ")"; |
869 return; | 902 return ScopedVector<autofill::PasswordForm>(); |
870 } | 903 } |
871 | 904 |
905 ScopedVector<autofill::PasswordForm> forms; | |
872 if (version > 0) { | 906 if (version > 0) { |
873 // In current pickles, we expect 64-bit sizes. Failure is an error. | 907 // In current pickles, we expect 64-bit sizes. Failure is an error. |
874 DeserializeValueSize(signon_realm, iter, version, false, false, forms); | 908 DeserializeValueSize(signon_realm, iter, version, false, false, &forms); |
875 return; | 909 return forms.Pass(); |
876 } | 910 } |
877 | 911 |
878 const size_t saved_forms_size = forms->size(); | |
879 const bool size_32 = sizeof(size_t) == sizeof(uint32_t); | 912 const bool size_32 = sizeof(size_t) == sizeof(uint32_t); |
880 if (!DeserializeValueSize( | 913 if (!DeserializeValueSize( |
881 signon_realm, iter, version, size_32, true, forms)) { | 914 signon_realm, iter, version, size_32, true, &forms)) { |
882 // We failed to read the pickle using the native architecture of the system. | 915 // We failed to read the pickle using the native architecture of the system. |
883 // Try again with the opposite architecture. Note that we do this even on | 916 // Try again with the opposite architecture. Note that we do this even on |
884 // 32-bit machines, in case we're reading a 64-bit pickle. (Probably rare, | 917 // 32-bit machines, in case we're reading a 64-bit pickle. (Probably rare, |
885 // since mostly we expect upgrades, not downgrades, but both are possible.) | 918 // since mostly we expect upgrades, not downgrades, but both are possible.) |
886 forms->resize(saved_forms_size); | 919 DeserializeValueSize(signon_realm, iter, version, !size_32, false, &forms); |
887 DeserializeValueSize(signon_realm, iter, version, !size_32, false, forms); | |
888 } | 920 } |
921 return forms.Pass(); | |
889 } | 922 } |
890 | 923 |
891 int NativeBackendKWallet::WalletHandle() { | 924 int NativeBackendKWallet::WalletHandle() { |
892 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); | 925 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); |
893 | 926 |
894 // Open the wallet. | 927 // Open the wallet. |
895 // TODO(mdm): Are we leaking these handles? Find out. | 928 // TODO(mdm): Are we leaking these handles? Find out. |
896 int32_t handle = kInvalidKWalletHandle; | 929 int32_t handle = kInvalidKWalletHandle; |
897 { | 930 { |
898 dbus::MethodCall method_call(kKWalletInterface, "open"); | 931 dbus::MethodCall method_call(kKWalletInterface, "open"); |
(...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
970 } | 1003 } |
971 | 1004 |
972 return handle; | 1005 return handle; |
973 } | 1006 } |
974 | 1007 |
975 std::string NativeBackendKWallet::GetProfileSpecificFolderName() const { | 1008 std::string NativeBackendKWallet::GetProfileSpecificFolderName() const { |
976 // Originally, the folder name was always just "Chrome Form Data". | 1009 // Originally, the folder name was always just "Chrome Form Data". |
977 // Now we use it to distinguish passwords for different profiles. | 1010 // Now we use it to distinguish passwords for different profiles. |
978 return base::StringPrintf("%s (%d)", kKWalletFolder, profile_id_); | 1011 return base::StringPrintf("%s (%d)", kKWalletFolder, profile_id_); |
979 } | 1012 } |
OLD | NEW |