Home | History | Annotate | Download | only in password_manager
      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
      3 // found in the LICENSE file.
      4 
      5 #include "chrome/browser/password_manager/password_store.h"
      6 
      7 #include "base/bind.h"
      8 #include "base/memory/scoped_ptr.h"
      9 #include "base/message_loop/message_loop.h"
     10 #include "base/message_loop/message_loop_proxy.h"
     11 #include "base/stl_util.h"
     12 #include "chrome/browser/password_manager/password_store_consumer.h"
     13 #include "content/public/browser/browser_thread.h"
     14 #include "content/public/common/password_form.h"
     15 
     16 using content::BrowserThread;
     17 using std::vector;
     18 using content::PasswordForm;
     19 
     20 namespace {
     21 
     22 // PasswordStoreConsumer callback requires vector const reference.
     23 void RunConsumerCallbackIfNotCanceled(
     24     const CancelableTaskTracker::IsCanceledCallback& is_canceled_cb,
     25     PasswordStoreConsumer* consumer,
     26     const vector<PasswordForm*>* matched_forms) {
     27   if (is_canceled_cb.Run()) {
     28     STLDeleteContainerPointers(matched_forms->begin(), matched_forms->end());
     29     return;
     30   }
     31 
     32   // OnGetPasswordStoreResults owns PasswordForms in the vector.
     33   consumer->OnGetPasswordStoreResults(*matched_forms);
     34 }
     35 
     36 void PostConsumerCallback(
     37     base::TaskRunner* task_runner,
     38     const CancelableTaskTracker::IsCanceledCallback& is_canceled_cb,
     39     PasswordStoreConsumer* consumer,
     40     const base::Time& ignore_logins_cutoff,
     41     const vector<PasswordForm*>& matched_forms) {
     42   vector<PasswordForm*>* matched_forms_copy = new vector<PasswordForm*>();
     43   if (ignore_logins_cutoff.is_null()) {
     44     *matched_forms_copy = matched_forms;
     45   } else {
     46     // Apply |ignore_logins_cutoff| and delete old ones.
     47     for (size_t i = 0; i < matched_forms.size(); i++) {
     48       if (matched_forms[i]->date_created < ignore_logins_cutoff)
     49         delete matched_forms[i];
     50       else
     51         matched_forms_copy->push_back(matched_forms[i]);
     52     }
     53   }
     54 
     55   task_runner->PostTask(
     56       FROM_HERE,
     57       base::Bind(&RunConsumerCallbackIfNotCanceled,
     58                  is_canceled_cb, consumer, base::Owned(matched_forms_copy)));
     59 }
     60 
     61 }  // namespace
     62 
     63 PasswordStore::GetLoginsRequest::GetLoginsRequest(
     64     const GetLoginsCallback& callback)
     65     : CancelableRequest1<GetLoginsCallback, vector<PasswordForm*> >(callback) {
     66 }
     67 
     68 void PasswordStore::GetLoginsRequest::ApplyIgnoreLoginsCutoff() {
     69   if (!ignore_logins_cutoff_.is_null()) {
     70     // Count down rather than up since we may be deleting elements.
     71     // Note that in principle it could be more efficient to copy the whole array
     72     // since that's worst-case linear time, but we expect that elements will be
     73     // deleted rarely and lists will be small, so this avoids the copies.
     74     for (size_t i = value.size(); i > 0; --i) {
     75       if (value[i - 1]->date_created < ignore_logins_cutoff_) {
     76         delete value[i - 1];
     77         value.erase(value.begin() + (i - 1));
     78       }
     79     }
     80   }
     81 }
     82 
     83 PasswordStore::GetLoginsRequest::~GetLoginsRequest() {
     84   if (canceled()) {
     85     STLDeleteElements(&value);
     86   }
     87 }
     88 
     89 PasswordStore::PasswordStore() {
     90 }
     91 
     92 bool PasswordStore::Init() {
     93   ReportMetrics();
     94   return true;
     95 }
     96 
     97 void PasswordStore::AddLogin(const PasswordForm& form) {
     98   ScheduleTask(base::Bind(&PasswordStore::WrapModificationTask, this,
     99       base::Closure(base::Bind(&PasswordStore::AddLoginImpl, this, form))));
    100 }
    101 
    102 void PasswordStore::UpdateLogin(const PasswordForm& form) {
    103   ScheduleTask(base::Bind(&PasswordStore::WrapModificationTask, this,
    104       base::Closure(base::Bind(&PasswordStore::UpdateLoginImpl, this, form))));
    105 }
    106 
    107 void PasswordStore::RemoveLogin(const PasswordForm& form) {
    108   ScheduleTask(base::Bind(&PasswordStore::WrapModificationTask, this,
    109       base::Closure(base::Bind(&PasswordStore::RemoveLoginImpl, this, form))));
    110 }
    111 
    112 void PasswordStore::RemoveLoginsCreatedBetween(const base::Time& delete_begin,
    113                                                const base::Time& delete_end) {
    114   ScheduleTask(base::Bind(&PasswordStore::WrapModificationTask, this,
    115       base::Closure(
    116           base::Bind(&PasswordStore::RemoveLoginsCreatedBetweenImpl, this,
    117                      delete_begin, delete_end))));
    118 }
    119 
    120 CancelableTaskTracker::TaskId PasswordStore::GetLogins(
    121     const PasswordForm& form,
    122     PasswordStoreConsumer* consumer) {
    123   // Per http://crbug.com/121738, we deliberately ignore saved logins for
    124   // http*://www.google.com/ that were stored prior to 2012. (Google now uses
    125   // https://accounts.google.com/ for all login forms, so these should be
    126   // unused.) We don't delete them just yet, and they'll still be visible in the
    127   // password manager, but we won't use them to autofill any forms. This is a
    128   // security feature to help minimize damage that can be done by XSS attacks.
    129   // TODO(mdm): actually delete them at some point, say M24 or so.
    130   base::Time ignore_logins_cutoff;  // the null time
    131   if (form.scheme == PasswordForm::SCHEME_HTML &&
    132       (form.signon_realm == "http://www.google.com" ||
    133        form.signon_realm == "http://www.google.com/" ||
    134        form.signon_realm == "https://www.google.com" ||
    135        form.signon_realm == "https://www.google.com/")) {
    136     static const base::Time::Exploded exploded_cutoff =
    137         { 2012, 1, 0, 1, 0, 0, 0, 0 };  // 00:00 Jan 1 2012
    138     ignore_logins_cutoff = base::Time::FromUTCExploded(exploded_cutoff);
    139   }
    140 
    141   CancelableTaskTracker::IsCanceledCallback is_canceled_cb;
    142   CancelableTaskTracker::TaskId id =
    143       consumer->cancelable_task_tracker()->NewTrackedTaskId(&is_canceled_cb);
    144 
    145   ConsumerCallbackRunner callback_runner =
    146       base::Bind(&PostConsumerCallback,
    147                  base::MessageLoopProxy::current(),
    148                  is_canceled_cb,
    149                  consumer,
    150                  ignore_logins_cutoff);
    151   ScheduleTask(
    152       base::Bind(&PasswordStore::GetLoginsImpl, this, form, callback_runner));
    153   return id;
    154 }
    155 
    156 CancelableRequestProvider::Handle PasswordStore::GetAutofillableLogins(
    157     PasswordStoreConsumer* consumer) {
    158   return Schedule(&PasswordStore::GetAutofillableLoginsImpl, consumer);
    159 }
    160 
    161 CancelableRequestProvider::Handle PasswordStore::GetBlacklistLogins(
    162     PasswordStoreConsumer* consumer) {
    163   return Schedule(&PasswordStore::GetBlacklistLoginsImpl, consumer);
    164 }
    165 
    166 void PasswordStore::ReportMetrics() {
    167   ScheduleTask(base::Bind(&PasswordStore::ReportMetricsImpl, this));
    168 }
    169 
    170 void PasswordStore::AddObserver(Observer* observer) {
    171   observers_.AddObserver(observer);
    172 }
    173 
    174 void PasswordStore::RemoveObserver(Observer* observer) {
    175   observers_.RemoveObserver(observer);
    176 }
    177 
    178 PasswordStore::~PasswordStore() {}
    179 
    180 PasswordStore::GetLoginsRequest* PasswordStore::NewGetLoginsRequest(
    181     const GetLoginsCallback& callback) {
    182   return new GetLoginsRequest(callback);
    183 }
    184 
    185 bool PasswordStore::ScheduleTask(const base::Closure& task) {
    186   return BrowserThread::PostTask(BrowserThread::DB, FROM_HERE, task);
    187 }
    188 
    189 void PasswordStore::ForwardLoginsResult(GetLoginsRequest* request) {
    190   request->ApplyIgnoreLoginsCutoff();
    191   request->ForwardResult(request->handle(), request->value);
    192 }
    193 
    194 template<typename BackendFunc>
    195 CancelableRequestProvider::Handle PasswordStore::Schedule(
    196     BackendFunc func,
    197     PasswordStoreConsumer* consumer) {
    198   scoped_refptr<GetLoginsRequest> request(
    199       NewGetLoginsRequest(
    200           base::Bind(&PasswordStoreConsumer::OnPasswordStoreRequestDone,
    201                      base::Unretained(consumer))));
    202   AddRequest(request.get(), consumer->cancelable_consumer());
    203   ScheduleTask(base::Bind(func, this, request));
    204   return request->handle();
    205 }
    206 
    207 template<typename BackendFunc>
    208 CancelableRequestProvider::Handle PasswordStore::Schedule(
    209     BackendFunc func,
    210     PasswordStoreConsumer* consumer,
    211     const PasswordForm& form,
    212     const base::Time& ignore_logins_cutoff) {
    213   scoped_refptr<GetLoginsRequest> request(
    214       NewGetLoginsRequest(
    215           base::Bind(&PasswordStoreConsumer::OnPasswordStoreRequestDone,
    216                      base::Unretained(consumer))));
    217   request->set_ignore_logins_cutoff(ignore_logins_cutoff);
    218   AddRequest(request.get(), consumer->cancelable_consumer());
    219   ScheduleTask(base::Bind(func, this, request, form));
    220   return request->handle();
    221 }
    222 
    223 void PasswordStore::WrapModificationTask(base::Closure task) {
    224 #if !defined(OS_MACOSX)
    225   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
    226 #endif  // !defined(OS_MACOSX)
    227   task.Run();
    228   PostNotifyLoginsChanged();
    229 }
    230 
    231 void PasswordStore::PostNotifyLoginsChanged() {
    232 #if !defined(OS_MACOSX)
    233   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
    234 #endif  // !defined(OS_MACOSX)
    235   BrowserThread::PostTask(
    236       BrowserThread::UI, FROM_HERE,
    237       base::Bind(&PasswordStore::NotifyLoginsChanged, this));
    238 }
    239 
    240 void PasswordStore::NotifyLoginsChanged() {
    241   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    242   FOR_EACH_OBSERVER(Observer, observers_, OnLoginsChanged());
    243 }
    244