password_store_mac.cc revision 5d1f7b1de12d16ceb2c938c56701a3e8bfa558f7
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_mac.h" 6#include "chrome/browser/password_manager/password_store_mac_internal.h" 7 8#include <CoreServices/CoreServices.h> 9#include <set> 10#include <string> 11#include <utility> 12#include <vector> 13 14#include "base/callback.h" 15#include "base/logging.h" 16#include "base/mac/mac_logging.h" 17#include "base/mac/mac_util.h" 18#include "base/message_loop/message_loop.h" 19#include "base/stl_util.h" 20#include "base/strings/string_util.h" 21#include "base/strings/utf_string_conversions.h" 22#include "chrome/browser/mac/security_wrappers.h" 23#include "components/password_manager/core/browser/login_database.h" 24#include "components/password_manager/core/browser/password_store_change.h" 25#include "crypto/apple_keychain.h" 26 27using autofill::PasswordForm; 28using crypto::AppleKeychain; 29 30// Utility class to handle the details of constructing and running a keychain 31// search from a set of attributes. 32class KeychainSearch { 33 public: 34 explicit KeychainSearch(const AppleKeychain& keychain); 35 ~KeychainSearch(); 36 37 // Sets up a keycahin search based on an non "null" (NULL for char*, 38 // The appropriate "Any" entry for other types) arguments. 39 // 40 // IMPORTANT: Any paramaters passed in *must* remain valid for as long as the 41 // KeychainSearch object, since the search uses them by reference. 42 void Init(const char* server, const UInt32& port, 43 const SecProtocolType& protocol, 44 const SecAuthenticationType& auth_type, const char* security_domain, 45 const char* path, const char* username, OSType creator); 46 47 // Fills |items| with all Keychain items that match the Init'd search. 48 // If the search fails for any reason, |items| will be unchanged. 49 void FindMatchingItems(std::vector<SecKeychainItemRef>* matches); 50 51 private: 52 const AppleKeychain* keychain_; 53 SecKeychainAttributeList search_attributes_; 54 SecKeychainSearchRef search_ref_; 55}; 56 57KeychainSearch::KeychainSearch(const AppleKeychain& keychain) 58 : keychain_(&keychain), search_ref_(NULL) { 59 search_attributes_.count = 0; 60 search_attributes_.attr = NULL; 61} 62 63KeychainSearch::~KeychainSearch() { 64 if (search_attributes_.attr) { 65 free(search_attributes_.attr); 66 } 67} 68 69void KeychainSearch::Init(const char* server, const UInt32& port, 70 const SecProtocolType& protocol, 71 const SecAuthenticationType& auth_type, 72 const char* security_domain, const char* path, 73 const char* username, OSType creator) { 74 // Allocate enough to hold everything we might use. 75 const unsigned int kMaxEntryCount = 8; 76 search_attributes_.attr = 77 static_cast<SecKeychainAttribute*>(calloc(kMaxEntryCount, 78 sizeof(SecKeychainAttribute))); 79 unsigned int entries = 0; 80 // We only use search_attributes_ with SearchCreateFromAttributes, which takes 81 // a "const SecKeychainAttributeList *", so we trust that they won't try 82 // to modify the list, and that casting away const-ness is thus safe. 83 if (server != NULL) { 84 DCHECK_LT(entries, kMaxEntryCount); 85 search_attributes_.attr[entries].tag = kSecServerItemAttr; 86 search_attributes_.attr[entries].length = strlen(server); 87 search_attributes_.attr[entries].data = 88 const_cast<void*>(reinterpret_cast<const void*>(server)); 89 ++entries; 90 } 91 if (port != kAnyPort) { 92 DCHECK_LE(entries, kMaxEntryCount); 93 search_attributes_.attr[entries].tag = kSecPortItemAttr; 94 search_attributes_.attr[entries].length = sizeof(port); 95 search_attributes_.attr[entries].data = 96 const_cast<void*>(reinterpret_cast<const void*>(&port)); 97 ++entries; 98 } 99 if (protocol != kSecProtocolTypeAny) { 100 DCHECK_LE(entries, kMaxEntryCount); 101 search_attributes_.attr[entries].tag = kSecProtocolItemAttr; 102 search_attributes_.attr[entries].length = sizeof(protocol); 103 search_attributes_.attr[entries].data = 104 const_cast<void*>(reinterpret_cast<const void*>(&protocol)); 105 ++entries; 106 } 107 if (auth_type != kSecAuthenticationTypeAny) { 108 DCHECK_LE(entries, kMaxEntryCount); 109 search_attributes_.attr[entries].tag = kSecAuthenticationTypeItemAttr; 110 search_attributes_.attr[entries].length = sizeof(auth_type); 111 search_attributes_.attr[entries].data = 112 const_cast<void*>(reinterpret_cast<const void*>(&auth_type)); 113 ++entries; 114 } 115 if (security_domain != NULL && strlen(security_domain) > 0) { 116 DCHECK_LE(entries, kMaxEntryCount); 117 search_attributes_.attr[entries].tag = kSecSecurityDomainItemAttr; 118 search_attributes_.attr[entries].length = strlen(security_domain); 119 search_attributes_.attr[entries].data = 120 const_cast<void*>(reinterpret_cast<const void*>(security_domain)); 121 ++entries; 122 } 123 if (path != NULL && strlen(path) > 0 && strcmp(path, "/") != 0) { 124 DCHECK_LE(entries, kMaxEntryCount); 125 search_attributes_.attr[entries].tag = kSecPathItemAttr; 126 search_attributes_.attr[entries].length = strlen(path); 127 search_attributes_.attr[entries].data = 128 const_cast<void*>(reinterpret_cast<const void*>(path)); 129 ++entries; 130 } 131 if (username != NULL) { 132 DCHECK_LE(entries, kMaxEntryCount); 133 search_attributes_.attr[entries].tag = kSecAccountItemAttr; 134 search_attributes_.attr[entries].length = strlen(username); 135 search_attributes_.attr[entries].data = 136 const_cast<void*>(reinterpret_cast<const void*>(username)); 137 ++entries; 138 } 139 if (creator != 0) { 140 DCHECK_LE(entries, kMaxEntryCount); 141 search_attributes_.attr[entries].tag = kSecCreatorItemAttr; 142 search_attributes_.attr[entries].length = sizeof(creator); 143 search_attributes_.attr[entries].data = 144 const_cast<void*>(reinterpret_cast<const void*>(&creator)); 145 ++entries; 146 } 147 search_attributes_.count = entries; 148} 149 150void KeychainSearch::FindMatchingItems(std::vector<SecKeychainItemRef>* items) { 151 OSStatus result = keychain_->SearchCreateFromAttributes( 152 NULL, kSecInternetPasswordItemClass, &search_attributes_, &search_ref_); 153 154 if (result != noErr) { 155 OSSTATUS_LOG(ERROR, result) << "Keychain lookup failed"; 156 return; 157 } 158 159 SecKeychainItemRef keychain_item; 160 while (keychain_->SearchCopyNext(search_ref_, &keychain_item) == noErr) { 161 // Consumer is responsible for freeing the items. 162 items->push_back(keychain_item); 163 } 164 165 keychain_->Free(search_ref_); 166 search_ref_ = NULL; 167} 168 169#pragma mark - 170 171// TODO(stuartmorgan): Convert most of this to private helpers in 172// MacKeychainPasswordFormAdapter once it has sufficient higher-level public 173// methods to provide test coverage. 174namespace internal_keychain_helpers { 175 176// Returns a URL built from the given components. To create a URL without a 177// port, pass kAnyPort for the |port| parameter. 178GURL URLFromComponents(bool is_secure, const std::string& host, int port, 179 const std::string& path) { 180 GURL::Replacements url_components; 181 std::string scheme(is_secure ? "https" : "http"); 182 url_components.SetSchemeStr(scheme); 183 url_components.SetHostStr(host); 184 std::string port_string; // Must remain in scope until after we do replacing. 185 if (port != kAnyPort) { 186 std::ostringstream port_stringstream; 187 port_stringstream << port; 188 port_string = port_stringstream.str(); 189 url_components.SetPortStr(port_string); 190 } 191 url_components.SetPathStr(path); 192 193 GURL url("http://dummy.com"); // ReplaceComponents needs a valid URL. 194 return url.ReplaceComponents(url_components); 195} 196 197// Converts a Keychain time string to a Time object, returning true if 198// time_string_bytes was parsable. If the return value is false, the value of 199// |time| is unchanged. 200bool TimeFromKeychainTimeString(const char* time_string_bytes, 201 unsigned int byte_length, 202 base::Time* time) { 203 DCHECK(time); 204 205 char* time_string = static_cast<char*>(malloc(byte_length + 1)); 206 memcpy(time_string, time_string_bytes, byte_length); 207 time_string[byte_length] = '\0'; 208 base::Time::Exploded exploded_time; 209 bzero(&exploded_time, sizeof(exploded_time)); 210 // The time string is of the form "yyyyMMddHHmmss'Z", in UTC time. 211 int assignments = sscanf(time_string, "%4d%2d%2d%2d%2d%2dZ", 212 &exploded_time.year, &exploded_time.month, 213 &exploded_time.day_of_month, &exploded_time.hour, 214 &exploded_time.minute, &exploded_time.second); 215 free(time_string); 216 217 if (assignments == 6) { 218 *time = base::Time::FromUTCExploded(exploded_time); 219 return true; 220 } 221 return false; 222} 223 224// Returns the PasswordForm Scheme corresponding to |auth_type|. 225PasswordForm::Scheme SchemeForAuthType(SecAuthenticationType auth_type) { 226 switch (auth_type) { 227 case kSecAuthenticationTypeHTMLForm: return PasswordForm::SCHEME_HTML; 228 case kSecAuthenticationTypeHTTPBasic: return PasswordForm::SCHEME_BASIC; 229 case kSecAuthenticationTypeHTTPDigest: return PasswordForm::SCHEME_DIGEST; 230 default: return PasswordForm::SCHEME_OTHER; 231 } 232} 233 234bool FillPasswordFormFromKeychainItem(const AppleKeychain& keychain, 235 const SecKeychainItemRef& keychain_item, 236 PasswordForm* form, 237 bool extract_password_data) { 238 DCHECK(form); 239 240 SecKeychainAttributeInfo attrInfo; 241 UInt32 tags[] = { kSecAccountItemAttr, 242 kSecServerItemAttr, 243 kSecPortItemAttr, 244 kSecPathItemAttr, 245 kSecProtocolItemAttr, 246 kSecAuthenticationTypeItemAttr, 247 kSecSecurityDomainItemAttr, 248 kSecCreationDateItemAttr, 249 kSecNegativeItemAttr }; 250 attrInfo.count = arraysize(tags); 251 attrInfo.tag = tags; 252 attrInfo.format = NULL; 253 254 SecKeychainAttributeList *attrList; 255 UInt32 password_length; 256 257 // If |extract_password_data| is false, do not pass in a reference to 258 // |password_data|. ItemCopyAttributesAndData will then extract only the 259 // attributes of |keychain_item| (doesn't require OS authorization), and not 260 // attempt to extract its password data (requires OS authorization). 261 void* password_data = NULL; 262 void** password_data_ref = extract_password_data ? &password_data : NULL; 263 264 OSStatus result = keychain.ItemCopyAttributesAndData(keychain_item, &attrInfo, 265 NULL, &attrList, 266 &password_length, 267 password_data_ref); 268 269 if (result != noErr) { 270 // We don't log errSecAuthFailed because that just means that the user 271 // chose not to allow us access to the item. 272 if (result != errSecAuthFailed) { 273 OSSTATUS_LOG(ERROR, result) << "Keychain data load failed"; 274 } 275 return false; 276 } 277 278 if (extract_password_data) { 279 base::UTF8ToUTF16(static_cast<const char *>(password_data), password_length, 280 &(form->password_value)); 281 } 282 283 int port = kAnyPort; 284 std::string server; 285 std::string security_domain; 286 std::string path; 287 for (unsigned int i = 0; i < attrList->count; i++) { 288 SecKeychainAttribute attr = attrList->attr[i]; 289 if (!attr.data) { 290 continue; 291 } 292 switch (attr.tag) { 293 case kSecAccountItemAttr: 294 base::UTF8ToUTF16(static_cast<const char *>(attr.data), attr.length, 295 &(form->username_value)); 296 break; 297 case kSecServerItemAttr: 298 server.assign(static_cast<const char *>(attr.data), attr.length); 299 break; 300 case kSecPortItemAttr: 301 port = *(static_cast<UInt32*>(attr.data)); 302 break; 303 case kSecPathItemAttr: 304 path.assign(static_cast<const char *>(attr.data), attr.length); 305 break; 306 case kSecProtocolItemAttr: 307 { 308 SecProtocolType protocol = *(static_cast<SecProtocolType*>(attr.data)); 309 // TODO(stuartmorgan): Handle proxy types 310 form->ssl_valid = (protocol == kSecProtocolTypeHTTPS); 311 break; 312 } 313 case kSecAuthenticationTypeItemAttr: 314 { 315 SecAuthenticationType auth_type = 316 *(static_cast<SecAuthenticationType*>(attr.data)); 317 form->scheme = SchemeForAuthType(auth_type); 318 break; 319 } 320 case kSecSecurityDomainItemAttr: 321 security_domain.assign(static_cast<const char *>(attr.data), 322 attr.length); 323 break; 324 case kSecCreationDateItemAttr: 325 // The only way to get a date out of Keychain is as a string. Really. 326 // (The docs claim it's an int, but the header is correct.) 327 TimeFromKeychainTimeString(static_cast<char*>(attr.data), attr.length, 328 &form->date_created); 329 break; 330 case kSecNegativeItemAttr: 331 Boolean negative_item = *(static_cast<Boolean*>(attr.data)); 332 if (negative_item) { 333 form->blacklisted_by_user = true; 334 } 335 break; 336 } 337 } 338 keychain.ItemFreeAttributesAndData(attrList, password_data); 339 340 // kSecNegativeItemAttr doesn't seem to actually be in widespread use. In 341 // practice, other browsers seem to use a "" or " " password (and a special 342 // user name) to indicated blacklist entries. 343 if (extract_password_data && (form->password_value.empty() || 344 EqualsASCII(form->password_value, " "))) { 345 form->blacklisted_by_user = true; 346 } 347 348 form->origin = URLFromComponents(form->ssl_valid, server, port, path); 349 // TODO(stuartmorgan): Handle proxies, which need a different signon_realm 350 // format. 351 form->signon_realm = form->origin.GetOrigin().spec(); 352 if (form->scheme != PasswordForm::SCHEME_HTML) { 353 form->signon_realm.append(security_domain); 354 } 355 return true; 356} 357 358bool FormsMatchForMerge(const PasswordForm& form_a, 359 const PasswordForm& form_b, 360 FormMatchStrictness strictness) { 361 // We never merge blacklist entries between our store and the keychain. 362 if (form_a.blacklisted_by_user || form_b.blacklisted_by_user) { 363 return false; 364 } 365 bool equal_realm = form_a.signon_realm == form_b.signon_realm; 366 if (strictness == FUZZY_FORM_MATCH) { 367 equal_realm |= (!form_a.original_signon_realm.empty()) && 368 form_a.original_signon_realm == form_b.signon_realm; 369 } 370 return form_a.scheme == form_b.scheme && equal_realm && 371 form_a.username_value == form_b.username_value; 372} 373 374// Returns an the best match for |base_form| from |keychain_forms|, or NULL if 375// there is no suitable match. 376PasswordForm* BestKeychainFormForForm( 377 const PasswordForm& base_form, 378 const std::vector<PasswordForm*>* keychain_forms) { 379 PasswordForm* partial_match = NULL; 380 for (std::vector<PasswordForm*>::const_iterator i = keychain_forms->begin(); 381 i != keychain_forms->end(); ++i) { 382 // TODO(stuartmorgan): We should really be scoring path matches and picking 383 // the best, rather than just checking exact-or-not (although in practice 384 // keychain items with paths probably came from us). 385 if (FormsMatchForMerge(base_form, *(*i), FUZZY_FORM_MATCH)) { 386 if (base_form.origin == (*i)->origin) { 387 return *i; 388 } else if (!partial_match) { 389 partial_match = *i; 390 } 391 } 392 } 393 return partial_match; 394} 395 396// Returns entries from |forms| that are blacklist entries, after removing 397// them from |forms|. 398std::vector<PasswordForm*> ExtractBlacklistForms( 399 std::vector<PasswordForm*>* forms) { 400 std::vector<PasswordForm*> blacklist_forms; 401 for (std::vector<PasswordForm*>::iterator i = forms->begin(); 402 i != forms->end();) { 403 PasswordForm* form = *i; 404 if (form->blacklisted_by_user) { 405 blacklist_forms.push_back(form); 406 i = forms->erase(i); 407 } else { 408 ++i; 409 } 410 } 411 return blacklist_forms; 412} 413 414// Deletes and removes from v any element that exists in s. 415template <class T> 416void DeleteVectorElementsInSet(std::vector<T*>* v, const std::set<T*>& s) { 417 for (typename std::vector<T*>::iterator i = v->begin(); i != v->end();) { 418 T* element = *i; 419 if (s.find(element) != s.end()) { 420 delete element; 421 i = v->erase(i); 422 } else { 423 ++i; 424 } 425 } 426} 427 428void MergePasswordForms(std::vector<PasswordForm*>* keychain_forms, 429 std::vector<PasswordForm*>* database_forms, 430 std::vector<PasswordForm*>* merged_forms) { 431 // Pull out the database blacklist items, since they are used as-is rather 432 // than being merged with keychain forms. 433 std::vector<PasswordForm*> database_blacklist_forms = 434 ExtractBlacklistForms(database_forms); 435 436 // Merge the normal entries. 437 std::set<PasswordForm*> used_keychain_forms; 438 for (std::vector<PasswordForm*>::iterator i = database_forms->begin(); 439 i != database_forms->end();) { 440 PasswordForm* db_form = *i; 441 PasswordForm* best_match = BestKeychainFormForForm(*db_form, 442 keychain_forms); 443 if (best_match) { 444 used_keychain_forms.insert(best_match); 445 db_form->password_value = best_match->password_value; 446 merged_forms->push_back(db_form); 447 i = database_forms->erase(i); 448 } else { 449 ++i; 450 } 451 } 452 453 // Add in the blacklist entries from the database. 454 merged_forms->insert(merged_forms->end(), 455 database_blacklist_forms.begin(), 456 database_blacklist_forms.end()); 457 458 // Clear out all the Keychain entries we used. 459 DeleteVectorElementsInSet(keychain_forms, used_keychain_forms); 460} 461 462std::vector<ItemFormPair> ExtractAllKeychainItemAttributesIntoPasswordForms( 463 std::vector<SecKeychainItemRef>* keychain_items, 464 const AppleKeychain& keychain) { 465 DCHECK(keychain_items); 466 MacKeychainPasswordFormAdapter keychain_adapter(&keychain); 467 *keychain_items = keychain_adapter.GetAllPasswordFormKeychainItems(); 468 std::vector<ItemFormPair> item_form_pairs; 469 for (std::vector<SecKeychainItemRef>::iterator i = keychain_items->begin(); 470 i != keychain_items->end(); ++i) { 471 PasswordForm* form_without_password = new PasswordForm(); 472 internal_keychain_helpers::FillPasswordFormFromKeychainItem( 473 keychain, 474 *i, 475 form_without_password, 476 false); // Load password attributes, but not password data. 477 item_form_pairs.push_back(std::make_pair(&(*i), form_without_password)); 478 } 479 return item_form_pairs; 480} 481 482std::vector<PasswordForm*> GetPasswordsForForms( 483 const AppleKeychain& keychain, 484 std::vector<PasswordForm*>* database_forms) { 485 // First load the attributes of all items in the keychain without loading 486 // their password data, and then match items in |database_forms| against them. 487 // This avoids individually searching through the keychain for passwords 488 // matching each form in |database_forms|, and results in a significant 489 // performance gain, replacing O(N) keychain search operations with a single 490 // operation that loads all keychain items, and then selective reads of only 491 // the relevant passwords. See crbug.com/263685. 492 std::vector<SecKeychainItemRef> keychain_items; 493 std::vector<ItemFormPair> item_form_pairs = 494 ExtractAllKeychainItemAttributesIntoPasswordForms(&keychain_items, 495 keychain); 496 497 // Next, compare the attributes of the PasswordForms in |database_forms| 498 // against those in |item_form_pairs|, and extract password data for each 499 // matching PasswordForm using its corresponding SecKeychainItemRef. 500 std::vector<PasswordForm*> merged_forms; 501 for (std::vector<PasswordForm*>::iterator i = database_forms->begin(); 502 i != database_forms->end();) { 503 std::vector<PasswordForm*> db_form_container(1, *i); 504 std::vector<PasswordForm*> keychain_matches = 505 ExtractPasswordsMergeableWithForm(keychain, item_form_pairs, **i); 506 MergePasswordForms(&keychain_matches, &db_form_container, &merged_forms); 507 if (db_form_container.empty()) { 508 i = database_forms->erase(i); 509 } else { 510 ++i; 511 } 512 STLDeleteElements(&keychain_matches); 513 } 514 515 // Clean up temporary PasswordForms and SecKeychainItemRefs. 516 STLDeleteContainerPairSecondPointers(item_form_pairs.begin(), 517 item_form_pairs.end()); 518 for (std::vector<SecKeychainItemRef>::iterator i = keychain_items.begin(); 519 i != keychain_items.end(); ++i) { 520 keychain.Free(*i); 521 } 522 return merged_forms; 523} 524 525// TODO(stuartmorgan): signon_realm for proxies is not yet supported. 526bool ExtractSignonRealmComponents( 527 const std::string& signon_realm, std::string* server, int* port, 528 bool* is_secure, std::string* security_domain) { 529 // The signon_realm will be the Origin portion of a URL for an HTML form, 530 // and the same but with the security domain as a path for HTTP auth. 531 GURL realm_as_url(signon_realm); 532 if (!realm_as_url.is_valid()) { 533 return false; 534 } 535 536 if (server) 537 *server = realm_as_url.host(); 538 if (is_secure) 539 *is_secure = realm_as_url.SchemeIsSecure(); 540 if (port) 541 *port = realm_as_url.has_port() ? atoi(realm_as_url.port().c_str()) : 0; 542 if (security_domain) { 543 // Strip the leading '/' off of the path to get the security domain. 544 if (realm_as_url.path().length() > 0) 545 *security_domain = realm_as_url.path().substr(1); 546 else 547 security_domain->clear(); 548 } 549 return true; 550} 551 552bool FormIsValidAndMatchesOtherForm(const PasswordForm& query_form, 553 const PasswordForm& other_form) { 554 std::string server; 555 std::string security_domain; 556 int port; 557 bool is_secure; 558 if (!ExtractSignonRealmComponents(query_form.signon_realm, &server, &port, 559 &is_secure, &security_domain)) { 560 return false; 561 } 562 return internal_keychain_helpers::FormsMatchForMerge( 563 query_form, other_form, STRICT_FORM_MATCH); 564} 565 566std::vector<PasswordForm*> ExtractPasswordsMergeableWithForm( 567 const AppleKeychain& keychain, 568 const std::vector<ItemFormPair>& item_form_pairs, 569 const PasswordForm& query_form) { 570 std::vector<PasswordForm*> matches; 571 for (std::vector<ItemFormPair>::const_iterator i = item_form_pairs.begin(); 572 i != item_form_pairs.end(); ++i) { 573 if (FormIsValidAndMatchesOtherForm(query_form, *(i->second))) { 574 // Create a new object, since the caller is responsible for deleting the 575 // returned forms. 576 scoped_ptr<PasswordForm> form_with_password(new PasswordForm()); 577 internal_keychain_helpers::FillPasswordFormFromKeychainItem( 578 keychain, 579 *(i->first), 580 form_with_password.get(), 581 true); // Load password attributes and data. 582 // Do not include blacklisted items found in the keychain. 583 if (!form_with_password->blacklisted_by_user) 584 matches.push_back(form_with_password.release()); 585 } 586 } 587 return matches; 588} 589 590} // namespace internal_keychain_helpers 591 592#pragma mark - 593 594MacKeychainPasswordFormAdapter::MacKeychainPasswordFormAdapter( 595 const AppleKeychain* keychain) 596 : keychain_(keychain), finds_only_owned_(false) { 597} 598 599std::vector<PasswordForm*> MacKeychainPasswordFormAdapter::PasswordsFillingForm( 600 const std::string& signon_realm, 601 PasswordForm::Scheme scheme) { 602 std::vector<SecKeychainItemRef> keychain_items = 603 MatchingKeychainItems(signon_realm, scheme, NULL, NULL); 604 605 return ConvertKeychainItemsToForms(&keychain_items); 606} 607 608PasswordForm* MacKeychainPasswordFormAdapter::PasswordExactlyMatchingForm( 609 const PasswordForm& query_form) { 610 SecKeychainItemRef keychain_item = KeychainItemForForm(query_form); 611 if (keychain_item) { 612 PasswordForm* form = new PasswordForm(); 613 internal_keychain_helpers::FillPasswordFormFromKeychainItem(*keychain_, 614 keychain_item, 615 form, 616 true); 617 keychain_->Free(keychain_item); 618 return form; 619 } 620 return NULL; 621} 622 623bool MacKeychainPasswordFormAdapter::HasPasswordsMergeableWithForm( 624 const PasswordForm& query_form) { 625 std::string username = base::UTF16ToUTF8(query_form.username_value); 626 std::vector<SecKeychainItemRef> matches = 627 MatchingKeychainItems(query_form.signon_realm, query_form.scheme, 628 NULL, username.c_str()); 629 for (std::vector<SecKeychainItemRef>::iterator i = matches.begin(); 630 i != matches.end(); ++i) { 631 keychain_->Free(*i); 632 } 633 634 return !matches.empty(); 635} 636 637std::vector<SecKeychainItemRef> 638 MacKeychainPasswordFormAdapter::GetAllPasswordFormKeychainItems() { 639 SecAuthenticationType supported_auth_types[] = { 640 kSecAuthenticationTypeHTMLForm, 641 kSecAuthenticationTypeHTTPBasic, 642 kSecAuthenticationTypeHTTPDigest, 643 }; 644 645 std::vector<SecKeychainItemRef> matches; 646 for (unsigned int i = 0; i < arraysize(supported_auth_types); ++i) { 647 KeychainSearch keychain_search(*keychain_); 648 keychain_search.Init(NULL, 0, kSecProtocolTypeAny, supported_auth_types[i], 649 NULL, NULL, NULL, CreatorCodeForSearch()); 650 keychain_search.FindMatchingItems(&matches); 651 } 652 return matches; 653} 654 655std::vector<PasswordForm*> 656 MacKeychainPasswordFormAdapter::GetAllPasswordFormPasswords() { 657 std::vector<SecKeychainItemRef> items = GetAllPasswordFormKeychainItems(); 658 return ConvertKeychainItemsToForms(&items); 659} 660 661bool MacKeychainPasswordFormAdapter::AddPassword(const PasswordForm& form) { 662 // We should never be trying to store a blacklist in the keychain. 663 DCHECK(!form.blacklisted_by_user); 664 665 std::string server; 666 std::string security_domain; 667 int port; 668 bool is_secure; 669 if (!internal_keychain_helpers::ExtractSignonRealmComponents( 670 form.signon_realm, &server, &port, &is_secure, &security_domain)) { 671 return false; 672 } 673 std::string username = base::UTF16ToUTF8(form.username_value); 674 std::string password = base::UTF16ToUTF8(form.password_value); 675 std::string path = form.origin.path(); 676 SecProtocolType protocol = is_secure ? kSecProtocolTypeHTTPS 677 : kSecProtocolTypeHTTP; 678 SecKeychainItemRef new_item = NULL; 679 OSStatus result = keychain_->AddInternetPassword( 680 NULL, server.size(), server.c_str(), 681 security_domain.size(), security_domain.c_str(), 682 username.size(), username.c_str(), 683 path.size(), path.c_str(), 684 port, protocol, AuthTypeForScheme(form.scheme), 685 password.size(), password.c_str(), &new_item); 686 687 if (result == noErr) { 688 SetKeychainItemCreatorCode(new_item, 689 base::mac::CreatorCodeForApplication()); 690 keychain_->Free(new_item); 691 } else if (result == errSecDuplicateItem) { 692 // If we collide with an existing item, find and update it instead. 693 SecKeychainItemRef existing_item = KeychainItemForForm(form); 694 if (!existing_item) { 695 return false; 696 } 697 bool changed = SetKeychainItemPassword(existing_item, password); 698 keychain_->Free(existing_item); 699 return changed; 700 } 701 702 return result == noErr; 703} 704 705bool MacKeychainPasswordFormAdapter::RemovePassword(const PasswordForm& form) { 706 SecKeychainItemRef keychain_item = KeychainItemForForm(form); 707 if (keychain_item == NULL) 708 return false; 709 OSStatus result = keychain_->ItemDelete(keychain_item); 710 keychain_->Free(keychain_item); 711 return result == noErr; 712} 713 714void MacKeychainPasswordFormAdapter::SetFindsOnlyOwnedItems( 715 bool finds_only_owned) { 716 finds_only_owned_ = finds_only_owned; 717} 718 719std::vector<PasswordForm*> 720 MacKeychainPasswordFormAdapter::ConvertKeychainItemsToForms( 721 std::vector<SecKeychainItemRef>* items) { 722 std::vector<PasswordForm*> keychain_forms; 723 for (std::vector<SecKeychainItemRef>::const_iterator i = items->begin(); 724 i != items->end(); ++i) { 725 PasswordForm* form = new PasswordForm(); 726 if (internal_keychain_helpers::FillPasswordFormFromKeychainItem( 727 *keychain_, *i, form, true)) { 728 keychain_forms.push_back(form); 729 } 730 keychain_->Free(*i); 731 } 732 items->clear(); 733 return keychain_forms; 734} 735 736SecKeychainItemRef MacKeychainPasswordFormAdapter::KeychainItemForForm( 737 const PasswordForm& form) { 738 // We don't store blacklist entries in the keychain, so the answer to "what 739 // Keychain item goes with this form" is always "nothing" for blacklists. 740 if (form.blacklisted_by_user) { 741 return NULL; 742 } 743 744 std::string path = form.origin.path(); 745 std::string username = base::UTF16ToUTF8(form.username_value); 746 std::vector<SecKeychainItemRef> matches = MatchingKeychainItems( 747 form.signon_realm, form.scheme, path.c_str(), username.c_str()); 748 749 if (matches.empty()) { 750 return NULL; 751 } 752 // Free all items after the first, since we won't be returning them. 753 for (std::vector<SecKeychainItemRef>::iterator i = matches.begin() + 1; 754 i != matches.end(); ++i) { 755 keychain_->Free(*i); 756 } 757 return matches[0]; 758} 759 760std::vector<SecKeychainItemRef> 761 MacKeychainPasswordFormAdapter::MatchingKeychainItems( 762 const std::string& signon_realm, 763 autofill::PasswordForm::Scheme scheme, 764 const char* path, const char* username) { 765 std::vector<SecKeychainItemRef> matches; 766 767 std::string server; 768 std::string security_domain; 769 int port; 770 bool is_secure; 771 if (!internal_keychain_helpers::ExtractSignonRealmComponents( 772 signon_realm, &server, &port, &is_secure, &security_domain)) { 773 // TODO(stuartmorgan): Proxies will currently fail here, since their 774 // signon_realm is not a URL. We need to detect the proxy case and handle 775 // it specially. 776 return matches; 777 } 778 SecProtocolType protocol = is_secure ? kSecProtocolTypeHTTPS 779 : kSecProtocolTypeHTTP; 780 SecAuthenticationType auth_type = AuthTypeForScheme(scheme); 781 const char* auth_domain = (scheme == PasswordForm::SCHEME_HTML) ? 782 NULL : security_domain.c_str(); 783 KeychainSearch keychain_search(*keychain_); 784 keychain_search.Init(server.c_str(), port, protocol, auth_type, 785 auth_domain, path, username, CreatorCodeForSearch()); 786 keychain_search.FindMatchingItems(&matches); 787 return matches; 788} 789 790// Returns the Keychain SecAuthenticationType type corresponding to |scheme|. 791SecAuthenticationType MacKeychainPasswordFormAdapter::AuthTypeForScheme( 792 PasswordForm::Scheme scheme) { 793 switch (scheme) { 794 case PasswordForm::SCHEME_HTML: return kSecAuthenticationTypeHTMLForm; 795 case PasswordForm::SCHEME_BASIC: return kSecAuthenticationTypeHTTPBasic; 796 case PasswordForm::SCHEME_DIGEST: return kSecAuthenticationTypeHTTPDigest; 797 case PasswordForm::SCHEME_OTHER: return kSecAuthenticationTypeDefault; 798 } 799 NOTREACHED(); 800 return kSecAuthenticationTypeDefault; 801} 802 803bool MacKeychainPasswordFormAdapter::SetKeychainItemPassword( 804 const SecKeychainItemRef& keychain_item, const std::string& password) { 805 OSStatus result = keychain_->ItemModifyAttributesAndData(keychain_item, NULL, 806 password.size(), 807 password.c_str()); 808 return result == noErr; 809} 810 811bool MacKeychainPasswordFormAdapter::SetKeychainItemCreatorCode( 812 const SecKeychainItemRef& keychain_item, OSType creator_code) { 813 SecKeychainAttribute attr = { kSecCreatorItemAttr, sizeof(creator_code), 814 &creator_code }; 815 SecKeychainAttributeList attrList = { 1, &attr }; 816 OSStatus result = keychain_->ItemModifyAttributesAndData(keychain_item, 817 &attrList, 0, NULL); 818 return result == noErr; 819} 820 821OSType MacKeychainPasswordFormAdapter::CreatorCodeForSearch() { 822 return finds_only_owned_ ? base::mac::CreatorCodeForApplication() : 0; 823} 824 825#pragma mark - 826 827PasswordStoreMac::PasswordStoreMac( 828 scoped_refptr<base::SingleThreadTaskRunner> main_thread_runner, 829 scoped_refptr<base::SingleThreadTaskRunner> db_thread_runner, 830 AppleKeychain* keychain, 831 LoginDatabase* login_db) 832 : PasswordStore(main_thread_runner, db_thread_runner), 833 keychain_(keychain), 834 login_metadata_db_(login_db) { 835 DCHECK(keychain_.get()); 836 DCHECK(login_metadata_db_.get()); 837} 838 839PasswordStoreMac::~PasswordStoreMac() {} 840 841bool PasswordStoreMac::Init() { 842 thread_.reset(new base::Thread("Chrome_PasswordStore_Thread")); 843 844 if (!thread_->Start()) { 845 thread_.reset(NULL); 846 return false; 847 } 848 return PasswordStore::Init(); 849} 850 851// Mac stores passwords in the system keychain, which can block for an 852// arbitrarily long time (most notably, it can block on user confirmation 853// from a dialog). Run tasks on a dedicated thread to avoid blocking the DB 854// thread. 855scoped_refptr<base::SingleThreadTaskRunner> 856PasswordStoreMac::GetBackgroundTaskRunner() { 857 return (thread_.get()) ? thread_->message_loop_proxy() : NULL; 858} 859 860void PasswordStoreMac::ReportMetricsImpl() { 861 login_metadata_db_->ReportMetrics(); 862} 863 864PasswordStoreChangeList PasswordStoreMac::AddLoginImpl( 865 const PasswordForm& form) { 866 PasswordStoreChangeList changes; 867 if (AddToKeychainIfNecessary(form)) { 868 if (login_metadata_db_->AddLogin(form)) { 869 changes.push_back(PasswordStoreChange(PasswordStoreChange::ADD, form)); 870 } 871 } 872 return changes; 873} 874 875PasswordStoreChangeList PasswordStoreMac::UpdateLoginImpl( 876 const PasswordForm& form) { 877 PasswordStoreChangeList changes; 878 int update_count = 0; 879 if (!login_metadata_db_->UpdateLogin(form, &update_count)) 880 return changes; 881 882 MacKeychainPasswordFormAdapter keychain_adapter(keychain_.get()); 883 if (update_count == 0 && 884 !keychain_adapter.HasPasswordsMergeableWithForm(form)) { 885 // If the password isn't in either the DB or the keychain, then it must have 886 // been deleted after autofill happened, and should not be re-added. 887 return changes; 888 } 889 890 // The keychain add will update if there is a collision and add if there 891 // isn't, which is the behavior we want, so there's no separate update call. 892 if (AddToKeychainIfNecessary(form)) { 893 if (update_count == 0) { 894 if (login_metadata_db_->AddLogin(form)) { 895 changes.push_back(PasswordStoreChange(PasswordStoreChange::ADD, 896 form)); 897 } 898 } else { 899 changes.push_back(PasswordStoreChange(PasswordStoreChange::UPDATE, 900 form)); 901 } 902 } 903 return changes; 904} 905 906PasswordStoreChangeList PasswordStoreMac::RemoveLoginImpl( 907 const PasswordForm& form) { 908 PasswordStoreChangeList changes; 909 if (login_metadata_db_->RemoveLogin(form)) { 910 // See if we own a Keychain item associated with this item. We can do an 911 // exact search rather than messing around with trying to do fuzzy matching 912 // because passwords that we created will always have an exact-match 913 // database entry. 914 // (If a user does lose their profile but not their keychain we'll treat the 915 // entries we find like other imported entries anyway, so it's reasonable to 916 // handle deletes on them the way we would for an imported item.) 917 MacKeychainPasswordFormAdapter owned_keychain_adapter(keychain_.get()); 918 owned_keychain_adapter.SetFindsOnlyOwnedItems(true); 919 PasswordForm* owned_password_form = 920 owned_keychain_adapter.PasswordExactlyMatchingForm(form); 921 if (owned_password_form) { 922 // If we don't have other forms using it (i.e., a form differing only by 923 // the names of the form elements), delete the keychain entry. 924 if (!DatabaseHasFormMatchingKeychainForm(form)) { 925 owned_keychain_adapter.RemovePassword(form); 926 } 927 } 928 929 changes.push_back(PasswordStoreChange(PasswordStoreChange::REMOVE, form)); 930 } 931 return changes; 932} 933 934PasswordStoreChangeList PasswordStoreMac::RemoveLoginsCreatedBetweenImpl( 935 const base::Time& delete_begin, const base::Time& delete_end) { 936 PasswordStoreChangeList changes; 937 std::vector<PasswordForm*> forms; 938 if (login_metadata_db_->GetLoginsCreatedBetween(delete_begin, delete_end, 939 &forms)) { 940 if (login_metadata_db_->RemoveLoginsCreatedBetween(delete_begin, 941 delete_end)) { 942 // We can't delete from the Keychain by date because we may be sharing 943 // items with database entries that weren't in the delete range. Instead, 944 // we find all the Keychain items we own but aren't using any more and 945 // delete those. 946 std::vector<PasswordForm*> orphan_keychain_forms = 947 GetUnusedKeychainForms(); 948 // This is inefficient, since we have to re-look-up each keychain item 949 // one at a time to delete it even though the search step already had a 950 // list of Keychain item references. If this turns out to be noticeably 951 // slow we'll need to rearchitect to allow the search and deletion steps 952 // to share. 953 RemoveKeychainForms(orphan_keychain_forms); 954 STLDeleteElements(&orphan_keychain_forms); 955 956 for (std::vector<PasswordForm*>::const_iterator it = forms.begin(); 957 it != forms.end(); ++it) { 958 changes.push_back(PasswordStoreChange(PasswordStoreChange::REMOVE, 959 **it)); 960 } 961 LogStatsForBulkDeletion(changes.size()); 962 } 963 } 964 return changes; 965} 966 967void PasswordStoreMac::GetLoginsImpl( 968 const autofill::PasswordForm& form, 969 AuthorizationPromptPolicy prompt_policy, 970 const ConsumerCallbackRunner& callback_runner) { 971 chrome::ScopedSecKeychainSetUserInteractionAllowed user_interaction_allowed( 972 prompt_policy == ALLOW_PROMPT); 973 974 std::vector<PasswordForm*> database_forms; 975 login_metadata_db_->GetLogins(form, &database_forms); 976 977 // Let's gather all signon realms we want to match with keychain entries. 978 std::set<std::string> realm_set; 979 realm_set.insert(form.signon_realm); 980 for (std::vector<PasswordForm*>::const_iterator db_form = 981 database_forms.begin(); 982 db_form != database_forms.end(); 983 ++db_form) { 984 // TODO(vabr): We should not be getting different schemes here. 985 // http://crbug.com/340112 986 if (form.scheme != (*db_form)->scheme) 987 continue; // Forms with different schemes never match. 988 const std::string& original_singon_realm((*db_form)->original_signon_realm); 989 if (!original_singon_realm.empty()) 990 realm_set.insert(original_singon_realm); 991 } 992 std::vector<PasswordForm*> keychain_forms; 993 for (std::set<std::string>::const_iterator realm = realm_set.begin(); 994 realm != realm_set.end(); 995 ++realm) { 996 MacKeychainPasswordFormAdapter keychain_adapter(keychain_.get()); 997 std::vector<PasswordForm*> temp_keychain_forms = 998 keychain_adapter.PasswordsFillingForm(*realm, form.scheme); 999 keychain_forms.insert(keychain_forms.end(), 1000 temp_keychain_forms.begin(), 1001 temp_keychain_forms.end()); 1002 } 1003 1004 std::vector<PasswordForm*> matched_forms; 1005 internal_keychain_helpers::MergePasswordForms(&keychain_forms, 1006 &database_forms, 1007 &matched_forms); 1008 1009 // Strip any blacklist entries out of the unused Keychain array, then take 1010 // all the entries that are left (which we can use as imported passwords). 1011 std::vector<PasswordForm*> keychain_blacklist_forms = 1012 internal_keychain_helpers::ExtractBlacklistForms(&keychain_forms); 1013 matched_forms.insert(matched_forms.end(), 1014 keychain_forms.begin(), 1015 keychain_forms.end()); 1016 keychain_forms.clear(); 1017 STLDeleteElements(&keychain_blacklist_forms); 1018 1019 // Clean up any orphaned database entries. 1020 RemoveDatabaseForms(database_forms); 1021 STLDeleteElements(&database_forms); 1022 1023 callback_runner.Run(matched_forms); 1024} 1025 1026void PasswordStoreMac::GetBlacklistLoginsImpl(GetLoginsRequest* request) { 1027 FillBlacklistLogins(request->result()); 1028 ForwardLoginsResult(request); 1029} 1030 1031void PasswordStoreMac::GetAutofillableLoginsImpl(GetLoginsRequest* request) { 1032 FillAutofillableLogins(request->result()); 1033 ForwardLoginsResult(request); 1034} 1035 1036bool PasswordStoreMac::FillAutofillableLogins( 1037 std::vector<PasswordForm*>* forms) { 1038 DCHECK(thread_->message_loop() == base::MessageLoop::current()); 1039 1040 std::vector<PasswordForm*> database_forms; 1041 login_metadata_db_->GetAutofillableLogins(&database_forms); 1042 1043 std::vector<PasswordForm*> merged_forms = 1044 internal_keychain_helpers::GetPasswordsForForms(*keychain_, 1045 &database_forms); 1046 1047 // Clean up any orphaned database entries. 1048 RemoveDatabaseForms(database_forms); 1049 STLDeleteElements(&database_forms); 1050 1051 forms->insert(forms->end(), merged_forms.begin(), merged_forms.end()); 1052 return true; 1053} 1054 1055bool PasswordStoreMac::FillBlacklistLogins( 1056 std::vector<PasswordForm*>* forms) { 1057 DCHECK(thread_->message_loop() == base::MessageLoop::current()); 1058 return login_metadata_db_->GetBlacklistLogins(forms); 1059} 1060 1061bool PasswordStoreMac::AddToKeychainIfNecessary(const PasswordForm& form) { 1062 if (form.blacklisted_by_user) { 1063 return true; 1064 } 1065 MacKeychainPasswordFormAdapter keychainAdapter(keychain_.get()); 1066 return keychainAdapter.AddPassword(form); 1067} 1068 1069bool PasswordStoreMac::DatabaseHasFormMatchingKeychainForm( 1070 const autofill::PasswordForm& form) { 1071 bool has_match = false; 1072 std::vector<PasswordForm*> database_forms; 1073 login_metadata_db_->GetLogins(form, &database_forms); 1074 for (std::vector<PasswordForm*>::iterator i = database_forms.begin(); 1075 i != database_forms.end(); ++i) { 1076 // Below we filter out forms with non-empty original_signon_realm, because 1077 // those signal fuzzy matches, and we are only interested in exact ones. 1078 if ((*i)->original_signon_realm.empty() && 1079 internal_keychain_helpers::FormsMatchForMerge( 1080 form, **i, internal_keychain_helpers::STRICT_FORM_MATCH) && 1081 (*i)->origin == form.origin) { 1082 has_match = true; 1083 break; 1084 } 1085 } 1086 STLDeleteElements(&database_forms); 1087 return has_match; 1088} 1089 1090std::vector<PasswordForm*> PasswordStoreMac::GetUnusedKeychainForms() { 1091 std::vector<PasswordForm*> database_forms; 1092 login_metadata_db_->GetAutofillableLogins(&database_forms); 1093 1094 MacKeychainPasswordFormAdapter owned_keychain_adapter(keychain_.get()); 1095 owned_keychain_adapter.SetFindsOnlyOwnedItems(true); 1096 std::vector<PasswordForm*> owned_keychain_forms = 1097 owned_keychain_adapter.GetAllPasswordFormPasswords(); 1098 1099 // Run a merge; anything left in owned_keychain_forms when we are done no 1100 // longer has a matching database entry. 1101 std::vector<PasswordForm*> merged_forms; 1102 internal_keychain_helpers::MergePasswordForms(&owned_keychain_forms, 1103 &database_forms, 1104 &merged_forms); 1105 STLDeleteElements(&merged_forms); 1106 STLDeleteElements(&database_forms); 1107 1108 return owned_keychain_forms; 1109} 1110 1111void PasswordStoreMac::RemoveDatabaseForms( 1112 const std::vector<PasswordForm*>& forms) { 1113 for (std::vector<PasswordForm*>::const_iterator i = forms.begin(); 1114 i != forms.end(); ++i) { 1115 login_metadata_db_->RemoveLogin(**i); 1116 } 1117} 1118 1119void PasswordStoreMac::RemoveKeychainForms( 1120 const std::vector<PasswordForm*>& forms) { 1121 MacKeychainPasswordFormAdapter owned_keychain_adapter(keychain_.get()); 1122 owned_keychain_adapter.SetFindsOnlyOwnedItems(true); 1123 for (std::vector<PasswordForm*>::const_iterator i = forms.begin(); 1124 i != forms.end(); ++i) { 1125 owned_keychain_adapter.RemovePassword(**i); 1126 } 1127} 1128