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