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