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/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 "chrome/browser/password_manager/password_form_manager.h" 15 #include "chrome/browser/password_manager/password_manager_delegate.h" 16 #include "chrome/browser/profiles/profile.h" 17 #include "chrome/common/chrome_switches.h" 18 #include "chrome/common/chrome_version_info.h" 19 #include "chrome/common/pref_names.h" 20 #include "components/autofill/core/common/autofill_messages.h" 21 #include "components/user_prefs/pref_registry_syncable.h" 22 #include "content/public/browser/navigation_details.h" 23 #include "content/public/browser/user_metrics.h" 24 #include "content/public/browser/web_contents.h" 25 #include "content/public/common/frame_navigate_params.h" 26 #include "grit/generated_resources.h" 27 28 using content::UserMetricsAction; 29 using content::WebContents; 30 using content::PasswordForm; 31 using content::PasswordFormMap; 32 33 DEFINE_WEB_CONTENTS_USER_DATA_KEY(PasswordManager); 34 35 namespace { 36 37 const char kSpdyProxyRealm[] = "/SpdyProxy"; 38 const char kOtherPossibleUsernamesExperiment[] = 39 "PasswordManagerOtherPossibleUsernames"; 40 41 // This routine is called when PasswordManagers are constructed. 42 // 43 // Currently we report metrics only once at startup. We require 44 // that this is only ever called from a single thread in order to 45 // avoid needing to lock (a static boolean flag is then sufficient to 46 // guarantee running only once). 47 void ReportMetrics(bool password_manager_enabled) { 48 static base::PlatformThreadId initial_thread_id = 49 base::PlatformThread::CurrentId(); 50 DCHECK(initial_thread_id == base::PlatformThread::CurrentId()); 51 52 static bool ran_once = false; 53 if (ran_once) 54 return; 55 ran_once = true; 56 57 // TODO(isherman): This does not actually measure a user action. It should be 58 // a boolean histogram. 59 if (password_manager_enabled) 60 content::RecordAction(UserMetricsAction("PasswordManager_Enabled")); 61 else 62 content::RecordAction(UserMetricsAction("PasswordManager_Disabled")); 63 } 64 65 } // namespace 66 67 // static 68 void PasswordManager::RegisterProfilePrefs( 69 user_prefs::PrefRegistrySyncable* registry) { 70 registry->RegisterBooleanPref( 71 prefs::kPasswordManagerEnabled, 72 true, 73 user_prefs::PrefRegistrySyncable::SYNCABLE_PREF); 74 registry->RegisterBooleanPref( 75 prefs::kPasswordManagerAllowShowPasswords, 76 true, 77 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); 78 } 79 80 // static 81 void PasswordManager::CreateForWebContentsAndDelegate( 82 content::WebContents* contents, 83 PasswordManagerDelegate* delegate) { 84 if (FromWebContents(contents)) { 85 DCHECK_EQ(delegate, FromWebContents(contents)->delegate_); 86 return; 87 } 88 89 contents->SetUserData(UserDataKey(), 90 new PasswordManager(contents, delegate)); 91 } 92 93 PasswordManager::PasswordManager(WebContents* web_contents, 94 PasswordManagerDelegate* delegate) 95 : content::WebContentsObserver(web_contents), 96 delegate_(delegate) { 97 DCHECK(delegate_); 98 password_manager_enabled_.Init(prefs::kPasswordManagerEnabled, 99 delegate_->GetProfile()->GetPrefs()); 100 101 ReportMetrics(*password_manager_enabled_); 102 } 103 104 PasswordManager::~PasswordManager() { 105 FOR_EACH_OBSERVER(LoginModelObserver, observers_, OnLoginModelDestroying()); 106 } 107 108 void 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 134 bool PasswordManager::IsSavingEnabled() const { 135 return *password_manager_enabled_ && 136 !delegate_->GetProfile()->IsOffTheRecord(); 137 } 138 139 void 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 213 void PasswordManager::AddObserver(LoginModelObserver* observer) { 214 observers_.AddObserver(observer); 215 } 216 217 void PasswordManager::RemoveObserver(LoginModelObserver* observer) { 218 observers_.RemoveObserver(observer); 219 } 220 221 void PasswordManager::DidNavigateAnyFrame( 222 const content::LoadCommittedDetails& details, 223 const content::FrameNavigateParams& params) { 224 bool password_form_submitted = params.password_form.origin.is_valid(); 225 226 // Try to save the password if one was submitted. 227 if (password_form_submitted) 228 ProvisionallySavePassword(params.password_form); 229 230 // Clear data after submission or main frame navigation. We don't want 231 // to clear data after subframe navigation as there might be password 232 // forms on other frames that could be submitted. 233 if (password_form_submitted || details.is_main_frame) 234 pending_login_managers_.clear(); 235 } 236 237 bool PasswordManager::OnMessageReceived(const IPC::Message& message) { 238 bool handled = true; 239 IPC_BEGIN_MESSAGE_MAP(PasswordManager, message) 240 IPC_MESSAGE_HANDLER(AutofillHostMsg_PasswordFormsParsed, 241 OnPasswordFormsParsed) 242 IPC_MESSAGE_HANDLER(AutofillHostMsg_PasswordFormsRendered, 243 OnPasswordFormsRendered) 244 IPC_MESSAGE_UNHANDLED(handled = false) 245 IPC_END_MESSAGE_MAP() 246 return handled; 247 } 248 249 void PasswordManager::OnPasswordFormsParsed( 250 const std::vector<PasswordForm>& forms) { 251 // Ask the SSLManager for current security. 252 bool had_ssl_error = delegate_->DidLastPageLoadEncounterSSLErrors(); 253 254 for (std::vector<PasswordForm>::const_iterator iter = forms.begin(); 255 iter != forms.end(); ++iter) { 256 // Don't involve the password manager if this form corresponds to 257 // SpdyProxy authentication, as indicated by the realm. 258 if (EndsWith(iter->signon_realm, kSpdyProxyRealm, true)) 259 continue; 260 261 bool ssl_valid = iter->origin.SchemeIsSecure() && !had_ssl_error; 262 PasswordFormManager* manager = 263 new PasswordFormManager(delegate_->GetProfile(), 264 this, 265 web_contents(), 266 *iter, 267 ssl_valid); 268 pending_login_managers_.push_back(manager); 269 manager->FetchMatchingLoginsFromPasswordStore(); 270 } 271 } 272 273 bool PasswordManager::ShouldShowSavePasswordInfoBar() const { 274 return provisional_save_manager_->IsNewLogin() && 275 !provisional_save_manager_->HasGeneratedPassword() && 276 !provisional_save_manager_->IsPendingCredentialsPublicSuffixMatch(); 277 } 278 279 void PasswordManager::OnPasswordFormsRendered( 280 const std::vector<PasswordForm>& visible_forms) { 281 if (!provisional_save_manager_.get()) 282 return; 283 284 DCHECK(IsSavingEnabled()); 285 286 // First, check for a failed login attempt. 287 for (std::vector<PasswordForm>::const_iterator iter = visible_forms.begin(); 288 iter != visible_forms.end(); ++iter) { 289 if (provisional_save_manager_->DoesManage( 290 *iter, PasswordFormManager::ACTION_MATCH_REQUIRED)) { 291 // The form trying to be saved has immediately re-appeared. Assume login 292 // failure and abort this save, by clearing provisional_save_manager_. 293 provisional_save_manager_->SubmitFailed(); 294 provisional_save_manager_.reset(); 295 return; 296 } 297 } 298 299 if (!provisional_save_manager_->HasValidPasswordForm()) { 300 // Form is not completely valid - we do not support it. 301 NOTREACHED(); 302 provisional_save_manager_.reset(); 303 return; 304 } 305 306 // Looks like a successful login attempt. Either show an infobar or 307 // automatically save the login data. We prompt when the user hasn't already 308 // given consent, either through previously accepting the infobar or by having 309 // the browser generate the password. 310 provisional_save_manager_->SubmitPassed(); 311 if (provisional_save_manager_->HasGeneratedPassword()) 312 UMA_HISTOGRAM_COUNTS("PasswordGeneration.Submitted", 1); 313 314 if (!CommandLine::ForCurrentProcess()->HasSwitch( 315 switches::kEnableSavePasswordBubble)) { 316 if (ShouldShowSavePasswordInfoBar()) { 317 delegate_->AddSavePasswordInfoBarIfPermitted( 318 provisional_save_manager_.release()); 319 } else { 320 provisional_save_manager_->Save(); 321 provisional_save_manager_.reset(); 322 } 323 } 324 } 325 326 void PasswordManager::PossiblyInitializeUsernamesExperiment( 327 const PasswordFormMap& best_matches) const { 328 if (base::FieldTrialList::Find(kOtherPossibleUsernamesExperiment)) 329 return; 330 331 bool other_possible_usernames_exist = false; 332 for (content::PasswordFormMap::const_iterator it = best_matches.begin(); 333 it != best_matches.end(); ++it) { 334 if (!it->second->other_possible_usernames.empty()) { 335 other_possible_usernames_exist = true; 336 break; 337 } 338 } 339 340 if (!other_possible_usernames_exist) 341 return; 342 343 const base::FieldTrial::Probability kDivisor = 100; 344 scoped_refptr<base::FieldTrial> trial( 345 base::FieldTrialList::FactoryGetFieldTrial( 346 kOtherPossibleUsernamesExperiment, 347 kDivisor, "Disabled", 2013, 12, 31, 348 base::FieldTrial::ONE_TIME_RANDOMIZED, NULL)); 349 base::FieldTrial::Probability enabled_probability = 0; 350 351 switch (chrome::VersionInfo::GetChannel()) { 352 case chrome::VersionInfo::CHANNEL_DEV: 353 case chrome::VersionInfo::CHANNEL_BETA: 354 enabled_probability = 50; 355 break; 356 default: 357 break; 358 } 359 360 trial->AppendGroup("Enabled", enabled_probability); 361 } 362 363 bool PasswordManager::OtherPossibleUsernamesEnabled() const { 364 return base::FieldTrialList::FindFullName( 365 kOtherPossibleUsernamesExperiment) == "Enabled"; 366 } 367 368 void PasswordManager::Autofill( 369 const PasswordForm& form_for_autofill, 370 const PasswordFormMap& best_matches, 371 const PasswordForm& preferred_match, 372 bool wait_for_username) const { 373 PossiblyInitializeUsernamesExperiment(best_matches); 374 switch (form_for_autofill.scheme) { 375 case PasswordForm::SCHEME_HTML: { 376 // Note the check above is required because the observer_ for a non-HTML 377 // schemed password form may have been freed, so we need to distinguish. 378 autofill::PasswordFormFillData fill_data; 379 InitPasswordFormFillData(form_for_autofill, 380 best_matches, 381 &preferred_match, 382 wait_for_username, 383 OtherPossibleUsernamesEnabled(), 384 &fill_data); 385 delegate_->FillPasswordForm(fill_data); 386 return; 387 } 388 default: 389 FOR_EACH_OBSERVER( 390 LoginModelObserver, 391 observers_, 392 OnAutofillDataAvailable(preferred_match.username_value, 393 preferred_match.password_value)); 394 } 395 } 396