| 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 = 5; |
| 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 // |forms| contents). |size_32| controls reading the size field within the |
| 109 // pickle as 32 bits. We used to use Pickle::WriteSize() to write the number of |
| 110 // password forms, but that has a different size on 32- and 64-bit systems. So, |
| 111 // now we always write a 64-bit quantity, but we support trying to read it as |
| 112 // either size when reading old pickles that fail to deserialize using the |
| 113 // native size. Returns true on success, false on failure (erasing |forms| in |
| 114 // that case). |
| 115 bool DeserializeValueSize(const std::string& signon_realm, |
| 116 const PickleIterator& init_iter, |
| 117 int version, |
| 118 bool size_32, |
| 119 bool warn_only, |
| 120 ScopedVector<autofill::PasswordForm>* forms) { |
| 121 forms->clear(); |
| 122 PickleIterator iter = init_iter; |
| 123 |
| 124 size_t count = 0; |
| 125 if (size_32) { |
| 126 uint32_t count_32 = 0; |
| 127 if (!iter.ReadUInt32(&count_32)) { |
| 128 LOG(ERROR) << "Failed to deserialize KWallet entry " |
| 129 << "(realm: " << signon_realm << ")"; |
| 130 return false; |
| 131 } |
| 132 count = count_32; |
| 133 } else { |
| 134 if (!iter.ReadSizeT(&count)) { |
| 135 LOG(ERROR) << "Failed to deserialize KWallet entry " |
| 136 << "(realm: " << signon_realm << ")"; |
| 137 return false; |
| 138 } |
| 139 } |
| 140 |
| 141 if (count > 0xFFFF) { |
| 142 // Trying to pin down the cause of http://crbug.com/80728 (or fix it). |
| 143 // This is a very large number of passwords to be saved for a single realm. |
| 144 // It is almost certainly a corrupt pickle and not real data. Ignore it. |
| 145 // This very well might actually be http://crbug.com/107701, so if we're |
| 146 // reading an old pickle, we don't even log this the first time we try to |
| 147 // read it. (That is, when we're reading the native architecture size.) |
| 148 if (!warn_only) { |
| 149 LOG(ERROR) << "Suspiciously large number of entries in KWallet entry " |
| 150 << "(" << count << "; realm: " << signon_realm << ")"; |
| 151 } |
| 152 return false; |
| 153 } |
| 154 |
| 155 // We'll swap |converted_forms| with |*forms| on success, to make sure we |
| 156 // don't return partial results on failure. |
| 157 ScopedVector<autofill::PasswordForm> converted_forms; |
| 158 converted_forms.reserve(count); |
| 159 for (size_t i = 0; i < count; ++i) { |
| 160 scoped_ptr<PasswordForm> form(new PasswordForm()); |
| 161 form->signon_realm.assign(signon_realm); |
| 162 |
| 163 int scheme = 0; |
| 164 int64 date_created = 0; |
| 165 int type = 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 converted_forms.push_back(form.release()); |
| 221 } |
| 222 |
| 223 forms->swap(converted_forms); |
| 224 return true; |
| 225 } |
| 226 |
| 227 // Serializes a list of PasswordForms to be stored in the wallet. |
| 228 void SerializeValue(const std::vector<autofill::PasswordForm*>& forms, |
| 229 Pickle* pickle) { |
| 230 pickle->WriteInt(kPickleVersion); |
| 231 pickle->WriteSizeT(forms.size()); |
| 232 for (autofill::PasswordForm* form : forms) { |
| 233 pickle->WriteInt(form->scheme); |
| 234 pickle->WriteString(form->origin.spec()); |
| 235 pickle->WriteString(form->action.spec()); |
| 236 pickle->WriteString16(form->username_element); |
| 237 pickle->WriteString16(form->username_value); |
| 238 pickle->WriteString16(form->password_element); |
| 239 pickle->WriteString16(form->password_value); |
| 240 pickle->WriteString16(form->submit_element); |
| 241 pickle->WriteBool(form->ssl_valid); |
| 242 pickle->WriteBool(form->preferred); |
| 243 pickle->WriteBool(form->blacklisted_by_user); |
| 244 pickle->WriteInt64(form->date_created.ToInternalValue()); |
| 245 pickle->WriteInt(form->type); |
| 246 pickle->WriteInt(form->times_used); |
| 247 autofill::SerializeFormData(form->form_data, pickle); |
| 248 pickle->WriteInt64(form->date_synced.ToInternalValue()); |
| 249 pickle->WriteString16(form->display_name); |
| 250 pickle->WriteString(form->avatar_url.spec()); |
| 251 pickle->WriteString(form->federation_url.spec()); |
| 252 pickle->WriteBool(form->skip_zero_click); |
| 253 } |
| 254 } |
| 255 |
| 256 // Moves the content of |second| to the end of |first|. |
| 257 void AppendSecondToFirst(ScopedVector<autofill::PasswordForm>* first, |
| 258 ScopedVector<autofill::PasswordForm> second) { |
| 259 first->reserve(first->size() + second.size()); |
| 260 first->insert(first->end(), second.begin(), second.end()); |
| 261 second.weak_clear(); |
| 262 } |
| 263 |
| 103 } // namespace | 264 } // namespace |
| 104 | 265 |
| 105 NativeBackendKWallet::NativeBackendKWallet(LocalProfileId id) | 266 NativeBackendKWallet::NativeBackendKWallet(LocalProfileId id) |
| 106 : profile_id_(id), | 267 : profile_id_(id), |
| 107 kwallet_proxy_(NULL), | 268 kwallet_proxy_(NULL), |
| 108 app_name_(l10n_util::GetStringUTF8(IDS_PRODUCT_NAME)) { | 269 app_name_(l10n_util::GetStringUTF8(IDS_PRODUCT_NAME)) { |
| 109 folder_name_ = GetProfileSpecificFolderName(); | 270 folder_name_ = GetProfileSpecificFolderName(); |
| 110 } | 271 } |
| 111 | 272 |
| 112 NativeBackendKWallet::~NativeBackendKWallet() { | 273 NativeBackendKWallet::~NativeBackendKWallet() { |
| (...skipping 262 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 375 if (wallet_handle == kInvalidKWalletHandle) | 536 if (wallet_handle == kInvalidKWalletHandle) |
| 376 return false; | 537 return false; |
| 377 return GetLoginsList(form.signon_realm, wallet_handle, forms); | 538 return GetLoginsList(form.signon_realm, wallet_handle, forms); |
| 378 } | 539 } |
| 379 | 540 |
| 380 bool NativeBackendKWallet::GetAutofillableLogins( | 541 bool NativeBackendKWallet::GetAutofillableLogins( |
| 381 ScopedVector<autofill::PasswordForm>* forms) { | 542 ScopedVector<autofill::PasswordForm>* forms) { |
| 382 int wallet_handle = WalletHandle(); | 543 int wallet_handle = WalletHandle(); |
| 383 if (wallet_handle == kInvalidKWalletHandle) | 544 if (wallet_handle == kInvalidKWalletHandle) |
| 384 return false; | 545 return false; |
| 385 return GetLoginsList(true, wallet_handle, forms); | 546 return GetLoginsList(BlacklistOptions::AUTOFILLABLE, wallet_handle, forms); |
| 386 } | 547 } |
| 387 | 548 |
| 388 bool NativeBackendKWallet::GetBlacklistLogins( | 549 bool NativeBackendKWallet::GetBlacklistLogins( |
| 389 ScopedVector<autofill::PasswordForm>* forms) { | 550 ScopedVector<autofill::PasswordForm>* forms) { |
| 390 int wallet_handle = WalletHandle(); | 551 int wallet_handle = WalletHandle(); |
| 391 if (wallet_handle == kInvalidKWalletHandle) | 552 if (wallet_handle == kInvalidKWalletHandle) |
| 392 return false; | 553 return false; |
| 393 return GetLoginsList(false, wallet_handle, forms); | 554 return GetLoginsList(BlacklistOptions::BLACKLISTED, wallet_handle, forms); |
| 394 } | 555 } |
| 395 | 556 |
| 396 bool NativeBackendKWallet::GetLoginsList( | 557 bool NativeBackendKWallet::GetLoginsList( |
| 397 const std::string& signon_realm, | 558 const std::string& signon_realm, |
| 398 int wallet_handle, | 559 int wallet_handle, |
| 399 ScopedVector<autofill::PasswordForm>* forms) { | 560 ScopedVector<autofill::PasswordForm>* forms) { |
| 561 forms->clear(); |
| 400 // Is there an entry in the wallet? | 562 // Is there an entry in the wallet? |
| 401 { | 563 { |
| 402 dbus::MethodCall method_call(kKWalletInterface, "hasEntry"); | 564 dbus::MethodCall method_call(kKWalletInterface, "hasEntry"); |
| 403 dbus::MessageWriter builder(&method_call); | 565 dbus::MessageWriter builder(&method_call); |
| 404 builder.AppendInt32(wallet_handle); // handle | 566 builder.AppendInt32(wallet_handle); // handle |
| 405 builder.AppendString(folder_name_); // folder | 567 builder.AppendString(folder_name_); // folder |
| 406 builder.AppendString(signon_realm); // key | 568 builder.AppendString(signon_realm); // key |
| 407 builder.AppendString(app_name_); // appid | 569 builder.AppendString(app_name_); // appid |
| 408 scoped_ptr<dbus::Response> response( | 570 scoped_ptr<dbus::Response> response( |
| 409 kwallet_proxy_->CallMethodAndBlock( | 571 kwallet_proxy_->CallMethodAndBlock( |
| (...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 452 if (!CheckSerializedValue(bytes, length, signon_realm)) { | 614 if (!CheckSerializedValue(bytes, length, signon_realm)) { |
| 453 // This is weird, but we choose not to call it an error. There is an | 615 // 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 | 616 // 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 | 617 // repair without having to delete it using kwalletmanager (that is, by |
| 456 // just saving a new password within this realm to overwrite it). | 618 // just saving a new password within this realm to overwrite it). |
| 457 return true; | 619 return true; |
| 458 } | 620 } |
| 459 | 621 |
| 460 // Can't we all just agree on whether bytes are signed or not? Please? | 622 // Can't we all just agree on whether bytes are signed or not? Please? |
| 461 Pickle pickle(reinterpret_cast<const char*>(bytes), length); | 623 Pickle pickle(reinterpret_cast<const char*>(bytes), length); |
| 462 DeserializeValue(signon_realm, pickle, forms); | 624 *forms = DeserializeValue(signon_realm, pickle); |
| 463 } | 625 } |
| 464 | 626 |
| 465 return true; | 627 return true; |
| 466 } | 628 } |
| 467 | 629 |
| 468 bool NativeBackendKWallet::GetLoginsList( | 630 bool NativeBackendKWallet::GetLoginsList( |
| 469 bool autofillable, | 631 BlacklistOptions options, |
| 470 int wallet_handle, | 632 int wallet_handle, |
| 471 ScopedVector<autofill::PasswordForm>* forms) { | 633 ScopedVector<autofill::PasswordForm>* forms) { |
| 634 forms->clear(); |
| 472 ScopedVector<autofill::PasswordForm> all_forms; | 635 ScopedVector<autofill::PasswordForm> all_forms; |
| 473 if (!GetAllLogins(wallet_handle, &all_forms)) | 636 if (!GetAllLogins(wallet_handle, &all_forms)) |
| 474 return false; | 637 return false; |
| 475 | 638 |
| 476 // We have to read all the entries, and then filter them here. | 639 // We have to read all the entries, and then filter them here. |
| 477 forms->reserve(forms->size() + all_forms.size()); | 640 forms->reserve(all_forms.size()); |
| 478 for (auto& saved_form : all_forms) { | 641 for (auto& saved_form : all_forms) { |
| 479 if (saved_form->blacklisted_by_user == !autofillable) { | 642 if (saved_form->blacklisted_by_user == |
| 643 (options == BlacklistOptions::BLACKLISTED)) { |
| 480 forms->push_back(saved_form); | 644 forms->push_back(saved_form); |
| 481 saved_form = nullptr; | 645 saved_form = nullptr; |
| 482 } | 646 } |
| 483 } | 647 } |
| 484 | 648 |
| 485 return true; | 649 return true; |
| 486 } | 650 } |
| 487 | 651 |
| 488 bool NativeBackendKWallet::GetAllLogins( | 652 bool NativeBackendKWallet::GetAllLogins( |
| 489 int wallet_handle, | 653 int wallet_handle, |
| 490 ScopedVector<autofill::PasswordForm>* forms) { | 654 ScopedVector<autofill::PasswordForm>* forms) { |
| 655 forms->clear(); |
| 491 // We could probably also use readEntryList here. | 656 // We could probably also use readEntryList here. |
| 492 std::vector<std::string> realm_list; | 657 std::vector<std::string> realm_list; |
| 493 { | 658 { |
| 494 dbus::MethodCall method_call(kKWalletInterface, "entryList"); | 659 dbus::MethodCall method_call(kKWalletInterface, "entryList"); |
| 495 dbus::MessageWriter builder(&method_call); | 660 dbus::MessageWriter builder(&method_call); |
| 496 builder.AppendInt32(wallet_handle); // handle | 661 builder.AppendInt32(wallet_handle); // handle |
| 497 builder.AppendString(folder_name_); // folder | 662 builder.AppendString(folder_name_); // folder |
| 498 builder.AppendString(app_name_); // appid | 663 builder.AppendString(app_name_); // appid |
| 499 scoped_ptr<dbus::Response> response( | 664 scoped_ptr<dbus::Response> response( |
| 500 kwallet_proxy_->CallMethodAndBlock( | 665 kwallet_proxy_->CallMethodAndBlock( |
| 501 &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT)); | 666 &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT)); |
| 502 if (!response.get()) { | 667 if (!response.get()) { |
| 503 LOG(ERROR) << "Error contacting kwalletd (entryList)"; | 668 LOG(ERROR) << "Error contacting kwalletd (entryList)"; |
| 504 return false; | 669 return false; |
| 505 } | 670 } |
| 506 dbus::MessageReader reader(response.get()); | 671 dbus::MessageReader reader(response.get()); |
| 507 if (!reader.PopArrayOfStrings(&realm_list)) { | 672 if (!reader.PopArrayOfStrings(&realm_list)) { |
| 508 LOG(ERROR) << "Error reading response from kwalletd (entryList): " | 673 LOG(ERROR) << "Error reading response from kwalletd (entryList): " |
| 509 << response->ToString(); | 674 << response->ToString(); |
| 510 return false; | 675 return false; |
| 511 } | 676 } |
| 512 } | 677 } |
| 513 | 678 |
| 514 for (size_t i = 0; i < realm_list.size(); ++i) { | 679 // Swapping |collected_forms| with |*forms| just before "return true" makes |
| 515 const std::string& signon_realm = realm_list[i]; | 680 // sure partial results are not returned on failure. |
| 681 ScopedVector<autofill::PasswordForm> collected_forms; |
| 682 for (const std::string& signon_realm : realm_list) { |
| 516 dbus::MethodCall method_call(kKWalletInterface, "readEntry"); | 683 dbus::MethodCall method_call(kKWalletInterface, "readEntry"); |
| 517 dbus::MessageWriter builder(&method_call); | 684 dbus::MessageWriter builder(&method_call); |
| 518 builder.AppendInt32(wallet_handle); // handle | 685 builder.AppendInt32(wallet_handle); // handle |
| 519 builder.AppendString(folder_name_); // folder | 686 builder.AppendString(folder_name_); // folder |
| 520 builder.AppendString(signon_realm); // key | 687 builder.AppendString(signon_realm); // key |
| 521 builder.AppendString(app_name_); // appid | 688 builder.AppendString(app_name_); // appid |
| 522 scoped_ptr<dbus::Response> response( | 689 scoped_ptr<dbus::Response> response( |
| 523 kwallet_proxy_->CallMethodAndBlock( | 690 kwallet_proxy_->CallMethodAndBlock( |
| 524 &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT)); | 691 &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT)); |
| 525 if (!response.get()) { | 692 if (!response.get()) { |
| 526 LOG(ERROR) << "Error contacting kwalletd (readEntry)"; | 693 LOG(ERROR) << "Error contacting kwalletd (readEntry)"; |
| 527 return false; | 694 return false; |
| 528 } | 695 } |
| 529 dbus::MessageReader reader(response.get()); | 696 dbus::MessageReader reader(response.get()); |
| 530 const uint8_t* bytes = NULL; | 697 const uint8_t* bytes = NULL; |
| 531 size_t length = 0; | 698 size_t length = 0; |
| 532 if (!reader.PopArrayOfBytes(&bytes, &length)) { | 699 if (!reader.PopArrayOfBytes(&bytes, &length)) { |
| 533 LOG(ERROR) << "Error reading response from kwalletd (readEntry): " | 700 LOG(ERROR) << "Error reading response from kwalletd (readEntry): " |
| 534 << response->ToString(); | 701 << response->ToString(); |
| 535 return false; | 702 return false; |
| 536 } | 703 } |
| 537 if (!bytes || !CheckSerializedValue(bytes, length, signon_realm)) | 704 if (!bytes || !CheckSerializedValue(bytes, length, signon_realm)) |
| 538 continue; | 705 continue; |
| 539 | 706 |
| 540 // Can't we all just agree on whether bytes are signed or not? Please? | 707 // Can't we all just agree on whether bytes are signed or not? Please? |
| 541 Pickle pickle(reinterpret_cast<const char*>(bytes), length); | 708 Pickle pickle(reinterpret_cast<const char*>(bytes), length); |
| 542 DeserializeValue(signon_realm, pickle, forms); | 709 AppendSecondToFirst(&collected_forms, |
| 710 DeserializeValue(signon_realm, pickle)); |
| 543 } | 711 } |
| 712 forms->swap(collected_forms); |
| 544 return true; | 713 return true; |
| 545 } | 714 } |
| 546 | 715 |
| 547 bool NativeBackendKWallet::SetLoginsList( | 716 bool NativeBackendKWallet::SetLoginsList( |
| 548 const std::vector<autofill::PasswordForm*>& forms, | 717 const std::vector<autofill::PasswordForm*>& forms, |
| 549 const std::string& signon_realm, | 718 const std::string& signon_realm, |
| 550 int wallet_handle) { | 719 int wallet_handle) { |
| 551 if (forms.empty()) { | 720 if (forms.empty()) { |
| 552 // No items left? Remove the entry from the wallet. | 721 // No items left? Remove the entry from the wallet. |
| 553 dbus::MethodCall method_call(kKWalletInterface, "removeEntry"); | 722 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)) { | 838 if (!reader.PopArrayOfBytes(&bytes, &length)) { |
| 670 LOG(ERROR) << "Error reading response from kwalletd (readEntry): " | 839 LOG(ERROR) << "Error reading response from kwalletd (readEntry): " |
| 671 << response->ToString(); | 840 << response->ToString(); |
| 672 continue; | 841 continue; |
| 673 } | 842 } |
| 674 if (!bytes || !CheckSerializedValue(bytes, length, signon_realm)) | 843 if (!bytes || !CheckSerializedValue(bytes, length, signon_realm)) |
| 675 continue; | 844 continue; |
| 676 | 845 |
| 677 // Can't we all just agree on whether bytes are signed or not? Please? | 846 // Can't we all just agree on whether bytes are signed or not? Please? |
| 678 Pickle pickle(reinterpret_cast<const char*>(bytes), length); | 847 Pickle pickle(reinterpret_cast<const char*>(bytes), length); |
| 679 ScopedVector<autofill::PasswordForm> all_forms; | 848 ScopedVector<autofill::PasswordForm> all_forms = |
| 680 DeserializeValue(signon_realm, pickle, &all_forms); | 849 DeserializeValue(signon_realm, pickle); |
| 681 | 850 |
| 682 ScopedVector<autofill::PasswordForm> kept_forms; | 851 ScopedVector<autofill::PasswordForm> kept_forms; |
| 683 kept_forms.reserve(all_forms.size()); | 852 kept_forms.reserve(all_forms.size()); |
| 684 base::Time autofill::PasswordForm::*date_member = | 853 base::Time autofill::PasswordForm::*date_member = |
| 685 date_to_compare == CREATION_TIMESTAMP | 854 date_to_compare == CREATION_TIMESTAMP |
| 686 ? &autofill::PasswordForm::date_created | 855 ? &autofill::PasswordForm::date_created |
| 687 : &autofill::PasswordForm::date_synced; | 856 : &autofill::PasswordForm::date_synced; |
| 688 for (auto& saved_form : all_forms) { | 857 for (auto& saved_form : all_forms) { |
| 689 if (delete_begin <= saved_form->*date_member && | 858 if (delete_begin <= saved_form->*date_member && |
| 690 (delete_end.is_null() || saved_form->*date_member < delete_end)) { | 859 (delete_end.is_null() || saved_form->*date_member < delete_end)) { |
| 691 changes->push_back(password_manager::PasswordStoreChange( | 860 changes->push_back(password_manager::PasswordStoreChange( |
| 692 password_manager::PasswordStoreChange::REMOVE, *saved_form)); | 861 password_manager::PasswordStoreChange::REMOVE, *saved_form)); |
| 693 } else { | 862 } else { |
| 694 kept_forms.push_back(saved_form); | 863 kept_forms.push_back(saved_form); |
| 695 saved_form = nullptr; | 864 saved_form = nullptr; |
| 696 } | 865 } |
| 697 } | 866 } |
| 698 | 867 |
| 699 if (!SetLoginsList(kept_forms.get(), signon_realm, wallet_handle)) { | 868 if (!SetLoginsList(kept_forms.get(), signon_realm, wallet_handle)) { |
| 700 ok = false; | 869 ok = false; |
| 701 changes->clear(); | 870 changes->clear(); |
| 702 } | 871 } |
| 703 } | 872 } |
| 704 return ok; | 873 return ok; |
| 705 } | 874 } |
| 706 | 875 |
| 707 // static | 876 // static |
| 708 void NativeBackendKWallet::SerializeValue( | 877 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 } | |
| 735 } | |
| 736 | |
| 737 // static | |
| 738 bool NativeBackendKWallet::DeserializeValueSize( | |
| 739 const std::string& signon_realm, | 878 const std::string& signon_realm, |
| 740 const PickleIterator& init_iter, | 879 const Pickle& pickle) { |
| 741 int version, | |
| 742 bool size_32, | |
| 743 bool warn_only, | |
| 744 ScopedVector<autofill::PasswordForm>* forms) { | |
| 745 PickleIterator iter = init_iter; | |
| 746 | |
| 747 size_t count = 0; | |
| 748 if (size_32) { | |
| 749 uint32_t count_32 = 0; | |
| 750 if (!iter.ReadUInt32(&count_32)) { | |
| 751 LOG(ERROR) << "Failed to deserialize KWallet entry " | |
| 752 << "(realm: " << signon_realm << ")"; | |
| 753 return false; | |
| 754 } | |
| 755 count = count_32; | |
| 756 } else { | |
| 757 if (!iter.ReadSizeT(&count)) { | |
| 758 LOG(ERROR) << "Failed to deserialize KWallet entry " | |
| 759 << "(realm: " << signon_realm << ")"; | |
| 760 return false; | |
| 761 } | |
| 762 } | |
| 763 | |
| 764 if (count > 0xFFFF) { | |
| 765 // Trying to pin down the cause of http://crbug.com/80728 (or fix it). | |
| 766 // This is a very large number of passwords to be saved for a single realm. | |
| 767 // It is almost certainly a corrupt pickle and not real data. Ignore it. | |
| 768 // This very well might actually be http://crbug.com/107701, so if we're | |
| 769 // reading an old pickle, we don't even log this the first time we try to | |
| 770 // read it. (That is, when we're reading the native architecture size.) | |
| 771 if (!warn_only) { | |
| 772 LOG(ERROR) << "Suspiciously large number of entries in KWallet entry " | |
| 773 << "(" << count << "; realm: " << signon_realm << ")"; | |
| 774 } | |
| 775 return false; | |
| 776 } | |
| 777 | |
| 778 forms->reserve(forms->size() + count); | |
| 779 for (size_t i = 0; i < count; ++i) { | |
| 780 scoped_ptr<PasswordForm> form(new PasswordForm()); | |
| 781 form->signon_realm.assign(signon_realm); | |
| 782 | |
| 783 int scheme = 0; | |
| 784 int64 date_created = 0; | |
| 785 int type = 0; | |
| 786 // Note that these will be read back in the order listed due to | |
| 787 // short-circuit evaluation. This is important. | |
| 788 if (!iter.ReadInt(&scheme) || | |
| 789 !ReadGURL(&iter, warn_only, &form->origin) || | |
| 790 !ReadGURL(&iter, warn_only, &form->action) || | |
| 791 !iter.ReadString16(&form->username_element) || | |
| 792 !iter.ReadString16(&form->username_value) || | |
| 793 !iter.ReadString16(&form->password_element) || | |
| 794 !iter.ReadString16(&form->password_value) || | |
| 795 !iter.ReadString16(&form->submit_element) || | |
| 796 !iter.ReadBool(&form->ssl_valid) || | |
| 797 !iter.ReadBool(&form->preferred) || | |
| 798 !iter.ReadBool(&form->blacklisted_by_user) || | |
| 799 !iter.ReadInt64(&date_created)) { | |
| 800 LogDeserializationWarning(version, signon_realm, warn_only); | |
| 801 return false; | |
| 802 } | |
| 803 form->scheme = static_cast<PasswordForm::Scheme>(scheme); | |
| 804 | |
| 805 if (version > 1) { | |
| 806 if (!iter.ReadInt(&type) || | |
| 807 !iter.ReadInt(&form->times_used) || | |
| 808 !autofill::DeserializeFormData(&iter, &form->form_data)) { | |
| 809 LogDeserializationWarning(version, signon_realm, false); | |
| 810 return false; | |
| 811 } | |
| 812 form->type = static_cast<PasswordForm::Type>(type); | |
| 813 } | |
| 814 | |
| 815 if (version > 2) { | |
| 816 int64 date_synced = 0; | |
| 817 if (!iter.ReadInt64(&date_synced)) { | |
| 818 LogDeserializationWarning(version, signon_realm, false); | |
| 819 return false; | |
| 820 } | |
| 821 form->date_synced = base::Time::FromInternalValue(date_synced); | |
| 822 } | |
| 823 | |
| 824 if (version > 3) { | |
| 825 if (!iter.ReadString16(&form->display_name) || | |
| 826 !ReadGURL(&iter, warn_only, &form->avatar_url) || | |
| 827 !ReadGURL(&iter, warn_only, &form->federation_url) || | |
| 828 !iter.ReadBool(&form->skip_zero_click)) { | |
| 829 LogDeserializationWarning(version, signon_realm, false); | |
| 830 return false; | |
| 831 } | |
| 832 } | |
| 833 | |
| 834 if (version > 4) { | |
| 835 form->date_created = base::Time::FromInternalValue(date_created); | |
| 836 } else { | |
| 837 form->date_created = base::Time::FromTimeT(date_created); | |
| 838 } | |
| 839 | |
| 840 forms->push_back(form.release()); | |
| 841 } | |
| 842 | |
| 843 return true; | |
| 844 } | |
| 845 | |
| 846 // static | |
| 847 void NativeBackendKWallet::DeserializeValue( | |
| 848 const std::string& signon_realm, | |
| 849 const Pickle& pickle, | |
| 850 ScopedVector<autofill::PasswordForm>* forms) { | |
| 851 PickleIterator iter(pickle); | 880 PickleIterator iter(pickle); |
| 852 | 881 |
| 853 int version = -1; | 882 int version = -1; |
| 854 if (!iter.ReadInt(&version) || | 883 if (!iter.ReadInt(&version) || |
| 855 version < 0 || version > kPickleVersion) { | 884 version < 0 || version > kPickleVersion) { |
| 856 LOG(ERROR) << "Failed to deserialize KWallet entry " | 885 LOG(ERROR) << "Failed to deserialize KWallet entry " |
| 857 << "(realm: " << signon_realm << ")"; | 886 << "(realm: " << signon_realm << ")"; |
| 858 return; | 887 return ScopedVector<autofill::PasswordForm>(); |
| 859 } | 888 } |
| 860 | 889 |
| 890 ScopedVector<autofill::PasswordForm> forms; |
| 861 if (version > 0) { | 891 if (version > 0) { |
| 862 // In current pickles, we expect 64-bit sizes. Failure is an error. | 892 // In current pickles, we expect 64-bit sizes. Failure is an error. |
| 863 DeserializeValueSize(signon_realm, iter, version, false, false, forms); | 893 DeserializeValueSize(signon_realm, iter, version, false, false, &forms); |
| 864 return; | 894 return forms.Pass(); |
| 865 } | 895 } |
| 866 | 896 |
| 867 const size_t saved_forms_size = forms->size(); | |
| 868 const bool size_32 = sizeof(size_t) == sizeof(uint32_t); | 897 const bool size_32 = sizeof(size_t) == sizeof(uint32_t); |
| 869 if (!DeserializeValueSize( | 898 if (!DeserializeValueSize( |
| 870 signon_realm, iter, version, size_32, true, forms)) { | 899 signon_realm, iter, version, size_32, true, &forms)) { |
| 871 // We failed to read the pickle using the native architecture of the system. | 900 // We failed to read the pickle using the native architecture of the system. |
| 872 // Try again with the opposite architecture. Note that we do this even on | 901 // Try again with the opposite architecture. Note that we do this even on |
| 873 // 32-bit machines, in case we're reading a 64-bit pickle. (Probably rare, | 902 // 32-bit machines, in case we're reading a 64-bit pickle. (Probably rare, |
| 874 // since mostly we expect upgrades, not downgrades, but both are possible.) | 903 // since mostly we expect upgrades, not downgrades, but both are possible.) |
| 875 forms->resize(saved_forms_size); | 904 DeserializeValueSize(signon_realm, iter, version, !size_32, false, &forms); |
| 876 DeserializeValueSize(signon_realm, iter, version, !size_32, false, forms); | |
| 877 } | 905 } |
| 906 return forms.Pass(); |
| 878 } | 907 } |
| 879 | 908 |
| 880 int NativeBackendKWallet::WalletHandle() { | 909 int NativeBackendKWallet::WalletHandle() { |
| 881 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); | 910 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); |
| 882 | 911 |
| 883 // Open the wallet. | 912 // Open the wallet. |
| 884 // TODO(mdm): Are we leaking these handles? Find out. | 913 // TODO(mdm): Are we leaking these handles? Find out. |
| 885 int32_t handle = kInvalidKWalletHandle; | 914 int32_t handle = kInvalidKWalletHandle; |
| 886 { | 915 { |
| 887 dbus::MethodCall method_call(kKWalletInterface, "open"); | 916 dbus::MethodCall method_call(kKWalletInterface, "open"); |
| (...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 959 } | 988 } |
| 960 | 989 |
| 961 return handle; | 990 return handle; |
| 962 } | 991 } |
| 963 | 992 |
| 964 std::string NativeBackendKWallet::GetProfileSpecificFolderName() const { | 993 std::string NativeBackendKWallet::GetProfileSpecificFolderName() const { |
| 965 // Originally, the folder name was always just "Chrome Form Data". | 994 // Originally, the folder name was always just "Chrome Form Data". |
| 966 // Now we use it to distinguish passwords for different profiles. | 995 // Now we use it to distinguish passwords for different profiles. |
| 967 return base::StringPrintf("%s (%d)", kKWalletFolder, profile_id_); | 996 return base::StringPrintf("%s (%d)", kKWalletFolder, profile_id_); |
| 968 } | 997 } |
| OLD | NEW |