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_form_manager.h" 6 7 #include <algorithm> 8 9 #include "base/metrics/histogram.h" 10 #include "base/strings/string_split.h" 11 #include "base/strings/string_util.h" 12 #include "chrome/browser/password_manager/password_manager.h" 13 #include "chrome/browser/password_manager/password_store.h" 14 #include "chrome/browser/password_manager/password_store_factory.h" 15 #include "chrome/browser/profiles/profile.h" 16 #include "components/autofill/core/browser/validation.h" 17 #include "components/autofill/core/common/autofill_messages.h" 18 #include "content/public/browser/render_view_host.h" 19 #include "content/public/browser/web_contents.h" 20 #include "content/public/common/password_form.h" 21 22 using base::Time; 23 using content::PasswordForm; 24 using content::PasswordFormMap; 25 26 PasswordFormManager::PasswordFormManager(Profile* profile, 27 PasswordManager* password_manager, 28 content::WebContents* web_contents, 29 const PasswordForm& observed_form, 30 bool ssl_valid) 31 : best_matches_deleter_(&best_matches_), 32 observed_form_(observed_form), 33 is_new_login_(true), 34 has_generated_password_(false), 35 password_manager_(password_manager), 36 preferred_match_(NULL), 37 state_(PRE_MATCHING_PHASE), 38 profile_(profile), 39 web_contents_(web_contents), 40 manager_action_(kManagerActionNone), 41 user_action_(kUserActionNone), 42 submit_result_(kSubmitResultNotSubmitted) { 43 DCHECK(profile_); 44 if (observed_form_.origin.is_valid()) 45 base::SplitString(observed_form_.origin.path(), '/', &form_path_tokens_); 46 observed_form_.ssl_valid = ssl_valid; 47 } 48 49 PasswordFormManager::~PasswordFormManager() { 50 UMA_HISTOGRAM_ENUMERATION("PasswordManager.ActionsTaken", 51 GetActionsTaken(), 52 kMaxNumActionsTaken); 53 } 54 55 int PasswordFormManager::GetActionsTaken() { 56 return user_action_ + kUserActionMax * (manager_action_ + 57 kManagerActionMax * submit_result_); 58 }; 59 60 // TODO(timsteele): use a hash of some sort in the future? 61 bool PasswordFormManager::DoesManage(const PasswordForm& form, 62 ActionMatch action_match) const { 63 if (form.scheme != PasswordForm::SCHEME_HTML) 64 return observed_form_.signon_realm == form.signon_realm; 65 66 // HTML form case. 67 // At a minimum, username and password element must match. 68 if (!((form.username_element == observed_form_.username_element) && 69 (form.password_element == observed_form_.password_element))) { 70 return false; 71 } 72 73 // When action match is required, the action URL must match, but 74 // the form is allowed to have an empty action URL (See bug 1107719). 75 // Otherwise ignore action URL, this is to allow saving password form with 76 // dynamically changed action URL (See bug 27246). 77 if (form.action.is_valid() && (form.action != observed_form_.action)) { 78 if (action_match == ACTION_MATCH_REQUIRED) 79 return false; 80 } 81 82 // If this is a replay of the same form in the case a user entered an invalid 83 // password, the origin of the new form may equal the action of the "first" 84 // form. 85 if (!((form.origin == observed_form_.origin) || 86 (form.origin == observed_form_.action))) { 87 if (form.origin.SchemeIsSecure() && 88 !observed_form_.origin.SchemeIsSecure()) { 89 // Compare origins, ignoring scheme. There is no easy way to do this 90 // with GURL because clearing the scheme would result in an invalid url. 91 // This is for some sites (such as Hotmail) that begin on an http page and 92 // head to https for the retry when password was invalid. 93 std::string::const_iterator after_scheme1 = form.origin.spec().begin() + 94 form.origin.scheme().length(); 95 std::string::const_iterator after_scheme2 = 96 observed_form_.origin.spec().begin() + 97 observed_form_.origin.scheme().length(); 98 return std::search(after_scheme1, 99 form.origin.spec().end(), 100 after_scheme2, 101 observed_form_.origin.spec().end()) 102 != form.origin.spec().end(); 103 } 104 return false; 105 } 106 return true; 107 } 108 109 bool PasswordFormManager::IsBlacklisted() { 110 DCHECK_EQ(state_, POST_MATCHING_PHASE); 111 if (preferred_match_ && preferred_match_->blacklisted_by_user) 112 return true; 113 return false; 114 } 115 116 void PasswordFormManager::PermanentlyBlacklist() { 117 DCHECK_EQ(state_, POST_MATCHING_PHASE); 118 119 // Configure the form about to be saved for blacklist status. 120 pending_credentials_.preferred = true; 121 pending_credentials_.blacklisted_by_user = true; 122 pending_credentials_.username_value.clear(); 123 pending_credentials_.password_value.clear(); 124 125 // Retroactively forget existing matches for this form, so we NEVER prompt or 126 // autofill it again. 127 if (!best_matches_.empty()) { 128 PasswordFormMap::const_iterator iter; 129 PasswordStore* password_store = PasswordStoreFactory::GetForProfile( 130 profile_, Profile::EXPLICIT_ACCESS).get(); 131 if (!password_store) { 132 NOTREACHED(); 133 return; 134 } 135 for (iter = best_matches_.begin(); iter != best_matches_.end(); ++iter) { 136 // We want to remove existing matches for this form so that the exact 137 // origin match with |blackisted_by_user == true| is the only result that 138 // shows up in the future for this origin URL. However, we don't want to 139 // delete logins that were actually saved on a different page (hence with 140 // different origin URL) and just happened to match this form because of 141 // the scoring algorithm. See bug 1204493. 142 if (iter->second->origin == observed_form_.origin) 143 password_store->RemoveLogin(*iter->second); 144 } 145 } 146 147 // Save the pending_credentials_ entry marked as blacklisted. 148 SaveAsNewLogin(false); 149 } 150 151 bool PasswordFormManager::IsNewLogin() { 152 DCHECK_EQ(state_, POST_MATCHING_PHASE); 153 return is_new_login_; 154 } 155 156 bool PasswordFormManager::IsPendingCredentialsPublicSuffixMatch() { 157 return pending_credentials_.IsPublicSuffixMatch(); 158 } 159 160 void PasswordFormManager::SetHasGeneratedPassword() { 161 has_generated_password_ = true; 162 } 163 164 bool PasswordFormManager::HasGeneratedPassword() { 165 // This check is permissive, as the user may have generated a password and 166 // then edited it in the form itself. However, even in this case the user 167 // has already given consent, so we treat these cases the same. 168 return has_generated_password_; 169 } 170 171 bool PasswordFormManager::HasValidPasswordForm() { 172 DCHECK_EQ(state_, POST_MATCHING_PHASE); 173 // Non-HTML password forms (primarily HTTP and FTP autentication) 174 // do not contain username_element and password_element values. 175 if (observed_form_.scheme != PasswordForm::SCHEME_HTML) 176 return true; 177 return !observed_form_.username_element.empty() && 178 !observed_form_.password_element.empty(); 179 } 180 181 void PasswordFormManager::ProvisionallySave( 182 const PasswordForm& credentials, 183 OtherPossibleUsernamesAction action) { 184 DCHECK_EQ(state_, POST_MATCHING_PHASE); 185 DCHECK(DoesManage(credentials, ACTION_MATCH_NOT_REQUIRED)); 186 187 // Make sure the important fields stay the same as the initially observed or 188 // autofilled ones, as they may have changed if the user experienced a login 189 // failure. 190 // Look for these credentials in the list containing auto-fill entries. 191 PasswordFormMap::const_iterator it = 192 best_matches_.find(credentials.username_value); 193 if (it != best_matches_.end()) { 194 // The user signed in with a login we autofilled. 195 pending_credentials_ = *it->second; 196 197 // Public suffix matches should always be new logins, since we want to store 198 // them so they can automatically be filled in later. 199 is_new_login_ = IsPendingCredentialsPublicSuffixMatch(); 200 201 // Check to see if we're using a known username but a new password. 202 if (pending_credentials_.password_value != credentials.password_value) 203 user_action_ = kUserActionOverride; 204 } else if (action == ALLOW_OTHER_POSSIBLE_USERNAMES && 205 UpdatePendingCredentialsIfOtherPossibleUsername( 206 credentials.username_value)) { 207 // |pending_credentials_| is now set. Note we don't update 208 // |pending_credentials_.username_value| to |credentials.username_value| 209 // yet because we need to keep the original username to modify the stored 210 // credential. 211 selected_username_ = credentials.username_value; 212 is_new_login_ = false; 213 } else { 214 // User typed in a new, unknown username. 215 user_action_ = kUserActionOverride; 216 pending_credentials_ = observed_form_; 217 pending_credentials_.username_value = credentials.username_value; 218 pending_credentials_.other_possible_usernames = 219 credentials.other_possible_usernames; 220 } 221 222 pending_credentials_.action = credentials.action; 223 // If the user selected credentials we autofilled from a PasswordForm 224 // that contained no action URL (IE6/7 imported passwords, for example), 225 // bless it with the action URL from the observed form. See bug 1107719. 226 if (pending_credentials_.action.is_empty()) 227 pending_credentials_.action = observed_form_.action; 228 229 pending_credentials_.password_value = credentials.password_value; 230 pending_credentials_.preferred = credentials.preferred; 231 232 if (has_generated_password_) 233 pending_credentials_.type = PasswordForm::TYPE_GENERATED; 234 } 235 236 void PasswordFormManager::Save() { 237 DCHECK_EQ(state_, POST_MATCHING_PHASE); 238 DCHECK(!profile_->IsOffTheRecord()); 239 240 if (IsNewLogin()) 241 SaveAsNewLogin(true); 242 else 243 UpdateLogin(); 244 } 245 246 void PasswordFormManager::FetchMatchingLoginsFromPasswordStore() { 247 DCHECK_EQ(state_, PRE_MATCHING_PHASE); 248 state_ = MATCHING_PHASE; 249 PasswordStore* password_store = PasswordStoreFactory::GetForProfile( 250 profile_, Profile::EXPLICIT_ACCESS).get(); 251 if (!password_store) { 252 NOTREACHED(); 253 return; 254 } 255 password_store->GetLogins(observed_form_, this); 256 } 257 258 bool PasswordFormManager::HasCompletedMatching() { 259 return state_ == POST_MATCHING_PHASE; 260 } 261 262 void PasswordFormManager::OnRequestDone( 263 const std::vector<PasswordForm*>& logins_result) { 264 // Note that the result gets deleted after this call completes, but we own 265 // the PasswordForm objects pointed to by the result vector, thus we keep 266 // copies to a minimum here. 267 268 int best_score = 0; 269 std::vector<PasswordForm> empties; // Empty-path matches in result set. 270 for (size_t i = 0; i < logins_result.size(); i++) { 271 if (IgnoreResult(*logins_result[i])) { 272 delete logins_result[i]; 273 continue; 274 } 275 // Score and update best matches. 276 int current_score = ScoreResult(*logins_result[i]); 277 // This check is here so we can append empty path matches in the event 278 // they don't score as high as others and aren't added to best_matches_. 279 // This is most commonly imported firefox logins. We skip blacklisted 280 // ones because clearly we don't want to autofill them, and secondly 281 // because they only mean something when we have no other matches already 282 // saved in Chrome - in which case they'll make it through the regular 283 // scoring flow below by design. Note signon_realm == origin implies empty 284 // path logins_result, since signon_realm is a prefix of origin for HTML 285 // password forms. 286 // TODO(timsteele): Bug 1269400. We probably should do something more 287 // elegant for any shorter-path match instead of explicitly handling empty 288 // path matches. 289 if ((observed_form_.scheme == PasswordForm::SCHEME_HTML) && 290 (observed_form_.signon_realm == logins_result[i]->origin.spec()) && 291 (current_score > 0) && (!logins_result[i]->blacklisted_by_user)) { 292 empties.push_back(*logins_result[i]); 293 } 294 295 if (current_score < best_score) { 296 delete logins_result[i]; 297 continue; 298 } 299 if (current_score == best_score) { 300 best_matches_[logins_result[i]->username_value] = logins_result[i]; 301 } else if (current_score > best_score) { 302 best_score = current_score; 303 // This new login has a better score than all those up to this point 304 // Note 'this' owns all the PasswordForms in best_matches_. 305 STLDeleteValues(&best_matches_); 306 best_matches_.clear(); 307 preferred_match_ = NULL; // Don't delete, its owned by best_matches_. 308 best_matches_[logins_result[i]->username_value] = logins_result[i]; 309 } 310 preferred_match_ = logins_result[i]->preferred ? logins_result[i] 311 : preferred_match_; 312 } 313 // We're done matching now. 314 state_ = POST_MATCHING_PHASE; 315 316 if (best_score <= 0) { 317 return; 318 } 319 320 for (std::vector<PasswordForm>::const_iterator it = empties.begin(); 321 it != empties.end(); ++it) { 322 // If we don't already have a result with the same username, add the 323 // lower-scored empty-path match (if it had equal score it would already be 324 // in best_matches_). 325 if (best_matches_.find(it->username_value) == best_matches_.end()) 326 best_matches_[it->username_value] = new PasswordForm(*it); 327 } 328 329 // It is possible we have at least one match but have no preferred_match_, 330 // because a user may have chosen to 'Forget' the preferred match. So we 331 // just pick the first one and whichever the user selects for submit will 332 // be saved as preferred. 333 DCHECK(!best_matches_.empty()); 334 if (!preferred_match_) 335 preferred_match_ = best_matches_.begin()->second; 336 337 // Check to see if the user told us to ignore this site in the past. 338 if (preferred_match_->blacklisted_by_user) { 339 manager_action_ = kManagerActionBlacklisted; 340 return; 341 } 342 343 // If not blacklisted, send a message to allow password generation. 344 SendNotBlacklistedToRenderer(); 345 346 // Proceed to autofill. 347 // Note that we provide the choices but don't actually prefill a value if: 348 // (1) we are in Incognito mode, (2) the ACTION paths don't match, 349 // or (3) if it matched using public suffix domain matching. 350 bool wait_for_username = 351 profile_->IsOffTheRecord() || 352 observed_form_.action.GetWithEmptyPath() != 353 preferred_match_->action.GetWithEmptyPath() || 354 preferred_match_->IsPublicSuffixMatch(); 355 if (wait_for_username) 356 manager_action_ = kManagerActionNone; 357 else 358 manager_action_ = kManagerActionAutofilled; 359 password_manager_->Autofill(observed_form_, best_matches_, 360 *preferred_match_, wait_for_username); 361 } 362 363 void PasswordFormManager::OnPasswordStoreRequestDone( 364 CancelableRequestProvider::Handle handle, 365 const std::vector<content::PasswordForm*>& result) { 366 // TODO(kaiwang): Remove this function. 367 NOTREACHED(); 368 } 369 370 void PasswordFormManager::OnGetPasswordStoreResults( 371 const std::vector<content::PasswordForm*>& results) { 372 DCHECK_EQ(state_, MATCHING_PHASE); 373 374 if (results.empty()) { 375 state_ = POST_MATCHING_PHASE; 376 // No result means that we visit this site the first time so we don't need 377 // to check whether this site is blacklisted or not. Just send a message 378 // to allow password generation. 379 SendNotBlacklistedToRenderer(); 380 return; 381 } 382 OnRequestDone(results); 383 } 384 385 bool PasswordFormManager::IgnoreResult(const PasswordForm& form) const { 386 // Ignore change password forms until we have some change password 387 // functionality 388 if (observed_form_.old_password_element.length() != 0) { 389 return true; 390 } 391 // Don't match an invalid SSL form with one saved under secure 392 // circumstances. 393 if (form.ssl_valid && !observed_form_.ssl_valid) { 394 return true; 395 } 396 return false; 397 } 398 399 void PasswordFormManager::SaveAsNewLogin(bool reset_preferred_login) { 400 DCHECK_EQ(state_, POST_MATCHING_PHASE); 401 DCHECK(IsNewLogin()); 402 // The new_form is being used to sign in, so it is preferred. 403 DCHECK(pending_credentials_.preferred); 404 // new_form contains the same basic data as observed_form_ (because its the 405 // same form), but with the newly added credentials. 406 407 DCHECK(!profile_->IsOffTheRecord()); 408 409 PasswordStore* password_store = PasswordStoreFactory::GetForProfile( 410 profile_, Profile::IMPLICIT_ACCESS).get(); 411 if (!password_store) { 412 NOTREACHED(); 413 return; 414 } 415 416 pending_credentials_.date_created = Time::Now(); 417 SanitizePossibleUsernames(&pending_credentials_); 418 password_store->AddLogin(pending_credentials_); 419 420 if (reset_preferred_login) { 421 UpdatePreferredLoginState(password_store); 422 } 423 } 424 425 void PasswordFormManager::SanitizePossibleUsernames(PasswordForm* form) { 426 // Remove any possible usernames that could be credit cards or SSN for privacy 427 // reasons. Also remove duplicates, both in other_possible_usernames and 428 // between other_possible_usernames and username_value. 429 std::set<string16> set; 430 for (std::vector<string16>::iterator it = 431 form->other_possible_usernames.begin(); 432 it != form->other_possible_usernames.end(); ++it) { 433 if (!autofill::IsValidCreditCardNumber(*it) && !autofill::IsSSN(*it)) 434 set.insert(*it); 435 } 436 set.erase(form->username_value); 437 std::vector<string16> temp(set.begin(), set.end()); 438 form->other_possible_usernames.swap(temp); 439 } 440 441 void PasswordFormManager::UpdatePreferredLoginState( 442 PasswordStore* password_store) { 443 DCHECK(password_store); 444 PasswordFormMap::iterator iter; 445 for (iter = best_matches_.begin(); iter != best_matches_.end(); iter++) { 446 if (iter->second->username_value != pending_credentials_.username_value && 447 iter->second->preferred) { 448 // This wasn't the selected login but it used to be preferred. 449 iter->second->preferred = false; 450 if (user_action_ == kUserActionNone) 451 user_action_ = kUserActionChoose; 452 password_store->UpdateLogin(*iter->second); 453 } 454 } 455 } 456 457 void PasswordFormManager::UpdateLogin() { 458 DCHECK_EQ(state_, POST_MATCHING_PHASE); 459 DCHECK(preferred_match_); 460 // If we're doing an Update, we either autofilled correctly and need to 461 // update the stats, or the user typed in a new password for autofilled 462 // username, or the user selected one of the non-preferred matches, 463 // thus requiring a swap of preferred bits. 464 DCHECK(!IsNewLogin() && pending_credentials_.preferred); 465 DCHECK(!profile_->IsOffTheRecord()); 466 467 PasswordStore* password_store = PasswordStoreFactory::GetForProfile( 468 profile_, Profile::IMPLICIT_ACCESS).get(); 469 if (!password_store) { 470 NOTREACHED(); 471 return; 472 } 473 474 // Update metadata. 475 ++pending_credentials_.times_used; 476 477 UpdatePreferredLoginState(password_store); 478 479 // Remove alternate usernames. At this point we assume that we have found 480 // the right username. 481 pending_credentials_.other_possible_usernames.clear(); 482 483 // Update the new preferred login. 484 if (!selected_username_.empty()) { 485 // An other possible username is selected. We set this selected username 486 // as the real username. The PasswordStore API isn't designed to update 487 // username, so we delete the old credentials and add a new one instead. 488 password_store->RemoveLogin(pending_credentials_); 489 pending_credentials_.username_value = selected_username_; 490 password_store->AddLogin(pending_credentials_); 491 } else if ((observed_form_.scheme == PasswordForm::SCHEME_HTML) && 492 (observed_form_.origin.spec().length() > 493 observed_form_.signon_realm.length()) && 494 (observed_form_.signon_realm == 495 pending_credentials_.origin.spec())) { 496 // Note origin.spec().length > signon_realm.length implies the origin has a 497 // path, since signon_realm is a prefix of origin for HTML password forms. 498 // 499 // The user logged in successfully with one of our autofilled logins on a 500 // page with non-empty path, but the autofilled entry was initially saved/ 501 // imported with an empty path. Rather than just mark this entry preferred, 502 // we create a more specific copy for this exact page and leave the "master" 503 // unchanged. This is to prevent the case where that master login is used 504 // on several sites (e.g site.com/a and site.com/b) but the user actually 505 // has a different preference on each site. For example, on /a, he wants the 506 // general empty-path login so it is flagged as preferred, but on /b he logs 507 // in with a different saved entry - we don't want to remove the preferred 508 // status of the former because upon return to /a it won't be the default- 509 // fill match. 510 // TODO(timsteele): Bug 1188626 - expire the master copies. 511 PasswordForm copy(pending_credentials_); 512 copy.origin = observed_form_.origin; 513 copy.action = observed_form_.action; 514 password_store->AddLogin(copy); 515 } else { 516 password_store->UpdateLogin(pending_credentials_); 517 } 518 } 519 520 bool PasswordFormManager::UpdatePendingCredentialsIfOtherPossibleUsername( 521 const string16& username) { 522 for (PasswordFormMap::const_iterator it = best_matches_.begin(); 523 it != best_matches_.end(); ++it) { 524 for (size_t i = 0; i < it->second->other_possible_usernames.size(); ++i) { 525 if (it->second->other_possible_usernames[i] == username) { 526 pending_credentials_ = *it->second; 527 return true; 528 } 529 } 530 } 531 return false; 532 } 533 534 int PasswordFormManager::ScoreResult(const PasswordForm& candidate) const { 535 DCHECK_EQ(state_, MATCHING_PHASE); 536 // For scoring of candidate login data: 537 // The most important element that should match is the origin, followed by 538 // the action, the password name, the submit button name, and finally the 539 // username input field name. 540 // Exact origin match gives an addition of 64 (1 << 6) + # of matching url 541 // dirs. 542 // Partial match gives an addition of 32 (1 << 5) + # matching url dirs 543 // That way, a partial match cannot trump an exact match even if 544 // the partial one matches all other attributes (action, elements) (and 545 // regardless of the matching depth in the URL path). 546 // If public suffix origin match was not used, it gives an addition of 547 // 16 (1 << 4). 548 int score = 0; 549 if (candidate.origin == observed_form_.origin) { 550 // This check is here for the most common case which 551 // is we have a single match in the db for the given host, 552 // so we don't generally need to walk the entire URL path (the else 553 // clause). 554 score += (1 << 6) + static_cast<int>(form_path_tokens_.size()); 555 } else { 556 // Walk the origin URL paths one directory at a time to see how 557 // deep the two match. 558 std::vector<std::string> candidate_path_tokens; 559 base::SplitString(candidate.origin.path(), '/', &candidate_path_tokens); 560 size_t depth = 0; 561 size_t max_dirs = std::min(form_path_tokens_.size(), 562 candidate_path_tokens.size()); 563 while ((depth < max_dirs) && (form_path_tokens_[depth] == 564 candidate_path_tokens[depth])) { 565 depth++; 566 score++; 567 } 568 // do we have a partial match? 569 score += (depth > 0) ? 1 << 5 : 0; 570 } 571 if (observed_form_.scheme == PasswordForm::SCHEME_HTML) { 572 if (!candidate.IsPublicSuffixMatch()) 573 score += 1 << 4; 574 if (candidate.action == observed_form_.action) 575 score += 1 << 3; 576 if (candidate.password_element == observed_form_.password_element) 577 score += 1 << 2; 578 if (candidate.submit_element == observed_form_.submit_element) 579 score += 1 << 1; 580 if (candidate.username_element == observed_form_.username_element) 581 score += 1 << 0; 582 } 583 584 return score; 585 } 586 587 void PasswordFormManager::SubmitPassed() { 588 submit_result_ = kSubmitResultPassed; 589 } 590 591 void PasswordFormManager::SubmitFailed() { 592 submit_result_ = kSubmitResultFailed; 593 } 594 595 void PasswordFormManager::SendNotBlacklistedToRenderer() { 596 content::RenderViewHost* host = web_contents_->GetRenderViewHost(); 597 host->Send(new AutofillMsg_FormNotBlacklisted(host->GetRoutingID(), 598 observed_form_)); 599 } 600