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