| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "chrome/browser/password_manager/password_store_mac.h" | |
| 6 | |
| 7 #include <CoreServices/CoreServices.h> | |
| 8 #include <stddef.h> | |
| 9 #include <algorithm> | |
| 10 #include <iterator> | |
| 11 #include <set> | |
| 12 #include <string> | |
| 13 #include <utility> | |
| 14 #include <vector> | |
| 15 | |
| 16 #include "base/callback.h" | |
| 17 #include "base/logging.h" | |
| 18 #include "base/mac/foundation_util.h" | |
| 19 #include "base/mac/mac_logging.h" | |
| 20 #include "base/macros.h" | |
| 21 #include "base/memory/ptr_util.h" | |
| 22 #include "base/message_loop/message_loop.h" | |
| 23 #include "base/metrics/histogram_macros.h" | |
| 24 #include "base/strings/string_util.h" | |
| 25 #include "base/strings/utf_string_conversions.h" | |
| 26 #include "chrome/browser/mac/security_wrappers.h" | |
| 27 #include "chrome/browser/password_manager/password_store_mac_internal.h" | |
| 28 #include "components/os_crypt/os_crypt.h" | |
| 29 #include "components/password_manager/core/browser/affiliation_utils.h" | |
| 30 #include "components/password_manager/core/browser/login_database.h" | |
| 31 #include "components/password_manager/core/browser/password_manager_util.h" | |
| 32 #include "components/password_manager/core/browser/password_store_change.h" | |
| 33 #include "content/public/browser/browser_thread.h" | |
| 34 #include "crypto/apple_keychain.h" | |
| 35 #include "url/origin.h" | |
| 36 | |
| 37 using autofill::PasswordForm; | |
| 38 using crypto::AppleKeychain; | |
| 39 using password_manager::PasswordStoreChange; | |
| 40 using password_manager::PasswordStoreChangeList; | |
| 41 | |
| 42 namespace { | |
| 43 | |
| 44 // Utility class to handle the details of constructing and running a keychain | |
| 45 // search from a set of attributes. | |
| 46 class KeychainSearch { | |
| 47 public: | |
| 48 explicit KeychainSearch(const AppleKeychain& keychain); | |
| 49 ~KeychainSearch(); | |
| 50 | |
| 51 // Sets up a keychain search based on an non "null" (NULL for char*, | |
| 52 // The appropriate "Any" entry for other types) arguments. | |
| 53 // | |
| 54 // IMPORTANT: Any parameters passed in *must* remain valid for as long as the | |
| 55 // KeychainSearch object, since the search uses them by reference. | |
| 56 void Init(const char* server, | |
| 57 const UInt32* port, | |
| 58 const SecProtocolType* protocol, | |
| 59 const SecAuthenticationType* auth_type, | |
| 60 const char* security_domain, | |
| 61 const char* path, | |
| 62 const char* username, | |
| 63 const OSType* creator); | |
| 64 | |
| 65 // Fills |items| with all Keychain items that match the Init'd search. | |
| 66 // If the search fails for any reason, |items| will be unchanged. | |
| 67 void FindMatchingItems(std::vector<SecKeychainItemRef>* matches); | |
| 68 | |
| 69 private: | |
| 70 const AppleKeychain* keychain_; | |
| 71 SecKeychainAttributeList search_attributes_; | |
| 72 SecKeychainSearchRef search_ref_; | |
| 73 }; | |
| 74 | |
| 75 KeychainSearch::KeychainSearch(const AppleKeychain& keychain) | |
| 76 : keychain_(&keychain), search_ref_(NULL) { | |
| 77 search_attributes_.count = 0; | |
| 78 search_attributes_.attr = NULL; | |
| 79 } | |
| 80 | |
| 81 KeychainSearch::~KeychainSearch() { | |
| 82 if (search_attributes_.attr) { | |
| 83 free(search_attributes_.attr); | |
| 84 } | |
| 85 } | |
| 86 | |
| 87 void KeychainSearch::Init(const char* server, | |
| 88 const UInt32* port, | |
| 89 const SecProtocolType* protocol, | |
| 90 const SecAuthenticationType* auth_type, | |
| 91 const char* security_domain, | |
| 92 const char* path, | |
| 93 const char* username, | |
| 94 const OSType* creator) { | |
| 95 // Allocate enough to hold everything we might use. | |
| 96 const unsigned int kMaxEntryCount = 8; | |
| 97 search_attributes_.attr = | |
| 98 static_cast<SecKeychainAttribute*>(calloc(kMaxEntryCount, | |
| 99 sizeof(SecKeychainAttribute))); | |
| 100 unsigned int entries = 0; | |
| 101 // We only use search_attributes_ with SearchCreateFromAttributes, which takes | |
| 102 // a "const SecKeychainAttributeList *", so we trust that they won't try | |
| 103 // to modify the list, and that casting away const-ness is thus safe. | |
| 104 if (server != NULL) { | |
| 105 DCHECK_LT(entries, kMaxEntryCount); | |
| 106 search_attributes_.attr[entries].tag = kSecServerItemAttr; | |
| 107 search_attributes_.attr[entries].length = strlen(server); | |
| 108 search_attributes_.attr[entries].data = | |
| 109 const_cast<void*>(static_cast<const void*>(server)); | |
| 110 ++entries; | |
| 111 } | |
| 112 if (port != NULL && *port != kAnyPort) { | |
| 113 DCHECK_LE(entries, kMaxEntryCount); | |
| 114 search_attributes_.attr[entries].tag = kSecPortItemAttr; | |
| 115 search_attributes_.attr[entries].length = sizeof(*port); | |
| 116 search_attributes_.attr[entries].data = | |
| 117 const_cast<void*>(static_cast<const void*>(port)); | |
| 118 ++entries; | |
| 119 } | |
| 120 if (protocol != NULL && *protocol != kSecProtocolTypeAny) { | |
| 121 DCHECK_LE(entries, kMaxEntryCount); | |
| 122 search_attributes_.attr[entries].tag = kSecProtocolItemAttr; | |
| 123 search_attributes_.attr[entries].length = sizeof(*protocol); | |
| 124 search_attributes_.attr[entries].data = | |
| 125 const_cast<void*>(static_cast<const void*>(protocol)); | |
| 126 ++entries; | |
| 127 } | |
| 128 if (auth_type != NULL && *auth_type != kSecAuthenticationTypeAny) { | |
| 129 DCHECK_LE(entries, kMaxEntryCount); | |
| 130 search_attributes_.attr[entries].tag = kSecAuthenticationTypeItemAttr; | |
| 131 search_attributes_.attr[entries].length = sizeof(*auth_type); | |
| 132 search_attributes_.attr[entries].data = | |
| 133 const_cast<void*>(static_cast<const void*>(auth_type)); | |
| 134 ++entries; | |
| 135 } | |
| 136 if (security_domain != NULL && strlen(security_domain) > 0) { | |
| 137 DCHECK_LE(entries, kMaxEntryCount); | |
| 138 search_attributes_.attr[entries].tag = kSecSecurityDomainItemAttr; | |
| 139 search_attributes_.attr[entries].length = strlen(security_domain); | |
| 140 search_attributes_.attr[entries].data = | |
| 141 const_cast<void*>(static_cast<const void*>(security_domain)); | |
| 142 ++entries; | |
| 143 } | |
| 144 if (path != NULL && strlen(path) > 0 && strcmp(path, "/") != 0) { | |
| 145 DCHECK_LE(entries, kMaxEntryCount); | |
| 146 search_attributes_.attr[entries].tag = kSecPathItemAttr; | |
| 147 search_attributes_.attr[entries].length = strlen(path); | |
| 148 search_attributes_.attr[entries].data = | |
| 149 const_cast<void*>(static_cast<const void*>(path)); | |
| 150 ++entries; | |
| 151 } | |
| 152 if (username != NULL) { | |
| 153 DCHECK_LE(entries, kMaxEntryCount); | |
| 154 search_attributes_.attr[entries].tag = kSecAccountItemAttr; | |
| 155 search_attributes_.attr[entries].length = strlen(username); | |
| 156 search_attributes_.attr[entries].data = | |
| 157 const_cast<void*>(static_cast<const void*>(username)); | |
| 158 ++entries; | |
| 159 } | |
| 160 if (creator != NULL) { | |
| 161 DCHECK_LE(entries, kMaxEntryCount); | |
| 162 search_attributes_.attr[entries].tag = kSecCreatorItemAttr; | |
| 163 search_attributes_.attr[entries].length = sizeof(*creator); | |
| 164 search_attributes_.attr[entries].data = | |
| 165 const_cast<void*>(static_cast<const void*>(creator)); | |
| 166 ++entries; | |
| 167 } | |
| 168 search_attributes_.count = entries; | |
| 169 } | |
| 170 | |
| 171 void KeychainSearch::FindMatchingItems(std::vector<SecKeychainItemRef>* items) { | |
| 172 OSStatus result = keychain_->SearchCreateFromAttributes( | |
| 173 NULL, kSecInternetPasswordItemClass, &search_attributes_, &search_ref_); | |
| 174 | |
| 175 if (result != noErr) { | |
| 176 OSSTATUS_LOG(ERROR, result) << "Keychain lookup failed"; | |
| 177 return; | |
| 178 } | |
| 179 | |
| 180 SecKeychainItemRef keychain_item; | |
| 181 while (keychain_->SearchCopyNext(search_ref_, &keychain_item) == noErr) { | |
| 182 // Consumer is responsible for freeing the items. | |
| 183 items->push_back(keychain_item); | |
| 184 } | |
| 185 | |
| 186 keychain_->Free(search_ref_); | |
| 187 search_ref_ = NULL; | |
| 188 } | |
| 189 | |
| 190 PasswordStoreChangeList FormsToRemoveChangeList( | |
| 191 const std::vector<std::unique_ptr<PasswordForm>>& forms) { | |
| 192 PasswordStoreChangeList changes; | |
| 193 for (const auto& form : forms) { | |
| 194 changes.push_back(PasswordStoreChange(PasswordStoreChange::REMOVE, *form)); | |
| 195 } | |
| 196 return changes; | |
| 197 } | |
| 198 | |
| 199 // Moves the content of |second| to the end of |first|. | |
| 200 void AppendSecondToFirst(std::vector<std::unique_ptr<PasswordForm>>* first, | |
| 201 std::vector<std::unique_ptr<PasswordForm>>* second) { | |
| 202 first->reserve(first->size() + second->size()); | |
| 203 std::move(second->begin(), second->end(), std::back_inserter(*first)); | |
| 204 second->clear(); | |
| 205 } | |
| 206 | |
| 207 // Returns the best match for |base_form| from |keychain_forms|, or nullptr if | |
| 208 // there is no suitable match. | |
| 209 const PasswordForm* BestKeychainFormForForm( | |
| 210 const PasswordForm& base_form, | |
| 211 const std::vector<std::unique_ptr<PasswordForm>>& keychain_forms) { | |
| 212 const PasswordForm* partial_match = nullptr; | |
| 213 for (const auto& keychain_form : keychain_forms) { | |
| 214 // TODO(stuartmorgan): We should really be scoring path matches and picking | |
| 215 // the best, rather than just checking exact-or-not (although in practice | |
| 216 // keychain items with paths probably came from us). | |
| 217 if (internal_keychain_helpers::FormsMatchForMerge( | |
| 218 base_form, *keychain_form, | |
| 219 internal_keychain_helpers::FUZZY_FORM_MATCH)) { | |
| 220 if (base_form.origin == keychain_form->origin) { | |
| 221 return keychain_form.get(); | |
| 222 } else if (!partial_match) { | |
| 223 partial_match = keychain_form.get(); | |
| 224 } | |
| 225 } | |
| 226 } | |
| 227 return partial_match; | |
| 228 } | |
| 229 | |
| 230 // True if the form has no password to be stored in Keychain. | |
| 231 bool IsLoginDatabaseOnlyForm(const PasswordForm& form) { | |
| 232 return form.blacklisted_by_user || !form.federation_origin.unique() || | |
| 233 form.scheme == PasswordForm::SCHEME_USERNAME_ONLY; | |
| 234 } | |
| 235 | |
| 236 } // namespace | |
| 237 | |
| 238 #pragma mark - | |
| 239 | |
| 240 // TODO(stuartmorgan): Convert most of this to private helpers in | |
| 241 // MacKeychainPasswordFormAdapter once it has sufficient higher-level public | |
| 242 // methods to provide test coverage. | |
| 243 namespace internal_keychain_helpers { | |
| 244 | |
| 245 // Returns a URL built from the given components. To create a URL without a | |
| 246 // port, pass kAnyPort for the |port| parameter. | |
| 247 GURL URLFromComponents(bool is_secure, const std::string& host, int port, | |
| 248 const std::string& path) { | |
| 249 GURL::Replacements url_components; | |
| 250 std::string scheme(is_secure ? "https" : "http"); | |
| 251 url_components.SetSchemeStr(scheme); | |
| 252 url_components.SetHostStr(host); | |
| 253 std::string port_string; // Must remain in scope until after we do replacing. | |
| 254 if (port != kAnyPort) { | |
| 255 std::ostringstream port_stringstream; | |
| 256 port_stringstream << port; | |
| 257 port_string = port_stringstream.str(); | |
| 258 url_components.SetPortStr(port_string); | |
| 259 } | |
| 260 url_components.SetPathStr(path); | |
| 261 | |
| 262 GURL url("http://dummy.com"); // ReplaceComponents needs a valid URL. | |
| 263 return url.ReplaceComponents(url_components); | |
| 264 } | |
| 265 | |
| 266 // Converts a Keychain time string to a Time object, returning true if | |
| 267 // time_string_bytes was parsable. | |
| 268 bool TimeFromKeychainTimeString(const char* time_string_bytes, | |
| 269 unsigned int byte_length, | |
| 270 base::Time* time) { | |
| 271 DCHECK(time); | |
| 272 | |
| 273 char* time_string = static_cast<char*>(malloc(byte_length + 1)); | |
| 274 memcpy(time_string, time_string_bytes, byte_length); | |
| 275 time_string[byte_length] = '\0'; | |
| 276 base::Time::Exploded exploded_time; | |
| 277 bzero(&exploded_time, sizeof(exploded_time)); | |
| 278 // The time string is of the form "yyyyMMddHHmmss'Z", in UTC time. | |
| 279 int assignments = sscanf(time_string, "%4d%2d%2d%2d%2d%2dZ", | |
| 280 &exploded_time.year, &exploded_time.month, | |
| 281 &exploded_time.day_of_month, &exploded_time.hour, | |
| 282 &exploded_time.minute, &exploded_time.second); | |
| 283 free(time_string); | |
| 284 | |
| 285 return assignments == 6 && base::Time::FromUTCExploded(exploded_time, time); | |
| 286 } | |
| 287 | |
| 288 // Returns the PasswordForm Scheme corresponding to |auth_type|. | |
| 289 PasswordForm::Scheme SchemeForAuthType(SecAuthenticationType auth_type) { | |
| 290 switch (auth_type) { | |
| 291 case kSecAuthenticationTypeHTMLForm: return PasswordForm::SCHEME_HTML; | |
| 292 case kSecAuthenticationTypeHTTPBasic: return PasswordForm::SCHEME_BASIC; | |
| 293 case kSecAuthenticationTypeHTTPDigest: return PasswordForm::SCHEME_DIGEST; | |
| 294 default: return PasswordForm::SCHEME_OTHER; | |
| 295 } | |
| 296 } | |
| 297 | |
| 298 bool FillPasswordFormFromKeychainItem(const AppleKeychain& keychain, | |
| 299 SecKeychainItemRef keychain_item, | |
| 300 PasswordForm* form, | |
| 301 bool extract_password_data) { | |
| 302 DCHECK(form); | |
| 303 | |
| 304 SecKeychainAttributeInfo attrInfo; | |
| 305 UInt32 tags[] = { kSecAccountItemAttr, | |
| 306 kSecServerItemAttr, | |
| 307 kSecPortItemAttr, | |
| 308 kSecPathItemAttr, | |
| 309 kSecProtocolItemAttr, | |
| 310 kSecAuthenticationTypeItemAttr, | |
| 311 kSecSecurityDomainItemAttr, | |
| 312 kSecCreationDateItemAttr, | |
| 313 kSecNegativeItemAttr }; | |
| 314 attrInfo.count = arraysize(tags); | |
| 315 attrInfo.tag = tags; | |
| 316 attrInfo.format = NULL; | |
| 317 | |
| 318 SecKeychainAttributeList* attrList; | |
| 319 UInt32 password_length; | |
| 320 | |
| 321 // If |extract_password_data| is false, do not pass in a reference to | |
| 322 // |password_data|. ItemCopyAttributesAndData will then extract only the | |
| 323 // attributes of |keychain_item| (doesn't require OS authorization), and not | |
| 324 // attempt to extract its password data (requires OS authorization). | |
| 325 void* password_data = NULL; | |
| 326 void** password_data_ref = extract_password_data ? &password_data : NULL; | |
| 327 | |
| 328 OSStatus result = keychain.ItemCopyAttributesAndData(keychain_item, &attrInfo, | |
| 329 NULL, &attrList, | |
| 330 &password_length, | |
| 331 password_data_ref); | |
| 332 | |
| 333 if (result != noErr) { | |
| 334 // We don't log errSecAuthFailed because that just means that the user | |
| 335 // chose not to allow us access to the item. | |
| 336 if (result != errSecAuthFailed) { | |
| 337 OSSTATUS_LOG(ERROR, result) << "Keychain data load failed"; | |
| 338 } | |
| 339 return false; | |
| 340 } | |
| 341 | |
| 342 if (extract_password_data) { | |
| 343 base::UTF8ToUTF16(static_cast<const char *>(password_data), password_length, | |
| 344 &(form->password_value)); | |
| 345 } | |
| 346 | |
| 347 int port = kAnyPort; | |
| 348 std::string server; | |
| 349 std::string security_domain; | |
| 350 std::string path; | |
| 351 bool is_secure = false; | |
| 352 for (unsigned int i = 0; i < attrList->count; i++) { | |
| 353 SecKeychainAttribute attr = attrList->attr[i]; | |
| 354 if (!attr.data) { | |
| 355 continue; | |
| 356 } | |
| 357 switch (attr.tag) { | |
| 358 case kSecAccountItemAttr: | |
| 359 base::UTF8ToUTF16(static_cast<const char *>(attr.data), attr.length, | |
| 360 &(form->username_value)); | |
| 361 break; | |
| 362 case kSecServerItemAttr: | |
| 363 server.assign(static_cast<const char *>(attr.data), attr.length); | |
| 364 break; | |
| 365 case kSecPortItemAttr: | |
| 366 port = *(static_cast<UInt32*>(attr.data)); | |
| 367 break; | |
| 368 case kSecPathItemAttr: | |
| 369 path.assign(static_cast<const char *>(attr.data), attr.length); | |
| 370 break; | |
| 371 case kSecProtocolItemAttr: | |
| 372 { | |
| 373 SecProtocolType protocol = *(static_cast<SecProtocolType*>(attr.data)); | |
| 374 // TODO(stuartmorgan): Handle proxy types | |
| 375 is_secure = (protocol == kSecProtocolTypeHTTPS); | |
| 376 break; | |
| 377 } | |
| 378 case kSecAuthenticationTypeItemAttr: | |
| 379 { | |
| 380 SecAuthenticationType auth_type = | |
| 381 *(static_cast<SecAuthenticationType*>(attr.data)); | |
| 382 form->scheme = SchemeForAuthType(auth_type); | |
| 383 break; | |
| 384 } | |
| 385 case kSecSecurityDomainItemAttr: | |
| 386 security_domain.assign(static_cast<const char *>(attr.data), | |
| 387 attr.length); | |
| 388 break; | |
| 389 case kSecCreationDateItemAttr: | |
| 390 // The only way to get a date out of Keychain is as a string. Really. | |
| 391 // (The docs claim it's an int, but the header is correct.) | |
| 392 TimeFromKeychainTimeString(static_cast<char*>(attr.data), attr.length, | |
| 393 &form->date_created); | |
| 394 break; | |
| 395 case kSecNegativeItemAttr: | |
| 396 Boolean negative_item = *(static_cast<Boolean*>(attr.data)); | |
| 397 if (negative_item) { | |
| 398 form->blacklisted_by_user = true; | |
| 399 } | |
| 400 break; | |
| 401 } | |
| 402 } | |
| 403 keychain.ItemFreeAttributesAndData(attrList, password_data); | |
| 404 | |
| 405 // kSecNegativeItemAttr doesn't seem to actually be in widespread use. In | |
| 406 // practice, other browsers seem to use a "" or " " password (and a special | |
| 407 // user name) to indicated blacklist entries. | |
| 408 if (extract_password_data && (form->password_value.empty() || | |
| 409 base::EqualsASCII(form->password_value, " "))) { | |
| 410 form->blacklisted_by_user = true; | |
| 411 } | |
| 412 | |
| 413 // Android facet URLs aren't parsed correctly by GURL and need to be handled | |
| 414 // separately. | |
| 415 if (password_manager::IsValidAndroidFacetURI(server)) { | |
| 416 form->signon_realm = server; | |
| 417 form->origin = GURL(); | |
| 418 } else { | |
| 419 form->origin = URLFromComponents(is_secure, server, port, path); | |
| 420 // TODO(stuartmorgan): Handle proxies, which need a different signon_realm | |
| 421 // format. | |
| 422 form->signon_realm = form->origin.GetOrigin().spec(); | |
| 423 if (form->scheme != PasswordForm::SCHEME_HTML) { | |
| 424 form->signon_realm.append(security_domain); | |
| 425 } | |
| 426 } | |
| 427 return true; | |
| 428 } | |
| 429 | |
| 430 bool HasChromeCreatorCode(const AppleKeychain& keychain, | |
| 431 SecKeychainItemRef keychain_item) { | |
| 432 SecKeychainAttributeInfo attr_info; | |
| 433 UInt32 tags[] = {kSecCreatorItemAttr}; | |
| 434 attr_info.count = arraysize(tags); | |
| 435 attr_info.tag = tags; | |
| 436 attr_info.format = nullptr; | |
| 437 | |
| 438 SecKeychainAttributeList* attr_list; | |
| 439 UInt32 password_length; | |
| 440 OSStatus result = keychain.ItemCopyAttributesAndData( | |
| 441 keychain_item, &attr_info, nullptr, &attr_list, | |
| 442 &password_length, nullptr); | |
| 443 if (result != noErr) | |
| 444 return false; | |
| 445 OSType creator_code = 0; | |
| 446 for (unsigned int i = 0; i < attr_list->count; i++) { | |
| 447 SecKeychainAttribute attr = attr_list->attr[i]; | |
| 448 if (!attr.data) { | |
| 449 continue; | |
| 450 } | |
| 451 if (attr.tag == kSecCreatorItemAttr) { | |
| 452 creator_code = *(static_cast<FourCharCode*>(attr.data)); | |
| 453 break; | |
| 454 } | |
| 455 } | |
| 456 keychain.ItemFreeAttributesAndData(attr_list, nullptr); | |
| 457 return creator_code && creator_code == base::mac::CreatorCodeForApplication(); | |
| 458 } | |
| 459 | |
| 460 bool FormsMatchForMerge(const PasswordForm& form_a, | |
| 461 const PasswordForm& form_b, | |
| 462 FormMatchStrictness strictness) { | |
| 463 if (IsLoginDatabaseOnlyForm(form_a) || IsLoginDatabaseOnlyForm(form_b)) | |
| 464 return false; | |
| 465 | |
| 466 bool equal_realm = form_a.signon_realm == form_b.signon_realm; | |
| 467 if (strictness == FUZZY_FORM_MATCH) { | |
| 468 equal_realm |= form_a.is_public_suffix_match; | |
| 469 } | |
| 470 return form_a.scheme == form_b.scheme && equal_realm && | |
| 471 form_a.username_value == form_b.username_value; | |
| 472 } | |
| 473 | |
| 474 // Moves entries from |forms| that represent either blacklisted or federated | |
| 475 // logins into |extracted|. These two types are stored only in the LoginDatabase | |
| 476 // and do not have corresponding Keychain entries. | |
| 477 void ExtractNonKeychainForms( | |
| 478 std::vector<std::unique_ptr<PasswordForm>>* forms, | |
| 479 std::vector<std::unique_ptr<PasswordForm>>* extracted) { | |
| 480 extracted->reserve(extracted->size() + forms->size()); | |
| 481 | |
| 482 std::vector<std::unique_ptr<PasswordForm>> remaining; | |
| 483 remaining.reserve(forms->size()); | |
| 484 | |
| 485 for (std::unique_ptr<PasswordForm>& form : *forms) { | |
| 486 if (IsLoginDatabaseOnlyForm(*form)) | |
| 487 extracted->push_back(std::move(form)); | |
| 488 else | |
| 489 remaining.push_back(std::move(form)); | |
| 490 } | |
| 491 forms->swap(remaining); | |
| 492 } | |
| 493 | |
| 494 // Takes |keychain_forms| and |database_forms| and moves the following 2 types | |
| 495 // of forms to |merged_forms|: | |
| 496 // (1) |database_forms| that by principle never have a corresponding Keychain | |
| 497 // entry (viz., blacklisted and federated logins), | |
| 498 // (2) |database_forms| which should have and do have a corresponding entry in | |
| 499 // |keychain_forms|. | |
| 500 // The database forms of type (2) have their password value updated from the | |
| 501 // corresponding keychain form, and all the keychain forms corresponding to some | |
| 502 // database form are removed from |keychain_forms| and deleted. | |
| 503 void MergePasswordForms( | |
| 504 std::vector<std::unique_ptr<PasswordForm>>* keychain_forms, | |
| 505 std::vector<std::unique_ptr<PasswordForm>>* database_forms, | |
| 506 std::vector<std::unique_ptr<PasswordForm>>* merged_forms) { | |
| 507 // Pull out the database blacklist items and federated logins, since they are | |
| 508 // used as-is rather than being merged with keychain forms. | |
| 509 ExtractNonKeychainForms(database_forms, merged_forms); | |
| 510 | |
| 511 // Merge the normal entries. | |
| 512 std::vector<std::unique_ptr<PasswordForm>> unused_database_forms; | |
| 513 unused_database_forms.reserve(database_forms->size()); | |
| 514 std::set<const PasswordForm*> used_keychain_forms; | |
| 515 // Move all database forms to either |merged_forms| or | |
| 516 // |unused_database_forms|, based on whether they have a match in the keychain | |
| 517 // forms or not. If there is a match, add its password to the DB form and | |
| 518 // mark the keychain form as used. | |
| 519 for (std::unique_ptr<PasswordForm>& db_form : *database_forms) { | |
| 520 const PasswordForm* best_match = | |
| 521 BestKeychainFormForForm(*db_form, *keychain_forms); | |
| 522 if (best_match) { | |
| 523 used_keychain_forms.insert(best_match); | |
| 524 db_form->password_value = best_match->password_value; | |
| 525 merged_forms->push_back(std::move(db_form)); | |
| 526 } else { | |
| 527 unused_database_forms.push_back(std::move(db_form)); | |
| 528 } | |
| 529 } | |
| 530 database_forms->swap(unused_database_forms); | |
| 531 | |
| 532 // Clear out all the Keychain entries we used. | |
| 533 std::vector<std::unique_ptr<PasswordForm>> unused_keychain_forms; | |
| 534 unused_keychain_forms.reserve(keychain_forms->size()); | |
| 535 for (std::unique_ptr<PasswordForm>& keychain_form : *keychain_forms) { | |
| 536 if (!base::ContainsKey(used_keychain_forms, keychain_form.get())) { | |
| 537 unused_keychain_forms.push_back(std::move(keychain_form)); | |
| 538 } | |
| 539 } | |
| 540 keychain_forms->swap(unused_keychain_forms); | |
| 541 } | |
| 542 | |
| 543 std::vector<ItemFormPair> ExtractAllKeychainItemAttributesIntoPasswordForms( | |
| 544 std::vector<SecKeychainItemRef>* keychain_items, | |
| 545 const AppleKeychain& keychain) { | |
| 546 DCHECK(keychain_items); | |
| 547 MacKeychainPasswordFormAdapter keychain_adapter(&keychain); | |
| 548 *keychain_items = keychain_adapter.GetAllPasswordFormKeychainItems(); | |
| 549 std::vector<ItemFormPair> item_form_pairs; | |
| 550 for (auto* keychain_item : *keychain_items) { | |
| 551 std::unique_ptr<PasswordForm> form_without_password = | |
| 552 base::MakeUnique<PasswordForm>(); | |
| 553 internal_keychain_helpers::FillPasswordFormFromKeychainItem( | |
| 554 keychain, keychain_item, form_without_password.get(), | |
| 555 false); // Load password attributes, but not password data. | |
| 556 item_form_pairs.push_back( | |
| 557 std::make_pair(keychain_item, std::move(form_without_password))); | |
| 558 } | |
| 559 return item_form_pairs; | |
| 560 } | |
| 561 | |
| 562 void GetPasswordsForForms( | |
| 563 const AppleKeychain& keychain, | |
| 564 std::vector<std::unique_ptr<PasswordForm>>* database_forms, | |
| 565 std::vector<std::unique_ptr<PasswordForm>>* passwords) { | |
| 566 // First load the attributes of all items in the keychain without loading | |
| 567 // their password data, and then match items in |database_forms| against them. | |
| 568 // This avoids individually searching through the keychain for passwords | |
| 569 // matching each form in |database_forms|, and results in a significant | |
| 570 // performance gain, replacing O(N) keychain search operations with a single | |
| 571 // operation that loads all keychain items, and then selective reads of only | |
| 572 // the relevant passwords. See crbug.com/263685. | |
| 573 std::vector<SecKeychainItemRef> keychain_items; | |
| 574 std::vector<ItemFormPair> item_form_pairs = | |
| 575 ExtractAllKeychainItemAttributesIntoPasswordForms(&keychain_items, | |
| 576 keychain); | |
| 577 | |
| 578 // Next, compare the attributes of the PasswordForms in |database_forms| | |
| 579 // against those in |item_form_pairs|, and extract password data for each | |
| 580 // matching PasswordForm using its corresponding SecKeychainItemRef. | |
| 581 std::vector<std::unique_ptr<PasswordForm>> unused_db_forms; | |
| 582 unused_db_forms.reserve(database_forms->size()); | |
| 583 // Move database forms with a password stored in |keychain| to |passwords|, | |
| 584 // including the password. The rest is moved to |unused_db_forms|. | |
| 585 for (std::unique_ptr<PasswordForm>& db_form : *database_forms) { | |
| 586 std::vector<std::unique_ptr<PasswordForm>> keychain_matches = | |
| 587 ExtractPasswordsMergeableWithForm(keychain, item_form_pairs, *db_form); | |
| 588 | |
| 589 std::vector<std::unique_ptr<PasswordForm>> db_form_container; | |
| 590 db_form_container.push_back(std::move(db_form)); | |
| 591 MergePasswordForms(&keychain_matches, &db_form_container, passwords); | |
| 592 AppendSecondToFirst(&unused_db_forms, &db_form_container); | |
| 593 } | |
| 594 database_forms->swap(unused_db_forms); | |
| 595 | |
| 596 for (SecKeychainItemRef item : keychain_items) { | |
| 597 keychain.Free(item); | |
| 598 } | |
| 599 } | |
| 600 | |
| 601 // TODO(stuartmorgan): signon_realm for proxies is not yet supported. | |
| 602 bool ExtractSignonRealmComponents(const std::string& signon_realm, | |
| 603 std::string* server, | |
| 604 UInt32* port, | |
| 605 bool* is_secure, | |
| 606 std::string* security_domain) { | |
| 607 // GURL does not parse Android facet URIs correctly. | |
| 608 if (password_manager::IsValidAndroidFacetURI(signon_realm)) { | |
| 609 if (server) | |
| 610 *server = signon_realm; | |
| 611 if (is_secure) | |
| 612 *is_secure = true; | |
| 613 if (port) | |
| 614 *port = 0; | |
| 615 if (security_domain) | |
| 616 security_domain->clear(); | |
| 617 return true; | |
| 618 } | |
| 619 | |
| 620 // The signon_realm will be the Origin portion of a URL for an HTML form, | |
| 621 // and the same but with the security domain as a path for HTTP auth. | |
| 622 GURL realm_as_url(signon_realm); | |
| 623 if (!realm_as_url.is_valid()) { | |
| 624 return false; | |
| 625 } | |
| 626 | |
| 627 if (server) | |
| 628 *server = realm_as_url.host(); | |
| 629 if (is_secure) | |
| 630 *is_secure = realm_as_url.SchemeIsCryptographic(); | |
| 631 if (port) | |
| 632 *port = realm_as_url.has_port() ? atoi(realm_as_url.port().c_str()) : 0; | |
| 633 if (security_domain) { | |
| 634 // Strip the leading '/' off of the path to get the security domain. | |
| 635 if (realm_as_url.path().length() > 0) | |
| 636 *security_domain = realm_as_url.path().substr(1); | |
| 637 else | |
| 638 security_domain->clear(); | |
| 639 } | |
| 640 return true; | |
| 641 } | |
| 642 | |
| 643 bool FormIsValidAndMatchesOtherForm(const PasswordForm& query_form, | |
| 644 const PasswordForm& other_form) { | |
| 645 std::string server; | |
| 646 std::string security_domain; | |
| 647 UInt32 port; | |
| 648 bool is_secure; | |
| 649 if (!ExtractSignonRealmComponents(query_form.signon_realm, &server, &port, | |
| 650 &is_secure, &security_domain)) { | |
| 651 return false; | |
| 652 } | |
| 653 return FormsMatchForMerge(query_form, other_form, STRICT_FORM_MATCH); | |
| 654 } | |
| 655 | |
| 656 std::vector<std::unique_ptr<PasswordForm>> ExtractPasswordsMergeableWithForm( | |
| 657 const AppleKeychain& keychain, | |
| 658 const std::vector<ItemFormPair>& item_form_pairs, | |
| 659 const PasswordForm& query_form) { | |
| 660 std::vector<std::unique_ptr<PasswordForm>> matches; | |
| 661 for (std::vector<ItemFormPair>::const_iterator i = item_form_pairs.begin(); | |
| 662 i != item_form_pairs.end(); ++i) { | |
| 663 if (FormIsValidAndMatchesOtherForm(query_form, *(i->second))) { | |
| 664 // Create a new object, since the caller is responsible for deleting the | |
| 665 // returned forms. | |
| 666 auto form_with_password = base::MakeUnique<PasswordForm>(); | |
| 667 FillPasswordFormFromKeychainItem( | |
| 668 keychain, i->first, form_with_password.get(), | |
| 669 true); // Load password attributes and data. | |
| 670 // Do not include blacklisted items found in the keychain. | |
| 671 if (!form_with_password->blacklisted_by_user) | |
| 672 matches.push_back(std::move(form_with_password)); | |
| 673 } | |
| 674 } | |
| 675 return matches; | |
| 676 } | |
| 677 | |
| 678 } // namespace internal_keychain_helpers | |
| 679 | |
| 680 #pragma mark - | |
| 681 | |
| 682 MacKeychainPasswordFormAdapter::MacKeychainPasswordFormAdapter( | |
| 683 const AppleKeychain* keychain) | |
| 684 : keychain_(keychain), finds_only_owned_(false) { | |
| 685 } | |
| 686 | |
| 687 std::vector<std::unique_ptr<PasswordForm>> | |
| 688 MacKeychainPasswordFormAdapter::PasswordsFillingForm( | |
| 689 const std::string& signon_realm, | |
| 690 PasswordForm::Scheme scheme) { | |
| 691 std::vector<SecKeychainItemRef> keychain_items = | |
| 692 MatchingKeychainItems(signon_realm, scheme, NULL, NULL); | |
| 693 return ConvertKeychainItemsToForms(&keychain_items); | |
| 694 } | |
| 695 | |
| 696 bool MacKeychainPasswordFormAdapter::HasPasswordExactlyMatchingForm( | |
| 697 const PasswordForm& query_form) { | |
| 698 SecKeychainItemRef keychain_item = KeychainItemForForm(query_form); | |
| 699 if (keychain_item) { | |
| 700 keychain_->Free(keychain_item); | |
| 701 return true; | |
| 702 } | |
| 703 return false; | |
| 704 } | |
| 705 | |
| 706 bool MacKeychainPasswordFormAdapter::HasPasswordsMergeableWithForm( | |
| 707 const PasswordForm& query_form) { | |
| 708 if (IsLoginDatabaseOnlyForm(query_form)) | |
| 709 return false; | |
| 710 std::string username = base::UTF16ToUTF8(query_form.username_value); | |
| 711 std::vector<SecKeychainItemRef> matches = | |
| 712 MatchingKeychainItems(query_form.signon_realm, query_form.scheme, | |
| 713 NULL, username.c_str()); | |
| 714 for (SecKeychainItemRef item : matches) | |
| 715 keychain_->Free(item); | |
| 716 | |
| 717 return !matches.empty(); | |
| 718 } | |
| 719 | |
| 720 std::vector<SecKeychainItemRef> | |
| 721 MacKeychainPasswordFormAdapter::GetAllPasswordFormKeychainItems() { | |
| 722 SecAuthenticationType supported_auth_types[] = { | |
| 723 kSecAuthenticationTypeHTMLForm, | |
| 724 kSecAuthenticationTypeHTTPBasic, | |
| 725 kSecAuthenticationTypeHTTPDigest, | |
| 726 }; | |
| 727 | |
| 728 std::vector<SecKeychainItemRef> matches; | |
| 729 for (unsigned int i = 0; i < arraysize(supported_auth_types); ++i) { | |
| 730 KeychainSearch keychain_search(*keychain_); | |
| 731 OSType creator = CreatorCodeForSearch(); | |
| 732 keychain_search.Init(NULL, | |
| 733 NULL, | |
| 734 NULL, | |
| 735 &supported_auth_types[i], | |
| 736 NULL, | |
| 737 NULL, | |
| 738 NULL, | |
| 739 creator ? &creator : NULL); | |
| 740 keychain_search.FindMatchingItems(&matches); | |
| 741 } | |
| 742 return matches; | |
| 743 } | |
| 744 | |
| 745 std::vector<std::unique_ptr<PasswordForm>> | |
| 746 MacKeychainPasswordFormAdapter::GetAllPasswordFormPasswords() { | |
| 747 std::vector<SecKeychainItemRef> items = GetAllPasswordFormKeychainItems(); | |
| 748 return ConvertKeychainItemsToForms(&items); | |
| 749 } | |
| 750 | |
| 751 bool MacKeychainPasswordFormAdapter::AddPassword(const PasswordForm& form) { | |
| 752 // We should never be trying to store a blacklist in the keychain. | |
| 753 DCHECK(!IsLoginDatabaseOnlyForm(form)); | |
| 754 | |
| 755 std::string server; | |
| 756 std::string security_domain; | |
| 757 UInt32 port; | |
| 758 bool is_secure; | |
| 759 if (!internal_keychain_helpers::ExtractSignonRealmComponents( | |
| 760 form.signon_realm, &server, &port, &is_secure, &security_domain)) { | |
| 761 return false; | |
| 762 } | |
| 763 std::string path; | |
| 764 // Path doesn't make sense for Android app credentials. | |
| 765 if (!password_manager::IsValidAndroidFacetURI(form.signon_realm)) | |
| 766 path = form.origin.path(); | |
| 767 std::string username = base::UTF16ToUTF8(form.username_value); | |
| 768 std::string password = base::UTF16ToUTF8(form.password_value); | |
| 769 SecProtocolType protocol = is_secure ? kSecProtocolTypeHTTPS | |
| 770 : kSecProtocolTypeHTTP; | |
| 771 SecKeychainItemRef new_item = NULL; | |
| 772 OSStatus result = keychain_->AddInternetPassword( | |
| 773 NULL, server.size(), server.c_str(), | |
| 774 security_domain.size(), security_domain.c_str(), | |
| 775 username.size(), username.c_str(), | |
| 776 path.size(), path.c_str(), | |
| 777 port, protocol, AuthTypeForScheme(form.scheme), | |
| 778 password.size(), password.c_str(), &new_item); | |
| 779 | |
| 780 if (result == noErr) { | |
| 781 SetKeychainItemCreatorCode(new_item, | |
| 782 base::mac::CreatorCodeForApplication()); | |
| 783 keychain_->Free(new_item); | |
| 784 } else if (result == errSecDuplicateItem) { | |
| 785 // If we collide with an existing item, find and update it instead. | |
| 786 SecKeychainItemRef existing_item = KeychainItemForForm(form); | |
| 787 if (!existing_item) { | |
| 788 return false; | |
| 789 } | |
| 790 bool changed = SetKeychainItemPassword(existing_item, password); | |
| 791 keychain_->Free(existing_item); | |
| 792 return changed; | |
| 793 } | |
| 794 | |
| 795 return result == noErr; | |
| 796 } | |
| 797 | |
| 798 bool MacKeychainPasswordFormAdapter::RemovePassword(const PasswordForm& form) { | |
| 799 SecKeychainItemRef keychain_item = KeychainItemForForm(form); | |
| 800 if (keychain_item == NULL) | |
| 801 return false; | |
| 802 OSStatus result = keychain_->ItemDelete(keychain_item); | |
| 803 keychain_->Free(keychain_item); | |
| 804 return result == noErr; | |
| 805 } | |
| 806 | |
| 807 void MacKeychainPasswordFormAdapter::SetFindsOnlyOwnedItems( | |
| 808 bool finds_only_owned) { | |
| 809 finds_only_owned_ = finds_only_owned; | |
| 810 } | |
| 811 | |
| 812 std::vector<std::unique_ptr<PasswordForm>> | |
| 813 MacKeychainPasswordFormAdapter::ConvertKeychainItemsToForms( | |
| 814 std::vector<SecKeychainItemRef>* items) { | |
| 815 std::vector<std::unique_ptr<PasswordForm>> forms; | |
| 816 for (SecKeychainItemRef item : *items) { | |
| 817 auto form = base::MakeUnique<PasswordForm>(); | |
| 818 if (internal_keychain_helpers::FillPasswordFormFromKeychainItem( | |
| 819 *keychain_, item, form.get(), true)) { | |
| 820 forms.push_back(std::move(form)); | |
| 821 } | |
| 822 keychain_->Free(item); | |
| 823 } | |
| 824 items->clear(); | |
| 825 return forms; | |
| 826 } | |
| 827 | |
| 828 SecKeychainItemRef MacKeychainPasswordFormAdapter::KeychainItemForForm( | |
| 829 const PasswordForm& form) { | |
| 830 // We don't store blacklist entries in the keychain, so the answer to "what | |
| 831 // Keychain item goes with this form" is always "nothing" for blacklists. | |
| 832 // Same goes for federated logins. | |
| 833 if (IsLoginDatabaseOnlyForm(form)) | |
| 834 return NULL; | |
| 835 | |
| 836 std::string path; | |
| 837 // Path doesn't make sense for Android app credentials. | |
| 838 if (!password_manager::IsValidAndroidFacetURI(form.signon_realm)) | |
| 839 path = form.origin.path(); | |
| 840 std::string username = base::UTF16ToUTF8(form.username_value); | |
| 841 std::vector<SecKeychainItemRef> matches = MatchingKeychainItems( | |
| 842 form.signon_realm, form.scheme, path.c_str(), username.c_str()); | |
| 843 | |
| 844 if (matches.empty()) { | |
| 845 return NULL; | |
| 846 } | |
| 847 | |
| 848 // Free all items after the first, since we won't be returning them. | |
| 849 for (auto i = matches.begin() + 1; i != matches.end(); ++i) | |
| 850 keychain_->Free(*i); | |
| 851 | |
| 852 return matches[0]; | |
| 853 } | |
| 854 | |
| 855 std::vector<SecKeychainItemRef> | |
| 856 MacKeychainPasswordFormAdapter::MatchingKeychainItems( | |
| 857 const std::string& signon_realm, | |
| 858 PasswordForm::Scheme scheme, | |
| 859 const char* path, | |
| 860 const char* username) { | |
| 861 std::vector<SecKeychainItemRef> matches; | |
| 862 | |
| 863 std::string server; | |
| 864 std::string security_domain; | |
| 865 UInt32 port; | |
| 866 bool is_secure; | |
| 867 if (!internal_keychain_helpers::ExtractSignonRealmComponents( | |
| 868 signon_realm, &server, &port, &is_secure, &security_domain)) { | |
| 869 // TODO(stuartmorgan): Proxies will currently fail here, since their | |
| 870 // signon_realm is not a URL. We need to detect the proxy case and handle | |
| 871 // it specially. | |
| 872 return matches; | |
| 873 } | |
| 874 SecProtocolType protocol = is_secure ? kSecProtocolTypeHTTPS | |
| 875 : kSecProtocolTypeHTTP; | |
| 876 SecAuthenticationType auth_type = AuthTypeForScheme(scheme); | |
| 877 const char* auth_domain = (scheme == PasswordForm::SCHEME_HTML) ? | |
| 878 NULL : security_domain.c_str(); | |
| 879 OSType creator = CreatorCodeForSearch(); | |
| 880 KeychainSearch keychain_search(*keychain_); | |
| 881 keychain_search.Init(server.c_str(), | |
| 882 &port, | |
| 883 &protocol, | |
| 884 &auth_type, | |
| 885 auth_domain, | |
| 886 path, | |
| 887 username, | |
| 888 creator ? &creator : NULL); | |
| 889 keychain_search.FindMatchingItems(&matches); | |
| 890 return matches; | |
| 891 } | |
| 892 | |
| 893 // Returns the Keychain SecAuthenticationType type corresponding to |scheme|. | |
| 894 SecAuthenticationType MacKeychainPasswordFormAdapter::AuthTypeForScheme( | |
| 895 PasswordForm::Scheme scheme) { | |
| 896 switch (scheme) { | |
| 897 case PasswordForm::SCHEME_HTML: return kSecAuthenticationTypeHTMLForm; | |
| 898 case PasswordForm::SCHEME_BASIC: return kSecAuthenticationTypeHTTPBasic; | |
| 899 case PasswordForm::SCHEME_DIGEST: return kSecAuthenticationTypeHTTPDigest; | |
| 900 case PasswordForm::SCHEME_OTHER: return kSecAuthenticationTypeDefault; | |
| 901 case PasswordForm::SCHEME_USERNAME_ONLY: | |
| 902 NOTREACHED(); | |
| 903 break; | |
| 904 } | |
| 905 NOTREACHED(); | |
| 906 return kSecAuthenticationTypeDefault; | |
| 907 } | |
| 908 | |
| 909 bool MacKeychainPasswordFormAdapter::SetKeychainItemPassword( | |
| 910 SecKeychainItemRef keychain_item, | |
| 911 const std::string& password) { | |
| 912 OSStatus result = keychain_->ItemModifyAttributesAndData(keychain_item, NULL, | |
| 913 password.size(), | |
| 914 password.c_str()); | |
| 915 return result == noErr; | |
| 916 } | |
| 917 | |
| 918 bool MacKeychainPasswordFormAdapter::SetKeychainItemCreatorCode( | |
| 919 SecKeychainItemRef keychain_item, | |
| 920 OSType creator_code) { | |
| 921 SecKeychainAttribute attr = { kSecCreatorItemAttr, sizeof(creator_code), | |
| 922 &creator_code }; | |
| 923 SecKeychainAttributeList attrList = { 1, &attr }; | |
| 924 OSStatus result = keychain_->ItemModifyAttributesAndData(keychain_item, | |
| 925 &attrList, 0, NULL); | |
| 926 return result == noErr; | |
| 927 } | |
| 928 | |
| 929 OSType MacKeychainPasswordFormAdapter::CreatorCodeForSearch() { | |
| 930 return finds_only_owned_ ? base::mac::CreatorCodeForApplication() : 0; | |
| 931 } | |
| 932 | |
| 933 #pragma mark - | |
| 934 | |
| 935 PasswordStoreMac::PasswordStoreMac( | |
| 936 scoped_refptr<base::SingleThreadTaskRunner> main_thread_runner, | |
| 937 scoped_refptr<base::SingleThreadTaskRunner> db_thread_runner, | |
| 938 std::unique_ptr<AppleKeychain> keychain) | |
| 939 : password_manager::PasswordStore(main_thread_runner, db_thread_runner), | |
| 940 keychain_(std::move(keychain)), | |
| 941 login_metadata_db_(nullptr) { | |
| 942 DCHECK(keychain_); | |
| 943 } | |
| 944 | |
| 945 PasswordStoreMac::~PasswordStoreMac() {} | |
| 946 | |
| 947 void PasswordStoreMac::InitWithTaskRunner( | |
| 948 scoped_refptr<base::SingleThreadTaskRunner> background_task_runner) { | |
| 949 db_thread_runner_ = background_task_runner; | |
| 950 DCHECK(GetBackgroundTaskRunner()->BelongsToCurrentThread()); | |
| 951 } | |
| 952 | |
| 953 // static | |
| 954 PasswordStoreMac::MigrationResult PasswordStoreMac::ImportFromKeychain( | |
| 955 password_manager::LoginDatabase* login_db, | |
| 956 crypto::AppleKeychain* keychain) { | |
| 957 std::vector<std::unique_ptr<PasswordForm>> database_forms; | |
| 958 if (!login_db->GetAutofillableLogins(&database_forms)) | |
| 959 return LOGIN_DB_FAILURE; | |
| 960 | |
| 961 std::vector<std::unique_ptr<PasswordForm>> uninteresting_forms; | |
| 962 internal_keychain_helpers::ExtractNonKeychainForms(&database_forms, | |
| 963 &uninteresting_forms); | |
| 964 // If there are no passwords to lookup in the Keychain then we're done. | |
| 965 if (database_forms.empty()) | |
| 966 return MIGRATION_OK; | |
| 967 | |
| 968 // Make sure that the encryption key is retrieved from the Keychain so the | |
| 969 // encryption can be done. | |
| 970 std::string ciphertext; | |
| 971 if (!OSCrypt::EncryptString("test", &ciphertext)) | |
| 972 return ENCRYPTOR_FAILURE; | |
| 973 | |
| 974 // Mute the Keychain. | |
| 975 chrome::ScopedSecKeychainSetUserInteractionAllowed user_interaction_allowed( | |
| 976 false); | |
| 977 | |
| 978 // Retrieve the passwords. | |
| 979 // Get all the password attributes first. | |
| 980 std::vector<SecKeychainItemRef> keychain_items; | |
| 981 std::vector<internal_keychain_helpers::ItemFormPair> item_form_pairs = | |
| 982 internal_keychain_helpers:: | |
| 983 ExtractAllKeychainItemAttributesIntoPasswordForms(&keychain_items, | |
| 984 *keychain); | |
| 985 | |
| 986 // Next, compare the attributes of the PasswordForms in |database_forms| | |
| 987 // against those in |item_form_pairs|, and extract password data for each | |
| 988 // matching PasswordForm using its corresponding SecKeychainItemRef. | |
| 989 size_t unmerged_forms_count = 0; | |
| 990 login_db->set_clear_password_values(false); | |
| 991 for (const auto& form : database_forms) { | |
| 992 std::vector<std::unique_ptr<PasswordForm>> keychain_matches = | |
| 993 internal_keychain_helpers::ExtractPasswordsMergeableWithForm( | |
| 994 *keychain, item_form_pairs, *form); | |
| 995 | |
| 996 const PasswordForm* best_match = | |
| 997 BestKeychainFormForForm(*form, keychain_matches); | |
| 998 if (best_match) { | |
| 999 form->password_value = best_match->password_value; | |
| 1000 PasswordStoreChangeList change = login_db->UpdateLogin(*form); | |
| 1001 DCHECK_EQ(1u, change.size()); | |
| 1002 } else { | |
| 1003 unmerged_forms_count++; | |
| 1004 bool removed = login_db->RemoveLogin(*form); | |
| 1005 DCHECK(removed); | |
| 1006 } | |
| 1007 } | |
| 1008 for (SecKeychainItemRef item : keychain_items) | |
| 1009 keychain->Free(item); | |
| 1010 | |
| 1011 if (unmerged_forms_count) { | |
| 1012 UMA_HISTOGRAM_COUNTS( | |
| 1013 "PasswordManager.KeychainMigration.NumPasswordsOnFailure", | |
| 1014 database_forms.size()); | |
| 1015 UMA_HISTOGRAM_COUNTS("PasswordManager.KeychainMigration.NumFailedPasswords", | |
| 1016 unmerged_forms_count); | |
| 1017 return MIGRATION_PARTIAL; | |
| 1018 } | |
| 1019 return MIGRATION_OK; | |
| 1020 } | |
| 1021 | |
| 1022 // static | |
| 1023 void PasswordStoreMac::CleanUpKeychain( | |
| 1024 crypto::AppleKeychain* keychain, | |
| 1025 const std::vector<std::unique_ptr<PasswordForm>>& forms) { | |
| 1026 MacKeychainPasswordFormAdapter keychain_adapter(keychain); | |
| 1027 keychain_adapter.SetFindsOnlyOwnedItems(true); | |
| 1028 for (const auto& form : forms) | |
| 1029 keychain_adapter.RemovePassword(*form); | |
| 1030 } | |
| 1031 | |
| 1032 void PasswordStoreMac::set_login_metadata_db( | |
| 1033 password_manager::LoginDatabase* login_db) { | |
| 1034 login_metadata_db_ = login_db; | |
| 1035 if (login_metadata_db_) | |
| 1036 login_metadata_db_->set_clear_password_values(true); | |
| 1037 } | |
| 1038 | |
| 1039 bool PasswordStoreMac::Init( | |
| 1040 const syncer::SyncableService::StartSyncFlare& flare, | |
| 1041 PrefService* prefs) { | |
| 1042 // The class should be used inside PasswordStoreProxyMac only. | |
| 1043 NOTREACHED(); | |
| 1044 return true; | |
| 1045 } | |
| 1046 | |
| 1047 void PasswordStoreMac::ReportMetricsImpl(const std::string& sync_username, | |
| 1048 bool custom_passphrase_sync_enabled) { | |
| 1049 if (!login_metadata_db_) | |
| 1050 return; | |
| 1051 login_metadata_db_->ReportMetrics(sync_username, | |
| 1052 custom_passphrase_sync_enabled); | |
| 1053 } | |
| 1054 | |
| 1055 PasswordStoreChangeList PasswordStoreMac::AddLoginImpl( | |
| 1056 const PasswordForm& form) { | |
| 1057 DCHECK(GetBackgroundTaskRunner()->BelongsToCurrentThread()); | |
| 1058 if (login_metadata_db_ && AddToKeychainIfNecessary(form)) | |
| 1059 return login_metadata_db_->AddLogin(form); | |
| 1060 return PasswordStoreChangeList(); | |
| 1061 } | |
| 1062 | |
| 1063 PasswordStoreChangeList PasswordStoreMac::UpdateLoginImpl( | |
| 1064 const PasswordForm& form) { | |
| 1065 DCHECK(GetBackgroundTaskRunner()->BelongsToCurrentThread()); | |
| 1066 if (!login_metadata_db_) | |
| 1067 return PasswordStoreChangeList(); | |
| 1068 | |
| 1069 PasswordStoreChangeList changes = login_metadata_db_->UpdateLogin(form); | |
| 1070 | |
| 1071 MacKeychainPasswordFormAdapter keychain_adapter(keychain_.get()); | |
| 1072 if (changes.empty() && | |
| 1073 !keychain_adapter.HasPasswordsMergeableWithForm(form)) { | |
| 1074 // If the password isn't in either the DB or the keychain, then it must have | |
| 1075 // been deleted after autofill happened, and should not be re-added. | |
| 1076 return changes; | |
| 1077 } | |
| 1078 | |
| 1079 // The keychain add will update if there is a collision and add if there | |
| 1080 // isn't, which is the behavior we want, so there's no separate update call. | |
| 1081 if (AddToKeychainIfNecessary(form) && changes.empty()) { | |
| 1082 changes = login_metadata_db_->AddLogin(form); | |
| 1083 } | |
| 1084 return changes; | |
| 1085 } | |
| 1086 | |
| 1087 PasswordStoreChangeList PasswordStoreMac::RemoveLoginImpl( | |
| 1088 const PasswordForm& form) { | |
| 1089 DCHECK(GetBackgroundTaskRunner()->BelongsToCurrentThread()); | |
| 1090 PasswordStoreChangeList changes; | |
| 1091 if (login_metadata_db_ && login_metadata_db_->RemoveLogin(form)) { | |
| 1092 // See if we own a Keychain item associated with this item. We can do an | |
| 1093 // exact search rather than messing around with trying to do fuzzy matching | |
| 1094 // because passwords that we created will always have an exact-match | |
| 1095 // database entry. | |
| 1096 // (If a user does lose their profile but not their keychain we'll treat the | |
| 1097 // entries we find like other imported entries anyway, so it's reasonable to | |
| 1098 // handle deletes on them the way we would for an imported item.) | |
| 1099 MacKeychainPasswordFormAdapter owned_keychain_adapter(keychain_.get()); | |
| 1100 owned_keychain_adapter.SetFindsOnlyOwnedItems(true); | |
| 1101 if (owned_keychain_adapter.HasPasswordExactlyMatchingForm(form)) { | |
| 1102 // If we don't have other forms using it (i.e., a form differing only by | |
| 1103 // the names of the form elements), delete the keychain entry. | |
| 1104 if (!DatabaseHasFormMatchingKeychainForm(form)) { | |
| 1105 owned_keychain_adapter.RemovePassword(form); | |
| 1106 } | |
| 1107 } | |
| 1108 | |
| 1109 changes.push_back(PasswordStoreChange(PasswordStoreChange::REMOVE, form)); | |
| 1110 } | |
| 1111 return changes; | |
| 1112 } | |
| 1113 | |
| 1114 PasswordStoreChangeList PasswordStoreMac::RemoveLoginsByURLAndTimeImpl( | |
| 1115 const base::Callback<bool(const GURL&)>& url_filter, | |
| 1116 base::Time delete_begin, | |
| 1117 base::Time delete_end) { | |
| 1118 PasswordStoreChangeList changes; | |
| 1119 std::vector<std::unique_ptr<PasswordForm>> forms_to_consider; | |
| 1120 std::vector<std::unique_ptr<PasswordForm>> forms_to_remove; | |
| 1121 if (login_metadata_db_ && | |
| 1122 login_metadata_db_->GetLoginsCreatedBetween(delete_begin, delete_end, | |
| 1123 &forms_to_consider)) { | |
| 1124 for (std::unique_ptr<PasswordForm>& form_to_consider : forms_to_consider) { | |
| 1125 if (url_filter.Run(form_to_consider->origin) && | |
| 1126 login_metadata_db_->RemoveLogin(*form_to_consider)) | |
| 1127 forms_to_remove.push_back(std::move(form_to_consider)); | |
| 1128 } | |
| 1129 if (!forms_to_remove.empty()) { | |
| 1130 RemoveKeychainForms(forms_to_remove); | |
| 1131 CleanOrphanedForms(&forms_to_remove); // Add the orphaned forms. | |
| 1132 changes = FormsToRemoveChangeList(forms_to_remove); | |
| 1133 LogStatsForBulkDeletion(changes.size()); | |
| 1134 } | |
| 1135 } | |
| 1136 return changes; | |
| 1137 } | |
| 1138 | |
| 1139 PasswordStoreChangeList PasswordStoreMac::RemoveLoginsCreatedBetweenImpl( | |
| 1140 base::Time delete_begin, | |
| 1141 base::Time delete_end) { | |
| 1142 PasswordStoreChangeList changes; | |
| 1143 std::vector<std::unique_ptr<PasswordForm>> forms_to_remove; | |
| 1144 if (login_metadata_db_ && | |
| 1145 login_metadata_db_->GetLoginsCreatedBetween(delete_begin, delete_end, | |
| 1146 &forms_to_remove) && | |
| 1147 login_metadata_db_->RemoveLoginsCreatedBetween(delete_begin, | |
| 1148 delete_end)) { | |
| 1149 RemoveKeychainForms(forms_to_remove); | |
| 1150 CleanOrphanedForms(&forms_to_remove); // Add the orphaned forms. | |
| 1151 changes = FormsToRemoveChangeList(forms_to_remove); | |
| 1152 LogStatsForBulkDeletion(changes.size()); | |
| 1153 } | |
| 1154 return changes; | |
| 1155 } | |
| 1156 | |
| 1157 PasswordStoreChangeList PasswordStoreMac::RemoveLoginsSyncedBetweenImpl( | |
| 1158 base::Time delete_begin, | |
| 1159 base::Time delete_end) { | |
| 1160 PasswordStoreChangeList changes; | |
| 1161 std::vector<std::unique_ptr<PasswordForm>> forms_to_remove; | |
| 1162 if (login_metadata_db_ && | |
| 1163 login_metadata_db_->GetLoginsSyncedBetween(delete_begin, delete_end, | |
| 1164 &forms_to_remove) && | |
| 1165 login_metadata_db_->RemoveLoginsSyncedBetween(delete_begin, delete_end)) { | |
| 1166 RemoveKeychainForms(forms_to_remove); | |
| 1167 CleanOrphanedForms(&forms_to_remove); // Add the orphaned forms_to_remove. | |
| 1168 changes = FormsToRemoveChangeList(forms_to_remove); | |
| 1169 LogStatsForBulkDeletionDuringRollback(changes.size()); | |
| 1170 } | |
| 1171 return changes; | |
| 1172 } | |
| 1173 | |
| 1174 PasswordStoreChangeList PasswordStoreMac::DisableAutoSignInForOriginsImpl( | |
| 1175 const base::Callback<bool(const GURL&)>& origin_filter) { | |
| 1176 std::vector<std::unique_ptr<PasswordForm>> forms; | |
| 1177 PasswordStoreChangeList changes; | |
| 1178 if (!login_metadata_db_ || | |
| 1179 !login_metadata_db_->GetAutoSignInLogins(&forms)) { | |
| 1180 return changes; | |
| 1181 } | |
| 1182 | |
| 1183 std::set<GURL> origins_to_update; | |
| 1184 for (const auto& form : forms) { | |
| 1185 if (origin_filter.Run(form->origin)) | |
| 1186 origins_to_update.insert(form->origin); | |
| 1187 } | |
| 1188 | |
| 1189 std::set<GURL> origins_updated; | |
| 1190 for (const GURL& origin : origins_to_update) { | |
| 1191 if (login_metadata_db_->DisableAutoSignInForOrigin(origin)) | |
| 1192 origins_updated.insert(origin); | |
| 1193 } | |
| 1194 | |
| 1195 for (const auto& form : forms) { | |
| 1196 if (origins_updated.count(form->origin)) { | |
| 1197 changes.push_back( | |
| 1198 PasswordStoreChange(PasswordStoreChange::UPDATE, *form)); | |
| 1199 } | |
| 1200 } | |
| 1201 | |
| 1202 return changes; | |
| 1203 } | |
| 1204 | |
| 1205 bool PasswordStoreMac::RemoveStatisticsByOriginAndTimeImpl( | |
| 1206 const base::Callback<bool(const GURL&)>& origin_filter, | |
| 1207 base::Time delete_begin, | |
| 1208 base::Time delete_end) { | |
| 1209 return login_metadata_db_ && | |
| 1210 login_metadata_db_->stats_table().RemoveStatsByOriginAndTime( | |
| 1211 origin_filter, delete_begin, delete_end); | |
| 1212 } | |
| 1213 | |
| 1214 std::vector<std::unique_ptr<PasswordForm>> PasswordStoreMac::FillMatchingLogins( | |
| 1215 const FormDigest& form) { | |
| 1216 chrome::ScopedSecKeychainSetUserInteractionAllowed user_interaction_allowed( | |
| 1217 false); | |
| 1218 | |
| 1219 std::vector<std::unique_ptr<PasswordForm>> database_forms; | |
| 1220 if (!login_metadata_db_ || | |
| 1221 !login_metadata_db_->GetLogins(form, &database_forms)) { | |
| 1222 return std::vector<std::unique_ptr<PasswordForm>>(); | |
| 1223 } | |
| 1224 | |
| 1225 // Let's gather all signon realms we want to match with keychain entries. | |
| 1226 std::set<std::string> realm_set; | |
| 1227 realm_set.insert(form.signon_realm); | |
| 1228 for (const std::unique_ptr<PasswordForm>& db_form : database_forms) { | |
| 1229 // TODO(vabr): We should not be getting different schemes here. | |
| 1230 // http://crbug.com/340112 | |
| 1231 if (form.scheme != db_form->scheme) | |
| 1232 continue; // Forms with different schemes never match. | |
| 1233 if (db_form->is_public_suffix_match || db_form->is_affiliation_based_match) | |
| 1234 realm_set.insert(db_form->signon_realm); | |
| 1235 } | |
| 1236 std::vector<std::unique_ptr<PasswordForm>> keychain_forms; | |
| 1237 for (std::set<std::string>::const_iterator realm = realm_set.begin(); | |
| 1238 realm != realm_set.end(); ++realm) { | |
| 1239 MacKeychainPasswordFormAdapter keychain_adapter(keychain_.get()); | |
| 1240 std::vector<std::unique_ptr<PasswordForm>> temp_keychain_forms = | |
| 1241 keychain_adapter.PasswordsFillingForm(*realm, form.scheme); | |
| 1242 AppendSecondToFirst(&keychain_forms, &temp_keychain_forms); | |
| 1243 } | |
| 1244 | |
| 1245 std::vector<std::unique_ptr<PasswordForm>> matched_forms; | |
| 1246 internal_keychain_helpers::MergePasswordForms( | |
| 1247 &keychain_forms, &database_forms, &matched_forms); | |
| 1248 | |
| 1249 // Strip any blacklist entries out of the unused Keychain array, then take | |
| 1250 // all the entries that are left (which we can use as imported passwords). | |
| 1251 std::vector<std::unique_ptr<PasswordForm>> keychain_blacklist_forms; | |
| 1252 internal_keychain_helpers::ExtractNonKeychainForms(&keychain_forms, | |
| 1253 &keychain_blacklist_forms); | |
| 1254 AppendSecondToFirst(&matched_forms, &keychain_forms); | |
| 1255 | |
| 1256 if (!database_forms.empty()) { | |
| 1257 RemoveDatabaseForms(&database_forms); | |
| 1258 NotifyLoginsChanged(FormsToRemoveChangeList(database_forms)); | |
| 1259 } | |
| 1260 | |
| 1261 return matched_forms; | |
| 1262 } | |
| 1263 | |
| 1264 std::vector<std::unique_ptr<autofill::PasswordForm>> | |
| 1265 PasswordStoreMac::FillLoginsForSameOrganizationName( | |
| 1266 const std::string& signon_realm) { | |
| 1267 // Not implemented. | |
| 1268 return std::vector<std::unique_ptr<autofill::PasswordForm>>(); | |
| 1269 } | |
| 1270 | |
| 1271 bool PasswordStoreMac::FillAutofillableLogins( | |
| 1272 std::vector<std::unique_ptr<PasswordForm>>* forms) { | |
| 1273 DCHECK(GetBackgroundTaskRunner()->BelongsToCurrentThread()); | |
| 1274 forms->clear(); | |
| 1275 | |
| 1276 std::vector<std::unique_ptr<PasswordForm>> database_forms; | |
| 1277 if (!login_metadata_db_ || | |
| 1278 !login_metadata_db_->GetAutofillableLogins(&database_forms)) | |
| 1279 return false; | |
| 1280 | |
| 1281 internal_keychain_helpers::GetPasswordsForForms(*keychain_, &database_forms, | |
| 1282 forms); | |
| 1283 | |
| 1284 if (!database_forms.empty()) { | |
| 1285 RemoveDatabaseForms(&database_forms); | |
| 1286 NotifyLoginsChanged(FormsToRemoveChangeList(database_forms)); | |
| 1287 } | |
| 1288 | |
| 1289 return true; | |
| 1290 } | |
| 1291 | |
| 1292 bool PasswordStoreMac::FillBlacklistLogins( | |
| 1293 std::vector<std::unique_ptr<PasswordForm>>* forms) { | |
| 1294 DCHECK(GetBackgroundTaskRunner()->BelongsToCurrentThread()); | |
| 1295 return login_metadata_db_ && login_metadata_db_->GetBlacklistLogins(forms); | |
| 1296 } | |
| 1297 | |
| 1298 void PasswordStoreMac::AddSiteStatsImpl( | |
| 1299 const password_manager::InteractionsStats& stats) { | |
| 1300 DCHECK(GetBackgroundTaskRunner()->BelongsToCurrentThread()); | |
| 1301 if (login_metadata_db_) | |
| 1302 login_metadata_db_->stats_table().AddRow(stats); | |
| 1303 } | |
| 1304 | |
| 1305 void PasswordStoreMac::RemoveSiteStatsImpl(const GURL& origin_domain) { | |
| 1306 DCHECK(GetBackgroundTaskRunner()->BelongsToCurrentThread()); | |
| 1307 if (login_metadata_db_) | |
| 1308 login_metadata_db_->stats_table().RemoveRow(origin_domain); | |
| 1309 } | |
| 1310 | |
| 1311 std::vector<password_manager::InteractionsStats> | |
| 1312 PasswordStoreMac::GetAllSiteStatsImpl() { | |
| 1313 DCHECK(GetBackgroundTaskRunner()->BelongsToCurrentThread()); | |
| 1314 return login_metadata_db_ | |
| 1315 ? login_metadata_db_->stats_table().GetAllRows() | |
| 1316 : std::vector<password_manager::InteractionsStats>(); | |
| 1317 } | |
| 1318 | |
| 1319 std::vector<password_manager::InteractionsStats> | |
| 1320 PasswordStoreMac::GetSiteStatsImpl(const GURL& origin_domain) { | |
| 1321 DCHECK(GetBackgroundTaskRunner()->BelongsToCurrentThread()); | |
| 1322 return login_metadata_db_ | |
| 1323 ? login_metadata_db_->stats_table().GetRows(origin_domain) | |
| 1324 : std::vector<password_manager::InteractionsStats>(); | |
| 1325 } | |
| 1326 | |
| 1327 bool PasswordStoreMac::AddToKeychainIfNecessary(const PasswordForm& form) { | |
| 1328 if (IsLoginDatabaseOnlyForm(form)) | |
| 1329 return true; | |
| 1330 MacKeychainPasswordFormAdapter keychainAdapter(keychain_.get()); | |
| 1331 return keychainAdapter.AddPassword(form); | |
| 1332 } | |
| 1333 | |
| 1334 bool PasswordStoreMac::DatabaseHasFormMatchingKeychainForm( | |
| 1335 const PasswordForm& form) { | |
| 1336 DCHECK(login_metadata_db_); | |
| 1337 bool has_match = false; | |
| 1338 std::vector<std::unique_ptr<PasswordForm>> database_forms; | |
| 1339 if (!login_metadata_db_->GetLogins( | |
| 1340 password_manager::PasswordStore::FormDigest(form), &database_forms)) | |
| 1341 return false; | |
| 1342 for (const auto& db_form : database_forms) { | |
| 1343 // Below we filter out fuzzy matched forms because we are only interested | |
| 1344 // in exact ones. | |
| 1345 if (!db_form->is_public_suffix_match && | |
| 1346 internal_keychain_helpers::FormsMatchForMerge( | |
| 1347 form, *db_form, internal_keychain_helpers::STRICT_FORM_MATCH) && | |
| 1348 db_form->origin == form.origin) { | |
| 1349 has_match = true; | |
| 1350 break; | |
| 1351 } | |
| 1352 } | |
| 1353 return has_match; | |
| 1354 } | |
| 1355 | |
| 1356 void PasswordStoreMac::RemoveDatabaseForms( | |
| 1357 std::vector<std::unique_ptr<PasswordForm>>* forms) { | |
| 1358 DCHECK(login_metadata_db_); | |
| 1359 std::vector<std::unique_ptr<PasswordForm>> removed_forms; | |
| 1360 for (std::unique_ptr<PasswordForm>& form : *forms) { | |
| 1361 if (login_metadata_db_->RemoveLogin(*form)) | |
| 1362 removed_forms.push_back(std::move(form)); | |
| 1363 } | |
| 1364 removed_forms.swap(*forms); | |
| 1365 } | |
| 1366 | |
| 1367 void PasswordStoreMac::RemoveKeychainForms( | |
| 1368 const std::vector<std::unique_ptr<PasswordForm>>& forms) { | |
| 1369 MacKeychainPasswordFormAdapter owned_keychain_adapter(keychain_.get()); | |
| 1370 owned_keychain_adapter.SetFindsOnlyOwnedItems(true); | |
| 1371 for (const auto& form : forms) { | |
| 1372 owned_keychain_adapter.RemovePassword(*form); | |
| 1373 } | |
| 1374 } | |
| 1375 | |
| 1376 void PasswordStoreMac::CleanOrphanedForms( | |
| 1377 std::vector<std::unique_ptr<PasswordForm>>* orphaned_forms) { | |
| 1378 DCHECK(orphaned_forms); | |
| 1379 DCHECK(login_metadata_db_); | |
| 1380 | |
| 1381 std::vector<std::unique_ptr<PasswordForm>> database_forms; | |
| 1382 if (!login_metadata_db_->GetAutofillableLogins(&database_forms)) | |
| 1383 return; | |
| 1384 | |
| 1385 // Filter forms with corresponding Keychain entry out of |database_forms|. | |
| 1386 std::vector<std::unique_ptr<PasswordForm>> forms_with_keychain_entry; | |
| 1387 internal_keychain_helpers::GetPasswordsForForms(*keychain_, &database_forms, | |
| 1388 &forms_with_keychain_entry); | |
| 1389 | |
| 1390 // Clean up any orphaned database entries. | |
| 1391 RemoveDatabaseForms(&database_forms); | |
| 1392 | |
| 1393 // Move the orphaned DB forms to the output parameter. | |
| 1394 AppendSecondToFirst(orphaned_forms, &database_forms); | |
| 1395 } | |
| OLD | NEW |