| 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);
|
| -}
|
|
|