password_store_mac.cc revision 21d179b334e59e9a3bfcaed4c4430bef1bc5759d
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_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, mac_util::CreatorCodeForApplication()); 569 keychain_->Free(new_item); 570 } else if (result == errSecDuplicateItem) { 571 // If we collide with an existing item, find and update it instead. 572 SecKeychainItemRef existing_item = KeychainItemForForm(form); 573 if (!existing_item) { 574 return false; 575 } 576 bool changed = SetKeychainItemPassword(existing_item, password); 577 keychain_->Free(existing_item); 578 return changed; 579 } 580 581 return result == noErr; 582} 583 584bool MacKeychainPasswordFormAdapter::RemovePassword(const PasswordForm& form) { 585 SecKeychainItemRef keychain_item = KeychainItemForForm(form); 586 if (keychain_item == NULL) 587 return false; 588 OSStatus result = keychain_->ItemDelete(keychain_item); 589 keychain_->Free(keychain_item); 590 return result == noErr; 591} 592 593void MacKeychainPasswordFormAdapter::SetFindsOnlyOwnedItems( 594 bool finds_only_owned) { 595 finds_only_owned_ = finds_only_owned; 596} 597 598std::vector<PasswordForm*> 599 MacKeychainPasswordFormAdapter::ConvertKeychainItemsToForms( 600 std::vector<SecKeychainItemRef>* items) { 601 std::vector<PasswordForm*> keychain_forms; 602 for (std::vector<SecKeychainItemRef>::const_iterator i = items->begin(); 603 i != items->end(); ++i) { 604 PasswordForm* form = new PasswordForm(); 605 if (internal_keychain_helpers::FillPasswordFormFromKeychainItem(*keychain_, 606 *i, form)) { 607 keychain_forms.push_back(form); 608 } 609 keychain_->Free(*i); 610 } 611 items->clear(); 612 return keychain_forms; 613} 614 615SecKeychainItemRef MacKeychainPasswordFormAdapter::KeychainItemForForm( 616 const PasswordForm& form) { 617 // We don't store blacklist entries in the keychain, so the answer to "what 618 // Keychain item goes with this form" is always "nothing" for blacklists. 619 if (form.blacklisted_by_user) { 620 return NULL; 621 } 622 623 std::string path = form.origin.path(); 624 std::string username = UTF16ToUTF8(form.username_value); 625 std::vector<SecKeychainItemRef> matches = MatchingKeychainItems( 626 form.signon_realm, form.scheme, path.c_str(), username.c_str()); 627 628 if (matches.size() == 0) { 629 return NULL; 630 } 631 // Free all items after the first, since we won't be returning them. 632 for (std::vector<SecKeychainItemRef>::iterator i = matches.begin() + 1; 633 i != matches.end(); ++i) { 634 keychain_->Free(*i); 635 } 636 return matches[0]; 637} 638 639std::vector<SecKeychainItemRef> 640 MacKeychainPasswordFormAdapter::MatchingKeychainItems( 641 const std::string& signon_realm, 642 webkit_glue::PasswordForm::Scheme scheme, 643 const char* path, const char* username) { 644 std::vector<SecKeychainItemRef> matches; 645 646 std::string server; 647 std::string security_domain; 648 int port; 649 bool is_secure; 650 if (!ExtractSignonRealmComponents(signon_realm, &server, &port, 651 &is_secure, &security_domain)) { 652 // TODO(stuartmorgan): Proxies will currently fail here, since their 653 // signon_realm is not a URL. We need to detect the proxy case and handle 654 // it specially. 655 return matches; 656 } 657 SecProtocolType protocol = is_secure ? kSecProtocolTypeHTTPS 658 : kSecProtocolTypeHTTP; 659 SecAuthenticationType auth_type = AuthTypeForScheme(scheme); 660 const char* auth_domain = (scheme == PasswordForm::SCHEME_HTML) ? 661 NULL : security_domain.c_str(); 662 KeychainSearch keychain_search(*keychain_); 663 keychain_search.Init(server.c_str(), port, protocol, auth_type, 664 auth_domain, path, username, CreatorCodeForSearch()); 665 keychain_search.FindMatchingItems(&matches); 666 return matches; 667} 668 669// TODO(stuartmorgan): signon_realm for proxies is not yet supported. 670bool MacKeychainPasswordFormAdapter::ExtractSignonRealmComponents( 671 const std::string& signon_realm, std::string* server, int* port, 672 bool* is_secure, std::string* security_domain) { 673 // The signon_realm will be the Origin portion of a URL for an HTML form, 674 // and the same but with the security domain as a path for HTTP auth. 675 GURL realm_as_url(signon_realm); 676 if (!realm_as_url.is_valid()) { 677 return false; 678 } 679 680 if (server) 681 *server = realm_as_url.host(); 682 if (is_secure) 683 *is_secure = realm_as_url.SchemeIsSecure(); 684 if (port) 685 *port = realm_as_url.has_port() ? atoi(realm_as_url.port().c_str()) : 0; 686 if (security_domain) { 687 // Strip the leading '/' off of the path to get the security domain. 688 *security_domain = realm_as_url.path().substr(1); 689 } 690 return true; 691} 692 693// Returns the Keychain SecAuthenticationType type corresponding to |scheme|. 694SecAuthenticationType MacKeychainPasswordFormAdapter::AuthTypeForScheme( 695 PasswordForm::Scheme scheme) { 696 switch (scheme) { 697 case PasswordForm::SCHEME_HTML: return kSecAuthenticationTypeHTMLForm; 698 case PasswordForm::SCHEME_BASIC: return kSecAuthenticationTypeHTTPBasic; 699 case PasswordForm::SCHEME_DIGEST: return kSecAuthenticationTypeHTTPDigest; 700 case PasswordForm::SCHEME_OTHER: return kSecAuthenticationTypeDefault; 701 } 702 NOTREACHED(); 703 return kSecAuthenticationTypeDefault; 704} 705 706bool MacKeychainPasswordFormAdapter::SetKeychainItemPassword( 707 const SecKeychainItemRef& keychain_item, const std::string& password) { 708 OSStatus result = keychain_->ItemModifyAttributesAndData(keychain_item, NULL, 709 password.size(), 710 password.c_str()); 711 return result == noErr; 712} 713 714bool MacKeychainPasswordFormAdapter::SetKeychainItemCreatorCode( 715 const SecKeychainItemRef& keychain_item, OSType creator_code) { 716 SecKeychainAttribute attr = { kSecCreatorItemAttr, sizeof(creator_code), 717 &creator_code }; 718 SecKeychainAttributeList attrList = { 1, &attr }; 719 OSStatus result = keychain_->ItemModifyAttributesAndData(keychain_item, 720 &attrList, 0, NULL); 721 return result == noErr; 722} 723 724OSType MacKeychainPasswordFormAdapter::CreatorCodeForSearch() { 725 return finds_only_owned_ ? mac_util::CreatorCodeForApplication() : 0; 726} 727 728#pragma mark - 729 730PasswordStoreMac::PasswordStoreMac(MacKeychain* keychain, 731 LoginDatabase* login_db) 732 : keychain_(keychain), login_metadata_db_(login_db) { 733 DCHECK(keychain_.get()); 734 DCHECK(login_metadata_db_.get()); 735} 736 737PasswordStoreMac::~PasswordStoreMac() { 738 if (thread_.get()) { 739 thread_->message_loop()->DeleteSoon(FROM_HERE, 740 notification_service_.release()); 741 } 742} 743 744bool PasswordStoreMac::Init() { 745 thread_.reset(new base::Thread("Chrome_PasswordStore_Thread")); 746 747 if (!thread_->Start()) { 748 thread_.reset(NULL); 749 return false; 750 } 751 ScheduleTask(NewRunnableMethod(this, 752 &PasswordStoreMac::CreateNotificationService)); 753 return PasswordStore::Init(); 754} 755 756void PasswordStoreMac::ScheduleTask(Task* task) { 757 if (thread_.get()) { 758 thread_->message_loop()->PostTask(FROM_HERE, task); 759 } 760} 761 762void PasswordStoreMac::ReportMetricsImpl() { 763 login_metadata_db_->ReportMetrics(); 764} 765 766void PasswordStoreMac::AddLoginImpl(const PasswordForm& form) { 767 if (AddToKeychainIfNecessary(form)) { 768 if (login_metadata_db_->AddLogin(form)) { 769 PasswordStoreChangeList changes; 770 changes.push_back(PasswordStoreChange(PasswordStoreChange::ADD, form)); 771 NotificationService::current()->Notify( 772 NotificationType::LOGINS_CHANGED, 773 Source<PasswordStore>(this), 774 Details<PasswordStoreChangeList>(&changes)); 775 } 776 } 777} 778 779void PasswordStoreMac::UpdateLoginImpl(const PasswordForm& form) { 780 int update_count = 0; 781 if (!login_metadata_db_->UpdateLogin(form, &update_count)) 782 return; 783 784 MacKeychainPasswordFormAdapter keychain_adapter(keychain_.get()); 785 if (update_count == 0 && 786 !keychain_adapter.HasPasswordsMergeableWithForm(form)) { 787 // If the password isn't in either the DB or the keychain, then it must have 788 // been deleted after autofill happened, and should not be re-added. 789 return; 790 } 791 792 // The keychain add will update if there is a collision and add if there 793 // isn't, which is the behavior we want, so there's no separate update call. 794 if (AddToKeychainIfNecessary(form)) { 795 PasswordStoreChangeList changes; 796 if (update_count == 0) { 797 if (login_metadata_db_->AddLogin(form)) { 798 changes.push_back(PasswordStoreChange(PasswordStoreChange::ADD, 799 form)); 800 } 801 } else { 802 changes.push_back(PasswordStoreChange(PasswordStoreChange::UPDATE, 803 form)); 804 } 805 if (!changes.empty()) { 806 NotificationService::current()->Notify( 807 NotificationType::LOGINS_CHANGED, 808 Source<PasswordStore>(this), 809 Details<PasswordStoreChangeList>(&changes)); 810 } 811 } 812} 813 814void PasswordStoreMac::RemoveLoginImpl(const PasswordForm& form) { 815 if (login_metadata_db_->RemoveLogin(form)) { 816 // See if we own a Keychain item associated with this item. We can do an 817 // exact search rather than messing around with trying to do fuzzy matching 818 // because passwords that we created will always have an exact-match 819 // database entry. 820 // (If a user does lose their profile but not their keychain we'll treat the 821 // entries we find like other imported entries anyway, so it's reasonable to 822 // handle deletes on them the way we would for an imported item.) 823 MacKeychainPasswordFormAdapter owned_keychain_adapter(keychain_.get()); 824 owned_keychain_adapter.SetFindsOnlyOwnedItems(true); 825 PasswordForm* owned_password_form = 826 owned_keychain_adapter.PasswordExactlyMatchingForm(form); 827 if (owned_password_form) { 828 // If we don't have other forms using it (i.e., a form differing only by 829 // the names of the form elements), delete the keychain entry. 830 if (!DatabaseHasFormMatchingKeychainForm(form)) { 831 owned_keychain_adapter.RemovePassword(form); 832 } 833 } 834 835 PasswordStoreChangeList changes; 836 changes.push_back(PasswordStoreChange(PasswordStoreChange::REMOVE, form)); 837 NotificationService::current()->Notify( 838 NotificationType::LOGINS_CHANGED, 839 Source<PasswordStore>(this), 840 Details<PasswordStoreChangeList>(&changes)); 841 } 842} 843 844void PasswordStoreMac::RemoveLoginsCreatedBetweenImpl( 845 const base::Time& delete_begin, const base::Time& delete_end) { 846 std::vector<PasswordForm*> forms; 847 if (login_metadata_db_->GetLoginsCreatedBetween(delete_begin, delete_end, 848 &forms)) { 849 if (login_metadata_db_->RemoveLoginsCreatedBetween(delete_begin, 850 delete_end)) { 851 // We can't delete from the Keychain by date because we may be sharing 852 // items with database entries that weren't in the delete range. Instead, 853 // we find all the Keychain items we own but aren't using any more and 854 // delete those. 855 std::vector<PasswordForm*> orphan_keychain_forms = 856 GetUnusedKeychainForms(); 857 // This is inefficient, since we have to re-look-up each keychain item 858 // one at a time to delete it even though the search step already had a 859 // list of Keychain item references. If this turns out to be noticeably 860 // slow we'll need to rearchitect to allow the search and deletion steps 861 // to share. 862 RemoveKeychainForms(orphan_keychain_forms); 863 STLDeleteElements(&orphan_keychain_forms); 864 865 PasswordStoreChangeList changes; 866 for (std::vector<PasswordForm*>::const_iterator it = forms.begin(); 867 it != forms.end(); ++it) { 868 changes.push_back(PasswordStoreChange(PasswordStoreChange::REMOVE, 869 **it)); 870 } 871 NotificationService::current()->Notify( 872 NotificationType::LOGINS_CHANGED, 873 Source<PasswordStore>(this), 874 Details<PasswordStoreChangeList>(&changes)); 875 } 876 } 877} 878 879void PasswordStoreMac::GetLoginsImpl(GetLoginsRequest* request, 880 const webkit_glue::PasswordForm& form) { 881 MacKeychainPasswordFormAdapter keychain_adapter(keychain_.get()); 882 std::vector<PasswordForm*> keychain_forms = 883 keychain_adapter.PasswordsFillingForm(form); 884 885 std::vector<PasswordForm*> database_forms; 886 login_metadata_db_->GetLogins(form, &database_forms); 887 888 std::vector<PasswordForm*> merged_forms; 889 internal_keychain_helpers::MergePasswordForms(&keychain_forms, 890 &database_forms, 891 &merged_forms); 892 893 // Strip any blacklist entries out of the unused Keychain array, then take 894 // all the entries that are left (which we can use as imported passwords). 895 std::vector<PasswordForm*> keychain_blacklist_forms = 896 internal_keychain_helpers::ExtractBlacklistForms(&keychain_forms); 897 merged_forms.insert(merged_forms.end(), keychain_forms.begin(), 898 keychain_forms.end()); 899 keychain_forms.clear(); 900 STLDeleteElements(&keychain_blacklist_forms); 901 902 // Clean up any orphaned database entries. 903 RemoveDatabaseForms(database_forms); 904 STLDeleteElements(&database_forms); 905 906 NotifyConsumer(request, merged_forms); 907} 908 909void PasswordStoreMac::GetBlacklistLoginsImpl(GetLoginsRequest* request) { 910 std::vector<PasswordForm*> database_forms; 911 FillBlacklistLogins(&database_forms); 912 NotifyConsumer(request, database_forms); 913} 914 915void PasswordStoreMac::GetAutofillableLoginsImpl(GetLoginsRequest* request) { 916 std::vector<PasswordForm*> database_forms; 917 FillAutofillableLogins(&database_forms); 918 NotifyConsumer(request, database_forms); 919} 920 921bool PasswordStoreMac::FillAutofillableLogins( 922 std::vector<PasswordForm*>* forms) { 923 DCHECK(thread_->message_loop() == MessageLoop::current()); 924 925 std::vector<PasswordForm*> database_forms; 926 login_metadata_db_->GetAutofillableLogins(&database_forms); 927 928 std::vector<PasswordForm*> merged_forms = 929 internal_keychain_helpers::GetPasswordsForForms(*keychain_, 930 &database_forms); 931 932 // Clean up any orphaned database entries. 933 RemoveDatabaseForms(database_forms); 934 STLDeleteElements(&database_forms); 935 936 forms->insert(forms->end(), merged_forms.begin(), merged_forms.end()); 937 return true; 938} 939 940bool PasswordStoreMac::FillBlacklistLogins( 941 std::vector<PasswordForm*>* forms) { 942 DCHECK(thread_->message_loop() == MessageLoop::current()); 943 return login_metadata_db_->GetBlacklistLogins(forms); 944} 945 946bool PasswordStoreMac::AddToKeychainIfNecessary(const PasswordForm& form) { 947 if (form.blacklisted_by_user) { 948 return true; 949 } 950 MacKeychainPasswordFormAdapter keychainAdapter(keychain_.get()); 951 return keychainAdapter.AddPassword(form); 952} 953 954bool PasswordStoreMac::DatabaseHasFormMatchingKeychainForm( 955 const webkit_glue::PasswordForm& form) { 956 bool has_match = false; 957 std::vector<PasswordForm*> database_forms; 958 login_metadata_db_->GetLogins(form, &database_forms); 959 for (std::vector<PasswordForm*>::iterator i = database_forms.begin(); 960 i != database_forms.end(); ++i) { 961 if (internal_keychain_helpers::FormsMatchForMerge(form, **i) && 962 (*i)->origin == form.origin) { 963 has_match = true; 964 break; 965 } 966 } 967 STLDeleteElements(&database_forms); 968 return has_match; 969} 970 971std::vector<PasswordForm*> PasswordStoreMac::GetUnusedKeychainForms() { 972 std::vector<PasswordForm*> database_forms; 973 login_metadata_db_->GetAutofillableLogins(&database_forms); 974 975 MacKeychainPasswordFormAdapter owned_keychain_adapter(keychain_.get()); 976 owned_keychain_adapter.SetFindsOnlyOwnedItems(true); 977 std::vector<PasswordForm*> owned_keychain_forms = 978 owned_keychain_adapter.GetAllPasswordFormPasswords(); 979 980 // Run a merge; anything left in owned_keychain_forms when we are done no 981 // longer has a matching database entry. 982 std::vector<PasswordForm*> merged_forms; 983 internal_keychain_helpers::MergePasswordForms(&owned_keychain_forms, 984 &database_forms, 985 &merged_forms); 986 STLDeleteElements(&merged_forms); 987 STLDeleteElements(&database_forms); 988 989 return owned_keychain_forms; 990} 991 992void PasswordStoreMac::RemoveDatabaseForms( 993 const std::vector<PasswordForm*>& forms) { 994 for (std::vector<PasswordForm*>::const_iterator i = forms.begin(); 995 i != forms.end(); ++i) { 996 login_metadata_db_->RemoveLogin(**i); 997 } 998} 999 1000void PasswordStoreMac::RemoveKeychainForms( 1001 const std::vector<PasswordForm*>& forms) { 1002 MacKeychainPasswordFormAdapter owned_keychain_adapter(keychain_.get()); 1003 owned_keychain_adapter.SetFindsOnlyOwnedItems(true); 1004 for (std::vector<PasswordForm*>::const_iterator i = forms.begin(); 1005 i != forms.end(); ++i) { 1006 owned_keychain_adapter.RemovePassword(**i); 1007 } 1008} 1009 1010void PasswordStoreMac::CreateNotificationService() { 1011 notification_service_.reset(new NotificationService); 1012} 1013