password_store_mac.cc revision d0247b1b59f9c528cb6df88b4f2b9afaf80d181e
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 <vector> 12 13#include "base/callback.h" 14#include "base/debug/crash_logging.h" 15#include "base/debug/stack_trace.h" 16#include "base/logging.h" 17#include "base/mac/mac_logging.h" 18#include "base/mac/mac_util.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/chrome_notification_types.h" 24#include "chrome/browser/password_manager/login_database.h" 25#include "chrome/browser/password_manager/password_store_change.h" 26#include "chrome/common/crash_keys.h" 27#include "content/public/browser/notification_service.h" 28#include "crypto/apple_keychain.h" 29 30using autofill::PasswordForm; 31using crypto::AppleKeychain; 32 33// Utility class to handle the details of constructing and running a keychain 34// search from a set of attributes. 35class 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 60KeychainSearch::KeychainSearch(const AppleKeychain& keychain) 61 : keychain_(&keychain), search_ref_(NULL) { 62 search_attributes_.count = 0; 63 search_attributes_.attr = NULL; 64} 65 66KeychainSearch::~KeychainSearch() { 67 if (search_attributes_.attr) { 68 free(search_attributes_.attr); 69 } 70} 71 72void 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 153void 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. 177namespace 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. 181GURL 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. 203bool 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|. 228PasswordForm::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 237bool FillPasswordFormFromKeychainItem(const AppleKeychain& keychain, 238 const SecKeychainItemRef& keychain_item, 239 PasswordForm* form) { 240 DCHECK(form); 241 242 SecKeychainAttributeInfo attrInfo; 243 UInt32 tags[] = { kSecAccountItemAttr, 244 kSecServerItemAttr, 245 kSecPortItemAttr, 246 kSecPathItemAttr, 247 kSecProtocolItemAttr, 248 kSecAuthenticationTypeItemAttr, 249 kSecSecurityDomainItemAttr, 250 kSecCreationDateItemAttr, 251 kSecNegativeItemAttr }; 252 attrInfo.count = arraysize(tags); 253 attrInfo.tag = tags; 254 attrInfo.format = NULL; 255 256 SecKeychainAttributeList *attrList; 257 UInt32 password_length; 258 void* password_data; 259 OSStatus result = keychain.ItemCopyAttributesAndData(keychain_item, &attrInfo, 260 NULL, &attrList, 261 &password_length, 262 &password_data); 263 264 if (result != noErr) { 265 // We don't log errSecAuthFailed because that just means that the user 266 // chose not to allow us access to the item. 267 if (result != errSecAuthFailed) { 268 OSSTATUS_LOG(ERROR, result) << "Keychain data load failed"; 269 } 270 return false; 271 } 272 273 UTF8ToUTF16(static_cast<const char *>(password_data), password_length, 274 &(form->password_value)); 275 276 int port = kAnyPort; 277 std::string server; 278 std::string security_domain; 279 std::string path; 280 for (unsigned int i = 0; i < attrList->count; i++) { 281 SecKeychainAttribute attr = attrList->attr[i]; 282 if (!attr.data) { 283 continue; 284 } 285 switch (attr.tag) { 286 case kSecAccountItemAttr: 287 UTF8ToUTF16(static_cast<const char *>(attr.data), attr.length, 288 &(form->username_value)); 289 break; 290 case kSecServerItemAttr: 291 server.assign(static_cast<const char *>(attr.data), attr.length); 292 break; 293 case kSecPortItemAttr: 294 port = *(static_cast<UInt32*>(attr.data)); 295 break; 296 case kSecPathItemAttr: 297 path.assign(static_cast<const char *>(attr.data), attr.length); 298 break; 299 case kSecProtocolItemAttr: 300 { 301 SecProtocolType protocol = *(static_cast<SecProtocolType*>(attr.data)); 302 // TODO(stuartmorgan): Handle proxy types 303 form->ssl_valid = (protocol == kSecProtocolTypeHTTPS); 304 break; 305 } 306 case kSecAuthenticationTypeItemAttr: 307 { 308 SecAuthenticationType auth_type = 309 *(static_cast<SecAuthenticationType*>(attr.data)); 310 form->scheme = SchemeForAuthType(auth_type); 311 break; 312 } 313 case kSecSecurityDomainItemAttr: 314 security_domain.assign(static_cast<const char *>(attr.data), 315 attr.length); 316 break; 317 case kSecCreationDateItemAttr: 318 // The only way to get a date out of Keychain is as a string. Really. 319 // (The docs claim it's an int, but the header is correct.) 320 TimeFromKeychainTimeString(static_cast<char*>(attr.data), attr.length, 321 &form->date_created); 322 break; 323 case kSecNegativeItemAttr: 324 Boolean negative_item = *(static_cast<Boolean*>(attr.data)); 325 if (negative_item) { 326 form->blacklisted_by_user = true; 327 } 328 break; 329 } 330 } 331 keychain.ItemFreeAttributesAndData(attrList, password_data); 332 333 // kSecNegativeItemAttr doesn't seem to actually be in widespread use. In 334 // practice, other browsers seem to use a "" or " " password (and a special 335 // user name) to indicated blacklist entries. 336 if (form->password_value.empty() || EqualsASCII(form->password_value, " ")) { 337 form->blacklisted_by_user = true; 338 } 339 340 form->origin = URLFromComponents(form->ssl_valid, server, port, path); 341 // TODO(stuartmorgan): Handle proxies, which need a different signon_realm 342 // format. 343 form->signon_realm = form->origin.GetOrigin().spec(); 344 if (form->scheme != PasswordForm::SCHEME_HTML) { 345 form->signon_realm.append(security_domain); 346 } 347 return true; 348} 349 350bool FormsMatchForMerge(const PasswordForm& form_a, 351 const PasswordForm& form_b) { 352 // We never merge blacklist entries between our store and the keychain. 353 if (form_a.blacklisted_by_user || form_b.blacklisted_by_user) { 354 return false; 355 } 356 return form_a.scheme == form_b.scheme && 357 form_a.signon_realm == form_b.signon_realm && 358 form_a.username_value == form_b.username_value; 359} 360 361// Returns an the best match for |form| from |keychain_forms|, or NULL if there 362// is no suitable match. 363PasswordForm* BestKeychainFormForForm( 364 const PasswordForm& base_form, 365 const std::vector<PasswordForm*>* keychain_forms) { 366 PasswordForm* partial_match = NULL; 367 for (std::vector<PasswordForm*>::const_iterator i = keychain_forms->begin(); 368 i != keychain_forms->end(); ++i) { 369 // TODO(stuartmorgan): We should really be scoring path matches and picking 370 // the best, rather than just checking exact-or-not (although in practice 371 // keychain items with paths probably came from us). 372 if (FormsMatchForMerge(base_form, *(*i))) { 373 if (base_form.origin == (*i)->origin) { 374 return *i; 375 } else if (!partial_match) { 376 partial_match = *i; 377 } 378 } 379 } 380 return partial_match; 381} 382 383// Returns entries from |forms| that are blacklist entries, after removing 384// them from |forms|. 385std::vector<PasswordForm*> ExtractBlacklistForms( 386 std::vector<PasswordForm*>* forms) { 387 std::vector<PasswordForm*> blacklist_forms; 388 for (std::vector<PasswordForm*>::iterator i = forms->begin(); 389 i != forms->end();) { 390 PasswordForm* form = *i; 391 if (form->blacklisted_by_user) { 392 blacklist_forms.push_back(form); 393 i = forms->erase(i); 394 } else { 395 ++i; 396 } 397 } 398 return blacklist_forms; 399} 400 401// Deletes and removes from v any element that exists in s. 402template <class T> 403void DeleteVectorElementsInSet(std::vector<T*>* v, const std::set<T*>& s) { 404 for (typename std::vector<T*>::iterator i = v->begin(); i != v->end();) { 405 T* element = *i; 406 if (s.find(element) != s.end()) { 407 delete element; 408 i = v->erase(i); 409 } else { 410 ++i; 411 } 412 } 413} 414 415void MergePasswordForms(std::vector<PasswordForm*>* keychain_forms, 416 std::vector<PasswordForm*>* database_forms, 417 std::vector<PasswordForm*>* merged_forms) { 418 // Pull out the database blacklist items, since they are used as-is rather 419 // than being merged with keychain forms. 420 std::vector<PasswordForm*> database_blacklist_forms = 421 ExtractBlacklistForms(database_forms); 422 423 // Merge the normal entries. 424 std::set<PasswordForm*> used_keychain_forms; 425 for (std::vector<PasswordForm*>::iterator i = database_forms->begin(); 426 i != database_forms->end();) { 427 PasswordForm* db_form = *i; 428 PasswordForm* best_match = BestKeychainFormForForm(*db_form, 429 keychain_forms); 430 if (best_match) { 431 used_keychain_forms.insert(best_match); 432 db_form->password_value = best_match->password_value; 433 merged_forms->push_back(db_form); 434 i = database_forms->erase(i); 435 } else { 436 ++i; 437 } 438 } 439 440 // Add in the blacklist entries from the database. 441 merged_forms->insert(merged_forms->end(), 442 database_blacklist_forms.begin(), 443 database_blacklist_forms.end()); 444 445 // Clear out all the Keychain entries we used. 446 DeleteVectorElementsInSet(keychain_forms, used_keychain_forms); 447} 448 449std::vector<PasswordForm*> GetPasswordsForForms( 450 const AppleKeychain& keychain, 451 std::vector<PasswordForm*>* database_forms) { 452 MacKeychainPasswordFormAdapter keychain_adapter(&keychain); 453 454 std::vector<PasswordForm*> merged_forms; 455 for (std::vector<PasswordForm*>::iterator i = database_forms->begin(); 456 i != database_forms->end();) { 457 std::vector<PasswordForm*> db_form_container(1, *i); 458 std::vector<PasswordForm*> keychain_matches = 459 keychain_adapter.PasswordsMergeableWithForm(**i); 460 MergePasswordForms(&keychain_matches, &db_form_container, &merged_forms); 461 if (db_form_container.empty()) { 462 i = database_forms->erase(i); 463 } else { 464 ++i; 465 } 466 STLDeleteElements(&keychain_matches); 467 } 468 return merged_forms; 469} 470 471class Thread : public base::Thread { 472 public: 473 Thread(const char* name) : base::Thread(name) {} 474 virtual ~Thread() { 475 base::debug::SetCrashKeyToStackTrace( 476 crash_keys::mac::kPasswordThreadDtorTrace, base::debug::StackTrace()); 477 } 478}; 479 480} // namespace internal_keychain_helpers 481 482#pragma mark - 483 484MacKeychainPasswordFormAdapter::MacKeychainPasswordFormAdapter( 485 const AppleKeychain* keychain) 486 : keychain_(keychain), finds_only_owned_(false) { 487} 488 489std::vector<PasswordForm*> 490 MacKeychainPasswordFormAdapter::PasswordsFillingForm( 491 const PasswordForm& query_form) { 492 std::vector<SecKeychainItemRef> keychain_items = 493 MatchingKeychainItems(query_form.signon_realm, query_form.scheme, 494 NULL, NULL); 495 496 return ConvertKeychainItemsToForms(&keychain_items); 497} 498 499std::vector<PasswordForm*> 500 MacKeychainPasswordFormAdapter::PasswordsMergeableWithForm( 501 const PasswordForm& query_form) { 502 std::string username = UTF16ToUTF8(query_form.username_value); 503 std::vector<SecKeychainItemRef> keychain_items = 504 MatchingKeychainItems(query_form.signon_realm, query_form.scheme, 505 NULL, username.c_str()); 506 507 return ConvertKeychainItemsToForms(&keychain_items); 508} 509 510PasswordForm* MacKeychainPasswordFormAdapter::PasswordExactlyMatchingForm( 511 const PasswordForm& query_form) { 512 SecKeychainItemRef keychain_item = KeychainItemForForm(query_form); 513 if (keychain_item) { 514 PasswordForm* form = new PasswordForm(); 515 internal_keychain_helpers::FillPasswordFormFromKeychainItem(*keychain_, 516 keychain_item, 517 form); 518 keychain_->Free(keychain_item); 519 return form; 520 } 521 return NULL; 522} 523 524bool MacKeychainPasswordFormAdapter::HasPasswordsMergeableWithForm( 525 const PasswordForm& query_form) { 526 std::string username = UTF16ToUTF8(query_form.username_value); 527 std::vector<SecKeychainItemRef> matches = 528 MatchingKeychainItems(query_form.signon_realm, query_form.scheme, 529 NULL, username.c_str()); 530 for (std::vector<SecKeychainItemRef>::iterator i = matches.begin(); 531 i != matches.end(); ++i) { 532 keychain_->Free(*i); 533 } 534 535 return !matches.empty(); 536} 537 538std::vector<PasswordForm*> 539 MacKeychainPasswordFormAdapter::GetAllPasswordFormPasswords() { 540 SecAuthenticationType supported_auth_types[] = { 541 kSecAuthenticationTypeHTMLForm, 542 kSecAuthenticationTypeHTTPBasic, 543 kSecAuthenticationTypeHTTPDigest, 544 }; 545 546 std::vector<SecKeychainItemRef> matches; 547 for (unsigned int i = 0; i < arraysize(supported_auth_types); ++i) { 548 KeychainSearch keychain_search(*keychain_); 549 keychain_search.Init(NULL, 0, kSecProtocolTypeAny, supported_auth_types[i], 550 NULL, NULL, NULL, CreatorCodeForSearch()); 551 keychain_search.FindMatchingItems(&matches); 552 } 553 554 return ConvertKeychainItemsToForms(&matches); 555} 556 557bool MacKeychainPasswordFormAdapter::AddPassword(const PasswordForm& form) { 558 // We should never be trying to store a blacklist in the keychain. 559 DCHECK(!form.blacklisted_by_user); 560 561 std::string server; 562 std::string security_domain; 563 int port; 564 bool is_secure; 565 if (!ExtractSignonRealmComponents(form.signon_realm, &server, &port, 566 &is_secure, &security_domain)) { 567 return false; 568 } 569 std::string username = UTF16ToUTF8(form.username_value); 570 std::string password = UTF16ToUTF8(form.password_value); 571 std::string path = form.origin.path(); 572 SecProtocolType protocol = is_secure ? kSecProtocolTypeHTTPS 573 : kSecProtocolTypeHTTP; 574 SecKeychainItemRef new_item = NULL; 575 OSStatus result = keychain_->AddInternetPassword( 576 NULL, server.size(), server.c_str(), 577 security_domain.size(), security_domain.c_str(), 578 username.size(), username.c_str(), 579 path.size(), path.c_str(), 580 port, protocol, AuthTypeForScheme(form.scheme), 581 password.size(), password.c_str(), &new_item); 582 583 if (result == noErr) { 584 SetKeychainItemCreatorCode(new_item, 585 base::mac::CreatorCodeForApplication()); 586 keychain_->Free(new_item); 587 } else if (result == errSecDuplicateItem) { 588 // If we collide with an existing item, find and update it instead. 589 SecKeychainItemRef existing_item = KeychainItemForForm(form); 590 if (!existing_item) { 591 return false; 592 } 593 bool changed = SetKeychainItemPassword(existing_item, password); 594 keychain_->Free(existing_item); 595 return changed; 596 } 597 598 return result == noErr; 599} 600 601bool MacKeychainPasswordFormAdapter::RemovePassword(const PasswordForm& form) { 602 SecKeychainItemRef keychain_item = KeychainItemForForm(form); 603 if (keychain_item == NULL) 604 return false; 605 OSStatus result = keychain_->ItemDelete(keychain_item); 606 keychain_->Free(keychain_item); 607 return result == noErr; 608} 609 610void MacKeychainPasswordFormAdapter::SetFindsOnlyOwnedItems( 611 bool finds_only_owned) { 612 finds_only_owned_ = finds_only_owned; 613} 614 615std::vector<PasswordForm*> 616 MacKeychainPasswordFormAdapter::ConvertKeychainItemsToForms( 617 std::vector<SecKeychainItemRef>* items) { 618 std::vector<PasswordForm*> keychain_forms; 619 for (std::vector<SecKeychainItemRef>::const_iterator i = items->begin(); 620 i != items->end(); ++i) { 621 PasswordForm* form = new PasswordForm(); 622 if (internal_keychain_helpers::FillPasswordFormFromKeychainItem(*keychain_, 623 *i, form)) { 624 keychain_forms.push_back(form); 625 } 626 keychain_->Free(*i); 627 } 628 items->clear(); 629 return keychain_forms; 630} 631 632SecKeychainItemRef MacKeychainPasswordFormAdapter::KeychainItemForForm( 633 const PasswordForm& form) { 634 // We don't store blacklist entries in the keychain, so the answer to "what 635 // Keychain item goes with this form" is always "nothing" for blacklists. 636 if (form.blacklisted_by_user) { 637 return NULL; 638 } 639 640 std::string path = form.origin.path(); 641 std::string username = UTF16ToUTF8(form.username_value); 642 std::vector<SecKeychainItemRef> matches = MatchingKeychainItems( 643 form.signon_realm, form.scheme, path.c_str(), username.c_str()); 644 645 if (matches.empty()) { 646 return NULL; 647 } 648 // Free all items after the first, since we won't be returning them. 649 for (std::vector<SecKeychainItemRef>::iterator i = matches.begin() + 1; 650 i != matches.end(); ++i) { 651 keychain_->Free(*i); 652 } 653 return matches[0]; 654} 655 656std::vector<SecKeychainItemRef> 657 MacKeychainPasswordFormAdapter::MatchingKeychainItems( 658 const std::string& signon_realm, 659 autofill::PasswordForm::Scheme scheme, 660 const char* path, const char* username) { 661 std::vector<SecKeychainItemRef> matches; 662 663 std::string server; 664 std::string security_domain; 665 int port; 666 bool is_secure; 667 if (!ExtractSignonRealmComponents(signon_realm, &server, &port, 668 &is_secure, &security_domain)) { 669 // TODO(stuartmorgan): Proxies will currently fail here, since their 670 // signon_realm is not a URL. We need to detect the proxy case and handle 671 // it specially. 672 return matches; 673 } 674 SecProtocolType protocol = is_secure ? kSecProtocolTypeHTTPS 675 : kSecProtocolTypeHTTP; 676 SecAuthenticationType auth_type = AuthTypeForScheme(scheme); 677 const char* auth_domain = (scheme == PasswordForm::SCHEME_HTML) ? 678 NULL : security_domain.c_str(); 679 KeychainSearch keychain_search(*keychain_); 680 keychain_search.Init(server.c_str(), port, protocol, auth_type, 681 auth_domain, path, username, CreatorCodeForSearch()); 682 keychain_search.FindMatchingItems(&matches); 683 return matches; 684} 685 686// TODO(stuartmorgan): signon_realm for proxies is not yet supported. 687bool MacKeychainPasswordFormAdapter::ExtractSignonRealmComponents( 688 const std::string& signon_realm, std::string* server, int* port, 689 bool* is_secure, std::string* security_domain) { 690 // The signon_realm will be the Origin portion of a URL for an HTML form, 691 // and the same but with the security domain as a path for HTTP auth. 692 GURL realm_as_url(signon_realm); 693 if (!realm_as_url.is_valid()) { 694 return false; 695 } 696 697 if (server) 698 *server = realm_as_url.host(); 699 if (is_secure) 700 *is_secure = realm_as_url.SchemeIsSecure(); 701 if (port) 702 *port = realm_as_url.has_port() ? atoi(realm_as_url.port().c_str()) : 0; 703 if (security_domain) { 704 // Strip the leading '/' off of the path to get the security domain. 705 if (realm_as_url.path().length() > 0) 706 *security_domain = realm_as_url.path().substr(1); 707 else 708 security_domain->clear(); 709 } 710 return true; 711} 712 713// Returns the Keychain SecAuthenticationType type corresponding to |scheme|. 714SecAuthenticationType MacKeychainPasswordFormAdapter::AuthTypeForScheme( 715 PasswordForm::Scheme scheme) { 716 switch (scheme) { 717 case PasswordForm::SCHEME_HTML: return kSecAuthenticationTypeHTMLForm; 718 case PasswordForm::SCHEME_BASIC: return kSecAuthenticationTypeHTTPBasic; 719 case PasswordForm::SCHEME_DIGEST: return kSecAuthenticationTypeHTTPDigest; 720 case PasswordForm::SCHEME_OTHER: return kSecAuthenticationTypeDefault; 721 } 722 NOTREACHED(); 723 return kSecAuthenticationTypeDefault; 724} 725 726bool MacKeychainPasswordFormAdapter::SetKeychainItemPassword( 727 const SecKeychainItemRef& keychain_item, const std::string& password) { 728 OSStatus result = keychain_->ItemModifyAttributesAndData(keychain_item, NULL, 729 password.size(), 730 password.c_str()); 731 return result == noErr; 732} 733 734bool MacKeychainPasswordFormAdapter::SetKeychainItemCreatorCode( 735 const SecKeychainItemRef& keychain_item, OSType creator_code) { 736 SecKeychainAttribute attr = { kSecCreatorItemAttr, sizeof(creator_code), 737 &creator_code }; 738 SecKeychainAttributeList attrList = { 1, &attr }; 739 OSStatus result = keychain_->ItemModifyAttributesAndData(keychain_item, 740 &attrList, 0, NULL); 741 return result == noErr; 742} 743 744OSType MacKeychainPasswordFormAdapter::CreatorCodeForSearch() { 745 return finds_only_owned_ ? base::mac::CreatorCodeForApplication() : 0; 746} 747 748#pragma mark - 749 750PasswordStoreMac::PasswordStoreMac(AppleKeychain* keychain, 751 LoginDatabase* login_db) 752 : keychain_(keychain), login_metadata_db_(login_db) { 753 DCHECK(keychain_.get()); 754 DCHECK(login_metadata_db_.get()); 755} 756 757PasswordStoreMac::~PasswordStoreMac() { 758 if (thread_.get()) { 759 thread_->message_loop()->DeleteSoon(FROM_HERE, 760 notification_service_.release()); 761 } 762} 763 764bool PasswordStoreMac::Init() { 765 thread_.reset( 766 new internal_keychain_helpers::Thread("Chrome_PasswordStore_Thread")); 767 768 if (!thread_->Start()) { 769 thread_.reset(NULL); 770 return false; 771 } 772 ScheduleTask(base::Bind(&PasswordStoreMac::CreateNotificationService, this)); 773 return PasswordStore::Init(); 774} 775 776void PasswordStoreMac::ShutdownOnUIThread() { 777} 778 779// Mac stores passwords in the system keychain, which can block for an 780// arbitrarily long time (most notably, it can block on user confirmation 781// from a dialog). Use a dedicated thread to avoid blocking DB thread. 782bool PasswordStoreMac::ScheduleTask(const base::Closure& task) { 783 if (thread_.get()) { 784 thread_->message_loop()->PostTask(FROM_HERE, task); 785 return true; 786 } 787 return false; 788} 789 790void PasswordStoreMac::ReportMetricsImpl() { 791 login_metadata_db_->ReportMetrics(); 792} 793 794void PasswordStoreMac::AddLoginImpl(const PasswordForm& form) { 795 if (AddToKeychainIfNecessary(form)) { 796 if (login_metadata_db_->AddLogin(form)) { 797 PasswordStoreChangeList changes; 798 changes.push_back(PasswordStoreChange(PasswordStoreChange::ADD, form)); 799 content::NotificationService::current()->Notify( 800 chrome::NOTIFICATION_LOGINS_CHANGED, 801 content::Source<PasswordStore>(this), 802 content::Details<PasswordStoreChangeList>(&changes)); 803 } 804 } 805} 806 807void PasswordStoreMac::UpdateLoginImpl(const PasswordForm& form) { 808 int update_count = 0; 809 if (!login_metadata_db_->UpdateLogin(form, &update_count)) 810 return; 811 812 MacKeychainPasswordFormAdapter keychain_adapter(keychain_.get()); 813 if (update_count == 0 && 814 !keychain_adapter.HasPasswordsMergeableWithForm(form)) { 815 // If the password isn't in either the DB or the keychain, then it must have 816 // been deleted after autofill happened, and should not be re-added. 817 return; 818 } 819 820 // The keychain add will update if there is a collision and add if there 821 // isn't, which is the behavior we want, so there's no separate update call. 822 if (AddToKeychainIfNecessary(form)) { 823 PasswordStoreChangeList changes; 824 if (update_count == 0) { 825 if (login_metadata_db_->AddLogin(form)) { 826 changes.push_back(PasswordStoreChange(PasswordStoreChange::ADD, 827 form)); 828 } 829 } else { 830 changes.push_back(PasswordStoreChange(PasswordStoreChange::UPDATE, 831 form)); 832 } 833 if (!changes.empty()) { 834 content::NotificationService::current()->Notify( 835 chrome::NOTIFICATION_LOGINS_CHANGED, 836 content::Source<PasswordStore>(this), 837 content::Details<PasswordStoreChangeList>(&changes)); 838 } 839 } 840} 841 842void PasswordStoreMac::RemoveLoginImpl(const PasswordForm& form) { 843 if (login_metadata_db_->RemoveLogin(form)) { 844 // See if we own a Keychain item associated with this item. We can do an 845 // exact search rather than messing around with trying to do fuzzy matching 846 // because passwords that we created will always have an exact-match 847 // database entry. 848 // (If a user does lose their profile but not their keychain we'll treat the 849 // entries we find like other imported entries anyway, so it's reasonable to 850 // handle deletes on them the way we would for an imported item.) 851 MacKeychainPasswordFormAdapter owned_keychain_adapter(keychain_.get()); 852 owned_keychain_adapter.SetFindsOnlyOwnedItems(true); 853 PasswordForm* owned_password_form = 854 owned_keychain_adapter.PasswordExactlyMatchingForm(form); 855 if (owned_password_form) { 856 // If we don't have other forms using it (i.e., a form differing only by 857 // the names of the form elements), delete the keychain entry. 858 if (!DatabaseHasFormMatchingKeychainForm(form)) { 859 owned_keychain_adapter.RemovePassword(form); 860 } 861 } 862 863 PasswordStoreChangeList changes; 864 changes.push_back(PasswordStoreChange(PasswordStoreChange::REMOVE, form)); 865 content::NotificationService::current()->Notify( 866 chrome::NOTIFICATION_LOGINS_CHANGED, 867 content::Source<PasswordStore>(this), 868 content::Details<PasswordStoreChangeList>(&changes)); 869 } 870} 871 872void PasswordStoreMac::RemoveLoginsCreatedBetweenImpl( 873 const base::Time& delete_begin, const base::Time& delete_end) { 874 std::vector<PasswordForm*> forms; 875 if (login_metadata_db_->GetLoginsCreatedBetween(delete_begin, delete_end, 876 &forms)) { 877 if (login_metadata_db_->RemoveLoginsCreatedBetween(delete_begin, 878 delete_end)) { 879 // We can't delete from the Keychain by date because we may be sharing 880 // items with database entries that weren't in the delete range. Instead, 881 // we find all the Keychain items we own but aren't using any more and 882 // delete those. 883 std::vector<PasswordForm*> orphan_keychain_forms = 884 GetUnusedKeychainForms(); 885 // This is inefficient, since we have to re-look-up each keychain item 886 // one at a time to delete it even though the search step already had a 887 // list of Keychain item references. If this turns out to be noticeably 888 // slow we'll need to rearchitect to allow the search and deletion steps 889 // to share. 890 RemoveKeychainForms(orphan_keychain_forms); 891 STLDeleteElements(&orphan_keychain_forms); 892 893 PasswordStoreChangeList changes; 894 for (std::vector<PasswordForm*>::const_iterator it = forms.begin(); 895 it != forms.end(); ++it) { 896 changes.push_back(PasswordStoreChange(PasswordStoreChange::REMOVE, 897 **it)); 898 } 899 content::NotificationService::current()->Notify( 900 chrome::NOTIFICATION_LOGINS_CHANGED, 901 content::Source<PasswordStore>(this), 902 content::Details<PasswordStoreChangeList>(&changes)); 903 } 904 } 905} 906 907void PasswordStoreMac::GetLoginsImpl( 908 const autofill::PasswordForm& form, 909 const ConsumerCallbackRunner& callback_runner) { 910 MacKeychainPasswordFormAdapter keychain_adapter(keychain_.get()); 911 std::vector<PasswordForm*> keychain_forms = 912 keychain_adapter.PasswordsFillingForm(form); 913 914 std::vector<PasswordForm*> database_forms; 915 login_metadata_db_->GetLogins(form, &database_forms); 916 917 std::vector<PasswordForm*> matched_forms; 918 internal_keychain_helpers::MergePasswordForms(&keychain_forms, 919 &database_forms, 920 &matched_forms); 921 922 // Strip any blacklist entries out of the unused Keychain array, then take 923 // all the entries that are left (which we can use as imported passwords). 924 std::vector<PasswordForm*> keychain_blacklist_forms = 925 internal_keychain_helpers::ExtractBlacklistForms(&keychain_forms); 926 matched_forms.insert(matched_forms.end(), 927 keychain_forms.begin(), 928 keychain_forms.end()); 929 keychain_forms.clear(); 930 STLDeleteElements(&keychain_blacklist_forms); 931 932 // Clean up any orphaned database entries. 933 RemoveDatabaseForms(database_forms); 934 STLDeleteElements(&database_forms); 935 936 callback_runner.Run(matched_forms); 937} 938 939void PasswordStoreMac::GetBlacklistLoginsImpl(GetLoginsRequest* request) { 940 FillBlacklistLogins(&request->value); 941 ForwardLoginsResult(request); 942} 943 944void PasswordStoreMac::GetAutofillableLoginsImpl(GetLoginsRequest* request) { 945 FillAutofillableLogins(&request->value); 946 ForwardLoginsResult(request); 947} 948 949bool PasswordStoreMac::FillAutofillableLogins( 950 std::vector<PasswordForm*>* forms) { 951 DCHECK(thread_->message_loop() == base::MessageLoop::current()); 952 953 std::vector<PasswordForm*> database_forms; 954 login_metadata_db_->GetAutofillableLogins(&database_forms); 955 956 std::vector<PasswordForm*> merged_forms = 957 internal_keychain_helpers::GetPasswordsForForms(*keychain_, 958 &database_forms); 959 960 // Clean up any orphaned database entries. 961 RemoveDatabaseForms(database_forms); 962 STLDeleteElements(&database_forms); 963 964 forms->insert(forms->end(), merged_forms.begin(), merged_forms.end()); 965 return true; 966} 967 968bool PasswordStoreMac::FillBlacklistLogins( 969 std::vector<PasswordForm*>* forms) { 970 DCHECK(thread_->message_loop() == base::MessageLoop::current()); 971 return login_metadata_db_->GetBlacklistLogins(forms); 972} 973 974bool PasswordStoreMac::AddToKeychainIfNecessary(const PasswordForm& form) { 975 if (form.blacklisted_by_user) { 976 return true; 977 } 978 MacKeychainPasswordFormAdapter keychainAdapter(keychain_.get()); 979 return keychainAdapter.AddPassword(form); 980} 981 982bool PasswordStoreMac::DatabaseHasFormMatchingKeychainForm( 983 const autofill::PasswordForm& form) { 984 bool has_match = false; 985 std::vector<PasswordForm*> database_forms; 986 login_metadata_db_->GetLogins(form, &database_forms); 987 for (std::vector<PasswordForm*>::iterator i = database_forms.begin(); 988 i != database_forms.end(); ++i) { 989 if (internal_keychain_helpers::FormsMatchForMerge(form, **i) && 990 (*i)->origin == form.origin) { 991 has_match = true; 992 break; 993 } 994 } 995 STLDeleteElements(&database_forms); 996 return has_match; 997} 998 999std::vector<PasswordForm*> PasswordStoreMac::GetUnusedKeychainForms() { 1000 std::vector<PasswordForm*> database_forms; 1001 login_metadata_db_->GetAutofillableLogins(&database_forms); 1002 1003 MacKeychainPasswordFormAdapter owned_keychain_adapter(keychain_.get()); 1004 owned_keychain_adapter.SetFindsOnlyOwnedItems(true); 1005 std::vector<PasswordForm*> owned_keychain_forms = 1006 owned_keychain_adapter.GetAllPasswordFormPasswords(); 1007 1008 // Run a merge; anything left in owned_keychain_forms when we are done no 1009 // longer has a matching database entry. 1010 std::vector<PasswordForm*> merged_forms; 1011 internal_keychain_helpers::MergePasswordForms(&owned_keychain_forms, 1012 &database_forms, 1013 &merged_forms); 1014 STLDeleteElements(&merged_forms); 1015 STLDeleteElements(&database_forms); 1016 1017 return owned_keychain_forms; 1018} 1019 1020void PasswordStoreMac::RemoveDatabaseForms( 1021 const std::vector<PasswordForm*>& forms) { 1022 for (std::vector<PasswordForm*>::const_iterator i = forms.begin(); 1023 i != forms.end(); ++i) { 1024 login_metadata_db_->RemoveLogin(**i); 1025 } 1026} 1027 1028void PasswordStoreMac::RemoveKeychainForms( 1029 const std::vector<PasswordForm*>& forms) { 1030 MacKeychainPasswordFormAdapter owned_keychain_adapter(keychain_.get()); 1031 owned_keychain_adapter.SetFindsOnlyOwnedItems(true); 1032 for (std::vector<PasswordForm*>::const_iterator i = forms.begin(); 1033 i != forms.end(); ++i) { 1034 owned_keychain_adapter.RemovePassword(**i); 1035 } 1036} 1037 1038void PasswordStoreMac::CreateNotificationService() { 1039 notification_service_.reset(content::NotificationService::Create()); 1040} 1041