Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(194)

Unified Diff: chrome/browser/password_manager/password_store_mac.cc

Issue 23477015: [sync] Significantly speed up password model association on mac (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Rebase Created 7 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | chrome/browser/password_manager/password_store_mac_internal.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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
index 2b34732c8b14d2985745e2d17b1475a92883bb52..8037a8a662b961c084d506ad9850a241a0b78866 100644
--- a/chrome/browser/password_manager/password_store_mac.cc
+++ b/chrome/browser/password_manager/password_store_mac.cc
@@ -8,6 +8,7 @@
#include <CoreServices/CoreServices.h>
#include <set>
#include <string>
+#include <utility>
#include <vector>
#include "base/callback.h"
@@ -233,7 +234,8 @@ PasswordForm::Scheme SchemeForAuthType(SecAuthenticationType auth_type) {
bool FillPasswordFormFromKeychainItem(const AppleKeychain& keychain,
const SecKeychainItemRef& keychain_item,
- PasswordForm* form) {
+ PasswordForm* form,
+ bool extract_password_data) {
DCHECK(form);
SecKeychainAttributeInfo attrInfo;
@@ -252,11 +254,18 @@ bool FillPasswordFormFromKeychainItem(const AppleKeychain& keychain,
SecKeychainAttributeList *attrList;
UInt32 password_length;
- void* password_data;
+
+ // 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);
+ password_data_ref);
if (result != noErr) {
// We don't log errSecAuthFailed because that just means that the user
@@ -267,8 +276,10 @@ bool FillPasswordFormFromKeychainItem(const AppleKeychain& keychain,
return false;
}
- UTF8ToUTF16(static_cast<const char *>(password_data), password_length,
- &(form->password_value));
+ if (extract_password_data) {
+ UTF8ToUTF16(static_cast<const char *>(password_data), password_length,
+ &(form->password_value));
+ }
int port = kAnyPort;
std::string server;
@@ -330,7 +341,8 @@ bool FillPasswordFormFromKeychainItem(const AppleKeychain& keychain,
// 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 (form->password_value.empty() || EqualsASCII(form->password_value, " ")) {
+ if (extract_password_data && (form->password_value.empty() ||
+ EqualsASCII(form->password_value, " "))) {
form->blacklisted_by_user = true;
}
@@ -443,17 +455,50 @@ void MergePasswordForms(std::vector<PasswordForm*>* keychain_forms,
DeleteVectorElementsInSet(keychain_forms, used_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 (std::vector<SecKeychainItemRef>::iterator i = keychain_items->begin();
+ i != keychain_items->end(); ++i) {
+ PasswordForm* form_without_password = new PasswordForm();
+ internal_keychain_helpers::FillPasswordFormFromKeychainItem(
+ keychain,
+ *i,
+ form_without_password,
+ false); // Load password attributes, but not password data.
+ item_form_pairs.push_back(std::make_pair(&(*i), form_without_password));
+ }
+ return item_form_pairs;
+}
+
std::vector<PasswordForm*> GetPasswordsForForms(
const AppleKeychain& keychain,
std::vector<PasswordForm*>* database_forms) {
- MacKeychainPasswordFormAdapter keychain_adapter(&keychain);
-
+ // 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<PasswordForm*> merged_forms;
for (std::vector<PasswordForm*>::iterator i = database_forms->begin();
i != database_forms->end();) {
std::vector<PasswordForm*> db_form_container(1, *i);
std::vector<PasswordForm*> keychain_matches =
- keychain_adapter.PasswordsMergeableWithForm(**i);
+ ExtractPasswordsMergeableWithForm(keychain, item_form_pairs, **i);
MergePasswordForms(&keychain_matches, &db_form_container, &merged_forms);
if (db_form_container.empty()) {
i = database_forms->erase(i);
@@ -462,9 +507,81 @@ std::vector<PasswordForm*> GetPasswordsForForms(
}
STLDeleteElements(&keychain_matches);
}
+
+ // Clean up temporary PasswordForms and SecKeychainItemRefs.
+ STLDeleteContainerPairSecondPointers(item_form_pairs.begin(),
+ item_form_pairs.end());
+ for (std::vector<SecKeychainItemRef>::iterator i = keychain_items.begin();
+ i != keychain_items.end(); ++i) {
+ keychain.Free(*i);
+ }
return merged_forms;
}
+// TODO(stuartmorgan): signon_realm for proxies is not yet supported.
+bool ExtractSignonRealmComponents(
+ const std::string& signon_realm, std::string* server, int* port,
+ bool* is_secure, std::string* security_domain) {
+ // 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.SchemeIsSecure();
+ 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;
+ int port;
+ bool is_secure;
+ if (!ExtractSignonRealmComponents(query_form.signon_realm, &server, &port,
+ &is_secure, &security_domain)) {
+ return false;
+ }
+ return internal_keychain_helpers::FormsMatchForMerge(query_form, other_form);
+}
+
+std::vector<PasswordForm*> ExtractPasswordsMergeableWithForm(
+ const AppleKeychain& keychain,
+ const std::vector<ItemFormPair>& item_form_pairs,
+ const PasswordForm& query_form) {
+ std::vector<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.
+ scoped_ptr<PasswordForm> form_with_password(new PasswordForm());
+ internal_keychain_helpers::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(form_with_password.release());
+ }
+ }
+ return matches;
+}
+
} // namespace internal_keychain_helpers
#pragma mark -
@@ -484,17 +601,6 @@ std::vector<PasswordForm*>
return ConvertKeychainItemsToForms(&keychain_items);
}
-std::vector<PasswordForm*>
- MacKeychainPasswordFormAdapter::PasswordsMergeableWithForm(
- const PasswordForm& query_form) {
- std::string username = UTF16ToUTF8(query_form.username_value);
- std::vector<SecKeychainItemRef> keychain_items =
- MatchingKeychainItems(query_form.signon_realm, query_form.scheme,
- NULL, username.c_str());
-
- return ConvertKeychainItemsToForms(&keychain_items);
-}
-
PasswordForm* MacKeychainPasswordFormAdapter::PasswordExactlyMatchingForm(
const PasswordForm& query_form) {
SecKeychainItemRef keychain_item = KeychainItemForForm(query_form);
@@ -502,7 +608,8 @@ PasswordForm* MacKeychainPasswordFormAdapter::PasswordExactlyMatchingForm(
PasswordForm* form = new PasswordForm();
internal_keychain_helpers::FillPasswordFormFromKeychainItem(*keychain_,
keychain_item,
- form);
+ form,
+ true);
keychain_->Free(keychain_item);
return form;
}
@@ -523,8 +630,8 @@ bool MacKeychainPasswordFormAdapter::HasPasswordsMergeableWithForm(
return !matches.empty();
}
-std::vector<PasswordForm*>
- MacKeychainPasswordFormAdapter::GetAllPasswordFormPasswords() {
+std::vector<SecKeychainItemRef>
+ MacKeychainPasswordFormAdapter::GetAllPasswordFormKeychainItems() {
SecAuthenticationType supported_auth_types[] = {
kSecAuthenticationTypeHTMLForm,
kSecAuthenticationTypeHTTPBasic,
@@ -538,8 +645,13 @@ std::vector<PasswordForm*>
NULL, NULL, NULL, CreatorCodeForSearch());
keychain_search.FindMatchingItems(&matches);
}
+ return matches;
+}
- return ConvertKeychainItemsToForms(&matches);
+std::vector<PasswordForm*>
+ MacKeychainPasswordFormAdapter::GetAllPasswordFormPasswords() {
+ std::vector<SecKeychainItemRef> items = GetAllPasswordFormKeychainItems();
+ return ConvertKeychainItemsToForms(&items);
}
bool MacKeychainPasswordFormAdapter::AddPassword(const PasswordForm& form) {
@@ -550,8 +662,8 @@ bool MacKeychainPasswordFormAdapter::AddPassword(const PasswordForm& form) {
std::string security_domain;
int port;
bool is_secure;
- if (!ExtractSignonRealmComponents(form.signon_realm, &server, &port,
- &is_secure, &security_domain)) {
+ if (!internal_keychain_helpers::ExtractSignonRealmComponents(
+ form.signon_realm, &server, &port, &is_secure, &security_domain)) {
return false;
}
std::string username = UTF16ToUTF8(form.username_value);
@@ -607,8 +719,8 @@ std::vector<PasswordForm*>
for (std::vector<SecKeychainItemRef>::const_iterator i = items->begin();
i != items->end(); ++i) {
PasswordForm* form = new PasswordForm();
- if (internal_keychain_helpers::FillPasswordFormFromKeychainItem(*keychain_,
- *i, form)) {
+ if (internal_keychain_helpers::FillPasswordFormFromKeychainItem(
+ *keychain_, *i, form, true)) {
keychain_forms.push_back(form);
}
keychain_->Free(*i);
@@ -652,8 +764,8 @@ std::vector<SecKeychainItemRef>
std::string security_domain;
int port;
bool is_secure;
- if (!ExtractSignonRealmComponents(signon_realm, &server, &port,
- &is_secure, &security_domain)) {
+ 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.
@@ -671,33 +783,6 @@ std::vector<SecKeychainItemRef>
return matches;
}
-// TODO(stuartmorgan): signon_realm for proxies is not yet supported.
-bool MacKeychainPasswordFormAdapter::ExtractSignonRealmComponents(
- const std::string& signon_realm, std::string* server, int* port,
- bool* is_secure, std::string* security_domain) {
- // 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.SchemeIsSecure();
- 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;
-}
-
// Returns the Keychain SecAuthenticationType type corresponding to |scheme|.
SecAuthenticationType MacKeychainPasswordFormAdapter::AuthTypeForScheme(
PasswordForm::Scheme scheme) {
« no previous file with comments | « no previous file | chrome/browser/password_manager/password_store_mac_internal.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698