#include "base/stl_util.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
-#include "chrome/browser/chrome_notification_types.h"
-#include "chrome/browser/password_manager/login_database.h"
-#include "chrome/browser/password_manager/password_store_change.h"
-#include "content/public/browser/notification_service.h"
+#include "chrome/browser/mac/security_wrappers.h"
+#include "components/password_manager/core/browser/login_database.h"
+#include "components/password_manager/core/browser/password_store_change.h"
#include "crypto/apple_keychain.h"
using autofill::PasswordForm;
}
if (extract_password_data) {
- UTF8ToUTF16(static_cast<const char *>(password_data), password_length,
- &(form->password_value));
+ base::UTF8ToUTF16(static_cast<const char *>(password_data), password_length,
+ &(form->password_value));
}
int port = kAnyPort;
}
switch (attr.tag) {
case kSecAccountItemAttr:
- UTF8ToUTF16(static_cast<const char *>(attr.data), attr.length,
- &(form->username_value));
+ 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);
}
bool FormsMatchForMerge(const PasswordForm& form_a,
- const PasswordForm& form_b) {
+ const PasswordForm& form_b,
+ FormMatchStrictness strictness) {
// We never merge blacklist entries between our store and the keychain.
if (form_a.blacklisted_by_user || form_b.blacklisted_by_user) {
return false;
}
- return form_a.scheme == form_b.scheme &&
- form_a.signon_realm == form_b.signon_realm &&
+ bool equal_realm = form_a.signon_realm == form_b.signon_realm;
+ if (strictness == FUZZY_FORM_MATCH) {
+ equal_realm |= (!form_a.original_signon_realm.empty()) &&
+ form_a.original_signon_realm == form_b.signon_realm;
+ }
+ return form_a.scheme == form_b.scheme && equal_realm &&
form_a.username_value == form_b.username_value;
}
-// Returns an the best match for |form| from |keychain_forms|, or NULL if there
-// is no suitable match.
+// Returns an the best match for |base_form| from |keychain_forms|, or NULL if
+// there is no suitable match.
PasswordForm* BestKeychainFormForForm(
const PasswordForm& base_form,
const std::vector<PasswordForm*>* 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 (FormsMatchForMerge(base_form, *(*i))) {
+ if (FormsMatchForMerge(base_form, *(*i), FUZZY_FORM_MATCH)) {
if (base_form.origin == (*i)->origin) {
return *i;
} else if (!partial_match) {
&is_secure, &security_domain)) {
return false;
}
- return internal_keychain_helpers::FormsMatchForMerge(query_form, other_form);
+ return internal_keychain_helpers::FormsMatchForMerge(
+ query_form, other_form, STRICT_FORM_MATCH);
}
std::vector<PasswordForm*> ExtractPasswordsMergeableWithForm(
: keychain_(keychain), finds_only_owned_(false) {
}
-std::vector<PasswordForm*>
- MacKeychainPasswordFormAdapter::PasswordsFillingForm(
- const PasswordForm& query_form) {
+std::vector<PasswordForm*> MacKeychainPasswordFormAdapter::PasswordsFillingForm(
+ const std::string& signon_realm,
+ PasswordForm::Scheme scheme) {
std::vector<SecKeychainItemRef> keychain_items =
- MatchingKeychainItems(query_form.signon_realm, query_form.scheme,
- NULL, NULL);
+ MatchingKeychainItems(signon_realm, scheme, NULL, NULL);
return ConvertKeychainItemsToForms(&keychain_items);
}
bool MacKeychainPasswordFormAdapter::HasPasswordsMergeableWithForm(
const PasswordForm& query_form) {
- std::string username = UTF16ToUTF8(query_form.username_value);
+ 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());
form.signon_realm, &server, &port, &is_secure, &security_domain)) {
return false;
}
- std::string username = UTF16ToUTF8(form.username_value);
- std::string password = UTF16ToUTF8(form.password_value);
+ std::string username = base::UTF16ToUTF8(form.username_value);
+ std::string password = base::UTF16ToUTF8(form.password_value);
std::string path = form.origin.path();
SecProtocolType protocol = is_secure ? kSecProtocolTypeHTTPS
: kSecProtocolTypeHTTP;
}
std::string path = form.origin.path();
- std::string username = UTF16ToUTF8(form.username_value);
+ std::string username = base::UTF16ToUTF8(form.username_value);
std::vector<SecKeychainItemRef> matches = MatchingKeychainItems(
form.signon_realm, form.scheme, path.c_str(), username.c_str());
#pragma mark -
-PasswordStoreMac::PasswordStoreMac(AppleKeychain* keychain,
- LoginDatabase* login_db)
- : keychain_(keychain), login_metadata_db_(login_db) {
+PasswordStoreMac::PasswordStoreMac(
+ scoped_refptr<base::SingleThreadTaskRunner> main_thread_runner,
+ scoped_refptr<base::SingleThreadTaskRunner> db_thread_runner,
+ AppleKeychain* keychain,
+ LoginDatabase* login_db)
+ : PasswordStore(main_thread_runner, db_thread_runner),
+ keychain_(keychain),
+ login_metadata_db_(login_db) {
DCHECK(keychain_.get());
DCHECK(login_metadata_db_.get());
}
-PasswordStoreMac::~PasswordStoreMac() {
- if (thread_.get()) {
- thread_->message_loop()->DeleteSoon(FROM_HERE,
- notification_service_.release());
- }
-}
+PasswordStoreMac::~PasswordStoreMac() {}
bool PasswordStoreMac::Init() {
thread_.reset(new base::Thread("Chrome_PasswordStore_Thread"));
thread_.reset(NULL);
return false;
}
- ScheduleTask(base::Bind(&PasswordStoreMac::CreateNotificationService, this));
return PasswordStore::Init();
}
-void PasswordStoreMac::ShutdownOnUIThread() {
-}
-
// Mac stores passwords in the system keychain, which can block for an
// arbitrarily long time (most notably, it can block on user confirmation
-// from a dialog). Use a dedicated thread to avoid blocking DB thread.
-bool PasswordStoreMac::ScheduleTask(const base::Closure& task) {
- if (thread_.get()) {
- thread_->message_loop()->PostTask(FROM_HERE, task);
- return true;
- }
- return false;
+// from a dialog). Run tasks on a dedicated thread to avoid blocking the DB
+// thread.
+scoped_refptr<base::SingleThreadTaskRunner>
+PasswordStoreMac::GetBackgroundTaskRunner() {
+ return (thread_.get()) ? thread_->message_loop_proxy() : NULL;
}
void PasswordStoreMac::ReportMetricsImpl() {
login_metadata_db_->ReportMetrics();
}
-void PasswordStoreMac::AddLoginImpl(const PasswordForm& form) {
+PasswordStoreChangeList PasswordStoreMac::AddLoginImpl(
+ const PasswordForm& form) {
+ PasswordStoreChangeList changes;
if (AddToKeychainIfNecessary(form)) {
if (login_metadata_db_->AddLogin(form)) {
- PasswordStoreChangeList changes;
changes.push_back(PasswordStoreChange(PasswordStoreChange::ADD, form));
- content::NotificationService::current()->Notify(
- chrome::NOTIFICATION_LOGINS_CHANGED,
- content::Source<PasswordStore>(this),
- content::Details<PasswordStoreChangeList>(&changes));
}
}
+ return changes;
}
-void PasswordStoreMac::UpdateLoginImpl(const PasswordForm& form) {
+PasswordStoreChangeList PasswordStoreMac::UpdateLoginImpl(
+ const PasswordForm& form) {
+ PasswordStoreChangeList changes;
int update_count = 0;
if (!login_metadata_db_->UpdateLogin(form, &update_count))
- return;
+ return changes;
MacKeychainPasswordFormAdapter keychain_adapter(keychain_.get());
if (update_count == 0 &&
!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;
+ 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)) {
- PasswordStoreChangeList changes;
if (update_count == 0) {
if (login_metadata_db_->AddLogin(form)) {
changes.push_back(PasswordStoreChange(PasswordStoreChange::ADD,
changes.push_back(PasswordStoreChange(PasswordStoreChange::UPDATE,
form));
}
- if (!changes.empty()) {
- content::NotificationService::current()->Notify(
- chrome::NOTIFICATION_LOGINS_CHANGED,
- content::Source<PasswordStore>(this),
- content::Details<PasswordStoreChangeList>(&changes));
- }
}
+ return changes;
}
-void PasswordStoreMac::RemoveLoginImpl(const PasswordForm& form) {
+PasswordStoreChangeList PasswordStoreMac::RemoveLoginImpl(
+ const PasswordForm& form) {
+ PasswordStoreChangeList changes;
if (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
}
}
- PasswordStoreChangeList changes;
changes.push_back(PasswordStoreChange(PasswordStoreChange::REMOVE, form));
- content::NotificationService::current()->Notify(
- chrome::NOTIFICATION_LOGINS_CHANGED,
- content::Source<PasswordStore>(this),
- content::Details<PasswordStoreChangeList>(&changes));
}
+ return changes;
}
-void PasswordStoreMac::RemoveLoginsCreatedBetweenImpl(
+PasswordStoreChangeList PasswordStoreMac::RemoveLoginsCreatedBetweenImpl(
const base::Time& delete_begin, const base::Time& delete_end) {
+ PasswordStoreChangeList changes;
std::vector<PasswordForm*> forms;
if (login_metadata_db_->GetLoginsCreatedBetween(delete_begin, delete_end,
&forms)) {
RemoveKeychainForms(orphan_keychain_forms);
STLDeleteElements(&orphan_keychain_forms);
- PasswordStoreChangeList changes;
for (std::vector<PasswordForm*>::const_iterator it = forms.begin();
it != forms.end(); ++it) {
changes.push_back(PasswordStoreChange(PasswordStoreChange::REMOVE,
**it));
}
LogStatsForBulkDeletion(changes.size());
- content::NotificationService::current()->Notify(
- chrome::NOTIFICATION_LOGINS_CHANGED,
- content::Source<PasswordStore>(this),
- content::Details<PasswordStoreChangeList>(&changes));
}
}
+ return changes;
}
void PasswordStoreMac::GetLoginsImpl(
const autofill::PasswordForm& form,
+ AuthorizationPromptPolicy prompt_policy,
const ConsumerCallbackRunner& callback_runner) {
- MacKeychainPasswordFormAdapter keychain_adapter(keychain_.get());
- std::vector<PasswordForm*> keychain_forms =
- keychain_adapter.PasswordsFillingForm(form);
+ chrome::ScopedSecKeychainSetUserInteractionAllowed user_interaction_allowed(
+ prompt_policy == ALLOW_PROMPT);
std::vector<PasswordForm*> database_forms;
login_metadata_db_->GetLogins(form, &database_forms);
+ // 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 (std::vector<PasswordForm*>::const_iterator db_form =
+ database_forms.begin();
+ db_form != database_forms.end();
+ ++db_form) {
+ // 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.
+ const std::string& original_singon_realm((*db_form)->original_signon_realm);
+ if (!original_singon_realm.empty())
+ realm_set.insert(original_singon_realm);
+ }
+ std::vector<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<PasswordForm*> temp_keychain_forms =
+ keychain_adapter.PasswordsFillingForm(*realm, form.scheme);
+ keychain_forms.insert(keychain_forms.end(),
+ temp_keychain_forms.begin(),
+ temp_keychain_forms.end());
+ }
+
std::vector<PasswordForm*> matched_forms;
internal_keychain_helpers::MergePasswordForms(&keychain_forms,
&database_forms,
}
void PasswordStoreMac::GetBlacklistLoginsImpl(GetLoginsRequest* request) {
- FillBlacklistLogins(&request->value);
+ FillBlacklistLogins(request->result());
ForwardLoginsResult(request);
}
void PasswordStoreMac::GetAutofillableLoginsImpl(GetLoginsRequest* request) {
- FillAutofillableLogins(&request->value);
+ FillAutofillableLogins(request->result());
ForwardLoginsResult(request);
}
login_metadata_db_->GetLogins(form, &database_forms);
for (std::vector<PasswordForm*>::iterator i = database_forms.begin();
i != database_forms.end(); ++i) {
- if (internal_keychain_helpers::FormsMatchForMerge(form, **i) &&
+ // Below we filter out forms with non-empty original_signon_realm, because
+ // those signal fuzzy matches, and we are only interested in exact ones.
+ if ((*i)->original_signon_realm.empty() &&
+ internal_keychain_helpers::FormsMatchForMerge(
+ form, **i, internal_keychain_helpers::STRICT_FORM_MATCH) &&
(*i)->origin == form.origin) {
has_match = true;
break;
owned_keychain_adapter.RemovePassword(**i);
}
}
-
-void PasswordStoreMac::CreateNotificationService() {
- notification_service_.reset(content::NotificationService::Create());
-}