blob: 4b19fa6110c90eef9386a29bd1cdb03024a4ef07 [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/password_manager/password_store_x.h"
#include <algorithm>
#include <map>
#include <utility>
#include <vector>
#include "base/bind.h"
#include "base/files/file_util.h"
#include "base/logging.h"
#include "base/memory/weak_ptr.h"
#include "base/metrics/histogram_functions.h"
#include "base/stl_util.h"
#include "base/time/time.h"
#include "chrome/browser/chrome_notification_types.h"
#include "components/password_manager/core/browser/password_manager_metrics_util.h"
#include "components/password_manager/core/browser/password_manager_util.h"
#include "components/password_manager/core/browser/password_store_change.h"
#include "components/password_manager/core/common/password_manager_features.h"
#include "components/password_manager/core/common/password_manager_pref_names.h"
#include "components/pref_registry/pref_registry_syncable.h"
#include "components/prefs/pref_service.h"
#include "content/public/browser/notification_service.h"
using autofill::PasswordForm;
using password_manager::PasswordStoreChange;
using password_manager::PasswordStoreChangeList;
using password_manager::PasswordStoreDefault;
namespace {
// Returns the password_manager::metrics_util::LinuxBackendMigrationStatus
// equivalent for |step|.
password_manager::metrics_util::LinuxBackendMigrationStatus StepForMetrics(
PasswordStoreX::MigrationToLoginDBStep step) {
using password_manager::metrics_util::LinuxBackendMigrationStatus;
switch (step) {
case PasswordStoreX::NOT_ATTEMPTED:
return LinuxBackendMigrationStatus::kNotAttempted;
case PasswordStoreX::DEPRECATED_FAILED:
return LinuxBackendMigrationStatus::kDeprecatedFailed;
case PasswordStoreX::DEPRECATED_COPIED_ALL:
return LinuxBackendMigrationStatus::kDeprecatedCopiedAll;
case PasswordStoreX::LOGIN_DB_REPLACED:
return LinuxBackendMigrationStatus::kLoginDBReplaced;
case PasswordStoreX::STARTED:
return LinuxBackendMigrationStatus::kStarted;
case PasswordStoreX::POSTPONED:
return LinuxBackendMigrationStatus::kPostponed;
case PasswordStoreX::DEPRECATED_FAILED_CREATE_ENCRYPTED:
return LinuxBackendMigrationStatus::kDeprecatedFailedCreatedEncrypted;
case PasswordStoreX::DEPRECATED_FAILED_ACCESS_NATIVE:
return LinuxBackendMigrationStatus::kDeprecatedFailedAccessNative;
case PasswordStoreX::FAILED_REPLACE:
return LinuxBackendMigrationStatus::kFailedReplace;
case PasswordStoreX::FAILED_INIT_ENCRYPTED:
return LinuxBackendMigrationStatus::kFailedInitEncrypted;
case PasswordStoreX::DEPRECATED_FAILED_RECREATE_ENCRYPTED:
return LinuxBackendMigrationStatus::kDeprecatedFailedRecreateEncrypted;
case PasswordStoreX::FAILED_WRITE_TO_ENCRYPTED:
return LinuxBackendMigrationStatus::kFailedWriteToEncrypted;
}
NOTREACHED();
return LinuxBackendMigrationStatus::kNotAttempted;
}
// Reads and rewrites every entry in |login_db|, reading unecrypted and writing
// encrypted. This should run in a transaction. Returns LOGIN_DB_REPLACED on
// success, otherwise the step where the process failed.
PasswordStoreX::MigrationToLoginDBStep EncryptDatabase(
password_manager::LoginDatabase* login_db) {
// Read the unencrypted entries.
login_db->disable_encryption();
std::map<int, std::unique_ptr<autofill::PasswordForm>> logins;
if (login_db->GetAllLogins(&logins) !=
password_manager::FormRetrievalResult::kSuccess) {
return PasswordStoreX::FAILED_INIT_ENCRYPTED;
}
// Re-insert the entries, encrypted this time.
login_db->enable_encryption();
for (const auto& password_form : logins) {
password_manager::UpdateLoginError error;
PasswordStoreChangeList changes =
login_db->UpdateLogin(*password_form.second, &error);
// Check that an existing entry was replaced.
if (error != password_manager::UpdateLoginError::kNone || changes.empty() ||
changes[0].type() != password_manager::PasswordStoreChange::UPDATE) {
return PasswordStoreX::FAILED_WRITE_TO_ENCRYPTED;
}
}
return PasswordStoreX::LOGIN_DB_REPLACED;
}
// Reads and rewrites every entry in |login_db|, reading unecrypted and writing
// encrypted. Returns LOGIN_DB_REPLACED on success, otherwise the step where the
// process failed.
PasswordStoreX::MigrationToLoginDBStep MigrateUnencryptedDatabase(
password_manager::LoginDatabase* login_db) {
if (!login_db->BeginTransaction())
return PasswordStoreX::POSTPONED;
PasswordStoreX::MigrationToLoginDBStep encryption_result =
EncryptDatabase(login_db);
if (encryption_result == PasswordStoreX::LOGIN_DB_REPLACED) {
if (!login_db->CommitTransaction()) {
return PasswordStoreX::FAILED_REPLACE;
}
} else {
login_db->RollbackTransaction();
}
return encryption_result;
}
} // namespace
PasswordStoreX::PasswordStoreX(
std::unique_ptr<password_manager::LoginDatabase> login_db,
PrefService* prefs)
: PasswordStoreDefault(std::move(login_db)), migration_checked_(false) {
migration_step_pref_.Init(password_manager::prefs::kMigrationToLoginDBStep,
prefs);
migration_to_login_db_step_ =
static_cast<MigrationToLoginDBStep>(migration_step_pref_.GetValue());
base::UmaHistogramEnumeration(
"PasswordManager.LinuxBackendMigration.Adoption",
StepForMetrics(migration_to_login_db_step_));
}
PasswordStoreX::~PasswordStoreX() {}
scoped_refptr<base::SequencedTaskRunner>
PasswordStoreX::CreateBackgroundTaskRunner() const {
return PasswordStoreDefault::CreateBackgroundTaskRunner();
}
PasswordStoreChangeList PasswordStoreX::AddLoginImpl(
const PasswordForm& form,
password_manager::AddLoginError* error) {
CheckMigration();
return PasswordStoreDefault::AddLoginImpl(form, error);
}
PasswordStoreChangeList PasswordStoreX::UpdateLoginImpl(
const PasswordForm& form,
password_manager::UpdateLoginError* error) {
CheckMigration();
return PasswordStoreDefault::UpdateLoginImpl(form, error);
}
PasswordStoreChangeList PasswordStoreX::RemoveLoginImpl(
const PasswordForm& form) {
CheckMigration();
return PasswordStoreDefault::RemoveLoginImpl(form);
}
PasswordStoreChangeList PasswordStoreX::RemoveLoginsByURLAndTimeImpl(
const base::Callback<bool(const GURL&)>& url_filter,
base::Time delete_begin,
base::Time delete_end) {
CheckMigration();
return PasswordStoreDefault::RemoveLoginsByURLAndTimeImpl(
url_filter, delete_begin, delete_end);
}
PasswordStoreChangeList PasswordStoreX::RemoveLoginsCreatedBetweenImpl(
base::Time delete_begin,
base::Time delete_end) {
CheckMigration();
return PasswordStoreDefault::RemoveLoginsCreatedBetweenImpl(delete_begin,
delete_end);
}
PasswordStoreChangeList PasswordStoreX::DisableAutoSignInForOriginsImpl(
const base::Callback<bool(const GURL&)>& origin_filter) {
CheckMigration();
return PasswordStoreDefault::DisableAutoSignInForOriginsImpl(origin_filter);
}
std::vector<std::unique_ptr<PasswordForm>> PasswordStoreX::FillMatchingLogins(
const FormDigest& form) {
CheckMigration();
return PasswordStoreDefault::FillMatchingLogins(form);
}
std::vector<std::unique_ptr<PasswordForm>>
PasswordStoreX::FillMatchingLoginsByPassword(
const base::string16& plain_text_password) {
CheckMigration();
return PasswordStoreDefault::FillMatchingLoginsByPassword(
plain_text_password);
}
bool PasswordStoreX::FillAutofillableLogins(
std::vector<std::unique_ptr<PasswordForm>>* forms) {
CheckMigration();
return PasswordStoreDefault::FillAutofillableLogins(forms);
}
bool PasswordStoreX::FillBlacklistLogins(
std::vector<std::unique_ptr<PasswordForm>>* forms) {
CheckMigration();
return PasswordStoreDefault::FillBlacklistLogins(forms);
}
void PasswordStoreX::CheckMigration() {
DCHECK(background_task_runner()->RunsTasksInCurrentSequence());
if (migration_checked_)
return;
migration_checked_ = true;
if (migration_to_login_db_step_ == LOGIN_DB_REPLACED) {
return;
}
if (!login_db()) {
LOG(ERROR) << "Could not start the migration into the encrypted "
"LoginDatabase because the database failed to initialise.";
return;
}
// If the db is empty, there are no records to migrate, and we then can call
// it a completed migration.
if (login_db()->IsEmpty()) {
UpdateMigrationToLoginDBStep(LOGIN_DB_REPLACED);
} else {
// The user has unencrypted entries in their login database. We will try to
// encrypt them.
UpdateMigrationToLoginDBStep(STARTED);
MigrationToLoginDBStep migration_result =
MigrateUnencryptedDatabase(login_db());
UpdateMigrationToLoginDBStep(migration_result);
// If the migration did not complete, then we will continue using the
// database without encryption.
if (migration_result == LOGIN_DB_REPLACED)
login_db()->enable_encryption();
else
login_db()->disable_encryption();
}
base::UmaHistogramEnumeration(
"PasswordManager.LinuxBackendMigration.AttemptResult",
StepForMetrics(migration_to_login_db_step_));
}
void PasswordStoreX::UpdateMigrationToLoginDBStep(MigrationToLoginDBStep step) {
migration_to_login_db_step_ = step;
main_task_runner()->PostTask(
FROM_HERE, base::BindOnce(&PasswordStoreX::UpdateMigrationPref,
weak_ptr_factory_.GetWeakPtr(),
migration_to_login_db_step_));
}
void PasswordStoreX::UpdateMigrationPref(MigrationToLoginDBStep step) {
migration_step_pref_.SetValue(step);
}
void PasswordStoreX::ShutdownOnUIThread() {
migration_step_pref_.Destroy();
// Invalidate the weak pointer to preempt any posted tasks in
// UpdateMigrationToLoginDBStep() because they cannot use the
// |migration_step_pref_| anymore. Both ShutdownOnUIThread() and
// UpdateMigrationToLoginDBStep() are only executed on the UI thread.
weak_ptr_factory_.InvalidateWeakPtrs();
PasswordStoreDefault::ShutdownOnUIThread();
}
password_manager::FormRetrievalResult PasswordStoreX::ReadAllLogins(
password_manager::PrimaryKeyToFormMap* key_to_form_map) {
CheckMigration();
return PasswordStoreDefault::ReadAllLogins(key_to_form_map);
}
PasswordStoreChangeList PasswordStoreX::RemoveLoginByPrimaryKeySync(
int primary_key) {
CheckMigration();
return PasswordStoreDefault::RemoveLoginByPrimaryKeySync(primary_key);
}
password_manager::PasswordStoreSync::MetadataStore*
PasswordStoreX::GetMetadataStore() {
CheckMigration();
return PasswordStoreDefault::GetMetadataStore();
}