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