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