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