| Index: chrome/browser/password_manager/native_backend_kwallet_x.cc
|
| diff --git a/chrome/browser/password_manager/native_backend_kwallet_x.cc b/chrome/browser/password_manager/native_backend_kwallet_x.cc
|
| index bb3d5f5e249c259b936aad1f745d60fedff0fd18..7c9a0a9d4992b29a6c8f41b10aac5360f216aab4 100644
|
| --- a/chrome/browser/password_manager/native_backend_kwallet_x.cc
|
| +++ b/chrome/browser/password_manager/native_backend_kwallet_x.cc
|
| @@ -27,6 +27,10 @@ using content::BrowserThread;
|
|
|
| namespace {
|
|
|
| +// In case the fields in the pickle ever change, version them so we can try to
|
| +// read old pickles. (Note: do not eat old pickles past the expiration date.)
|
| +const int kPickleVersion = 6;
|
| +
|
| // We could localize this string, but then changing your locale would cause
|
| // you to lose access to all your stored passwords. Maybe best not to do that.
|
| // Name of the folder to store passwords in.
|
| @@ -100,6 +104,171 @@ void LogDeserializationWarning(int version,
|
| }
|
| }
|
|
|
| +// Deserializes a list of credentials from the wallet to |forms| (replacing
|
| +// the contents of |forms|). |size_32| controls reading the size field within
|
| +// the pickle as 32 bits. We used to use Pickle::WriteSize() to write the number
|
| +// of password forms, but that has a different size on 32- and 64-bit systems.
|
| +// So, now we always write a 64-bit quantity, but we support trying to read it
|
| +// as either size when reading old pickles that fail to deserialize using the
|
| +// native size. Returns true on success.
|
| +bool DeserializeValueSize(const std::string& signon_realm,
|
| + const PickleIterator& init_iter,
|
| + int version,
|
| + bool size_32,
|
| + bool warn_only,
|
| + ScopedVector<autofill::PasswordForm>* forms) {
|
| + PickleIterator iter = init_iter;
|
| +
|
| + size_t count = 0;
|
| + if (size_32) {
|
| + uint32_t count_32 = 0;
|
| + if (!iter.ReadUInt32(&count_32)) {
|
| + LOG(ERROR) << "Failed to deserialize KWallet entry "
|
| + << "(realm: " << signon_realm << ")";
|
| + return false;
|
| + }
|
| + count = count_32;
|
| + } else {
|
| + if (!iter.ReadSizeT(&count)) {
|
| + LOG(ERROR) << "Failed to deserialize KWallet entry "
|
| + << "(realm: " << signon_realm << ")";
|
| + return false;
|
| + }
|
| + }
|
| +
|
| + if (count > 0xFFFF) {
|
| + // Trying to pin down the cause of http://crbug.com/80728 (or fix it).
|
| + // This is a very large number of passwords to be saved for a single realm.
|
| + // It is almost certainly a corrupt pickle and not real data. Ignore it.
|
| + // This very well might actually be http://crbug.com/107701, so if we're
|
| + // reading an old pickle, we don't even log this the first time we try to
|
| + // read it. (That is, when we're reading the native architecture size.)
|
| + if (!warn_only) {
|
| + LOG(ERROR) << "Suspiciously large number of entries in KWallet entry "
|
| + << "(" << count << "; realm: " << signon_realm << ")";
|
| + }
|
| + return false;
|
| + }
|
| +
|
| + // We'll swap |converted_forms| with |*forms| on success, to make sure we
|
| + // don't return partial results on failure.
|
| + ScopedVector<autofill::PasswordForm> converted_forms;
|
| + converted_forms.reserve(count);
|
| + for (size_t i = 0; i < count; ++i) {
|
| + scoped_ptr<PasswordForm> form(new PasswordForm());
|
| + form->signon_realm.assign(signon_realm);
|
| +
|
| + int scheme = 0;
|
| + int64 date_created = 0;
|
| + int type = 0;
|
| + int generation_upload_status = 0;
|
| + // Note that these will be read back in the order listed due to
|
| + // short-circuit evaluation. This is important.
|
| + if (!iter.ReadInt(&scheme) ||
|
| + !ReadGURL(&iter, warn_only, &form->origin) ||
|
| + !ReadGURL(&iter, warn_only, &form->action) ||
|
| + !iter.ReadString16(&form->username_element) ||
|
| + !iter.ReadString16(&form->username_value) ||
|
| + !iter.ReadString16(&form->password_element) ||
|
| + !iter.ReadString16(&form->password_value) ||
|
| + !iter.ReadString16(&form->submit_element) ||
|
| + !iter.ReadBool(&form->ssl_valid) ||
|
| + !iter.ReadBool(&form->preferred) ||
|
| + !iter.ReadBool(&form->blacklisted_by_user) ||
|
| + !iter.ReadInt64(&date_created)) {
|
| + LogDeserializationWarning(version, signon_realm, warn_only);
|
| + return false;
|
| + }
|
| + form->scheme = static_cast<PasswordForm::Scheme>(scheme);
|
| +
|
| + if (version > 1) {
|
| + if (!iter.ReadInt(&type) ||
|
| + !iter.ReadInt(&form->times_used) ||
|
| + !autofill::DeserializeFormData(&iter, &form->form_data)) {
|
| + LogDeserializationWarning(version, signon_realm, false);
|
| + return false;
|
| + }
|
| + form->type = static_cast<PasswordForm::Type>(type);
|
| + }
|
| +
|
| + if (version > 2) {
|
| + int64 date_synced = 0;
|
| + if (!iter.ReadInt64(&date_synced)) {
|
| + LogDeserializationWarning(version, signon_realm, false);
|
| + return false;
|
| + }
|
| + form->date_synced = base::Time::FromInternalValue(date_synced);
|
| + }
|
| +
|
| + if (version > 3) {
|
| + if (!iter.ReadString16(&form->display_name) ||
|
| + !ReadGURL(&iter, warn_only, &form->avatar_url) ||
|
| + !ReadGURL(&iter, warn_only, &form->federation_url) ||
|
| + !iter.ReadBool(&form->skip_zero_click)) {
|
| + LogDeserializationWarning(version, signon_realm, false);
|
| + return false;
|
| + }
|
| + }
|
| +
|
| + if (version > 4) {
|
| + form->date_created = base::Time::FromInternalValue(date_created);
|
| + } else {
|
| + form->date_created = base::Time::FromTimeT(date_created);
|
| + }
|
| +
|
| + if (version > 5) {
|
| + if (!iter.ReadInt(&generation_upload_status)) {
|
| + LogDeserializationWarning(version, signon_realm, false);
|
| + }
|
| + form->generation_upload_status =
|
| + static_cast<PasswordForm::GenerationUploadStatus>(
|
| + generation_upload_status);
|
| + }
|
| +
|
| + converted_forms.push_back(form.release());
|
| + }
|
| +
|
| + forms->swap(converted_forms);
|
| + return true;
|
| +}
|
| +
|
| +// Serializes a list of PasswordForms to be stored in the wallet.
|
| +void SerializeValue(const std::vector<autofill::PasswordForm*>& forms,
|
| + Pickle* pickle) {
|
| + pickle->WriteInt(kPickleVersion);
|
| + pickle->WriteSizeT(forms.size());
|
| + for (autofill::PasswordForm* form : forms) {
|
| + pickle->WriteInt(form->scheme);
|
| + pickle->WriteString(form->origin.spec());
|
| + pickle->WriteString(form->action.spec());
|
| + pickle->WriteString16(form->username_element);
|
| + pickle->WriteString16(form->username_value);
|
| + pickle->WriteString16(form->password_element);
|
| + pickle->WriteString16(form->password_value);
|
| + pickle->WriteString16(form->submit_element);
|
| + pickle->WriteBool(form->ssl_valid);
|
| + pickle->WriteBool(form->preferred);
|
| + pickle->WriteBool(form->blacklisted_by_user);
|
| + pickle->WriteInt64(form->date_created.ToInternalValue());
|
| + pickle->WriteInt(form->type);
|
| + pickle->WriteInt(form->times_used);
|
| + autofill::SerializeFormData(form->form_data, pickle);
|
| + pickle->WriteInt64(form->date_synced.ToInternalValue());
|
| + pickle->WriteString16(form->display_name);
|
| + pickle->WriteString(form->avatar_url.spec());
|
| + pickle->WriteString(form->federation_url.spec());
|
| + pickle->WriteBool(form->skip_zero_click);
|
| + }
|
| +}
|
| +
|
| +// Moves the content of |second| to the end of |first|.
|
| +void AppendSecondToFirst(ScopedVector<autofill::PasswordForm>* first,
|
| + ScopedVector<autofill::PasswordForm> second) {
|
| + first->reserve(first->size() + second.size());
|
| + first->insert(first->end(), second.begin(), second.end());
|
| + second.weak_clear();
|
| +}
|
| +
|
| } // namespace
|
|
|
| NativeBackendKWallet::NativeBackendKWallet(LocalProfileId id)
|
| @@ -272,7 +441,8 @@ password_manager::PasswordStoreChangeList NativeBackendKWallet::AddLogin(
|
| return password_manager::PasswordStoreChangeList();
|
|
|
| ScopedVector<autofill::PasswordForm> forms;
|
| - GetLoginsList(form.signon_realm, wallet_handle, &forms);
|
| + if (!GetLoginsList(form.signon_realm, wallet_handle, &forms))
|
| + return password_manager::PasswordStoreChangeList();
|
|
|
| // We search for a login to update, rather than unconditionally appending the
|
| // login, because in some cases (especially involving sync) we can be asked to
|
| @@ -311,7 +481,8 @@ bool NativeBackendKWallet::UpdateLogin(
|
| return false;
|
|
|
| ScopedVector<autofill::PasswordForm> forms;
|
| - GetLoginsList(form.signon_realm, wallet_handle, &forms);
|
| + if (!GetLoginsList(form.signon_realm, wallet_handle, &forms))
|
| + return false;
|
|
|
| bool updated = false;
|
| for (size_t i = 0; i < forms.size(); ++i) {
|
| @@ -338,7 +509,8 @@ bool NativeBackendKWallet::RemoveLogin(const PasswordForm& form) {
|
| return false;
|
|
|
| ScopedVector<autofill::PasswordForm> all_forms;
|
| - GetLoginsList(form.signon_realm, wallet_handle, &all_forms);
|
| + if (!GetLoginsList(form.signon_realm, wallet_handle, &all_forms))
|
| + return false;
|
|
|
| ScopedVector<autofill::PasswordForm> kept_forms;
|
| kept_forms.reserve(all_forms.size());
|
| @@ -382,7 +554,7 @@ bool NativeBackendKWallet::GetAutofillableLogins(
|
| int wallet_handle = WalletHandle();
|
| if (wallet_handle == kInvalidKWalletHandle)
|
| return false;
|
| - return GetLoginsList(true, wallet_handle, forms);
|
| + return GetLoginsList(BlacklistOptions::AUTOFILLABLE, wallet_handle, forms);
|
| }
|
|
|
| bool NativeBackendKWallet::GetBlacklistLogins(
|
| @@ -390,13 +562,14 @@ bool NativeBackendKWallet::GetBlacklistLogins(
|
| int wallet_handle = WalletHandle();
|
| if (wallet_handle == kInvalidKWalletHandle)
|
| return false;
|
| - return GetLoginsList(false, wallet_handle, forms);
|
| + return GetLoginsList(BlacklistOptions::BLACKLISTED, wallet_handle, forms);
|
| }
|
|
|
| bool NativeBackendKWallet::GetLoginsList(
|
| const std::string& signon_realm,
|
| int wallet_handle,
|
| ScopedVector<autofill::PasswordForm>* forms) {
|
| + forms->clear();
|
| // Is there an entry in the wallet?
|
| {
|
| dbus::MethodCall method_call(kKWalletInterface, "hasEntry");
|
| @@ -459,24 +632,26 @@ bool NativeBackendKWallet::GetLoginsList(
|
|
|
| // Can't we all just agree on whether bytes are signed or not? Please?
|
| Pickle pickle(reinterpret_cast<const char*>(bytes), length);
|
| - DeserializeValue(signon_realm, pickle, forms);
|
| + *forms = DeserializeValue(signon_realm, pickle);
|
| }
|
|
|
| return true;
|
| }
|
|
|
| bool NativeBackendKWallet::GetLoginsList(
|
| - bool autofillable,
|
| + BlacklistOptions options,
|
| int wallet_handle,
|
| ScopedVector<autofill::PasswordForm>* forms) {
|
| + forms->clear();
|
| ScopedVector<autofill::PasswordForm> all_forms;
|
| if (!GetAllLogins(wallet_handle, &all_forms))
|
| return false;
|
|
|
| // We have to read all the entries, and then filter them here.
|
| - forms->reserve(forms->size() + all_forms.size());
|
| + forms->reserve(all_forms.size());
|
| for (auto& saved_form : all_forms) {
|
| - if (saved_form->blacklisted_by_user == !autofillable) {
|
| + if (saved_form->blacklisted_by_user ==
|
| + (options == BlacklistOptions::BLACKLISTED)) {
|
| forms->push_back(saved_form);
|
| saved_form = nullptr;
|
| }
|
| @@ -511,8 +686,8 @@ bool NativeBackendKWallet::GetAllLogins(
|
| }
|
| }
|
|
|
| - for (size_t i = 0; i < realm_list.size(); ++i) {
|
| - const std::string& signon_realm = realm_list[i];
|
| + forms->clear();
|
| + for (const std::string& signon_realm : realm_list) {
|
| dbus::MethodCall method_call(kKWalletInterface, "readEntry");
|
| dbus::MessageWriter builder(&method_call);
|
| builder.AppendInt32(wallet_handle); // handle
|
| @@ -539,7 +714,7 @@ bool NativeBackendKWallet::GetAllLogins(
|
|
|
| // Can't we all just agree on whether bytes are signed or not? Please?
|
| Pickle pickle(reinterpret_cast<const char*>(bytes), length);
|
| - DeserializeValue(signon_realm, pickle, forms);
|
| + AppendSecondToFirst(forms, DeserializeValue(signon_realm, pickle));
|
| }
|
| return true;
|
| }
|
| @@ -676,8 +851,8 @@ bool NativeBackendKWallet::RemoveLoginsBetween(
|
|
|
| // Can't we all just agree on whether bytes are signed or not? Please?
|
| Pickle pickle(reinterpret_cast<const char*>(bytes), length);
|
| - ScopedVector<autofill::PasswordForm> all_forms;
|
| - DeserializeValue(signon_realm, pickle, &all_forms);
|
| + ScopedVector<autofill::PasswordForm> all_forms =
|
| + DeserializeValue(signon_realm, pickle);
|
|
|
| ScopedVector<autofill::PasswordForm> kept_forms;
|
| kept_forms.reserve(all_forms.size());
|
| @@ -705,160 +880,9 @@ bool NativeBackendKWallet::RemoveLoginsBetween(
|
| }
|
|
|
| // static
|
| -void NativeBackendKWallet::SerializeValue(
|
| - const std::vector<autofill::PasswordForm*>& forms,
|
| - Pickle* pickle) {
|
| - pickle->WriteInt(kPickleVersion);
|
| - pickle->WriteSizeT(forms.size());
|
| - for (autofill::PasswordForm* form : forms) {
|
| - pickle->WriteInt(form->scheme);
|
| - pickle->WriteString(form->origin.spec());
|
| - pickle->WriteString(form->action.spec());
|
| - pickle->WriteString16(form->username_element);
|
| - pickle->WriteString16(form->username_value);
|
| - pickle->WriteString16(form->password_element);
|
| - pickle->WriteString16(form->password_value);
|
| - pickle->WriteString16(form->submit_element);
|
| - pickle->WriteBool(form->ssl_valid);
|
| - pickle->WriteBool(form->preferred);
|
| - pickle->WriteBool(form->blacklisted_by_user);
|
| - pickle->WriteInt64(form->date_created.ToInternalValue());
|
| - pickle->WriteInt(form->type);
|
| - pickle->WriteInt(form->times_used);
|
| - autofill::SerializeFormData(form->form_data, pickle);
|
| - pickle->WriteInt64(form->date_synced.ToInternalValue());
|
| - pickle->WriteString16(form->display_name);
|
| - pickle->WriteString(form->avatar_url.spec());
|
| - pickle->WriteString(form->federation_url.spec());
|
| - pickle->WriteBool(form->skip_zero_click);
|
| - pickle->WriteInt(form->generation_upload_status);
|
| - }
|
| -}
|
| -
|
| -// static
|
| -bool NativeBackendKWallet::DeserializeValueSize(
|
| - const std::string& signon_realm,
|
| - const PickleIterator& init_iter,
|
| - int version,
|
| - bool size_32,
|
| - bool warn_only,
|
| - ScopedVector<autofill::PasswordForm>* forms) {
|
| - PickleIterator iter = init_iter;
|
| -
|
| - size_t count = 0;
|
| - if (size_32) {
|
| - uint32_t count_32 = 0;
|
| - if (!iter.ReadUInt32(&count_32)) {
|
| - LOG(ERROR) << "Failed to deserialize KWallet entry "
|
| - << "(realm: " << signon_realm << ")";
|
| - return false;
|
| - }
|
| - count = count_32;
|
| - } else {
|
| - if (!iter.ReadSizeT(&count)) {
|
| - LOG(ERROR) << "Failed to deserialize KWallet entry "
|
| - << "(realm: " << signon_realm << ")";
|
| - return false;
|
| - }
|
| - }
|
| -
|
| - if (count > 0xFFFF) {
|
| - // Trying to pin down the cause of http://crbug.com/80728 (or fix it).
|
| - // This is a very large number of passwords to be saved for a single realm.
|
| - // It is almost certainly a corrupt pickle and not real data. Ignore it.
|
| - // This very well might actually be http://crbug.com/107701, so if we're
|
| - // reading an old pickle, we don't even log this the first time we try to
|
| - // read it. (That is, when we're reading the native architecture size.)
|
| - if (!warn_only) {
|
| - LOG(ERROR) << "Suspiciously large number of entries in KWallet entry "
|
| - << "(" << count << "; realm: " << signon_realm << ")";
|
| - }
|
| - return false;
|
| - }
|
| -
|
| - forms->reserve(forms->size() + count);
|
| - for (size_t i = 0; i < count; ++i) {
|
| - scoped_ptr<PasswordForm> form(new PasswordForm());
|
| - form->signon_realm.assign(signon_realm);
|
| -
|
| - int scheme = 0;
|
| - int64 date_created = 0;
|
| - int type = 0;
|
| - int generation_upload_status = 0;
|
| - // Note that these will be read back in the order listed due to
|
| - // short-circuit evaluation. This is important.
|
| - if (!iter.ReadInt(&scheme) ||
|
| - !ReadGURL(&iter, warn_only, &form->origin) ||
|
| - !ReadGURL(&iter, warn_only, &form->action) ||
|
| - !iter.ReadString16(&form->username_element) ||
|
| - !iter.ReadString16(&form->username_value) ||
|
| - !iter.ReadString16(&form->password_element) ||
|
| - !iter.ReadString16(&form->password_value) ||
|
| - !iter.ReadString16(&form->submit_element) ||
|
| - !iter.ReadBool(&form->ssl_valid) ||
|
| - !iter.ReadBool(&form->preferred) ||
|
| - !iter.ReadBool(&form->blacklisted_by_user) ||
|
| - !iter.ReadInt64(&date_created)) {
|
| - LogDeserializationWarning(version, signon_realm, warn_only);
|
| - return false;
|
| - }
|
| - form->scheme = static_cast<PasswordForm::Scheme>(scheme);
|
| -
|
| - if (version > 1) {
|
| - if (!iter.ReadInt(&type) ||
|
| - !iter.ReadInt(&form->times_used) ||
|
| - !autofill::DeserializeFormData(&iter, &form->form_data)) {
|
| - LogDeserializationWarning(version, signon_realm, false);
|
| - return false;
|
| - }
|
| - form->type = static_cast<PasswordForm::Type>(type);
|
| - }
|
| -
|
| - if (version > 2) {
|
| - int64 date_synced = 0;
|
| - if (!iter.ReadInt64(&date_synced)) {
|
| - LogDeserializationWarning(version, signon_realm, false);
|
| - return false;
|
| - }
|
| - form->date_synced = base::Time::FromInternalValue(date_synced);
|
| - }
|
| -
|
| - if (version > 3) {
|
| - if (!iter.ReadString16(&form->display_name) ||
|
| - !ReadGURL(&iter, warn_only, &form->avatar_url) ||
|
| - !ReadGURL(&iter, warn_only, &form->federation_url) ||
|
| - !iter.ReadBool(&form->skip_zero_click)) {
|
| - LogDeserializationWarning(version, signon_realm, false);
|
| - return false;
|
| - }
|
| - }
|
| -
|
| - if (version > 4) {
|
| - form->date_created = base::Time::FromInternalValue(date_created);
|
| - } else {
|
| - form->date_created = base::Time::FromTimeT(date_created);
|
| - }
|
| -
|
| - if (version > 5) {
|
| - if (!iter.ReadInt(&generation_upload_status)) {
|
| - LogDeserializationWarning(version, signon_realm, false);
|
| - }
|
| - form->generation_upload_status =
|
| - static_cast<PasswordForm::GenerationUploadStatus>(
|
| - generation_upload_status);
|
| - }
|
| -
|
| - forms->push_back(form.release());
|
| - }
|
| -
|
| - return true;
|
| -}
|
| -
|
| -// static
|
| -void NativeBackendKWallet::DeserializeValue(
|
| +ScopedVector<autofill::PasswordForm> NativeBackendKWallet::DeserializeValue(
|
| const std::string& signon_realm,
|
| - const Pickle& pickle,
|
| - ScopedVector<autofill::PasswordForm>* forms) {
|
| + const Pickle& pickle) {
|
| PickleIterator iter(pickle);
|
|
|
| int version = -1;
|
| @@ -866,26 +890,26 @@ void NativeBackendKWallet::DeserializeValue(
|
| version < 0 || version > kPickleVersion) {
|
| LOG(ERROR) << "Failed to deserialize KWallet entry "
|
| << "(realm: " << signon_realm << ")";
|
| - return;
|
| + return ScopedVector<autofill::PasswordForm>();
|
| }
|
|
|
| + ScopedVector<autofill::PasswordForm> forms;
|
| if (version > 0) {
|
| // In current pickles, we expect 64-bit sizes. Failure is an error.
|
| - DeserializeValueSize(signon_realm, iter, version, false, false, forms);
|
| - return;
|
| + DeserializeValueSize(signon_realm, iter, version, false, false, &forms);
|
| + return forms.Pass();
|
| }
|
|
|
| - const size_t saved_forms_size = forms->size();
|
| const bool size_32 = sizeof(size_t) == sizeof(uint32_t);
|
| if (!DeserializeValueSize(
|
| - signon_realm, iter, version, size_32, true, forms)) {
|
| + signon_realm, iter, version, size_32, true, &forms)) {
|
| // We failed to read the pickle using the native architecture of the system.
|
| // Try again with the opposite architecture. Note that we do this even on
|
| // 32-bit machines, in case we're reading a 64-bit pickle. (Probably rare,
|
| // since mostly we expect upgrades, not downgrades, but both are possible.)
|
| - forms->resize(saved_forms_size);
|
| - DeserializeValueSize(signon_realm, iter, version, !size_32, false, forms);
|
| + DeserializeValueSize(signon_realm, iter, version, !size_32, false, &forms);
|
| }
|
| + return forms.Pass();
|
| }
|
|
|
| int NativeBackendKWallet::WalletHandle() {
|
|
|