Index: chrome/browser/password_manager/password_store_mac.cc |
diff --git a/chrome/browser/password_manager/password_store_mac.cc b/chrome/browser/password_manager/password_store_mac.cc |
deleted file mode 100644 |
index 25a1db966da81e72e11512d3eed42bbf675827c3..0000000000000000000000000000000000000000 |
--- a/chrome/browser/password_manager/password_store_mac.cc |
+++ /dev/null |
@@ -1,1395 +0,0 @@ |
-// Copyright (c) 2012 The Chromium Authors. All rights reserved. |
-// Use of this source code is governed by a BSD-style license that can be |
-// found in the LICENSE file. |
- |
-#include "chrome/browser/password_manager/password_store_mac.h" |
- |
-#include <CoreServices/CoreServices.h> |
-#include <stddef.h> |
-#include <algorithm> |
-#include <iterator> |
-#include <set> |
-#include <string> |
-#include <utility> |
-#include <vector> |
- |
-#include "base/callback.h" |
-#include "base/logging.h" |
-#include "base/mac/foundation_util.h" |
-#include "base/mac/mac_logging.h" |
-#include "base/macros.h" |
-#include "base/memory/ptr_util.h" |
-#include "base/message_loop/message_loop.h" |
-#include "base/metrics/histogram_macros.h" |
-#include "base/strings/string_util.h" |
-#include "base/strings/utf_string_conversions.h" |
-#include "chrome/browser/mac/security_wrappers.h" |
-#include "chrome/browser/password_manager/password_store_mac_internal.h" |
-#include "components/os_crypt/os_crypt.h" |
-#include "components/password_manager/core/browser/affiliation_utils.h" |
-#include "components/password_manager/core/browser/login_database.h" |
-#include "components/password_manager/core/browser/password_manager_util.h" |
-#include "components/password_manager/core/browser/password_store_change.h" |
-#include "content/public/browser/browser_thread.h" |
-#include "crypto/apple_keychain.h" |
-#include "url/origin.h" |
- |
-using autofill::PasswordForm; |
-using crypto::AppleKeychain; |
-using password_manager::PasswordStoreChange; |
-using password_manager::PasswordStoreChangeList; |
- |
-namespace { |
- |
-// Utility class to handle the details of constructing and running a keychain |
-// search from a set of attributes. |
-class KeychainSearch { |
- public: |
- explicit KeychainSearch(const AppleKeychain& keychain); |
- ~KeychainSearch(); |
- |
- // Sets up a keychain search based on an non "null" (NULL for char*, |
- // The appropriate "Any" entry for other types) arguments. |
- // |
- // IMPORTANT: Any parameters passed in *must* remain valid for as long as the |
- // KeychainSearch object, since the search uses them by reference. |
- void Init(const char* server, |
- const UInt32* port, |
- const SecProtocolType* protocol, |
- const SecAuthenticationType* auth_type, |
- const char* security_domain, |
- const char* path, |
- const char* username, |
- const OSType* creator); |
- |
- // Fills |items| with all Keychain items that match the Init'd search. |
- // If the search fails for any reason, |items| will be unchanged. |
- void FindMatchingItems(std::vector<SecKeychainItemRef>* matches); |
- |
- private: |
- const AppleKeychain* keychain_; |
- SecKeychainAttributeList search_attributes_; |
- SecKeychainSearchRef search_ref_; |
-}; |
- |
-KeychainSearch::KeychainSearch(const AppleKeychain& keychain) |
- : keychain_(&keychain), search_ref_(NULL) { |
- search_attributes_.count = 0; |
- search_attributes_.attr = NULL; |
-} |
- |
-KeychainSearch::~KeychainSearch() { |
- if (search_attributes_.attr) { |
- free(search_attributes_.attr); |
- } |
-} |
- |
-void KeychainSearch::Init(const char* server, |
- const UInt32* port, |
- const SecProtocolType* protocol, |
- const SecAuthenticationType* auth_type, |
- const char* security_domain, |
- const char* path, |
- const char* username, |
- const OSType* creator) { |
- // Allocate enough to hold everything we might use. |
- const unsigned int kMaxEntryCount = 8; |
- search_attributes_.attr = |
- static_cast<SecKeychainAttribute*>(calloc(kMaxEntryCount, |
- sizeof(SecKeychainAttribute))); |
- unsigned int entries = 0; |
- // We only use search_attributes_ with SearchCreateFromAttributes, which takes |
- // a "const SecKeychainAttributeList *", so we trust that they won't try |
- // to modify the list, and that casting away const-ness is thus safe. |
- if (server != NULL) { |
- DCHECK_LT(entries, kMaxEntryCount); |
- search_attributes_.attr[entries].tag = kSecServerItemAttr; |
- search_attributes_.attr[entries].length = strlen(server); |
- search_attributes_.attr[entries].data = |
- const_cast<void*>(static_cast<const void*>(server)); |
- ++entries; |
- } |
- if (port != NULL && *port != kAnyPort) { |
- DCHECK_LE(entries, kMaxEntryCount); |
- search_attributes_.attr[entries].tag = kSecPortItemAttr; |
- search_attributes_.attr[entries].length = sizeof(*port); |
- search_attributes_.attr[entries].data = |
- const_cast<void*>(static_cast<const void*>(port)); |
- ++entries; |
- } |
- if (protocol != NULL && *protocol != kSecProtocolTypeAny) { |
- DCHECK_LE(entries, kMaxEntryCount); |
- search_attributes_.attr[entries].tag = kSecProtocolItemAttr; |
- search_attributes_.attr[entries].length = sizeof(*protocol); |
- search_attributes_.attr[entries].data = |
- const_cast<void*>(static_cast<const void*>(protocol)); |
- ++entries; |
- } |
- if (auth_type != NULL && *auth_type != kSecAuthenticationTypeAny) { |
- DCHECK_LE(entries, kMaxEntryCount); |
- search_attributes_.attr[entries].tag = kSecAuthenticationTypeItemAttr; |
- search_attributes_.attr[entries].length = sizeof(*auth_type); |
- search_attributes_.attr[entries].data = |
- const_cast<void*>(static_cast<const void*>(auth_type)); |
- ++entries; |
- } |
- if (security_domain != NULL && strlen(security_domain) > 0) { |
- DCHECK_LE(entries, kMaxEntryCount); |
- search_attributes_.attr[entries].tag = kSecSecurityDomainItemAttr; |
- search_attributes_.attr[entries].length = strlen(security_domain); |
- search_attributes_.attr[entries].data = |
- const_cast<void*>(static_cast<const void*>(security_domain)); |
- ++entries; |
- } |
- if (path != NULL && strlen(path) > 0 && strcmp(path, "/") != 0) { |
- DCHECK_LE(entries, kMaxEntryCount); |
- search_attributes_.attr[entries].tag = kSecPathItemAttr; |
- search_attributes_.attr[entries].length = strlen(path); |
- search_attributes_.attr[entries].data = |
- const_cast<void*>(static_cast<const void*>(path)); |
- ++entries; |
- } |
- if (username != NULL) { |
- DCHECK_LE(entries, kMaxEntryCount); |
- search_attributes_.attr[entries].tag = kSecAccountItemAttr; |
- search_attributes_.attr[entries].length = strlen(username); |
- search_attributes_.attr[entries].data = |
- const_cast<void*>(static_cast<const void*>(username)); |
- ++entries; |
- } |
- if (creator != NULL) { |
- DCHECK_LE(entries, kMaxEntryCount); |
- search_attributes_.attr[entries].tag = kSecCreatorItemAttr; |
- search_attributes_.attr[entries].length = sizeof(*creator); |
- search_attributes_.attr[entries].data = |
- const_cast<void*>(static_cast<const void*>(creator)); |
- ++entries; |
- } |
- search_attributes_.count = entries; |
-} |
- |
-void KeychainSearch::FindMatchingItems(std::vector<SecKeychainItemRef>* items) { |
- OSStatus result = keychain_->SearchCreateFromAttributes( |
- NULL, kSecInternetPasswordItemClass, &search_attributes_, &search_ref_); |
- |
- if (result != noErr) { |
- OSSTATUS_LOG(ERROR, result) << "Keychain lookup failed"; |
- return; |
- } |
- |
- SecKeychainItemRef keychain_item; |
- while (keychain_->SearchCopyNext(search_ref_, &keychain_item) == noErr) { |
- // Consumer is responsible for freeing the items. |
- items->push_back(keychain_item); |
- } |
- |
- keychain_->Free(search_ref_); |
- search_ref_ = NULL; |
-} |
- |
-PasswordStoreChangeList FormsToRemoveChangeList( |
- const std::vector<std::unique_ptr<PasswordForm>>& forms) { |
- PasswordStoreChangeList changes; |
- for (const auto& form : forms) { |
- changes.push_back(PasswordStoreChange(PasswordStoreChange::REMOVE, *form)); |
- } |
- return changes; |
-} |
- |
-// Moves the content of |second| to the end of |first|. |
-void AppendSecondToFirst(std::vector<std::unique_ptr<PasswordForm>>* first, |
- std::vector<std::unique_ptr<PasswordForm>>* second) { |
- first->reserve(first->size() + second->size()); |
- std::move(second->begin(), second->end(), std::back_inserter(*first)); |
- second->clear(); |
-} |
- |
-// Returns the best match for |base_form| from |keychain_forms|, or nullptr if |
-// there is no suitable match. |
-const PasswordForm* BestKeychainFormForForm( |
- const PasswordForm& base_form, |
- const std::vector<std::unique_ptr<PasswordForm>>& keychain_forms) { |
- const PasswordForm* partial_match = nullptr; |
- for (const auto& keychain_form : keychain_forms) { |
- // TODO(stuartmorgan): We should really be scoring path matches and picking |
- // the best, rather than just checking exact-or-not (although in practice |
- // keychain items with paths probably came from us). |
- if (internal_keychain_helpers::FormsMatchForMerge( |
- base_form, *keychain_form, |
- internal_keychain_helpers::FUZZY_FORM_MATCH)) { |
- if (base_form.origin == keychain_form->origin) { |
- return keychain_form.get(); |
- } else if (!partial_match) { |
- partial_match = keychain_form.get(); |
- } |
- } |
- } |
- return partial_match; |
-} |
- |
-// True if the form has no password to be stored in Keychain. |
-bool IsLoginDatabaseOnlyForm(const PasswordForm& form) { |
- return form.blacklisted_by_user || !form.federation_origin.unique() || |
- form.scheme == PasswordForm::SCHEME_USERNAME_ONLY; |
-} |
- |
-} // namespace |
- |
-#pragma mark - |
- |
-// TODO(stuartmorgan): Convert most of this to private helpers in |
-// MacKeychainPasswordFormAdapter once it has sufficient higher-level public |
-// methods to provide test coverage. |
-namespace internal_keychain_helpers { |
- |
-// Returns a URL built from the given components. To create a URL without a |
-// port, pass kAnyPort for the |port| parameter. |
-GURL URLFromComponents(bool is_secure, const std::string& host, int port, |
- const std::string& path) { |
- GURL::Replacements url_components; |
- std::string scheme(is_secure ? "https" : "http"); |
- url_components.SetSchemeStr(scheme); |
- url_components.SetHostStr(host); |
- std::string port_string; // Must remain in scope until after we do replacing. |
- if (port != kAnyPort) { |
- std::ostringstream port_stringstream; |
- port_stringstream << port; |
- port_string = port_stringstream.str(); |
- url_components.SetPortStr(port_string); |
- } |
- url_components.SetPathStr(path); |
- |
- GURL url("http://dummy.com"); // ReplaceComponents needs a valid URL. |
- return url.ReplaceComponents(url_components); |
-} |
- |
-// Converts a Keychain time string to a Time object, returning true if |
-// time_string_bytes was parsable. |
-bool TimeFromKeychainTimeString(const char* time_string_bytes, |
- unsigned int byte_length, |
- base::Time* time) { |
- DCHECK(time); |
- |
- char* time_string = static_cast<char*>(malloc(byte_length + 1)); |
- memcpy(time_string, time_string_bytes, byte_length); |
- time_string[byte_length] = '\0'; |
- base::Time::Exploded exploded_time; |
- bzero(&exploded_time, sizeof(exploded_time)); |
- // The time string is of the form "yyyyMMddHHmmss'Z", in UTC time. |
- int assignments = sscanf(time_string, "%4d%2d%2d%2d%2d%2dZ", |
- &exploded_time.year, &exploded_time.month, |
- &exploded_time.day_of_month, &exploded_time.hour, |
- &exploded_time.minute, &exploded_time.second); |
- free(time_string); |
- |
- return assignments == 6 && base::Time::FromUTCExploded(exploded_time, time); |
-} |
- |
-// Returns the PasswordForm Scheme corresponding to |auth_type|. |
-PasswordForm::Scheme SchemeForAuthType(SecAuthenticationType auth_type) { |
- switch (auth_type) { |
- case kSecAuthenticationTypeHTMLForm: return PasswordForm::SCHEME_HTML; |
- case kSecAuthenticationTypeHTTPBasic: return PasswordForm::SCHEME_BASIC; |
- case kSecAuthenticationTypeHTTPDigest: return PasswordForm::SCHEME_DIGEST; |
- default: return PasswordForm::SCHEME_OTHER; |
- } |
-} |
- |
-bool FillPasswordFormFromKeychainItem(const AppleKeychain& keychain, |
- SecKeychainItemRef keychain_item, |
- PasswordForm* form, |
- bool extract_password_data) { |
- DCHECK(form); |
- |
- SecKeychainAttributeInfo attrInfo; |
- UInt32 tags[] = { kSecAccountItemAttr, |
- kSecServerItemAttr, |
- kSecPortItemAttr, |
- kSecPathItemAttr, |
- kSecProtocolItemAttr, |
- kSecAuthenticationTypeItemAttr, |
- kSecSecurityDomainItemAttr, |
- kSecCreationDateItemAttr, |
- kSecNegativeItemAttr }; |
- attrInfo.count = arraysize(tags); |
- attrInfo.tag = tags; |
- attrInfo.format = NULL; |
- |
- SecKeychainAttributeList* attrList; |
- UInt32 password_length; |
- |
- // If |extract_password_data| is false, do not pass in a reference to |
- // |password_data|. ItemCopyAttributesAndData will then extract only the |
- // attributes of |keychain_item| (doesn't require OS authorization), and not |
- // attempt to extract its password data (requires OS authorization). |
- void* password_data = NULL; |
- void** password_data_ref = extract_password_data ? &password_data : NULL; |
- |
- OSStatus result = keychain.ItemCopyAttributesAndData(keychain_item, &attrInfo, |
- NULL, &attrList, |
- &password_length, |
- password_data_ref); |
- |
- if (result != noErr) { |
- // We don't log errSecAuthFailed because that just means that the user |
- // chose not to allow us access to the item. |
- if (result != errSecAuthFailed) { |
- OSSTATUS_LOG(ERROR, result) << "Keychain data load failed"; |
- } |
- return false; |
- } |
- |
- if (extract_password_data) { |
- base::UTF8ToUTF16(static_cast<const char *>(password_data), password_length, |
- &(form->password_value)); |
- } |
- |
- int port = kAnyPort; |
- std::string server; |
- std::string security_domain; |
- std::string path; |
- bool is_secure = false; |
- for (unsigned int i = 0; i < attrList->count; i++) { |
- SecKeychainAttribute attr = attrList->attr[i]; |
- if (!attr.data) { |
- continue; |
- } |
- switch (attr.tag) { |
- case kSecAccountItemAttr: |
- base::UTF8ToUTF16(static_cast<const char *>(attr.data), attr.length, |
- &(form->username_value)); |
- break; |
- case kSecServerItemAttr: |
- server.assign(static_cast<const char *>(attr.data), attr.length); |
- break; |
- case kSecPortItemAttr: |
- port = *(static_cast<UInt32*>(attr.data)); |
- break; |
- case kSecPathItemAttr: |
- path.assign(static_cast<const char *>(attr.data), attr.length); |
- break; |
- case kSecProtocolItemAttr: |
- { |
- SecProtocolType protocol = *(static_cast<SecProtocolType*>(attr.data)); |
- // TODO(stuartmorgan): Handle proxy types |
- is_secure = (protocol == kSecProtocolTypeHTTPS); |
- break; |
- } |
- case kSecAuthenticationTypeItemAttr: |
- { |
- SecAuthenticationType auth_type = |
- *(static_cast<SecAuthenticationType*>(attr.data)); |
- form->scheme = SchemeForAuthType(auth_type); |
- break; |
- } |
- case kSecSecurityDomainItemAttr: |
- security_domain.assign(static_cast<const char *>(attr.data), |
- attr.length); |
- break; |
- case kSecCreationDateItemAttr: |
- // The only way to get a date out of Keychain is as a string. Really. |
- // (The docs claim it's an int, but the header is correct.) |
- TimeFromKeychainTimeString(static_cast<char*>(attr.data), attr.length, |
- &form->date_created); |
- break; |
- case kSecNegativeItemAttr: |
- Boolean negative_item = *(static_cast<Boolean*>(attr.data)); |
- if (negative_item) { |
- form->blacklisted_by_user = true; |
- } |
- break; |
- } |
- } |
- keychain.ItemFreeAttributesAndData(attrList, password_data); |
- |
- // kSecNegativeItemAttr doesn't seem to actually be in widespread use. In |
- // practice, other browsers seem to use a "" or " " password (and a special |
- // user name) to indicated blacklist entries. |
- if (extract_password_data && (form->password_value.empty() || |
- base::EqualsASCII(form->password_value, " "))) { |
- form->blacklisted_by_user = true; |
- } |
- |
- // Android facet URLs aren't parsed correctly by GURL and need to be handled |
- // separately. |
- if (password_manager::IsValidAndroidFacetURI(server)) { |
- form->signon_realm = server; |
- form->origin = GURL(); |
- } else { |
- form->origin = URLFromComponents(is_secure, server, port, path); |
- // TODO(stuartmorgan): Handle proxies, which need a different signon_realm |
- // format. |
- form->signon_realm = form->origin.GetOrigin().spec(); |
- if (form->scheme != PasswordForm::SCHEME_HTML) { |
- form->signon_realm.append(security_domain); |
- } |
- } |
- return true; |
-} |
- |
-bool HasChromeCreatorCode(const AppleKeychain& keychain, |
- SecKeychainItemRef keychain_item) { |
- SecKeychainAttributeInfo attr_info; |
- UInt32 tags[] = {kSecCreatorItemAttr}; |
- attr_info.count = arraysize(tags); |
- attr_info.tag = tags; |
- attr_info.format = nullptr; |
- |
- SecKeychainAttributeList* attr_list; |
- UInt32 password_length; |
- OSStatus result = keychain.ItemCopyAttributesAndData( |
- keychain_item, &attr_info, nullptr, &attr_list, |
- &password_length, nullptr); |
- if (result != noErr) |
- return false; |
- OSType creator_code = 0; |
- for (unsigned int i = 0; i < attr_list->count; i++) { |
- SecKeychainAttribute attr = attr_list->attr[i]; |
- if (!attr.data) { |
- continue; |
- } |
- if (attr.tag == kSecCreatorItemAttr) { |
- creator_code = *(static_cast<FourCharCode*>(attr.data)); |
- break; |
- } |
- } |
- keychain.ItemFreeAttributesAndData(attr_list, nullptr); |
- return creator_code && creator_code == base::mac::CreatorCodeForApplication(); |
-} |
- |
-bool FormsMatchForMerge(const PasswordForm& form_a, |
- const PasswordForm& form_b, |
- FormMatchStrictness strictness) { |
- if (IsLoginDatabaseOnlyForm(form_a) || IsLoginDatabaseOnlyForm(form_b)) |
- return false; |
- |
- bool equal_realm = form_a.signon_realm == form_b.signon_realm; |
- if (strictness == FUZZY_FORM_MATCH) { |
- equal_realm |= form_a.is_public_suffix_match; |
- } |
- return form_a.scheme == form_b.scheme && equal_realm && |
- form_a.username_value == form_b.username_value; |
-} |
- |
-// Moves entries from |forms| that represent either blacklisted or federated |
-// logins into |extracted|. These two types are stored only in the LoginDatabase |
-// and do not have corresponding Keychain entries. |
-void ExtractNonKeychainForms( |
- std::vector<std::unique_ptr<PasswordForm>>* forms, |
- std::vector<std::unique_ptr<PasswordForm>>* extracted) { |
- extracted->reserve(extracted->size() + forms->size()); |
- |
- std::vector<std::unique_ptr<PasswordForm>> remaining; |
- remaining.reserve(forms->size()); |
- |
- for (std::unique_ptr<PasswordForm>& form : *forms) { |
- if (IsLoginDatabaseOnlyForm(*form)) |
- extracted->push_back(std::move(form)); |
- else |
- remaining.push_back(std::move(form)); |
- } |
- forms->swap(remaining); |
-} |
- |
-// Takes |keychain_forms| and |database_forms| and moves the following 2 types |
-// of forms to |merged_forms|: |
-// (1) |database_forms| that by principle never have a corresponding Keychain |
-// entry (viz., blacklisted and federated logins), |
-// (2) |database_forms| which should have and do have a corresponding entry in |
-// |keychain_forms|. |
-// The database forms of type (2) have their password value updated from the |
-// corresponding keychain form, and all the keychain forms corresponding to some |
-// database form are removed from |keychain_forms| and deleted. |
-void MergePasswordForms( |
- std::vector<std::unique_ptr<PasswordForm>>* keychain_forms, |
- std::vector<std::unique_ptr<PasswordForm>>* database_forms, |
- std::vector<std::unique_ptr<PasswordForm>>* merged_forms) { |
- // Pull out the database blacklist items and federated logins, since they are |
- // used as-is rather than being merged with keychain forms. |
- ExtractNonKeychainForms(database_forms, merged_forms); |
- |
- // Merge the normal entries. |
- std::vector<std::unique_ptr<PasswordForm>> unused_database_forms; |
- unused_database_forms.reserve(database_forms->size()); |
- std::set<const PasswordForm*> used_keychain_forms; |
- // Move all database forms to either |merged_forms| or |
- // |unused_database_forms|, based on whether they have a match in the keychain |
- // forms or not. If there is a match, add its password to the DB form and |
- // mark the keychain form as used. |
- for (std::unique_ptr<PasswordForm>& db_form : *database_forms) { |
- const PasswordForm* best_match = |
- BestKeychainFormForForm(*db_form, *keychain_forms); |
- if (best_match) { |
- used_keychain_forms.insert(best_match); |
- db_form->password_value = best_match->password_value; |
- merged_forms->push_back(std::move(db_form)); |
- } else { |
- unused_database_forms.push_back(std::move(db_form)); |
- } |
- } |
- database_forms->swap(unused_database_forms); |
- |
- // Clear out all the Keychain entries we used. |
- std::vector<std::unique_ptr<PasswordForm>> unused_keychain_forms; |
- unused_keychain_forms.reserve(keychain_forms->size()); |
- for (std::unique_ptr<PasswordForm>& keychain_form : *keychain_forms) { |
- if (!base::ContainsKey(used_keychain_forms, keychain_form.get())) { |
- unused_keychain_forms.push_back(std::move(keychain_form)); |
- } |
- } |
- keychain_forms->swap(unused_keychain_forms); |
-} |
- |
-std::vector<ItemFormPair> ExtractAllKeychainItemAttributesIntoPasswordForms( |
- std::vector<SecKeychainItemRef>* keychain_items, |
- const AppleKeychain& keychain) { |
- DCHECK(keychain_items); |
- MacKeychainPasswordFormAdapter keychain_adapter(&keychain); |
- *keychain_items = keychain_adapter.GetAllPasswordFormKeychainItems(); |
- std::vector<ItemFormPair> item_form_pairs; |
- for (auto* keychain_item : *keychain_items) { |
- std::unique_ptr<PasswordForm> form_without_password = |
- base::MakeUnique<PasswordForm>(); |
- internal_keychain_helpers::FillPasswordFormFromKeychainItem( |
- keychain, keychain_item, form_without_password.get(), |
- false); // Load password attributes, but not password data. |
- item_form_pairs.push_back( |
- std::make_pair(keychain_item, std::move(form_without_password))); |
- } |
- return item_form_pairs; |
-} |
- |
-void GetPasswordsForForms( |
- const AppleKeychain& keychain, |
- std::vector<std::unique_ptr<PasswordForm>>* database_forms, |
- std::vector<std::unique_ptr<PasswordForm>>* passwords) { |
- // First load the attributes of all items in the keychain without loading |
- // their password data, and then match items in |database_forms| against them. |
- // This avoids individually searching through the keychain for passwords |
- // matching each form in |database_forms|, and results in a significant |
- // performance gain, replacing O(N) keychain search operations with a single |
- // operation that loads all keychain items, and then selective reads of only |
- // the relevant passwords. See crbug.com/263685. |
- std::vector<SecKeychainItemRef> keychain_items; |
- std::vector<ItemFormPair> item_form_pairs = |
- ExtractAllKeychainItemAttributesIntoPasswordForms(&keychain_items, |
- keychain); |
- |
- // Next, compare the attributes of the PasswordForms in |database_forms| |
- // against those in |item_form_pairs|, and extract password data for each |
- // matching PasswordForm using its corresponding SecKeychainItemRef. |
- std::vector<std::unique_ptr<PasswordForm>> unused_db_forms; |
- unused_db_forms.reserve(database_forms->size()); |
- // Move database forms with a password stored in |keychain| to |passwords|, |
- // including the password. The rest is moved to |unused_db_forms|. |
- for (std::unique_ptr<PasswordForm>& db_form : *database_forms) { |
- std::vector<std::unique_ptr<PasswordForm>> keychain_matches = |
- ExtractPasswordsMergeableWithForm(keychain, item_form_pairs, *db_form); |
- |
- std::vector<std::unique_ptr<PasswordForm>> db_form_container; |
- db_form_container.push_back(std::move(db_form)); |
- MergePasswordForms(&keychain_matches, &db_form_container, passwords); |
- AppendSecondToFirst(&unused_db_forms, &db_form_container); |
- } |
- database_forms->swap(unused_db_forms); |
- |
- for (SecKeychainItemRef item : keychain_items) { |
- keychain.Free(item); |
- } |
-} |
- |
-// TODO(stuartmorgan): signon_realm for proxies is not yet supported. |
-bool ExtractSignonRealmComponents(const std::string& signon_realm, |
- std::string* server, |
- UInt32* port, |
- bool* is_secure, |
- std::string* security_domain) { |
- // GURL does not parse Android facet URIs correctly. |
- if (password_manager::IsValidAndroidFacetURI(signon_realm)) { |
- if (server) |
- *server = signon_realm; |
- if (is_secure) |
- *is_secure = true; |
- if (port) |
- *port = 0; |
- if (security_domain) |
- security_domain->clear(); |
- return true; |
- } |
- |
- // The signon_realm will be the Origin portion of a URL for an HTML form, |
- // and the same but with the security domain as a path for HTTP auth. |
- GURL realm_as_url(signon_realm); |
- if (!realm_as_url.is_valid()) { |
- return false; |
- } |
- |
- if (server) |
- *server = realm_as_url.host(); |
- if (is_secure) |
- *is_secure = realm_as_url.SchemeIsCryptographic(); |
- if (port) |
- *port = realm_as_url.has_port() ? atoi(realm_as_url.port().c_str()) : 0; |
- if (security_domain) { |
- // Strip the leading '/' off of the path to get the security domain. |
- if (realm_as_url.path().length() > 0) |
- *security_domain = realm_as_url.path().substr(1); |
- else |
- security_domain->clear(); |
- } |
- return true; |
-} |
- |
-bool FormIsValidAndMatchesOtherForm(const PasswordForm& query_form, |
- const PasswordForm& other_form) { |
- std::string server; |
- std::string security_domain; |
- UInt32 port; |
- bool is_secure; |
- if (!ExtractSignonRealmComponents(query_form.signon_realm, &server, &port, |
- &is_secure, &security_domain)) { |
- return false; |
- } |
- return FormsMatchForMerge(query_form, other_form, STRICT_FORM_MATCH); |
-} |
- |
-std::vector<std::unique_ptr<PasswordForm>> ExtractPasswordsMergeableWithForm( |
- const AppleKeychain& keychain, |
- const std::vector<ItemFormPair>& item_form_pairs, |
- const PasswordForm& query_form) { |
- std::vector<std::unique_ptr<PasswordForm>> matches; |
- for (std::vector<ItemFormPair>::const_iterator i = item_form_pairs.begin(); |
- i != item_form_pairs.end(); ++i) { |
- if (FormIsValidAndMatchesOtherForm(query_form, *(i->second))) { |
- // Create a new object, since the caller is responsible for deleting the |
- // returned forms. |
- auto form_with_password = base::MakeUnique<PasswordForm>(); |
- FillPasswordFormFromKeychainItem( |
- keychain, i->first, form_with_password.get(), |
- true); // Load password attributes and data. |
- // Do not include blacklisted items found in the keychain. |
- if (!form_with_password->blacklisted_by_user) |
- matches.push_back(std::move(form_with_password)); |
- } |
- } |
- return matches; |
-} |
- |
-} // namespace internal_keychain_helpers |
- |
-#pragma mark - |
- |
-MacKeychainPasswordFormAdapter::MacKeychainPasswordFormAdapter( |
- const AppleKeychain* keychain) |
- : keychain_(keychain), finds_only_owned_(false) { |
-} |
- |
-std::vector<std::unique_ptr<PasswordForm>> |
-MacKeychainPasswordFormAdapter::PasswordsFillingForm( |
- const std::string& signon_realm, |
- PasswordForm::Scheme scheme) { |
- std::vector<SecKeychainItemRef> keychain_items = |
- MatchingKeychainItems(signon_realm, scheme, NULL, NULL); |
- return ConvertKeychainItemsToForms(&keychain_items); |
-} |
- |
-bool MacKeychainPasswordFormAdapter::HasPasswordExactlyMatchingForm( |
- const PasswordForm& query_form) { |
- SecKeychainItemRef keychain_item = KeychainItemForForm(query_form); |
- if (keychain_item) { |
- keychain_->Free(keychain_item); |
- return true; |
- } |
- return false; |
-} |
- |
-bool MacKeychainPasswordFormAdapter::HasPasswordsMergeableWithForm( |
- const PasswordForm& query_form) { |
- if (IsLoginDatabaseOnlyForm(query_form)) |
- return false; |
- std::string username = base::UTF16ToUTF8(query_form.username_value); |
- std::vector<SecKeychainItemRef> matches = |
- MatchingKeychainItems(query_form.signon_realm, query_form.scheme, |
- NULL, username.c_str()); |
- for (SecKeychainItemRef item : matches) |
- keychain_->Free(item); |
- |
- return !matches.empty(); |
-} |
- |
-std::vector<SecKeychainItemRef> |
- MacKeychainPasswordFormAdapter::GetAllPasswordFormKeychainItems() { |
- SecAuthenticationType supported_auth_types[] = { |
- kSecAuthenticationTypeHTMLForm, |
- kSecAuthenticationTypeHTTPBasic, |
- kSecAuthenticationTypeHTTPDigest, |
- }; |
- |
- std::vector<SecKeychainItemRef> matches; |
- for (unsigned int i = 0; i < arraysize(supported_auth_types); ++i) { |
- KeychainSearch keychain_search(*keychain_); |
- OSType creator = CreatorCodeForSearch(); |
- keychain_search.Init(NULL, |
- NULL, |
- NULL, |
- &supported_auth_types[i], |
- NULL, |
- NULL, |
- NULL, |
- creator ? &creator : NULL); |
- keychain_search.FindMatchingItems(&matches); |
- } |
- return matches; |
-} |
- |
-std::vector<std::unique_ptr<PasswordForm>> |
-MacKeychainPasswordFormAdapter::GetAllPasswordFormPasswords() { |
- std::vector<SecKeychainItemRef> items = GetAllPasswordFormKeychainItems(); |
- return ConvertKeychainItemsToForms(&items); |
-} |
- |
-bool MacKeychainPasswordFormAdapter::AddPassword(const PasswordForm& form) { |
- // We should never be trying to store a blacklist in the keychain. |
- DCHECK(!IsLoginDatabaseOnlyForm(form)); |
- |
- std::string server; |
- std::string security_domain; |
- UInt32 port; |
- bool is_secure; |
- if (!internal_keychain_helpers::ExtractSignonRealmComponents( |
- form.signon_realm, &server, &port, &is_secure, &security_domain)) { |
- return false; |
- } |
- std::string path; |
- // Path doesn't make sense for Android app credentials. |
- if (!password_manager::IsValidAndroidFacetURI(form.signon_realm)) |
- path = form.origin.path(); |
- std::string username = base::UTF16ToUTF8(form.username_value); |
- std::string password = base::UTF16ToUTF8(form.password_value); |
- SecProtocolType protocol = is_secure ? kSecProtocolTypeHTTPS |
- : kSecProtocolTypeHTTP; |
- SecKeychainItemRef new_item = NULL; |
- OSStatus result = keychain_->AddInternetPassword( |
- NULL, server.size(), server.c_str(), |
- security_domain.size(), security_domain.c_str(), |
- username.size(), username.c_str(), |
- path.size(), path.c_str(), |
- port, protocol, AuthTypeForScheme(form.scheme), |
- password.size(), password.c_str(), &new_item); |
- |
- if (result == noErr) { |
- SetKeychainItemCreatorCode(new_item, |
- base::mac::CreatorCodeForApplication()); |
- keychain_->Free(new_item); |
- } else if (result == errSecDuplicateItem) { |
- // If we collide with an existing item, find and update it instead. |
- SecKeychainItemRef existing_item = KeychainItemForForm(form); |
- if (!existing_item) { |
- return false; |
- } |
- bool changed = SetKeychainItemPassword(existing_item, password); |
- keychain_->Free(existing_item); |
- return changed; |
- } |
- |
- return result == noErr; |
-} |
- |
-bool MacKeychainPasswordFormAdapter::RemovePassword(const PasswordForm& form) { |
- SecKeychainItemRef keychain_item = KeychainItemForForm(form); |
- if (keychain_item == NULL) |
- return false; |
- OSStatus result = keychain_->ItemDelete(keychain_item); |
- keychain_->Free(keychain_item); |
- return result == noErr; |
-} |
- |
-void MacKeychainPasswordFormAdapter::SetFindsOnlyOwnedItems( |
- bool finds_only_owned) { |
- finds_only_owned_ = finds_only_owned; |
-} |
- |
-std::vector<std::unique_ptr<PasswordForm>> |
-MacKeychainPasswordFormAdapter::ConvertKeychainItemsToForms( |
- std::vector<SecKeychainItemRef>* items) { |
- std::vector<std::unique_ptr<PasswordForm>> forms; |
- for (SecKeychainItemRef item : *items) { |
- auto form = base::MakeUnique<PasswordForm>(); |
- if (internal_keychain_helpers::FillPasswordFormFromKeychainItem( |
- *keychain_, item, form.get(), true)) { |
- forms.push_back(std::move(form)); |
- } |
- keychain_->Free(item); |
- } |
- items->clear(); |
- return forms; |
-} |
- |
-SecKeychainItemRef MacKeychainPasswordFormAdapter::KeychainItemForForm( |
- const PasswordForm& form) { |
- // We don't store blacklist entries in the keychain, so the answer to "what |
- // Keychain item goes with this form" is always "nothing" for blacklists. |
- // Same goes for federated logins. |
- if (IsLoginDatabaseOnlyForm(form)) |
- return NULL; |
- |
- std::string path; |
- // Path doesn't make sense for Android app credentials. |
- if (!password_manager::IsValidAndroidFacetURI(form.signon_realm)) |
- path = form.origin.path(); |
- std::string username = base::UTF16ToUTF8(form.username_value); |
- std::vector<SecKeychainItemRef> matches = MatchingKeychainItems( |
- form.signon_realm, form.scheme, path.c_str(), username.c_str()); |
- |
- if (matches.empty()) { |
- return NULL; |
- } |
- |
- // Free all items after the first, since we won't be returning them. |
- for (auto i = matches.begin() + 1; i != matches.end(); ++i) |
- keychain_->Free(*i); |
- |
- return matches[0]; |
-} |
- |
-std::vector<SecKeychainItemRef> |
-MacKeychainPasswordFormAdapter::MatchingKeychainItems( |
- const std::string& signon_realm, |
- PasswordForm::Scheme scheme, |
- const char* path, |
- const char* username) { |
- std::vector<SecKeychainItemRef> matches; |
- |
- std::string server; |
- std::string security_domain; |
- UInt32 port; |
- bool is_secure; |
- if (!internal_keychain_helpers::ExtractSignonRealmComponents( |
- signon_realm, &server, &port, &is_secure, &security_domain)) { |
- // TODO(stuartmorgan): Proxies will currently fail here, since their |
- // signon_realm is not a URL. We need to detect the proxy case and handle |
- // it specially. |
- return matches; |
- } |
- SecProtocolType protocol = is_secure ? kSecProtocolTypeHTTPS |
- : kSecProtocolTypeHTTP; |
- SecAuthenticationType auth_type = AuthTypeForScheme(scheme); |
- const char* auth_domain = (scheme == PasswordForm::SCHEME_HTML) ? |
- NULL : security_domain.c_str(); |
- OSType creator = CreatorCodeForSearch(); |
- KeychainSearch keychain_search(*keychain_); |
- keychain_search.Init(server.c_str(), |
- &port, |
- &protocol, |
- &auth_type, |
- auth_domain, |
- path, |
- username, |
- creator ? &creator : NULL); |
- keychain_search.FindMatchingItems(&matches); |
- return matches; |
-} |
- |
-// Returns the Keychain SecAuthenticationType type corresponding to |scheme|. |
-SecAuthenticationType MacKeychainPasswordFormAdapter::AuthTypeForScheme( |
- PasswordForm::Scheme scheme) { |
- switch (scheme) { |
- case PasswordForm::SCHEME_HTML: return kSecAuthenticationTypeHTMLForm; |
- case PasswordForm::SCHEME_BASIC: return kSecAuthenticationTypeHTTPBasic; |
- case PasswordForm::SCHEME_DIGEST: return kSecAuthenticationTypeHTTPDigest; |
- case PasswordForm::SCHEME_OTHER: return kSecAuthenticationTypeDefault; |
- case PasswordForm::SCHEME_USERNAME_ONLY: |
- NOTREACHED(); |
- break; |
- } |
- NOTREACHED(); |
- return kSecAuthenticationTypeDefault; |
-} |
- |
-bool MacKeychainPasswordFormAdapter::SetKeychainItemPassword( |
- SecKeychainItemRef keychain_item, |
- const std::string& password) { |
- OSStatus result = keychain_->ItemModifyAttributesAndData(keychain_item, NULL, |
- password.size(), |
- password.c_str()); |
- return result == noErr; |
-} |
- |
-bool MacKeychainPasswordFormAdapter::SetKeychainItemCreatorCode( |
- SecKeychainItemRef keychain_item, |
- OSType creator_code) { |
- SecKeychainAttribute attr = { kSecCreatorItemAttr, sizeof(creator_code), |
- &creator_code }; |
- SecKeychainAttributeList attrList = { 1, &attr }; |
- OSStatus result = keychain_->ItemModifyAttributesAndData(keychain_item, |
- &attrList, 0, NULL); |
- return result == noErr; |
-} |
- |
-OSType MacKeychainPasswordFormAdapter::CreatorCodeForSearch() { |
- return finds_only_owned_ ? base::mac::CreatorCodeForApplication() : 0; |
-} |
- |
-#pragma mark - |
- |
-PasswordStoreMac::PasswordStoreMac( |
- scoped_refptr<base::SingleThreadTaskRunner> main_thread_runner, |
- scoped_refptr<base::SingleThreadTaskRunner> db_thread_runner, |
- std::unique_ptr<AppleKeychain> keychain) |
- : password_manager::PasswordStore(main_thread_runner, db_thread_runner), |
- keychain_(std::move(keychain)), |
- login_metadata_db_(nullptr) { |
- DCHECK(keychain_); |
-} |
- |
-PasswordStoreMac::~PasswordStoreMac() {} |
- |
-void PasswordStoreMac::InitWithTaskRunner( |
- scoped_refptr<base::SingleThreadTaskRunner> background_task_runner) { |
- db_thread_runner_ = background_task_runner; |
- DCHECK(GetBackgroundTaskRunner()->BelongsToCurrentThread()); |
-} |
- |
-// static |
-PasswordStoreMac::MigrationResult PasswordStoreMac::ImportFromKeychain( |
- password_manager::LoginDatabase* login_db, |
- crypto::AppleKeychain* keychain) { |
- std::vector<std::unique_ptr<PasswordForm>> database_forms; |
- if (!login_db->GetAutofillableLogins(&database_forms)) |
- return LOGIN_DB_FAILURE; |
- |
- std::vector<std::unique_ptr<PasswordForm>> uninteresting_forms; |
- internal_keychain_helpers::ExtractNonKeychainForms(&database_forms, |
- &uninteresting_forms); |
- // If there are no passwords to lookup in the Keychain then we're done. |
- if (database_forms.empty()) |
- return MIGRATION_OK; |
- |
- // Make sure that the encryption key is retrieved from the Keychain so the |
- // encryption can be done. |
- std::string ciphertext; |
- if (!OSCrypt::EncryptString("test", &ciphertext)) |
- return ENCRYPTOR_FAILURE; |
- |
- // Mute the Keychain. |
- chrome::ScopedSecKeychainSetUserInteractionAllowed user_interaction_allowed( |
- false); |
- |
- // Retrieve the passwords. |
- // Get all the password attributes first. |
- std::vector<SecKeychainItemRef> keychain_items; |
- std::vector<internal_keychain_helpers::ItemFormPair> item_form_pairs = |
- internal_keychain_helpers:: |
- ExtractAllKeychainItemAttributesIntoPasswordForms(&keychain_items, |
- *keychain); |
- |
- // Next, compare the attributes of the PasswordForms in |database_forms| |
- // against those in |item_form_pairs|, and extract password data for each |
- // matching PasswordForm using its corresponding SecKeychainItemRef. |
- size_t unmerged_forms_count = 0; |
- login_db->set_clear_password_values(false); |
- for (const auto& form : database_forms) { |
- std::vector<std::unique_ptr<PasswordForm>> keychain_matches = |
- internal_keychain_helpers::ExtractPasswordsMergeableWithForm( |
- *keychain, item_form_pairs, *form); |
- |
- const PasswordForm* best_match = |
- BestKeychainFormForForm(*form, keychain_matches); |
- if (best_match) { |
- form->password_value = best_match->password_value; |
- PasswordStoreChangeList change = login_db->UpdateLogin(*form); |
- DCHECK_EQ(1u, change.size()); |
- } else { |
- unmerged_forms_count++; |
- bool removed = login_db->RemoveLogin(*form); |
- DCHECK(removed); |
- } |
- } |
- for (SecKeychainItemRef item : keychain_items) |
- keychain->Free(item); |
- |
- if (unmerged_forms_count) { |
- UMA_HISTOGRAM_COUNTS( |
- "PasswordManager.KeychainMigration.NumPasswordsOnFailure", |
- database_forms.size()); |
- UMA_HISTOGRAM_COUNTS("PasswordManager.KeychainMigration.NumFailedPasswords", |
- unmerged_forms_count); |
- return MIGRATION_PARTIAL; |
- } |
- return MIGRATION_OK; |
-} |
- |
-// static |
-void PasswordStoreMac::CleanUpKeychain( |
- crypto::AppleKeychain* keychain, |
- const std::vector<std::unique_ptr<PasswordForm>>& forms) { |
- MacKeychainPasswordFormAdapter keychain_adapter(keychain); |
- keychain_adapter.SetFindsOnlyOwnedItems(true); |
- for (const auto& form : forms) |
- keychain_adapter.RemovePassword(*form); |
-} |
- |
-void PasswordStoreMac::set_login_metadata_db( |
- password_manager::LoginDatabase* login_db) { |
- login_metadata_db_ = login_db; |
- if (login_metadata_db_) |
- login_metadata_db_->set_clear_password_values(true); |
-} |
- |
-bool PasswordStoreMac::Init( |
- const syncer::SyncableService::StartSyncFlare& flare, |
- PrefService* prefs) { |
- // The class should be used inside PasswordStoreProxyMac only. |
- NOTREACHED(); |
- return true; |
-} |
- |
-void PasswordStoreMac::ReportMetricsImpl(const std::string& sync_username, |
- bool custom_passphrase_sync_enabled) { |
- if (!login_metadata_db_) |
- return; |
- login_metadata_db_->ReportMetrics(sync_username, |
- custom_passphrase_sync_enabled); |
-} |
- |
-PasswordStoreChangeList PasswordStoreMac::AddLoginImpl( |
- const PasswordForm& form) { |
- DCHECK(GetBackgroundTaskRunner()->BelongsToCurrentThread()); |
- if (login_metadata_db_ && AddToKeychainIfNecessary(form)) |
- return login_metadata_db_->AddLogin(form); |
- return PasswordStoreChangeList(); |
-} |
- |
-PasswordStoreChangeList PasswordStoreMac::UpdateLoginImpl( |
- const PasswordForm& form) { |
- DCHECK(GetBackgroundTaskRunner()->BelongsToCurrentThread()); |
- if (!login_metadata_db_) |
- return PasswordStoreChangeList(); |
- |
- PasswordStoreChangeList changes = login_metadata_db_->UpdateLogin(form); |
- |
- MacKeychainPasswordFormAdapter keychain_adapter(keychain_.get()); |
- if (changes.empty() && |
- !keychain_adapter.HasPasswordsMergeableWithForm(form)) { |
- // If the password isn't in either the DB or the keychain, then it must have |
- // been deleted after autofill happened, and should not be re-added. |
- return changes; |
- } |
- |
- // The keychain add will update if there is a collision and add if there |
- // isn't, which is the behavior we want, so there's no separate update call. |
- if (AddToKeychainIfNecessary(form) && changes.empty()) { |
- changes = login_metadata_db_->AddLogin(form); |
- } |
- return changes; |
-} |
- |
-PasswordStoreChangeList PasswordStoreMac::RemoveLoginImpl( |
- const PasswordForm& form) { |
- DCHECK(GetBackgroundTaskRunner()->BelongsToCurrentThread()); |
- PasswordStoreChangeList changes; |
- if (login_metadata_db_ && login_metadata_db_->RemoveLogin(form)) { |
- // See if we own a Keychain item associated with this item. We can do an |
- // exact search rather than messing around with trying to do fuzzy matching |
- // because passwords that we created will always have an exact-match |
- // database entry. |
- // (If a user does lose their profile but not their keychain we'll treat the |
- // entries we find like other imported entries anyway, so it's reasonable to |
- // handle deletes on them the way we would for an imported item.) |
- MacKeychainPasswordFormAdapter owned_keychain_adapter(keychain_.get()); |
- owned_keychain_adapter.SetFindsOnlyOwnedItems(true); |
- if (owned_keychain_adapter.HasPasswordExactlyMatchingForm(form)) { |
- // If we don't have other forms using it (i.e., a form differing only by |
- // the names of the form elements), delete the keychain entry. |
- if (!DatabaseHasFormMatchingKeychainForm(form)) { |
- owned_keychain_adapter.RemovePassword(form); |
- } |
- } |
- |
- changes.push_back(PasswordStoreChange(PasswordStoreChange::REMOVE, form)); |
- } |
- return changes; |
-} |
- |
-PasswordStoreChangeList PasswordStoreMac::RemoveLoginsByURLAndTimeImpl( |
- const base::Callback<bool(const GURL&)>& url_filter, |
- base::Time delete_begin, |
- base::Time delete_end) { |
- PasswordStoreChangeList changes; |
- std::vector<std::unique_ptr<PasswordForm>> forms_to_consider; |
- std::vector<std::unique_ptr<PasswordForm>> forms_to_remove; |
- if (login_metadata_db_ && |
- login_metadata_db_->GetLoginsCreatedBetween(delete_begin, delete_end, |
- &forms_to_consider)) { |
- for (std::unique_ptr<PasswordForm>& form_to_consider : forms_to_consider) { |
- if (url_filter.Run(form_to_consider->origin) && |
- login_metadata_db_->RemoveLogin(*form_to_consider)) |
- forms_to_remove.push_back(std::move(form_to_consider)); |
- } |
- if (!forms_to_remove.empty()) { |
- RemoveKeychainForms(forms_to_remove); |
- CleanOrphanedForms(&forms_to_remove); // Add the orphaned forms. |
- changes = FormsToRemoveChangeList(forms_to_remove); |
- LogStatsForBulkDeletion(changes.size()); |
- } |
- } |
- return changes; |
-} |
- |
-PasswordStoreChangeList PasswordStoreMac::RemoveLoginsCreatedBetweenImpl( |
- base::Time delete_begin, |
- base::Time delete_end) { |
- PasswordStoreChangeList changes; |
- std::vector<std::unique_ptr<PasswordForm>> forms_to_remove; |
- if (login_metadata_db_ && |
- login_metadata_db_->GetLoginsCreatedBetween(delete_begin, delete_end, |
- &forms_to_remove) && |
- login_metadata_db_->RemoveLoginsCreatedBetween(delete_begin, |
- delete_end)) { |
- RemoveKeychainForms(forms_to_remove); |
- CleanOrphanedForms(&forms_to_remove); // Add the orphaned forms. |
- changes = FormsToRemoveChangeList(forms_to_remove); |
- LogStatsForBulkDeletion(changes.size()); |
- } |
- return changes; |
-} |
- |
-PasswordStoreChangeList PasswordStoreMac::RemoveLoginsSyncedBetweenImpl( |
- base::Time delete_begin, |
- base::Time delete_end) { |
- PasswordStoreChangeList changes; |
- std::vector<std::unique_ptr<PasswordForm>> forms_to_remove; |
- if (login_metadata_db_ && |
- login_metadata_db_->GetLoginsSyncedBetween(delete_begin, delete_end, |
- &forms_to_remove) && |
- login_metadata_db_->RemoveLoginsSyncedBetween(delete_begin, delete_end)) { |
- RemoveKeychainForms(forms_to_remove); |
- CleanOrphanedForms(&forms_to_remove); // Add the orphaned forms_to_remove. |
- changes = FormsToRemoveChangeList(forms_to_remove); |
- LogStatsForBulkDeletionDuringRollback(changes.size()); |
- } |
- return changes; |
-} |
- |
-PasswordStoreChangeList PasswordStoreMac::DisableAutoSignInForOriginsImpl( |
- const base::Callback<bool(const GURL&)>& origin_filter) { |
- std::vector<std::unique_ptr<PasswordForm>> forms; |
- PasswordStoreChangeList changes; |
- if (!login_metadata_db_ || |
- !login_metadata_db_->GetAutoSignInLogins(&forms)) { |
- return changes; |
- } |
- |
- std::set<GURL> origins_to_update; |
- for (const auto& form : forms) { |
- if (origin_filter.Run(form->origin)) |
- origins_to_update.insert(form->origin); |
- } |
- |
- std::set<GURL> origins_updated; |
- for (const GURL& origin : origins_to_update) { |
- if (login_metadata_db_->DisableAutoSignInForOrigin(origin)) |
- origins_updated.insert(origin); |
- } |
- |
- for (const auto& form : forms) { |
- if (origins_updated.count(form->origin)) { |
- changes.push_back( |
- PasswordStoreChange(PasswordStoreChange::UPDATE, *form)); |
- } |
- } |
- |
- return changes; |
-} |
- |
-bool PasswordStoreMac::RemoveStatisticsByOriginAndTimeImpl( |
- const base::Callback<bool(const GURL&)>& origin_filter, |
- base::Time delete_begin, |
- base::Time delete_end) { |
- return login_metadata_db_ && |
- login_metadata_db_->stats_table().RemoveStatsByOriginAndTime( |
- origin_filter, delete_begin, delete_end); |
-} |
- |
-std::vector<std::unique_ptr<PasswordForm>> PasswordStoreMac::FillMatchingLogins( |
- const FormDigest& form) { |
- chrome::ScopedSecKeychainSetUserInteractionAllowed user_interaction_allowed( |
- false); |
- |
- std::vector<std::unique_ptr<PasswordForm>> database_forms; |
- if (!login_metadata_db_ || |
- !login_metadata_db_->GetLogins(form, &database_forms)) { |
- return std::vector<std::unique_ptr<PasswordForm>>(); |
- } |
- |
- // Let's gather all signon realms we want to match with keychain entries. |
- std::set<std::string> realm_set; |
- realm_set.insert(form.signon_realm); |
- for (const std::unique_ptr<PasswordForm>& db_form : database_forms) { |
- // TODO(vabr): We should not be getting different schemes here. |
- // http://crbug.com/340112 |
- if (form.scheme != db_form->scheme) |
- continue; // Forms with different schemes never match. |
- if (db_form->is_public_suffix_match || db_form->is_affiliation_based_match) |
- realm_set.insert(db_form->signon_realm); |
- } |
- std::vector<std::unique_ptr<PasswordForm>> keychain_forms; |
- for (std::set<std::string>::const_iterator realm = realm_set.begin(); |
- realm != realm_set.end(); ++realm) { |
- MacKeychainPasswordFormAdapter keychain_adapter(keychain_.get()); |
- std::vector<std::unique_ptr<PasswordForm>> temp_keychain_forms = |
- keychain_adapter.PasswordsFillingForm(*realm, form.scheme); |
- AppendSecondToFirst(&keychain_forms, &temp_keychain_forms); |
- } |
- |
- std::vector<std::unique_ptr<PasswordForm>> matched_forms; |
- internal_keychain_helpers::MergePasswordForms( |
- &keychain_forms, &database_forms, &matched_forms); |
- |
- // Strip any blacklist entries out of the unused Keychain array, then take |
- // all the entries that are left (which we can use as imported passwords). |
- std::vector<std::unique_ptr<PasswordForm>> keychain_blacklist_forms; |
- internal_keychain_helpers::ExtractNonKeychainForms(&keychain_forms, |
- &keychain_blacklist_forms); |
- AppendSecondToFirst(&matched_forms, &keychain_forms); |
- |
- if (!database_forms.empty()) { |
- RemoveDatabaseForms(&database_forms); |
- NotifyLoginsChanged(FormsToRemoveChangeList(database_forms)); |
- } |
- |
- return matched_forms; |
-} |
- |
-std::vector<std::unique_ptr<autofill::PasswordForm>> |
-PasswordStoreMac::FillLoginsForSameOrganizationName( |
- const std::string& signon_realm) { |
- // Not implemented. |
- return std::vector<std::unique_ptr<autofill::PasswordForm>>(); |
-} |
- |
-bool PasswordStoreMac::FillAutofillableLogins( |
- std::vector<std::unique_ptr<PasswordForm>>* forms) { |
- DCHECK(GetBackgroundTaskRunner()->BelongsToCurrentThread()); |
- forms->clear(); |
- |
- std::vector<std::unique_ptr<PasswordForm>> database_forms; |
- if (!login_metadata_db_ || |
- !login_metadata_db_->GetAutofillableLogins(&database_forms)) |
- return false; |
- |
- internal_keychain_helpers::GetPasswordsForForms(*keychain_, &database_forms, |
- forms); |
- |
- if (!database_forms.empty()) { |
- RemoveDatabaseForms(&database_forms); |
- NotifyLoginsChanged(FormsToRemoveChangeList(database_forms)); |
- } |
- |
- return true; |
-} |
- |
-bool PasswordStoreMac::FillBlacklistLogins( |
- std::vector<std::unique_ptr<PasswordForm>>* forms) { |
- DCHECK(GetBackgroundTaskRunner()->BelongsToCurrentThread()); |
- return login_metadata_db_ && login_metadata_db_->GetBlacklistLogins(forms); |
-} |
- |
-void PasswordStoreMac::AddSiteStatsImpl( |
- const password_manager::InteractionsStats& stats) { |
- DCHECK(GetBackgroundTaskRunner()->BelongsToCurrentThread()); |
- if (login_metadata_db_) |
- login_metadata_db_->stats_table().AddRow(stats); |
-} |
- |
-void PasswordStoreMac::RemoveSiteStatsImpl(const GURL& origin_domain) { |
- DCHECK(GetBackgroundTaskRunner()->BelongsToCurrentThread()); |
- if (login_metadata_db_) |
- login_metadata_db_->stats_table().RemoveRow(origin_domain); |
-} |
- |
-std::vector<password_manager::InteractionsStats> |
-PasswordStoreMac::GetAllSiteStatsImpl() { |
- DCHECK(GetBackgroundTaskRunner()->BelongsToCurrentThread()); |
- return login_metadata_db_ |
- ? login_metadata_db_->stats_table().GetAllRows() |
- : std::vector<password_manager::InteractionsStats>(); |
-} |
- |
-std::vector<password_manager::InteractionsStats> |
-PasswordStoreMac::GetSiteStatsImpl(const GURL& origin_domain) { |
- DCHECK(GetBackgroundTaskRunner()->BelongsToCurrentThread()); |
- return login_metadata_db_ |
- ? login_metadata_db_->stats_table().GetRows(origin_domain) |
- : std::vector<password_manager::InteractionsStats>(); |
-} |
- |
-bool PasswordStoreMac::AddToKeychainIfNecessary(const PasswordForm& form) { |
- if (IsLoginDatabaseOnlyForm(form)) |
- return true; |
- MacKeychainPasswordFormAdapter keychainAdapter(keychain_.get()); |
- return keychainAdapter.AddPassword(form); |
-} |
- |
-bool PasswordStoreMac::DatabaseHasFormMatchingKeychainForm( |
- const PasswordForm& form) { |
- DCHECK(login_metadata_db_); |
- bool has_match = false; |
- std::vector<std::unique_ptr<PasswordForm>> database_forms; |
- if (!login_metadata_db_->GetLogins( |
- password_manager::PasswordStore::FormDigest(form), &database_forms)) |
- return false; |
- for (const auto& db_form : database_forms) { |
- // Below we filter out fuzzy matched forms because we are only interested |
- // in exact ones. |
- if (!db_form->is_public_suffix_match && |
- internal_keychain_helpers::FormsMatchForMerge( |
- form, *db_form, internal_keychain_helpers::STRICT_FORM_MATCH) && |
- db_form->origin == form.origin) { |
- has_match = true; |
- break; |
- } |
- } |
- return has_match; |
-} |
- |
-void PasswordStoreMac::RemoveDatabaseForms( |
- std::vector<std::unique_ptr<PasswordForm>>* forms) { |
- DCHECK(login_metadata_db_); |
- std::vector<std::unique_ptr<PasswordForm>> removed_forms; |
- for (std::unique_ptr<PasswordForm>& form : *forms) { |
- if (login_metadata_db_->RemoveLogin(*form)) |
- removed_forms.push_back(std::move(form)); |
- } |
- removed_forms.swap(*forms); |
-} |
- |
-void PasswordStoreMac::RemoveKeychainForms( |
- const std::vector<std::unique_ptr<PasswordForm>>& forms) { |
- MacKeychainPasswordFormAdapter owned_keychain_adapter(keychain_.get()); |
- owned_keychain_adapter.SetFindsOnlyOwnedItems(true); |
- for (const auto& form : forms) { |
- owned_keychain_adapter.RemovePassword(*form); |
- } |
-} |
- |
-void PasswordStoreMac::CleanOrphanedForms( |
- std::vector<std::unique_ptr<PasswordForm>>* orphaned_forms) { |
- DCHECK(orphaned_forms); |
- DCHECK(login_metadata_db_); |
- |
- std::vector<std::unique_ptr<PasswordForm>> database_forms; |
- if (!login_metadata_db_->GetAutofillableLogins(&database_forms)) |
- return; |
- |
- // Filter forms with corresponding Keychain entry out of |database_forms|. |
- std::vector<std::unique_ptr<PasswordForm>> forms_with_keychain_entry; |
- internal_keychain_helpers::GetPasswordsForForms(*keychain_, &database_forms, |
- &forms_with_keychain_entry); |
- |
- // Clean up any orphaned database entries. |
- RemoveDatabaseForms(&database_forms); |
- |
- // Move the orphaned DB forms to the output parameter. |
- AppendSecondToFirst(orphaned_forms, &database_forms); |
-} |