1 // Copyright (c) 2011 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 "content/common/notification_service.h" 24 25 using webkit_glue::PasswordForm; 26 27 // Utility class to handle the details of constructing and running a keychain 28 // search from a set of attributes. 29 class 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 54 KeychainSearch::KeychainSearch(const MacKeychain& keychain) 55 : keychain_(&keychain), search_ref_(NULL) { 56 search_attributes_.count = 0; 57 search_attributes_.attr = NULL; 58 } 59 60 KeychainSearch::~KeychainSearch() { 61 if (search_attributes_.attr) { 62 free(search_attributes_.attr); 63 } 64 } 65 66 void 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 147 void 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. 171 namespace 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. 175 GURL 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. 197 bool 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|. 222 PasswordForm::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 231 bool 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 344 bool 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. 357 PasswordForm* 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|. 379 std::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. 396 template <class T> 397 void 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 409 void 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 443 std::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.empty()) { 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 468 MacKeychainPasswordFormAdapter::MacKeychainPasswordFormAdapter( 469 const MacKeychain* keychain) 470 : keychain_(keychain), finds_only_owned_(false) { 471 } 472 473 std::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 483 std::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 494 PasswordForm* 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 508 bool 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.empty(); 520 } 521 522 std::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 541 bool 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 585 bool 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 594 void MacKeychainPasswordFormAdapter::SetFindsOnlyOwnedItems( 595 bool finds_only_owned) { 596 finds_only_owned_ = finds_only_owned; 597 } 598 599 std::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 616 SecKeychainItemRef 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.empty()) { 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 640 std::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. 671 bool 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|. 695 SecAuthenticationType 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 707 bool 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 715 bool 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 725 OSType MacKeychainPasswordFormAdapter::CreatorCodeForSearch() { 726 return finds_only_owned_ ? base::mac::CreatorCodeForApplication() : 0; 727 } 728 729 #pragma mark - 730 731 PasswordStoreMac::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 738 PasswordStoreMac::~PasswordStoreMac() { 739 if (thread_.get()) { 740 thread_->message_loop()->DeleteSoon(FROM_HERE, 741 notification_service_.release()); 742 } 743 } 744 745 bool 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 757 void PasswordStoreMac::ScheduleTask(Task* task) { 758 if (thread_.get()) { 759 thread_->message_loop()->PostTask(FROM_HERE, task); 760 } 761 } 762 763 void PasswordStoreMac::ReportMetricsImpl() { 764 login_metadata_db_->ReportMetrics(); 765 } 766 767 void 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 780 void 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 815 void 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 845 void 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 880 void 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 = request->value; 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 ForwardLoginsResult(request); 908 } 909 910 void PasswordStoreMac::GetBlacklistLoginsImpl(GetLoginsRequest* request) { 911 FillBlacklistLogins(&request->value); 912 ForwardLoginsResult(request); 913 } 914 915 void PasswordStoreMac::GetAutofillableLoginsImpl(GetLoginsRequest* request) { 916 FillAutofillableLogins(&request->value); 917 ForwardLoginsResult(request); 918 } 919 920 bool PasswordStoreMac::FillAutofillableLogins( 921 std::vector<PasswordForm*>* forms) { 922 DCHECK(thread_->message_loop() == MessageLoop::current()); 923 924 std::vector<PasswordForm*> database_forms; 925 login_metadata_db_->GetAutofillableLogins(&database_forms); 926 927 std::vector<PasswordForm*> merged_forms = 928 internal_keychain_helpers::GetPasswordsForForms(*keychain_, 929 &database_forms); 930 931 // Clean up any orphaned database entries. 932 RemoveDatabaseForms(database_forms); 933 STLDeleteElements(&database_forms); 934 935 forms->insert(forms->end(), merged_forms.begin(), merged_forms.end()); 936 return true; 937 } 938 939 bool PasswordStoreMac::FillBlacklistLogins( 940 std::vector<PasswordForm*>* forms) { 941 DCHECK(thread_->message_loop() == MessageLoop::current()); 942 return login_metadata_db_->GetBlacklistLogins(forms); 943 } 944 945 bool PasswordStoreMac::AddToKeychainIfNecessary(const PasswordForm& form) { 946 if (form.blacklisted_by_user) { 947 return true; 948 } 949 MacKeychainPasswordFormAdapter keychainAdapter(keychain_.get()); 950 return keychainAdapter.AddPassword(form); 951 } 952 953 bool PasswordStoreMac::DatabaseHasFormMatchingKeychainForm( 954 const webkit_glue::PasswordForm& form) { 955 bool has_match = false; 956 std::vector<PasswordForm*> database_forms; 957 login_metadata_db_->GetLogins(form, &database_forms); 958 for (std::vector<PasswordForm*>::iterator i = database_forms.begin(); 959 i != database_forms.end(); ++i) { 960 if (internal_keychain_helpers::FormsMatchForMerge(form, **i) && 961 (*i)->origin == form.origin) { 962 has_match = true; 963 break; 964 } 965 } 966 STLDeleteElements(&database_forms); 967 return has_match; 968 } 969 970 std::vector<PasswordForm*> PasswordStoreMac::GetUnusedKeychainForms() { 971 std::vector<PasswordForm*> database_forms; 972 login_metadata_db_->GetAutofillableLogins(&database_forms); 973 974 MacKeychainPasswordFormAdapter owned_keychain_adapter(keychain_.get()); 975 owned_keychain_adapter.SetFindsOnlyOwnedItems(true); 976 std::vector<PasswordForm*> owned_keychain_forms = 977 owned_keychain_adapter.GetAllPasswordFormPasswords(); 978 979 // Run a merge; anything left in owned_keychain_forms when we are done no 980 // longer has a matching database entry. 981 std::vector<PasswordForm*> merged_forms; 982 internal_keychain_helpers::MergePasswordForms(&owned_keychain_forms, 983 &database_forms, 984 &merged_forms); 985 STLDeleteElements(&merged_forms); 986 STLDeleteElements(&database_forms); 987 988 return owned_keychain_forms; 989 } 990 991 void PasswordStoreMac::RemoveDatabaseForms( 992 const std::vector<PasswordForm*>& forms) { 993 for (std::vector<PasswordForm*>::const_iterator i = forms.begin(); 994 i != forms.end(); ++i) { 995 login_metadata_db_->RemoveLogin(**i); 996 } 997 } 998 999 void PasswordStoreMac::RemoveKeychainForms( 1000 const std::vector<PasswordForm*>& forms) { 1001 MacKeychainPasswordFormAdapter owned_keychain_adapter(keychain_.get()); 1002 owned_keychain_adapter.SetFindsOnlyOwnedItems(true); 1003 for (std::vector<PasswordForm*>::const_iterator i = forms.begin(); 1004 i != forms.end(); ++i) { 1005 owned_keychain_adapter.RemovePassword(**i); 1006 } 1007 } 1008 1009 void PasswordStoreMac::CreateNotificationService() { 1010 notification_service_.reset(new NotificationService); 1011 } 1012