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