password_manager.cc revision 868fa2fe829687343ffae624259930155e16dbd8
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_manager.h" 6 7#include "base/metrics/field_trial.h" 8#include "base/metrics/histogram.h" 9#include "base/prefs/pref_service.h" 10#include "base/strings/string_util.h" 11#include "base/strings/utf_string_conversions.h" 12#include "base/threading/platform_thread.h" 13#include "chrome/browser/password_manager/password_form_manager.h" 14#include "chrome/browser/password_manager/password_manager_delegate.h" 15#include "chrome/browser/profiles/profile.h" 16#include "chrome/common/chrome_version_info.h" 17#include "chrome/common/pref_names.h" 18#include "components/autofill/common/autofill_messages.h" 19#include "components/user_prefs/pref_registry_syncable.h" 20#include "content/public/browser/navigation_details.h" 21#include "content/public/browser/user_metrics.h" 22#include "content/public/browser/web_contents.h" 23#include "content/public/common/frame_navigate_params.h" 24#include "grit/generated_resources.h" 25 26using content::UserMetricsAction; 27using content::WebContents; 28using content::PasswordForm; 29using content::PasswordFormMap; 30 31DEFINE_WEB_CONTENTS_USER_DATA_KEY(PasswordManager); 32 33namespace { 34 35const char kSpdyProxyRealm[] = "/SpdyProxy"; 36const char kOtherPossibleUsernamesExperiment[] = 37 "PasswordManagerOtherPossibleUsernames"; 38 39// This routine is called when PasswordManagers are constructed. 40// 41// Currently we report metrics only once at startup. We require 42// that this is only ever called from a single thread in order to 43// avoid needing to lock (a static boolean flag is then sufficient to 44// guarantee running only once). 45void ReportMetrics(bool password_manager_enabled) { 46 static base::PlatformThreadId initial_thread_id = 47 base::PlatformThread::CurrentId(); 48 DCHECK(initial_thread_id == base::PlatformThread::CurrentId()); 49 50 static bool ran_once = false; 51 if (ran_once) 52 return; 53 ran_once = true; 54 55 // TODO(isherman): This does not actually measure a user action. It should be 56 // a boolean histogram. 57 if (password_manager_enabled) 58 content::RecordAction(UserMetricsAction("PasswordManager_Enabled")); 59 else 60 content::RecordAction(UserMetricsAction("PasswordManager_Disabled")); 61} 62 63} // namespace 64 65// static 66void PasswordManager::RegisterUserPrefs( 67 user_prefs::PrefRegistrySyncable* registry) { 68 registry->RegisterBooleanPref( 69 prefs::kPasswordManagerEnabled, 70 true, 71 user_prefs::PrefRegistrySyncable::SYNCABLE_PREF); 72 registry->RegisterBooleanPref( 73 prefs::kPasswordManagerAllowShowPasswords, 74 true, 75 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); 76} 77 78// static 79void PasswordManager::CreateForWebContentsAndDelegate( 80 content::WebContents* contents, 81 PasswordManagerDelegate* delegate) { 82 if (FromWebContents(contents)) { 83 DCHECK_EQ(delegate, FromWebContents(contents)->delegate_); 84 return; 85 } 86 87 contents->SetUserData(UserDataKey(), 88 new PasswordManager(contents, delegate)); 89} 90 91PasswordManager::PasswordManager(WebContents* web_contents, 92 PasswordManagerDelegate* delegate) 93 : content::WebContentsObserver(web_contents), 94 delegate_(delegate), 95 observer_(NULL) { 96 DCHECK(delegate_); 97 password_manager_enabled_.Init(prefs::kPasswordManagerEnabled, 98 delegate_->GetProfile()->GetPrefs()); 99 100 ReportMetrics(*password_manager_enabled_); 101} 102 103PasswordManager::~PasswordManager() { 104 if (observer_) 105 observer_->OnLoginModelDestroying(); 106} 107 108void PasswordManager::SetFormHasGeneratedPassword(const PasswordForm& form) { 109 for (ScopedVector<PasswordFormManager>::iterator iter = 110 pending_login_managers_.begin(); 111 iter != pending_login_managers_.end(); ++iter) { 112 if ((*iter)->DoesManage( 113 form, PasswordFormManager::ACTION_MATCH_REQUIRED)) { 114 (*iter)->SetHasGeneratedPassword(); 115 return; 116 } 117 } 118 // If there is no corresponding PasswordFormManager, we create one. This is 119 // not the common case, and should only happen when there is a bug in our 120 // ability to detect forms. 121 bool ssl_valid = (form.origin.SchemeIsSecure() && 122 !delegate_->DidLastPageLoadEncounterSSLErrors()); 123 PasswordFormManager* manager = 124 new PasswordFormManager(delegate_->GetProfile(), 125 this, 126 web_contents(), 127 form, 128 ssl_valid); 129 pending_login_managers_.push_back(manager); 130 manager->SetHasGeneratedPassword(); 131 // TODO(gcasto): Add UMA stats to track this. 132} 133 134bool PasswordManager::IsSavingEnabled() const { 135 return *password_manager_enabled_ && 136 !delegate_->GetProfile()->IsOffTheRecord(); 137} 138 139void PasswordManager::ProvisionallySavePassword(const PasswordForm& form) { 140 if (!IsSavingEnabled()) 141 return; 142 143 // No password to save? Then don't. 144 if (form.password_value.empty()) 145 return; 146 147 scoped_ptr<PasswordFormManager> manager; 148 ScopedVector<PasswordFormManager>::iterator matched_manager_it = 149 pending_login_managers_.end(); 150 for (ScopedVector<PasswordFormManager>::iterator iter = 151 pending_login_managers_.begin(); 152 iter != pending_login_managers_.end(); ++iter) { 153 // If we find a manager that exactly matches the submitted form including 154 // the action URL, exit the loop. 155 if ((*iter)->DoesManage( 156 form, PasswordFormManager::ACTION_MATCH_REQUIRED)) { 157 matched_manager_it = iter; 158 break; 159 // If the current manager matches the submitted form excluding the action 160 // URL, remember it as a candidate and continue searching for an exact 161 // match. 162 } else if ((*iter)->DoesManage( 163 form, PasswordFormManager::ACTION_MATCH_NOT_REQUIRED)) { 164 matched_manager_it = iter; 165 } 166 } 167 // If we didn't find a manager, this means a form was submitted without 168 // first loading the page containing the form. Don't offer to save 169 // passwords in this case. 170 if (matched_manager_it != pending_login_managers_.end()) { 171 // Transfer ownership of the manager from |pending_login_managers_| to 172 // |manager|. 173 manager.reset(*matched_manager_it); 174 pending_login_managers_.weak_erase(matched_manager_it); 175 } else { 176 return; 177 } 178 179 // If we found a manager but it didn't finish matching yet, the user has 180 // tried to submit credentials before we had time to even find matching 181 // results for the given form and autofill. If this is the case, we just 182 // give up. 183 if (!manager->HasCompletedMatching()) 184 return; 185 186 // Also get out of here if the user told us to 'never remember' passwords for 187 // this form. 188 if (manager->IsBlacklisted()) 189 return; 190 191 // Bail if we're missing any of the necessary form components. 192 if (!manager->HasValidPasswordForm()) 193 return; 194 195 // Always save generated passwords, as the user expresses explicit intent for 196 // Chrome to manage such passwords. For other passwords, respect the 197 // autocomplete attribute. 198 if (!manager->HasGeneratedPassword() && !form.password_autocomplete_set) 199 return; 200 201 PasswordForm provisionally_saved_form(form); 202 provisionally_saved_form.ssl_valid = form.origin.SchemeIsSecure() && 203 !delegate_->DidLastPageLoadEncounterSSLErrors(); 204 provisionally_saved_form.preferred = true; 205 PasswordFormManager::OtherPossibleUsernamesAction action = 206 PasswordFormManager::IGNORE_OTHER_POSSIBLE_USERNAMES; 207 if (OtherPossibleUsernamesEnabled()) 208 action = PasswordFormManager::ALLOW_OTHER_POSSIBLE_USERNAMES; 209 manager->ProvisionallySave(provisionally_saved_form, action); 210 provisional_save_manager_.swap(manager); 211} 212 213void PasswordManager::SetObserver(LoginModelObserver* observer) { 214 observer_ = observer; 215} 216 217void PasswordManager::DidNavigateAnyFrame( 218 const content::LoadCommittedDetails& details, 219 const content::FrameNavigateParams& params) { 220 bool password_form_submitted = params.password_form.origin.is_valid(); 221 222 // Try to save the password if one was submitted. 223 if (password_form_submitted) 224 ProvisionallySavePassword(params.password_form); 225 226 // Clear data after submission or main frame navigation. We don't want 227 // to clear data after subframe navigation as there might be password 228 // forms on other frames that could be submitted. 229 if (password_form_submitted || details.is_main_frame) 230 pending_login_managers_.clear(); 231} 232 233bool PasswordManager::OnMessageReceived(const IPC::Message& message) { 234 bool handled = true; 235 IPC_BEGIN_MESSAGE_MAP(PasswordManager, message) 236 IPC_MESSAGE_HANDLER(AutofillHostMsg_PasswordFormsParsed, 237 OnPasswordFormsParsed) 238 IPC_MESSAGE_HANDLER(AutofillHostMsg_PasswordFormsRendered, 239 OnPasswordFormsRendered) 240 IPC_MESSAGE_UNHANDLED(handled = false) 241 IPC_END_MESSAGE_MAP() 242 return handled; 243} 244 245void PasswordManager::OnPasswordFormsParsed( 246 const std::vector<PasswordForm>& forms) { 247 // Ask the SSLManager for current security. 248 bool had_ssl_error = delegate_->DidLastPageLoadEncounterSSLErrors(); 249 250 for (std::vector<PasswordForm>::const_iterator iter = forms.begin(); 251 iter != forms.end(); ++iter) { 252 // Don't involve the password manager if this form corresponds to 253 // SpdyProxy authentication, as indicated by the realm. 254 if (EndsWith(iter->signon_realm, kSpdyProxyRealm, true)) 255 continue; 256 257 bool ssl_valid = iter->origin.SchemeIsSecure() && !had_ssl_error; 258 PasswordFormManager* manager = 259 new PasswordFormManager(delegate_->GetProfile(), 260 this, 261 web_contents(), 262 *iter, 263 ssl_valid); 264 pending_login_managers_.push_back(manager); 265 manager->FetchMatchingLoginsFromPasswordStore(); 266 } 267} 268 269void PasswordManager::OnPasswordFormsRendered( 270 const std::vector<PasswordForm>& visible_forms) { 271 if (!provisional_save_manager_.get()) 272 return; 273 274 DCHECK(IsSavingEnabled()); 275 276 // First, check for a failed login attempt. 277 for (std::vector<PasswordForm>::const_iterator iter = visible_forms.begin(); 278 iter != visible_forms.end(); ++iter) { 279 if (provisional_save_manager_->DoesManage( 280 *iter, PasswordFormManager::ACTION_MATCH_REQUIRED)) { 281 // The form trying to be saved has immediately re-appeared. Assume login 282 // failure and abort this save, by clearing provisional_save_manager_. 283 provisional_save_manager_->SubmitFailed(); 284 provisional_save_manager_.reset(); 285 return; 286 } 287 } 288 289 if (!provisional_save_manager_->HasValidPasswordForm()) { 290 // Form is not completely valid - we do not support it. 291 NOTREACHED(); 292 provisional_save_manager_.reset(); 293 return; 294 } 295 296 // Looks like a successful login attempt. Either show an infobar or 297 // automatically save the login data. We prompt when the user hasn't already 298 // given consent, either through previously accepting the infobar or by having 299 // the browser generate the password. 300 provisional_save_manager_->SubmitPassed(); 301 if (provisional_save_manager_->HasGeneratedPassword()) 302 UMA_HISTOGRAM_COUNTS("PasswordGeneration.Submitted", 1); 303 if (provisional_save_manager_->IsNewLogin() && 304 !provisional_save_manager_->HasGeneratedPassword()) { 305 delegate_->AddSavePasswordInfoBarIfPermitted( 306 provisional_save_manager_.release()); 307 } else { 308 provisional_save_manager_->Save(); 309 provisional_save_manager_.reset(); 310 } 311} 312 313void PasswordManager::PossiblyInitializeUsernamesExperiment( 314 const PasswordFormMap& best_matches) const { 315 if (base::FieldTrialList::Find(kOtherPossibleUsernamesExperiment)) 316 return; 317 318 bool other_possible_usernames_exist = false; 319 for (content::PasswordFormMap::const_iterator it = best_matches.begin(); 320 it != best_matches.end(); ++it) { 321 if (!it->second->other_possible_usernames.empty()) { 322 other_possible_usernames_exist = true; 323 break; 324 } 325 } 326 327 if (!other_possible_usernames_exist) 328 return; 329 330 const base::FieldTrial::Probability kDivisor = 100; 331 scoped_refptr<base::FieldTrial> trial( 332 base::FieldTrialList::FactoryGetFieldTrial( 333 kOtherPossibleUsernamesExperiment, 334 kDivisor, "Disabled", 2013, 12, 31, NULL)); 335 trial->UseOneTimeRandomization(); 336 base::FieldTrial::Probability enabled_probability = 0; 337 338 switch (chrome::VersionInfo::GetChannel()) { 339 case chrome::VersionInfo::CHANNEL_DEV: 340 case chrome::VersionInfo::CHANNEL_BETA: 341 enabled_probability = 50; 342 break; 343 default: 344 break; 345 } 346 347 trial->AppendGroup("Enabled", enabled_probability); 348} 349 350bool PasswordManager::OtherPossibleUsernamesEnabled() const { 351 return base::FieldTrialList::FindFullName( 352 kOtherPossibleUsernamesExperiment) == "Enabled"; 353} 354 355void PasswordManager::Autofill( 356 const PasswordForm& form_for_autofill, 357 const PasswordFormMap& best_matches, 358 const PasswordForm& preferred_match, 359 bool wait_for_username) const { 360 PossiblyInitializeUsernamesExperiment(best_matches); 361 switch (form_for_autofill.scheme) { 362 case PasswordForm::SCHEME_HTML: { 363 // Note the check above is required because the observer_ for a non-HTML 364 // schemed password form may have been freed, so we need to distinguish. 365 autofill::PasswordFormFillData fill_data; 366 InitPasswordFormFillData(form_for_autofill, 367 best_matches, 368 &preferred_match, 369 wait_for_username, 370 OtherPossibleUsernamesEnabled(), 371 &fill_data); 372 delegate_->FillPasswordForm(fill_data); 373 return; 374 } 375 default: 376 if (observer_) { 377 observer_->OnAutofillDataAvailable(preferred_match.username_value, 378 preferred_match.password_value); 379 } 380 } 381} 382