Home | History | Annotate | Download | only in password_manager
      1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "chrome/browser/password_manager/password_store_x.h"
      6 
      7 #include <map>
      8 #include <vector>
      9 
     10 #include "base/logging.h"
     11 #include "base/stl_util-inl.h"
     12 #include "chrome/browser/password_manager/password_store_change.h"
     13 #include "content/browser/browser_thread.h"
     14 #include "content/common/notification_service.h"
     15 
     16 using std::vector;
     17 using webkit_glue::PasswordForm;
     18 
     19 PasswordStoreX::PasswordStoreX(LoginDatabase* login_db,
     20                                Profile* profile,
     21                                WebDataService* web_data_service,
     22                                NativeBackend* backend)
     23     : PasswordStoreDefault(login_db, profile, web_data_service),
     24       backend_(backend), migration_checked_(!backend), allow_fallback_(false) {
     25 }
     26 
     27 PasswordStoreX::~PasswordStoreX() {
     28 }
     29 
     30 void PasswordStoreX::AddLoginImpl(const PasswordForm& form) {
     31   CheckMigration();
     32   if (use_native_backend() && backend_->AddLogin(form)) {
     33     PasswordStoreChangeList changes;
     34     changes.push_back(PasswordStoreChange(PasswordStoreChange::ADD, form));
     35     NotificationService::current()->Notify(
     36         NotificationType::LOGINS_CHANGED,
     37         Source<PasswordStore>(this),
     38         Details<PasswordStoreChangeList>(&changes));
     39     allow_fallback_ = false;
     40   } else if (allow_default_store()) {
     41     PasswordStoreDefault::AddLoginImpl(form);
     42   }
     43 }
     44 
     45 void PasswordStoreX::UpdateLoginImpl(const PasswordForm& form) {
     46   CheckMigration();
     47   if (use_native_backend() && backend_->UpdateLogin(form)) {
     48     PasswordStoreChangeList changes;
     49     changes.push_back(PasswordStoreChange(PasswordStoreChange::UPDATE, form));
     50     NotificationService::current()->Notify(
     51         NotificationType::LOGINS_CHANGED,
     52         Source<PasswordStore>(this),
     53         Details<PasswordStoreChangeList>(&changes));
     54     allow_fallback_ = false;
     55   } else if (allow_default_store()) {
     56     PasswordStoreDefault::UpdateLoginImpl(form);
     57   }
     58 }
     59 
     60 void PasswordStoreX::RemoveLoginImpl(const PasswordForm& form) {
     61   CheckMigration();
     62   if (use_native_backend() && backend_->RemoveLogin(form)) {
     63     PasswordStoreChangeList changes;
     64     changes.push_back(PasswordStoreChange(PasswordStoreChange::REMOVE, form));
     65     NotificationService::current()->Notify(
     66         NotificationType::LOGINS_CHANGED,
     67         Source<PasswordStore>(this),
     68         Details<PasswordStoreChangeList>(&changes));
     69     allow_fallback_ = false;
     70   } else if (allow_default_store()) {
     71     PasswordStoreDefault::RemoveLoginImpl(form);
     72   }
     73 }
     74 
     75 void PasswordStoreX::RemoveLoginsCreatedBetweenImpl(
     76     const base::Time& delete_begin,
     77     const base::Time& delete_end) {
     78   CheckMigration();
     79   vector<PasswordForm*> forms;
     80   if (use_native_backend() &&
     81       backend_->GetLoginsCreatedBetween(delete_begin, delete_end, &forms) &&
     82       backend_->RemoveLoginsCreatedBetween(delete_begin, delete_end)) {
     83     PasswordStoreChangeList changes;
     84     for (vector<PasswordForm*>::const_iterator it = forms.begin();
     85          it != forms.end(); ++it) {
     86       changes.push_back(PasswordStoreChange(PasswordStoreChange::REMOVE,
     87                                             **it));
     88     }
     89     NotificationService::current()->Notify(
     90         NotificationType::LOGINS_CHANGED,
     91         Source<PasswordStore>(this),
     92         Details<PasswordStoreChangeList>(&changes));
     93     allow_fallback_ = false;
     94   } else if (allow_default_store()) {
     95     PasswordStoreDefault::RemoveLoginsCreatedBetweenImpl(delete_begin,
     96                                                          delete_end);
     97   }
     98   STLDeleteElements(&forms);
     99 }
    100 
    101 void PasswordStoreX::GetLoginsImpl(GetLoginsRequest* request,
    102                                    const PasswordForm& form) {
    103   CheckMigration();
    104   if (use_native_backend() && backend_->GetLogins(form, &request->value)) {
    105     ForwardLoginsResult(request);
    106     allow_fallback_ = false;
    107   } else if (allow_default_store()) {
    108     PasswordStoreDefault::GetLoginsImpl(request, form);
    109   } else {
    110     // The consumer will be left hanging unless we reply.
    111     ForwardLoginsResult(request);
    112   }
    113 }
    114 
    115 void PasswordStoreX::GetAutofillableLoginsImpl(GetLoginsRequest* request) {
    116   CheckMigration();
    117   if (use_native_backend() &&
    118       backend_->GetAutofillableLogins(&request->value)) {
    119     ForwardLoginsResult(request);
    120     allow_fallback_ = false;
    121   } else if (allow_default_store()) {
    122     PasswordStoreDefault::GetAutofillableLoginsImpl(request);
    123   } else {
    124     // The consumer will be left hanging unless we reply.
    125     ForwardLoginsResult(request);
    126   }
    127 }
    128 
    129 void PasswordStoreX::GetBlacklistLoginsImpl(GetLoginsRequest* request) {
    130   CheckMigration();
    131   if (use_native_backend() &&
    132       backend_->GetBlacklistLogins(&request->value)) {
    133     ForwardLoginsResult(request);
    134     allow_fallback_ = false;
    135   } else if (allow_default_store()) {
    136     PasswordStoreDefault::GetBlacklistLoginsImpl(request);
    137   } else {
    138     // The consumer will be left hanging unless we reply.
    139     ForwardLoginsResult(request);
    140   }
    141 }
    142 
    143 bool PasswordStoreX::FillAutofillableLogins(vector<PasswordForm*>* forms) {
    144   CheckMigration();
    145   if (use_native_backend() && backend_->GetAutofillableLogins(forms)) {
    146     allow_fallback_ = false;
    147     return true;
    148   }
    149   if (allow_default_store())
    150     return PasswordStoreDefault::FillAutofillableLogins(forms);
    151   return false;
    152 }
    153 
    154 bool PasswordStoreX::FillBlacklistLogins(vector<PasswordForm*>* forms) {
    155   CheckMigration();
    156   if (use_native_backend() && backend_->GetBlacklistLogins(forms)) {
    157     allow_fallback_ = false;
    158     return true;
    159   }
    160   if (allow_default_store())
    161     return PasswordStoreDefault::FillBlacklistLogins(forms);
    162   return false;
    163 }
    164 
    165 void PasswordStoreX::CheckMigration() {
    166   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
    167   if (migration_checked_ || !backend_.get())
    168     return;
    169   migration_checked_ = true;
    170   ssize_t migrated = MigrateLogins();
    171   if (migrated > 0) {
    172     VLOG(1) << "Migrated " << migrated << " passwords to native store.";
    173   } else if (migrated == 0) {
    174     // As long as we are able to migrate some passwords, we know the native
    175     // store is working. But if there is nothing to migrate, the "migration"
    176     // can succeed even when the native store would fail. In this case we
    177     // allow a later fallback to the default store. Once any later operation
    178     // succeeds on the native store, we will no longer allow it.
    179     allow_fallback_ = true;
    180   } else {
    181     LOG(WARNING) << "Native password store migration failed! " <<
    182                  "Falling back on default (unencrypted) store.";
    183     backend_.reset(NULL);
    184   }
    185 }
    186 
    187 bool PasswordStoreX::allow_default_store() {
    188   if (allow_fallback_) {
    189     LOG(WARNING) << "Native password store failed! " <<
    190                  "Falling back on default (unencrypted) store.";
    191     backend_.reset(NULL);
    192     // Don't warn again. We'll use the default store because backend_ is NULL.
    193     allow_fallback_ = false;
    194   }
    195   return !backend_.get();
    196 }
    197 
    198 ssize_t PasswordStoreX::MigrateLogins() {
    199   DCHECK(backend_.get());
    200   vector<PasswordForm*> forms;
    201   bool ok = PasswordStoreDefault::FillAutofillableLogins(&forms) &&
    202       PasswordStoreDefault::FillBlacklistLogins(&forms);
    203   if (ok) {
    204     // We add all the passwords (and blacklist entries) to the native backend
    205     // before attempting to remove any from the login database, to make sure we
    206     // don't somehow end up with some of the passwords in one store and some in
    207     // another. We'll always have at least one intact store this way.
    208     for (size_t i = 0; i < forms.size(); ++i) {
    209       if (!backend_->AddLogin(*forms[i])) {
    210         ok = false;
    211         break;
    212       }
    213     }
    214     if (forms.empty()) {
    215       // If there's nothing to migrate, then we try to insert a dummy login form
    216       // just to force the native store to unlock if it was locked. We delete it
    217       // right away if we are successful. If the first operation we try to do is
    218       // a read, then in some cases this is just an error rather than an action
    219       // that causes the native store to prompt the user to unlock.
    220       // TODO(mdm): this means we no longer need the allow_fallback mechanism.
    221       // Remove it once this preemptive unlock by write is baked for a while.
    222       PasswordForm dummy;
    223       dummy.origin = GURL("http://www.example.com/force-keyring-unlock");
    224       dummy.signon_realm = "www.example.com";
    225       if (backend_->AddLogin(dummy))
    226         backend_->RemoveLogin(dummy);
    227       else
    228         ok = false;
    229     }
    230     if (ok) {
    231       for (size_t i = 0; i < forms.size(); ++i) {
    232         // If even one of these calls to RemoveLoginImpl() succeeds, then we
    233         // should prefer the native backend to the now-incomplete login
    234         // database. Thus we want to return a success status even in the case
    235         // where some fail. The only real problem with this is that we might
    236         // leave passwords in the login database and never come back to clean
    237         // them out if any of these calls do fail.
    238         PasswordStoreDefault::RemoveLoginImpl(*forms[i]);
    239       }
    240       // Finally, delete the database file itself. We remove the passwords from
    241       // it before deleting the file just in case there is some problem deleting
    242       // the file (e.g. directory is not writable, but file is), which would
    243       // otherwise cause passwords to re-migrate next (or maybe every) time.
    244       DeleteAndRecreateDatabaseFile();
    245     }
    246   }
    247   ssize_t result = ok ? forms.size() : -1;
    248   STLDeleteElements(&forms);
    249   return result;
    250 }
    251