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

Unified Diff: chrome/browser/password_manager/native_backend_kwallet_x.cc

Issue 906973007: PasswordStore: Clean up expectations about rewriting vectors of forms (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Just rebased Created 5 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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() {

Powered by Google App Engine
This is Rietveld 408576698