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 "components/password_manager/core/browser/password_manager.h"
6
7#include "base/command_line.h"
8#include "base/metrics/field_trial.h"
9#include "base/metrics/histogram.h"
10#include "base/prefs/pref_service.h"
11#include "base/strings/string_util.h"
12#include "base/strings/utf_string_conversions.h"
13#include "base/threading/platform_thread.h"
14#include "components/autofill/core/common/password_autofill_util.h"
15#include "components/password_manager/core/browser/browser_save_password_progress_logger.h"
16#include "components/password_manager/core/browser/password_autofill_manager.h"
17#include "components/password_manager/core/browser/password_form_manager.h"
18#include "components/password_manager/core/browser/password_manager_client.h"
19#include "components/password_manager/core/browser/password_manager_driver.h"
20#include "components/password_manager/core/browser/password_manager_metrics_util.h"
21#include "components/password_manager/core/common/password_manager_pref_names.h"
22#include "components/password_manager/core/common/password_manager_switches.h"
23#include "components/pref_registry/pref_registry_syncable.h"
24
25using autofill::PasswordForm;
26using autofill::PasswordFormMap;
27
28namespace password_manager {
29
30namespace {
31
32const char kSpdyProxyRealm[] = "/SpdyProxy";
33
34// Shorten the name to spare line breaks. The code provides enough context
35// already.
36typedef autofill::SavePasswordProgressLogger Logger;
37
38// This routine is called when PasswordManagers are constructed.
39//
40// Currently we report metrics only once at startup. We require
41// that this is only ever called from a single thread in order to
42// avoid needing to lock (a static boolean flag is then sufficient to
43// guarantee running only once).
44void ReportMetrics(bool password_manager_enabled) {
45  static base::PlatformThreadId initial_thread_id =
46      base::PlatformThread::CurrentId();
47  DCHECK(initial_thread_id == base::PlatformThread::CurrentId());
48
49  static bool ran_once = false;
50  if (ran_once)
51    return;
52  ran_once = true;
53
54  UMA_HISTOGRAM_BOOLEAN("PasswordManager.Enabled", password_manager_enabled);
55}
56
57bool ShouldDropSyncCredential() {
58  std::string group_name =
59      base::FieldTrialList::FindFullName("PasswordManagerDropSyncCredential");
60
61  CommandLine* command_line = CommandLine::ForCurrentProcess();
62  if (command_line->HasSwitch(switches::kEnableDropSyncCredential))
63    return true;
64
65  if (command_line->HasSwitch(switches::kDisableDropSyncCredential))
66    return false;
67
68  // Default to not saving.
69  return group_name != "Disabled";
70}
71
72bool URLsEqualUpToScheme(const GURL& a, const GURL& b) {
73  return (a.GetContent() == b.GetContent());
74}
75
76bool URLsEqualUpToHttpHttpsSubstitution(const GURL& a, const GURL& b) {
77  if (a == b)
78    return true;
79
80  // The first-time and retry login forms action URLs sometimes differ in
81  // switching from HTTP to HTTPS, see http://crbug.com/400769.
82  if (a.SchemeIsHTTPOrHTTPS() && b.SchemeIsHTTPOrHTTPS())
83    return URLsEqualUpToScheme(a, b);
84
85  return false;
86}
87
88}  // namespace
89
90const char PasswordManager::kOtherPossibleUsernamesExperiment[] =
91    "PasswordManagerOtherPossibleUsernames";
92
93// static
94void PasswordManager::RegisterProfilePrefs(
95    user_prefs::PrefRegistrySyncable* registry) {
96  registry->RegisterBooleanPref(
97      prefs::kPasswordManagerSavingEnabled,
98      true,
99      user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
100  registry->RegisterBooleanPref(
101      prefs::kPasswordManagerAllowShowPasswords,
102      true,
103      user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
104  registry->RegisterListPref(prefs::kPasswordManagerGroupsForDomains,
105                             user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
106}
107
108PasswordManager::PasswordManager(PasswordManagerClient* client)
109    : client_(client), driver_(client->GetDriver()) {
110  DCHECK(client_);
111  DCHECK(driver_);
112  saving_passwords_enabled_.Init(prefs::kPasswordManagerSavingEnabled,
113                                 client_->GetPrefs());
114
115  ReportMetrics(*saving_passwords_enabled_);
116}
117
118PasswordManager::~PasswordManager() {
119  FOR_EACH_OBSERVER(LoginModelObserver, observers_, OnLoginModelDestroying());
120}
121
122void PasswordManager::SetFormHasGeneratedPassword(const PasswordForm& form) {
123  DCHECK(IsSavingEnabledForCurrentPage());
124
125  for (ScopedVector<PasswordFormManager>::iterator iter =
126           pending_login_managers_.begin();
127       iter != pending_login_managers_.end();
128       ++iter) {
129    if ((*iter)->DoesManage(form) ==
130        PasswordFormManager::RESULT_COMPLETE_MATCH) {
131      (*iter)->SetHasGeneratedPassword();
132      return;
133    }
134  }
135  // If there is no corresponding PasswordFormManager, we create one. This is
136  // not the common case, and should only happen when there is a bug in our
137  // ability to detect forms.
138  bool ssl_valid = form.origin.SchemeIsSecure();
139  PasswordFormManager* manager =
140      new PasswordFormManager(this, client_, driver_, form, ssl_valid);
141  pending_login_managers_.push_back(manager);
142  manager->SetHasGeneratedPassword();
143  // TODO(gcasto): Add UMA stats to track this.
144}
145
146bool PasswordManager::IsEnabledForCurrentPage() const {
147  return !driver_->DidLastPageLoadEncounterSSLErrors() &&
148      client_->IsPasswordManagerEnabledForCurrentPage();
149}
150
151bool PasswordManager::IsSavingEnabledForCurrentPage() const {
152  return *saving_passwords_enabled_ && !driver_->IsOffTheRecord() &&
153         IsEnabledForCurrentPage();
154}
155
156void PasswordManager::ProvisionallySavePassword(const PasswordForm& form) {
157  bool is_saving_enabled = IsSavingEnabledForCurrentPage();
158
159  scoped_ptr<BrowserSavePasswordProgressLogger> logger;
160  if (client_->IsLoggingActive()) {
161    logger.reset(new BrowserSavePasswordProgressLogger(client_));
162    logger->LogMessage(Logger::STRING_PROVISIONALLY_SAVE_PASSWORD_METHOD);
163    logger->LogPasswordForm(Logger::STRING_PROVISIONALLY_SAVE_PASSWORD_FORM,
164                            form);
165    logger->LogBoolean(Logger::STRING_IS_SAVING_ENABLED, is_saving_enabled);
166    logger->LogBoolean(Logger::STRING_SSL_ERRORS_PRESENT,
167                       driver_->DidLastPageLoadEncounterSSLErrors());
168  }
169
170  if (!is_saving_enabled) {
171    RecordFailure(SAVING_DISABLED, form.origin.host(), logger.get());
172    return;
173  }
174
175  // No password to save? Then don't.
176  if ((form.new_password_element.empty() && form.password_value.empty()) ||
177      (!form.new_password_element.empty() && form.new_password_value.empty())) {
178    RecordFailure(EMPTY_PASSWORD, form.origin.host(), logger.get());
179    return;
180  }
181
182  scoped_ptr<PasswordFormManager> manager;
183  ScopedVector<PasswordFormManager>::iterator matched_manager_it =
184      pending_login_managers_.end();
185  // Below, "matching" is in DoesManage-sense and "not ready" in
186  // !HasCompletedMatching sense. We keep track of such PasswordFormManager
187  // instances for UMA.
188  bool has_found_matching_managers_which_were_not_ready = false;
189  for (ScopedVector<PasswordFormManager>::iterator iter =
190           pending_login_managers_.begin();
191       iter != pending_login_managers_.end();
192       ++iter) {
193    PasswordFormManager::MatchResultMask result = (*iter)->DoesManage(form);
194
195    if (!(*iter)->HasCompletedMatching()) {
196      if (result != PasswordFormManager::RESULT_NO_MATCH)
197        has_found_matching_managers_which_were_not_ready = true;
198      continue;
199    }
200
201    if (result == PasswordFormManager::RESULT_COMPLETE_MATCH) {
202      // If we find a manager that exactly matches the submitted form including
203      // the action URL, exit the loop.
204      if (logger)
205        logger->LogMessage(Logger::STRING_EXACT_MATCH);
206      matched_manager_it = iter;
207      break;
208    } else if (result == (PasswordFormManager::RESULT_COMPLETE_MATCH &
209                          ~PasswordFormManager::RESULT_ACTION_MATCH)) {
210      // If the current manager matches the submitted form excluding the action
211      // URL, remember it as a candidate and continue searching for an exact
212      // match. See http://crbug.com/27246 for an example where actions can
213      // change.
214      if (logger)
215        logger->LogMessage(Logger::STRING_MATCH_WITHOUT_ACTION);
216      matched_manager_it = iter;
217    }
218  }
219  // If we didn't find a manager, this means a form was submitted without
220  // first loading the page containing the form. Don't offer to save
221  // passwords in this case.
222  if (matched_manager_it != pending_login_managers_.end()) {
223    // Transfer ownership of the manager from |pending_login_managers_| to
224    // |manager|.
225    manager.reset(*matched_manager_it);
226    pending_login_managers_.weak_erase(matched_manager_it);
227  } else if (has_found_matching_managers_which_were_not_ready) {
228    // We found some managers, but none finished matching yet. The user has
229    // tried to submit credentials before we had time to even find matching
230    // results for the given form and autofill. If this is the case, we just
231    // give up.
232    RecordFailure(MATCHING_NOT_COMPLETE, form.origin.host(), logger.get());
233    return;
234  } else {
235    RecordFailure(NO_MATCHING_FORM, form.origin.host(), logger.get());
236    return;
237  }
238
239  // Also get out of here if the user told us to 'never remember' passwords for
240  // this form.
241  if (manager->IsBlacklisted()) {
242    RecordFailure(FORM_BLACKLISTED, form.origin.host(), logger.get());
243    return;
244  }
245
246  // Bail if we're missing any of the necessary form components.
247  if (!manager->HasValidPasswordForm()) {
248    RecordFailure(INVALID_FORM, form.origin.host(), logger.get());
249    return;
250  }
251
252  // Don't save credentials for the syncing account. See crbug.com/365832 for
253  // background.
254  if (ShouldDropSyncCredential() &&
255      client_->IsSyncAccountCredential(
256          base::UTF16ToUTF8(form.username_value), form.signon_realm)) {
257    RecordFailure(SYNC_CREDENTIAL, form.origin.host(), logger.get());
258    return;
259  }
260
261  // Always save generated passwords, as the user expresses explicit intent for
262  // Chrome to manage such passwords. For other passwords, respect the
263  // autocomplete attribute if autocomplete='off' is not ignored.
264  if (!autofill::ShouldIgnoreAutocompleteOffForPasswordFields() &&
265      !manager->HasGeneratedPassword() && !form.password_autocomplete_set) {
266    RecordFailure(AUTOCOMPLETE_OFF, form.origin.host(), logger.get());
267    return;
268  }
269
270  PasswordForm provisionally_saved_form(form);
271  provisionally_saved_form.ssl_valid =
272      form.origin.SchemeIsSecure() &&
273      !driver_->DidLastPageLoadEncounterSSLErrors();
274  provisionally_saved_form.preferred = true;
275  if (logger) {
276    logger->LogPasswordForm(Logger::STRING_PROVISIONALLY_SAVED_FORM,
277                            provisionally_saved_form);
278  }
279  PasswordFormManager::OtherPossibleUsernamesAction action =
280      PasswordFormManager::IGNORE_OTHER_POSSIBLE_USERNAMES;
281  if (OtherPossibleUsernamesEnabled())
282    action = PasswordFormManager::ALLOW_OTHER_POSSIBLE_USERNAMES;
283  if (logger) {
284    logger->LogBoolean(
285        Logger::STRING_IGNORE_POSSIBLE_USERNAMES,
286        action == PasswordFormManager::IGNORE_OTHER_POSSIBLE_USERNAMES);
287  }
288  manager->ProvisionallySave(provisionally_saved_form, action);
289  provisional_save_manager_.swap(manager);
290}
291
292void PasswordManager::RecordFailure(ProvisionalSaveFailure failure,
293                                    const std::string& form_origin,
294                                    BrowserSavePasswordProgressLogger* logger) {
295  UMA_HISTOGRAM_ENUMERATION(
296      "PasswordManager.ProvisionalSaveFailure", failure, MAX_FAILURE_VALUE);
297
298  std::string group_name = metrics_util::GroupIdToString(
299      metrics_util::MonitoredDomainGroupId(form_origin, client_->GetPrefs()));
300  if (!group_name.empty()) {
301    metrics_util::LogUMAHistogramEnumeration(
302        "PasswordManager.ProvisionalSaveFailure_" + group_name,
303        failure,
304        MAX_FAILURE_VALUE);
305  }
306
307  if (logger) {
308    switch (failure) {
309      case SAVING_DISABLED:
310        logger->LogMessage(Logger::STRING_SAVING_DISABLED);
311        break;
312      case EMPTY_PASSWORD:
313        logger->LogMessage(Logger::STRING_EMPTY_PASSWORD);
314        break;
315      case MATCHING_NOT_COMPLETE:
316        logger->LogMessage(Logger::STRING_MATCHING_NOT_COMPLETE);
317        break;
318      case NO_MATCHING_FORM:
319        logger->LogMessage(Logger::STRING_NO_MATCHING_FORM);
320        break;
321      case FORM_BLACKLISTED:
322        logger->LogMessage(Logger::STRING_FORM_BLACKLISTED);
323        break;
324      case INVALID_FORM:
325        logger->LogMessage(Logger::STRING_INVALID_FORM);
326        break;
327      case AUTOCOMPLETE_OFF:
328        logger->LogMessage(Logger::STRING_AUTOCOMPLETE_OFF);
329        break;
330      case SYNC_CREDENTIAL:
331        logger->LogMessage(Logger::STRING_SYNC_CREDENTIAL);
332        break;
333      case MAX_FAILURE_VALUE:
334        NOTREACHED();
335        return;
336    }
337    logger->LogMessage(Logger::STRING_DECISION_DROP);
338  }
339}
340
341void PasswordManager::AddSubmissionCallback(
342    const PasswordSubmittedCallback& callback) {
343  submission_callbacks_.push_back(callback);
344}
345
346void PasswordManager::AddObserver(LoginModelObserver* observer) {
347  observers_.AddObserver(observer);
348}
349
350void PasswordManager::RemoveObserver(LoginModelObserver* observer) {
351  observers_.RemoveObserver(observer);
352}
353
354void PasswordManager::DidNavigateMainFrame(bool is_in_page) {
355  // Clear data after main frame navigation if the navigation was to a
356  // different page.
357  if (!is_in_page) {
358    pending_login_managers_.clear();
359    driver_->GetPasswordAutofillManager()->Reset();
360  }
361}
362
363void PasswordManager::OnPasswordFormSubmitted(
364    const PasswordForm& password_form) {
365  ProvisionallySavePassword(password_form);
366  for (size_t i = 0; i < submission_callbacks_.size(); ++i) {
367    submission_callbacks_[i].Run(password_form);
368  }
369
370  pending_login_managers_.clear();
371}
372
373void PasswordManager::OnPasswordFormsParsed(
374    const std::vector<PasswordForm>& forms) {
375  CreatePendingLoginManagers(forms);
376}
377
378void PasswordManager::CreatePendingLoginManagers(
379    const std::vector<PasswordForm>& forms) {
380  if (!IsEnabledForCurrentPage())
381    return;
382
383  // Copy the weak pointers to the currently known login managers for comparison
384  // against the newly added.
385  std::vector<PasswordFormManager*> old_login_managers(
386      pending_login_managers_.get());
387  for (std::vector<PasswordForm>::const_iterator iter = forms.begin();
388       iter != forms.end();
389       ++iter) {
390    // Don't involve the password manager if this form corresponds to
391    // SpdyProxy authentication, as indicated by the realm.
392    if (EndsWith(iter->signon_realm, kSpdyProxyRealm, true))
393      continue;
394    bool old_manager_found = false;
395    for (std::vector<PasswordFormManager*>::const_iterator old_manager =
396             old_login_managers.begin();
397         !old_manager_found && old_manager != old_login_managers.end();
398         ++old_manager) {
399      old_manager_found = (*old_manager)->DoesManage(*iter) ==
400                          PasswordFormManager::RESULT_COMPLETE_MATCH;
401    }
402    if (old_manager_found)
403      continue;  // The current form is already managed.
404
405    bool ssl_valid = iter->origin.SchemeIsSecure();
406    PasswordFormManager* manager =
407        new PasswordFormManager(this, client_, driver_, *iter, ssl_valid);
408    pending_login_managers_.push_back(manager);
409
410    PasswordStore::AuthorizationPromptPolicy prompt_policy =
411        client_->GetAuthorizationPromptPolicy(*iter);
412
413    manager->FetchMatchingLoginsFromPasswordStore(prompt_policy);
414  }
415}
416
417bool PasswordManager::ShouldPromptUserToSavePassword() const {
418  return !client_->IsAutomaticPasswordSavingEnabled() &&
419         provisional_save_manager_->IsNewLogin() &&
420         !provisional_save_manager_->HasGeneratedPassword() &&
421         !provisional_save_manager_->IsPendingCredentialsPublicSuffixMatch();
422}
423
424void PasswordManager::OnPasswordFormsRendered(
425    const std::vector<PasswordForm>& visible_forms,
426    bool did_stop_loading) {
427  CreatePendingLoginManagers(visible_forms);
428  scoped_ptr<BrowserSavePasswordProgressLogger> logger;
429  if (client_->IsLoggingActive()) {
430    logger.reset(new BrowserSavePasswordProgressLogger(client_));
431    logger->LogMessage(Logger::STRING_ON_PASSWORD_FORMS_RENDERED_METHOD);
432  }
433
434  if (!provisional_save_manager_.get()) {
435    if (logger) {
436      logger->LogMessage(Logger::STRING_NO_PROVISIONAL_SAVE_MANAGER);
437      logger->LogMessage(Logger::STRING_DECISION_DROP);
438    }
439    return;
440  }
441
442  DCHECK(IsSavingEnabledForCurrentPage());
443
444  if (logger) {
445    logger->LogNumber(Logger::STRING_NUMBER_OF_VISIBLE_FORMS,
446                      visible_forms.size());
447  }
448
449  // Record all visible forms from the frame.
450  all_visible_forms_.insert(all_visible_forms_.end(),
451                            visible_forms.begin(),
452                            visible_forms.end());
453
454  // If we see the login form again, then the login failed.
455  if (did_stop_loading) {
456    for (size_t i = 0; i < all_visible_forms_.size(); ++i) {
457      // TODO(vabr): The similarity check is just action equality up to
458      // HTTP<->HTTPS substitution for now. If it becomes more complex, it may
459      // make sense to consider modifying and using
460      // PasswordFormManager::DoesManage for it.
461      if (all_visible_forms_[i].action.is_valid() &&
462          URLsEqualUpToHttpHttpsSubstitution(
463              provisional_save_manager_->pending_credentials().action,
464              all_visible_forms_[i].action)) {
465        if (logger) {
466          logger->LogPasswordForm(Logger::STRING_PASSWORD_FORM_REAPPEARED,
467                                  visible_forms[i]);
468          logger->LogMessage(Logger::STRING_DECISION_DROP);
469        }
470        provisional_save_manager_->SubmitFailed();
471        provisional_save_manager_.reset();
472        // Clear all_visible_forms_ once we found the match.
473        all_visible_forms_.clear();
474        return;
475      }
476    }
477
478    // Clear all_visible_forms_ after checking all the visible forms.
479    all_visible_forms_.clear();
480
481    // Looks like a successful login attempt. Either show an infobar or
482    // automatically save the login data. We prompt when the user hasn't
483    // already given consent, either through previously accepting the infobar
484    // or by having the browser generate the password.
485    provisional_save_manager_->SubmitPassed();
486
487    if (ShouldPromptUserToSavePassword()) {
488      if (logger)
489        logger->LogMessage(Logger::STRING_DECISION_ASK);
490      if (client_->PromptUserToSavePassword(provisional_save_manager_.Pass())) {
491        if (logger)
492          logger->LogMessage(Logger::STRING_SHOW_PASSWORD_PROMPT);
493      }
494    } else {
495      if (logger)
496        logger->LogMessage(Logger::STRING_DECISION_SAVE);
497      provisional_save_manager_->Save();
498
499      if (provisional_save_manager_->HasGeneratedPassword()) {
500        client_->AutomaticPasswordSave(provisional_save_manager_.Pass());
501      } else {
502        provisional_save_manager_.reset();
503      }
504    }
505  }
506}
507
508void PasswordManager::PossiblyInitializeUsernamesExperiment(
509    const PasswordFormMap& best_matches) const {
510  if (base::FieldTrialList::Find(kOtherPossibleUsernamesExperiment))
511    return;
512
513  bool other_possible_usernames_exist = false;
514  for (autofill::PasswordFormMap::const_iterator it = best_matches.begin();
515       it != best_matches.end();
516       ++it) {
517    if (!it->second->other_possible_usernames.empty()) {
518      other_possible_usernames_exist = true;
519      break;
520    }
521  }
522
523  if (!other_possible_usernames_exist)
524    return;
525
526  const base::FieldTrial::Probability kDivisor = 100;
527  scoped_refptr<base::FieldTrial> trial(
528      base::FieldTrialList::FactoryGetFieldTrial(
529          kOtherPossibleUsernamesExperiment,
530          kDivisor,
531          "Disabled",
532          2013, 12, 31,
533          base::FieldTrial::ONE_TIME_RANDOMIZED,
534          NULL));
535  base::FieldTrial::Probability enabled_probability =
536      client_->GetProbabilityForExperiment(kOtherPossibleUsernamesExperiment);
537  trial->AppendGroup("Enabled", enabled_probability);
538}
539
540bool PasswordManager::OtherPossibleUsernamesEnabled() const {
541  return base::FieldTrialList::FindFullName(
542             kOtherPossibleUsernamesExperiment) == "Enabled";
543}
544
545void PasswordManager::Autofill(const PasswordForm& form_for_autofill,
546                               const PasswordFormMap& best_matches,
547                               const PasswordForm& preferred_match,
548                               bool wait_for_username) const {
549  PossiblyInitializeUsernamesExperiment(best_matches);
550
551  // TODO(tedchoc): Switch to only requesting authentication if the user is
552  //                acting on the autofilled forms (crbug.com/342594) instead
553  //                of on page load.
554  bool authentication_required = preferred_match.use_additional_authentication;
555  for (autofill::PasswordFormMap::const_iterator it = best_matches.begin();
556       !authentication_required && it != best_matches.end();
557       ++it) {
558    if (it->second->use_additional_authentication)
559      authentication_required = true;
560  }
561
562  switch (form_for_autofill.scheme) {
563    case PasswordForm::SCHEME_HTML: {
564      // Note the check above is required because the observers_ for a non-HTML
565      // schemed password form may have been freed, so we need to distinguish.
566      scoped_ptr<autofill::PasswordFormFillData> fill_data(
567          new autofill::PasswordFormFillData());
568      InitPasswordFormFillData(form_for_autofill,
569                               best_matches,
570                               &preferred_match,
571                               wait_for_username,
572                               OtherPossibleUsernamesEnabled(),
573                               fill_data.get());
574      if (authentication_required)
575        client_->AuthenticateAutofillAndFillForm(fill_data.Pass());
576      else
577        driver_->FillPasswordForm(*fill_data.get());
578      break;
579    }
580    default:
581      FOR_EACH_OBSERVER(
582          LoginModelObserver,
583          observers_,
584          OnAutofillDataAvailable(preferred_match.username_value,
585                                  preferred_match.password_value));
586      break;
587  }
588
589  client_->PasswordWasAutofilled(best_matches);
590}
591
592}  // namespace password_manager
593