ozone: evdev: Sync caps lock LED state to evdev
[chromium-blink-merge.git] / chrome / browser / password_manager / password_store_mac.cc
blob31c0d2b43d67d07a11c33b539ebc98d31deac799
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/stl_util.h"
20 #include "base/strings/string_util.h"
21 #include "base/strings/utf_string_conversions.h"
22 #include "chrome/browser/mac/security_wrappers.h"
23 #include "components/password_manager/core/browser/affiliation_utils.h"
24 #include "components/password_manager/core/browser/login_database.h"
25 #include "components/password_manager/core/browser/password_store_change.h"
26 #include "content/public/browser/browser_thread.h"
27 #include "crypto/apple_keychain.h"
29 using autofill::PasswordForm;
30 using crypto::AppleKeychain;
31 using password_manager::PasswordStoreChange;
32 using password_manager::PasswordStoreChangeList;
34 namespace {
36 // Utility class to handle the details of constructing and running a keychain
37 // search from a set of attributes.
38 class KeychainSearch {
39 public:
40 explicit KeychainSearch(const AppleKeychain& keychain);
41 ~KeychainSearch();
43 // Sets up a keycahin search based on an non "null" (NULL for char*,
44 // The appropriate "Any" entry for other types) arguments.
46 // IMPORTANT: Any paramaters passed in *must* remain valid for as long as the
47 // KeychainSearch object, since the search uses them by reference.
48 void Init(const char* server,
49 const UInt32* port,
50 const SecProtocolType* protocol,
51 const SecAuthenticationType* auth_type,
52 const char* security_domain,
53 const char* path,
54 const char* username,
55 const OSType* creator);
57 // Fills |items| with all Keychain items that match the Init'd search.
58 // If the search fails for any reason, |items| will be unchanged.
59 void FindMatchingItems(std::vector<SecKeychainItemRef>* matches);
61 private:
62 const AppleKeychain* keychain_;
63 SecKeychainAttributeList search_attributes_;
64 SecKeychainSearchRef search_ref_;
67 KeychainSearch::KeychainSearch(const AppleKeychain& keychain)
68 : keychain_(&keychain), search_ref_(NULL) {
69 search_attributes_.count = 0;
70 search_attributes_.attr = NULL;
73 KeychainSearch::~KeychainSearch() {
74 if (search_attributes_.attr) {
75 free(search_attributes_.attr);
79 void KeychainSearch::Init(const char* server,
80 const UInt32* port,
81 const SecProtocolType* protocol,
82 const SecAuthenticationType* auth_type,
83 const char* security_domain,
84 const char* path,
85 const char* username,
86 const OSType* creator) {
87 // Allocate enough to hold everything we might use.
88 const unsigned int kMaxEntryCount = 8;
89 search_attributes_.attr =
90 static_cast<SecKeychainAttribute*>(calloc(kMaxEntryCount,
91 sizeof(SecKeychainAttribute)));
92 unsigned int entries = 0;
93 // We only use search_attributes_ with SearchCreateFromAttributes, which takes
94 // a "const SecKeychainAttributeList *", so we trust that they won't try
95 // to modify the list, and that casting away const-ness is thus safe.
96 if (server != NULL) {
97 DCHECK_LT(entries, kMaxEntryCount);
98 search_attributes_.attr[entries].tag = kSecServerItemAttr;
99 search_attributes_.attr[entries].length = strlen(server);
100 search_attributes_.attr[entries].data =
101 const_cast<void*>(static_cast<const void*>(server));
102 ++entries;
104 if (port != NULL && *port != kAnyPort) {
105 DCHECK_LE(entries, kMaxEntryCount);
106 search_attributes_.attr[entries].tag = kSecPortItemAttr;
107 search_attributes_.attr[entries].length = sizeof(*port);
108 search_attributes_.attr[entries].data =
109 const_cast<void*>(static_cast<const void*>(port));
110 ++entries;
112 if (protocol != NULL && *protocol != kSecProtocolTypeAny) {
113 DCHECK_LE(entries, kMaxEntryCount);
114 search_attributes_.attr[entries].tag = kSecProtocolItemAttr;
115 search_attributes_.attr[entries].length = sizeof(*protocol);
116 search_attributes_.attr[entries].data =
117 const_cast<void*>(static_cast<const void*>(protocol));
118 ++entries;
120 if (auth_type != NULL && *auth_type != kSecAuthenticationTypeAny) {
121 DCHECK_LE(entries, kMaxEntryCount);
122 search_attributes_.attr[entries].tag = kSecAuthenticationTypeItemAttr;
123 search_attributes_.attr[entries].length = sizeof(*auth_type);
124 search_attributes_.attr[entries].data =
125 const_cast<void*>(static_cast<const void*>(auth_type));
126 ++entries;
128 if (security_domain != NULL && strlen(security_domain) > 0) {
129 DCHECK_LE(entries, kMaxEntryCount);
130 search_attributes_.attr[entries].tag = kSecSecurityDomainItemAttr;
131 search_attributes_.attr[entries].length = strlen(security_domain);
132 search_attributes_.attr[entries].data =
133 const_cast<void*>(static_cast<const void*>(security_domain));
134 ++entries;
136 if (path != NULL && strlen(path) > 0 && strcmp(path, "/") != 0) {
137 DCHECK_LE(entries, kMaxEntryCount);
138 search_attributes_.attr[entries].tag = kSecPathItemAttr;
139 search_attributes_.attr[entries].length = strlen(path);
140 search_attributes_.attr[entries].data =
141 const_cast<void*>(static_cast<const void*>(path));
142 ++entries;
144 if (username != NULL) {
145 DCHECK_LE(entries, kMaxEntryCount);
146 search_attributes_.attr[entries].tag = kSecAccountItemAttr;
147 search_attributes_.attr[entries].length = strlen(username);
148 search_attributes_.attr[entries].data =
149 const_cast<void*>(static_cast<const void*>(username));
150 ++entries;
152 if (creator != NULL) {
153 DCHECK_LE(entries, kMaxEntryCount);
154 search_attributes_.attr[entries].tag = kSecCreatorItemAttr;
155 search_attributes_.attr[entries].length = sizeof(*creator);
156 search_attributes_.attr[entries].data =
157 const_cast<void*>(static_cast<const void*>(creator));
158 ++entries;
160 search_attributes_.count = entries;
163 void KeychainSearch::FindMatchingItems(std::vector<SecKeychainItemRef>* items) {
164 OSStatus result = keychain_->SearchCreateFromAttributes(
165 NULL, kSecInternetPasswordItemClass, &search_attributes_, &search_ref_);
167 if (result != noErr) {
168 OSSTATUS_LOG(ERROR, result) << "Keychain lookup failed";
169 return;
172 SecKeychainItemRef keychain_item;
173 while (keychain_->SearchCopyNext(search_ref_, &keychain_item) == noErr) {
174 // Consumer is responsible for freeing the items.
175 items->push_back(keychain_item);
178 keychain_->Free(search_ref_);
179 search_ref_ = NULL;
182 PasswordStoreChangeList FormsToRemoveChangeList(
183 const std::vector<PasswordForm*>& forms) {
184 PasswordStoreChangeList changes;
185 for (std::vector<PasswordForm*>::const_iterator i = forms.begin();
186 i != forms.end(); ++i) {
187 changes.push_back(PasswordStoreChange(PasswordStoreChange::REMOVE, **i));
189 return changes;
192 // Moves the content of |second| to the end of |first|.
193 void AppendSecondToFirst(ScopedVector<autofill::PasswordForm>* first,
194 ScopedVector<autofill::PasswordForm>* second) {
195 first->insert(first->end(), second->begin(), second->end());
196 second->weak_clear();
199 // Returns the best match for |base_form| from |keychain_forms|, or nullptr if
200 // there is no suitable match.
201 const PasswordForm* BestKeychainFormForForm(
202 const PasswordForm& base_form,
203 const std::vector<PasswordForm*>& keychain_forms) {
204 const PasswordForm* partial_match = nullptr;
205 for (const auto* keychain_form : keychain_forms) {
206 // TODO(stuartmorgan): We should really be scoring path matches and picking
207 // the best, rather than just checking exact-or-not (although in practice
208 // keychain items with paths probably came from us).
209 if (internal_keychain_helpers::FormsMatchForMerge(
210 base_form, *keychain_form,
211 internal_keychain_helpers::FUZZY_FORM_MATCH)) {
212 if (base_form.origin == keychain_form->origin) {
213 return keychain_form;
214 } else if (!partial_match) {
215 partial_match = keychain_form;
219 return partial_match;
222 // Iterates over all elements in |forms|, passes the pointed to objects to
223 // |move_form|, and clears |forms| efficiently. FormMover needs to be a callable
224 // entity, accepting scoped_ptr<autofill::PasswordForm> as its sole argument.
225 template <typename FormMover>
226 inline void MoveAllFormsOut(ScopedVector<autofill::PasswordForm>* forms,
227 FormMover mover) {
228 for (autofill::PasswordForm* form_ptr : *forms) {
229 mover(scoped_ptr<autofill::PasswordForm>(form_ptr));
231 // We moved the ownership of every form out of |forms|. For performance
232 // reasons, we can just weak_clear it, instead of nullptr-ing the respective
233 // elements and letting the vector's destructor to go through the list once
234 // more. This was tested on a benchmark, and seemed to make a difference on
235 // Mac.
236 forms->weak_clear();
239 } // namespace
241 #pragma mark -
243 // TODO(stuartmorgan): Convert most of this to private helpers in
244 // MacKeychainPasswordFormAdapter once it has sufficient higher-level public
245 // methods to provide test coverage.
246 namespace internal_keychain_helpers {
248 // Returns a URL built from the given components. To create a URL without a
249 // port, pass kAnyPort for the |port| parameter.
250 GURL URLFromComponents(bool is_secure, const std::string& host, int port,
251 const std::string& path) {
252 GURL::Replacements url_components;
253 std::string scheme(is_secure ? "https" : "http");
254 url_components.SetSchemeStr(scheme);
255 url_components.SetHostStr(host);
256 std::string port_string; // Must remain in scope until after we do replacing.
257 if (port != kAnyPort) {
258 std::ostringstream port_stringstream;
259 port_stringstream << port;
260 port_string = port_stringstream.str();
261 url_components.SetPortStr(port_string);
263 url_components.SetPathStr(path);
265 GURL url("http://dummy.com"); // ReplaceComponents needs a valid URL.
266 return url.ReplaceComponents(url_components);
269 // Converts a Keychain time string to a Time object, returning true if
270 // time_string_bytes was parsable. If the return value is false, the value of
271 // |time| is unchanged.
272 bool TimeFromKeychainTimeString(const char* time_string_bytes,
273 unsigned int byte_length,
274 base::Time* time) {
275 DCHECK(time);
277 char* time_string = static_cast<char*>(malloc(byte_length + 1));
278 memcpy(time_string, time_string_bytes, byte_length);
279 time_string[byte_length] = '\0';
280 base::Time::Exploded exploded_time;
281 bzero(&exploded_time, sizeof(exploded_time));
282 // The time string is of the form "yyyyMMddHHmmss'Z", in UTC time.
283 int assignments = sscanf(time_string, "%4d%2d%2d%2d%2d%2dZ",
284 &exploded_time.year, &exploded_time.month,
285 &exploded_time.day_of_month, &exploded_time.hour,
286 &exploded_time.minute, &exploded_time.second);
287 free(time_string);
289 if (assignments == 6) {
290 *time = base::Time::FromUTCExploded(exploded_time);
291 return true;
293 return false;
296 // Returns the PasswordForm Scheme corresponding to |auth_type|.
297 PasswordForm::Scheme SchemeForAuthType(SecAuthenticationType auth_type) {
298 switch (auth_type) {
299 case kSecAuthenticationTypeHTMLForm: return PasswordForm::SCHEME_HTML;
300 case kSecAuthenticationTypeHTTPBasic: return PasswordForm::SCHEME_BASIC;
301 case kSecAuthenticationTypeHTTPDigest: return PasswordForm::SCHEME_DIGEST;
302 default: return PasswordForm::SCHEME_OTHER;
306 bool FillPasswordFormFromKeychainItem(const AppleKeychain& keychain,
307 const SecKeychainItemRef& keychain_item,
308 PasswordForm* form,
309 bool extract_password_data) {
310 DCHECK(form);
312 SecKeychainAttributeInfo attrInfo;
313 UInt32 tags[] = { kSecAccountItemAttr,
314 kSecServerItemAttr,
315 kSecPortItemAttr,
316 kSecPathItemAttr,
317 kSecProtocolItemAttr,
318 kSecAuthenticationTypeItemAttr,
319 kSecSecurityDomainItemAttr,
320 kSecCreationDateItemAttr,
321 kSecNegativeItemAttr };
322 attrInfo.count = arraysize(tags);
323 attrInfo.tag = tags;
324 attrInfo.format = NULL;
326 SecKeychainAttributeList *attrList;
327 UInt32 password_length;
329 // If |extract_password_data| is false, do not pass in a reference to
330 // |password_data|. ItemCopyAttributesAndData will then extract only the
331 // attributes of |keychain_item| (doesn't require OS authorization), and not
332 // attempt to extract its password data (requires OS authorization).
333 void* password_data = NULL;
334 void** password_data_ref = extract_password_data ? &password_data : NULL;
336 OSStatus result = keychain.ItemCopyAttributesAndData(keychain_item, &attrInfo,
337 NULL, &attrList,
338 &password_length,
339 password_data_ref);
341 if (result != noErr) {
342 // We don't log errSecAuthFailed because that just means that the user
343 // chose not to allow us access to the item.
344 if (result != errSecAuthFailed) {
345 OSSTATUS_LOG(ERROR, result) << "Keychain data load failed";
347 return false;
350 if (extract_password_data) {
351 base::UTF8ToUTF16(static_cast<const char *>(password_data), password_length,
352 &(form->password_value));
355 int port = kAnyPort;
356 std::string server;
357 std::string security_domain;
358 std::string path;
359 for (unsigned int i = 0; i < attrList->count; i++) {
360 SecKeychainAttribute attr = attrList->attr[i];
361 if (!attr.data) {
362 continue;
364 switch (attr.tag) {
365 case kSecAccountItemAttr:
366 base::UTF8ToUTF16(static_cast<const char *>(attr.data), attr.length,
367 &(form->username_value));
368 break;
369 case kSecServerItemAttr:
370 server.assign(static_cast<const char *>(attr.data), attr.length);
371 break;
372 case kSecPortItemAttr:
373 port = *(static_cast<UInt32*>(attr.data));
374 break;
375 case kSecPathItemAttr:
376 path.assign(static_cast<const char *>(attr.data), attr.length);
377 break;
378 case kSecProtocolItemAttr:
380 SecProtocolType protocol = *(static_cast<SecProtocolType*>(attr.data));
381 // TODO(stuartmorgan): Handle proxy types
382 form->ssl_valid = (protocol == kSecProtocolTypeHTTPS);
383 break;
385 case kSecAuthenticationTypeItemAttr:
387 SecAuthenticationType auth_type =
388 *(static_cast<SecAuthenticationType*>(attr.data));
389 form->scheme = SchemeForAuthType(auth_type);
390 break;
392 case kSecSecurityDomainItemAttr:
393 security_domain.assign(static_cast<const char *>(attr.data),
394 attr.length);
395 break;
396 case kSecCreationDateItemAttr:
397 // The only way to get a date out of Keychain is as a string. Really.
398 // (The docs claim it's an int, but the header is correct.)
399 TimeFromKeychainTimeString(static_cast<char*>(attr.data), attr.length,
400 &form->date_created);
401 break;
402 case kSecNegativeItemAttr:
403 Boolean negative_item = *(static_cast<Boolean*>(attr.data));
404 if (negative_item) {
405 form->blacklisted_by_user = true;
407 break;
410 keychain.ItemFreeAttributesAndData(attrList, password_data);
412 // kSecNegativeItemAttr doesn't seem to actually be in widespread use. In
413 // practice, other browsers seem to use a "" or " " password (and a special
414 // user name) to indicated blacklist entries.
415 if (extract_password_data && (form->password_value.empty() ||
416 EqualsASCII(form->password_value, " "))) {
417 form->blacklisted_by_user = true;
420 // Android facet URLs aren't parsed correctly by GURL and need to be handled
421 // separately.
422 if (password_manager::IsValidAndroidFacetURI(server)) {
423 form->signon_realm = server;
424 form->origin = GURL();
425 } else {
426 form->origin = URLFromComponents(form->ssl_valid, server, port, path);
427 // TODO(stuartmorgan): Handle proxies, which need a different signon_realm
428 // format.
429 form->signon_realm = form->origin.GetOrigin().spec();
430 if (form->scheme != PasswordForm::SCHEME_HTML) {
431 form->signon_realm.append(security_domain);
434 return true;
437 bool FormsMatchForMerge(const PasswordForm& form_a,
438 const PasswordForm& form_b,
439 FormMatchStrictness strictness) {
440 // We never merge blacklist entries between our store and the keychain.
441 if (form_a.blacklisted_by_user || form_b.blacklisted_by_user) {
442 return false;
444 bool equal_realm = form_a.signon_realm == form_b.signon_realm;
445 if (strictness == FUZZY_FORM_MATCH) {
446 equal_realm |= (!form_a.original_signon_realm.empty()) &&
447 form_a.original_signon_realm == form_b.signon_realm;
449 return form_a.scheme == form_b.scheme && equal_realm &&
450 form_a.username_value == form_b.username_value;
453 // Moves entries from |forms| that are blacklist entries into |blacklist|.
454 void ExtractBlacklistForms(ScopedVector<autofill::PasswordForm>* forms,
455 ScopedVector<autofill::PasswordForm>* blacklist) {
456 blacklist->reserve(blacklist->size() + forms->size());
457 ScopedVector<autofill::PasswordForm> non_blacklist;
458 // Move forms in either |non_blacklist| or |blacklist|, depending on whether
459 // they are blacklisted by the user.
460 MoveAllFormsOut(forms, [&non_blacklist, blacklist](
461 scoped_ptr<autofill::PasswordForm> form) {
462 if (form->blacklisted_by_user)
463 blacklist->push_back(form.release());
464 else
465 non_blacklist.push_back(form.release());
467 forms->swap(non_blacklist);
470 // Takes |keychain_forms| and |database_forms| and moves the following 2 types
471 // of forms to |merged_forms|: (1) blacklisted |database_forms|, (2)
472 // |database_forms| which have a corresponding entry in |keychain_forms|. The
473 // database forms of type (2) have their password value updated from the
474 // corresponding keychain form, and all the keychain forms corresponding to some
475 // database form are removed from |keychain_forms| and deleted.
476 void MergePasswordForms(ScopedVector<autofill::PasswordForm>* keychain_forms,
477 ScopedVector<autofill::PasswordForm>* database_forms,
478 ScopedVector<autofill::PasswordForm>* merged_forms) {
479 // Pull out the database blacklist items, since they are used as-is rather
480 // than being merged with keychain forms.
481 ExtractBlacklistForms(database_forms, merged_forms);
483 // Merge the normal entries.
484 ScopedVector<autofill::PasswordForm> unused_database_forms;
485 unused_database_forms.reserve(database_forms->size());
486 std::set<const autofill::PasswordForm*> used_keychain_forms;
487 // Move all database forms to either |merged_forms| or
488 // |unused_database_forms|, based on whether they have a match in the keychain
489 // forms or not. If there is a match, add its password to the DB form and
490 // mark the keychain form as used.
491 MoveAllFormsOut(database_forms, [keychain_forms, &used_keychain_forms,
492 merged_forms, &unused_database_forms](
493 scoped_ptr<autofill::PasswordForm> form) {
494 const PasswordForm* best_match =
495 BestKeychainFormForForm(*form, keychain_forms->get());
496 if (best_match) {
497 used_keychain_forms.insert(best_match);
498 form->password_value = best_match->password_value;
499 merged_forms->push_back(form.release());
500 } else {
501 unused_database_forms.push_back(form.release());
504 database_forms->swap(unused_database_forms);
506 // Clear out all the Keychain entries we used.
507 ScopedVector<autofill::PasswordForm> unused_keychain_forms;
508 unused_keychain_forms.reserve(keychain_forms->size());
509 for (auto& keychain_form : *keychain_forms) {
510 if (!ContainsKey(used_keychain_forms, keychain_form)) {
511 unused_keychain_forms.push_back(keychain_form);
512 keychain_form = nullptr;
515 keychain_forms->swap(unused_keychain_forms);
518 std::vector<ItemFormPair> ExtractAllKeychainItemAttributesIntoPasswordForms(
519 std::vector<SecKeychainItemRef>* keychain_items,
520 const AppleKeychain& keychain) {
521 DCHECK(keychain_items);
522 MacKeychainPasswordFormAdapter keychain_adapter(&keychain);
523 *keychain_items = keychain_adapter.GetAllPasswordFormKeychainItems();
524 std::vector<ItemFormPair> item_form_pairs;
525 for (std::vector<SecKeychainItemRef>::iterator i = keychain_items->begin();
526 i != keychain_items->end(); ++i) {
527 PasswordForm* form_without_password = new PasswordForm();
528 internal_keychain_helpers::FillPasswordFormFromKeychainItem(
529 keychain,
531 form_without_password,
532 false); // Load password attributes, but not password data.
533 item_form_pairs.push_back(std::make_pair(&(*i), form_without_password));
535 return item_form_pairs;
538 void GetPasswordsForForms(const AppleKeychain& keychain,
539 ScopedVector<autofill::PasswordForm>* database_forms,
540 ScopedVector<autofill::PasswordForm>* passwords) {
541 // First load the attributes of all items in the keychain without loading
542 // their password data, and then match items in |database_forms| against them.
543 // This avoids individually searching through the keychain for passwords
544 // matching each form in |database_forms|, and results in a significant
545 // performance gain, replacing O(N) keychain search operations with a single
546 // operation that loads all keychain items, and then selective reads of only
547 // the relevant passwords. See crbug.com/263685.
548 std::vector<SecKeychainItemRef> keychain_items;
549 std::vector<ItemFormPair> item_form_pairs =
550 ExtractAllKeychainItemAttributesIntoPasswordForms(&keychain_items,
551 keychain);
553 // Next, compare the attributes of the PasswordForms in |database_forms|
554 // against those in |item_form_pairs|, and extract password data for each
555 // matching PasswordForm using its corresponding SecKeychainItemRef.
556 ScopedVector<autofill::PasswordForm> unused_db_forms;
557 unused_db_forms.reserve(database_forms->size());
558 // Move database forms with a password stored in |keychain| to |passwords|,
559 // including the password. The rest is moved to |unused_db_forms|.
560 MoveAllFormsOut(database_forms,
561 [&keychain, &item_form_pairs, passwords, &unused_db_forms](
562 scoped_ptr<autofill::PasswordForm> form) {
563 ScopedVector<autofill::PasswordForm> keychain_matches =
564 ExtractPasswordsMergeableWithForm(keychain, item_form_pairs, *form);
566 ScopedVector<autofill::PasswordForm> db_form_container;
567 db_form_container.push_back(form.release());
568 MergePasswordForms(&keychain_matches, &db_form_container, passwords);
569 AppendSecondToFirst(&unused_db_forms, &db_form_container);
571 database_forms->swap(unused_db_forms);
573 STLDeleteContainerPairSecondPointers(item_form_pairs.begin(),
574 item_form_pairs.end());
575 for (SecKeychainItemRef item : keychain_items) {
576 keychain.Free(item);
580 // TODO(stuartmorgan): signon_realm for proxies is not yet supported.
581 bool ExtractSignonRealmComponents(const std::string& signon_realm,
582 std::string* server,
583 UInt32* port,
584 bool* is_secure,
585 std::string* security_domain) {
586 // GURL does not parse Android facet URIs correctly.
587 if (password_manager::IsValidAndroidFacetURI(signon_realm)) {
588 if (server)
589 *server = signon_realm;
590 if (is_secure)
591 *is_secure = true;
592 if (port)
593 *port = 0;
594 if (security_domain)
595 security_domain->clear();
596 return true;
599 // The signon_realm will be the Origin portion of a URL for an HTML form,
600 // and the same but with the security domain as a path for HTTP auth.
601 GURL realm_as_url(signon_realm);
602 if (!realm_as_url.is_valid()) {
603 return false;
606 if (server)
607 *server = realm_as_url.host();
608 if (is_secure)
609 *is_secure = realm_as_url.SchemeIsSecure();
610 if (port)
611 *port = realm_as_url.has_port() ? atoi(realm_as_url.port().c_str()) : 0;
612 if (security_domain) {
613 // Strip the leading '/' off of the path to get the security domain.
614 if (realm_as_url.path().length() > 0)
615 *security_domain = realm_as_url.path().substr(1);
616 else
617 security_domain->clear();
619 return true;
622 bool FormIsValidAndMatchesOtherForm(const PasswordForm& query_form,
623 const PasswordForm& other_form) {
624 std::string server;
625 std::string security_domain;
626 UInt32 port;
627 bool is_secure;
628 if (!ExtractSignonRealmComponents(query_form.signon_realm, &server, &port,
629 &is_secure, &security_domain)) {
630 return false;
632 return FormsMatchForMerge(query_form, other_form, STRICT_FORM_MATCH);
635 ScopedVector<autofill::PasswordForm> ExtractPasswordsMergeableWithForm(
636 const AppleKeychain& keychain,
637 const std::vector<ItemFormPair>& item_form_pairs,
638 const PasswordForm& query_form) {
639 ScopedVector<autofill::PasswordForm> matches;
640 for (std::vector<ItemFormPair>::const_iterator i = item_form_pairs.begin();
641 i != item_form_pairs.end(); ++i) {
642 if (FormIsValidAndMatchesOtherForm(query_form, *(i->second))) {
643 // Create a new object, since the caller is responsible for deleting the
644 // returned forms.
645 scoped_ptr<PasswordForm> form_with_password(new PasswordForm());
646 FillPasswordFormFromKeychainItem(
647 keychain, *(i->first), form_with_password.get(),
648 true); // Load password attributes and data.
649 // Do not include blacklisted items found in the keychain.
650 if (!form_with_password->blacklisted_by_user)
651 matches.push_back(form_with_password.release());
654 return matches.Pass();
657 } // namespace internal_keychain_helpers
659 #pragma mark -
661 MacKeychainPasswordFormAdapter::MacKeychainPasswordFormAdapter(
662 const AppleKeychain* keychain)
663 : keychain_(keychain), finds_only_owned_(false) {
666 ScopedVector<autofill::PasswordForm>
667 MacKeychainPasswordFormAdapter::PasswordsFillingForm(
668 const std::string& signon_realm,
669 PasswordForm::Scheme scheme) {
670 std::vector<SecKeychainItemRef> keychain_items =
671 MatchingKeychainItems(signon_realm, scheme, NULL, NULL);
672 return ConvertKeychainItemsToForms(&keychain_items);
675 bool MacKeychainPasswordFormAdapter::HasPasswordExactlyMatchingForm(
676 const PasswordForm& query_form) {
677 SecKeychainItemRef keychain_item = KeychainItemForForm(query_form);
678 if (keychain_item) {
679 keychain_->Free(keychain_item);
680 return true;
682 return false;
685 bool MacKeychainPasswordFormAdapter::HasPasswordsMergeableWithForm(
686 const PasswordForm& query_form) {
687 std::string username = base::UTF16ToUTF8(query_form.username_value);
688 std::vector<SecKeychainItemRef> matches =
689 MatchingKeychainItems(query_form.signon_realm, query_form.scheme,
690 NULL, username.c_str());
691 for (std::vector<SecKeychainItemRef>::iterator i = matches.begin();
692 i != matches.end(); ++i) {
693 keychain_->Free(*i);
696 return !matches.empty();
699 std::vector<SecKeychainItemRef>
700 MacKeychainPasswordFormAdapter::GetAllPasswordFormKeychainItems() {
701 SecAuthenticationType supported_auth_types[] = {
702 kSecAuthenticationTypeHTMLForm,
703 kSecAuthenticationTypeHTTPBasic,
704 kSecAuthenticationTypeHTTPDigest,
707 std::vector<SecKeychainItemRef> matches;
708 for (unsigned int i = 0; i < arraysize(supported_auth_types); ++i) {
709 KeychainSearch keychain_search(*keychain_);
710 OSType creator = CreatorCodeForSearch();
711 keychain_search.Init(NULL,
712 NULL,
713 NULL,
714 &supported_auth_types[i],
715 NULL,
716 NULL,
717 NULL,
718 creator ? &creator : NULL);
719 keychain_search.FindMatchingItems(&matches);
721 return matches;
724 ScopedVector<autofill::PasswordForm>
725 MacKeychainPasswordFormAdapter::GetAllPasswordFormPasswords() {
726 std::vector<SecKeychainItemRef> items = GetAllPasswordFormKeychainItems();
727 return ConvertKeychainItemsToForms(&items);
730 bool MacKeychainPasswordFormAdapter::AddPassword(const PasswordForm& form) {
731 // We should never be trying to store a blacklist in the keychain.
732 DCHECK(!form.blacklisted_by_user);
734 std::string server;
735 std::string security_domain;
736 UInt32 port;
737 bool is_secure;
738 if (!internal_keychain_helpers::ExtractSignonRealmComponents(
739 form.signon_realm, &server, &port, &is_secure, &security_domain)) {
740 return false;
742 std::string path;
743 // Path doesn't make sense for Android app credentials.
744 if (!password_manager::IsValidAndroidFacetURI(form.signon_realm))
745 path = form.origin.path();
746 std::string username = base::UTF16ToUTF8(form.username_value);
747 std::string password = base::UTF16ToUTF8(form.password_value);
748 SecProtocolType protocol = is_secure ? kSecProtocolTypeHTTPS
749 : kSecProtocolTypeHTTP;
750 SecKeychainItemRef new_item = NULL;
751 OSStatus result = keychain_->AddInternetPassword(
752 NULL, server.size(), server.c_str(),
753 security_domain.size(), security_domain.c_str(),
754 username.size(), username.c_str(),
755 path.size(), path.c_str(),
756 port, protocol, AuthTypeForScheme(form.scheme),
757 password.size(), password.c_str(), &new_item);
759 if (result == noErr) {
760 SetKeychainItemCreatorCode(new_item,
761 base::mac::CreatorCodeForApplication());
762 keychain_->Free(new_item);
763 } else if (result == errSecDuplicateItem) {
764 // If we collide with an existing item, find and update it instead.
765 SecKeychainItemRef existing_item = KeychainItemForForm(form);
766 if (!existing_item) {
767 return false;
769 bool changed = SetKeychainItemPassword(existing_item, password);
770 keychain_->Free(existing_item);
771 return changed;
774 return result == noErr;
777 bool MacKeychainPasswordFormAdapter::RemovePassword(const PasswordForm& form) {
778 SecKeychainItemRef keychain_item = KeychainItemForForm(form);
779 if (keychain_item == NULL)
780 return false;
781 OSStatus result = keychain_->ItemDelete(keychain_item);
782 keychain_->Free(keychain_item);
783 return result == noErr;
786 void MacKeychainPasswordFormAdapter::SetFindsOnlyOwnedItems(
787 bool finds_only_owned) {
788 finds_only_owned_ = finds_only_owned;
791 ScopedVector<autofill::PasswordForm>
792 MacKeychainPasswordFormAdapter::ConvertKeychainItemsToForms(
793 std::vector<SecKeychainItemRef>* items) {
794 ScopedVector<autofill::PasswordForm> forms;
795 for (SecKeychainItemRef item : *items) {
796 scoped_ptr<PasswordForm> form(new PasswordForm());
797 if (internal_keychain_helpers::FillPasswordFormFromKeychainItem(
798 *keychain_, item, form.get(), true)) {
799 forms.push_back(form.release());
801 keychain_->Free(item);
803 items->clear();
804 return forms.Pass();
807 SecKeychainItemRef MacKeychainPasswordFormAdapter::KeychainItemForForm(
808 const PasswordForm& form) {
809 // We don't store blacklist entries in the keychain, so the answer to "what
810 // Keychain item goes with this form" is always "nothing" for blacklists.
811 if (form.blacklisted_by_user) {
812 return NULL;
815 std::string path = form.origin.path();
816 std::string username = base::UTF16ToUTF8(form.username_value);
817 std::vector<SecKeychainItemRef> matches = MatchingKeychainItems(
818 form.signon_realm, form.scheme, path.c_str(), username.c_str());
820 if (matches.empty()) {
821 return NULL;
823 // Free all items after the first, since we won't be returning them.
824 for (std::vector<SecKeychainItemRef>::iterator i = matches.begin() + 1;
825 i != matches.end(); ++i) {
826 keychain_->Free(*i);
828 return matches[0];
831 std::vector<SecKeychainItemRef>
832 MacKeychainPasswordFormAdapter::MatchingKeychainItems(
833 const std::string& signon_realm,
834 autofill::PasswordForm::Scheme scheme,
835 const char* path, const char* username) {
836 std::vector<SecKeychainItemRef> matches;
838 std::string server;
839 std::string security_domain;
840 UInt32 port;
841 bool is_secure;
842 if (!internal_keychain_helpers::ExtractSignonRealmComponents(
843 signon_realm, &server, &port, &is_secure, &security_domain)) {
844 // TODO(stuartmorgan): Proxies will currently fail here, since their
845 // signon_realm is not a URL. We need to detect the proxy case and handle
846 // it specially.
847 return matches;
849 SecProtocolType protocol = is_secure ? kSecProtocolTypeHTTPS
850 : kSecProtocolTypeHTTP;
851 SecAuthenticationType auth_type = AuthTypeForScheme(scheme);
852 const char* auth_domain = (scheme == PasswordForm::SCHEME_HTML) ?
853 NULL : security_domain.c_str();
854 OSType creator = CreatorCodeForSearch();
855 KeychainSearch keychain_search(*keychain_);
856 keychain_search.Init(server.c_str(),
857 &port,
858 &protocol,
859 &auth_type,
860 auth_domain,
861 path,
862 username,
863 creator ? &creator : NULL);
864 keychain_search.FindMatchingItems(&matches);
865 return matches;
868 // Returns the Keychain SecAuthenticationType type corresponding to |scheme|.
869 SecAuthenticationType MacKeychainPasswordFormAdapter::AuthTypeForScheme(
870 PasswordForm::Scheme scheme) {
871 switch (scheme) {
872 case PasswordForm::SCHEME_HTML: return kSecAuthenticationTypeHTMLForm;
873 case PasswordForm::SCHEME_BASIC: return kSecAuthenticationTypeHTTPBasic;
874 case PasswordForm::SCHEME_DIGEST: return kSecAuthenticationTypeHTTPDigest;
875 case PasswordForm::SCHEME_OTHER: return kSecAuthenticationTypeDefault;
877 NOTREACHED();
878 return kSecAuthenticationTypeDefault;
881 bool MacKeychainPasswordFormAdapter::SetKeychainItemPassword(
882 const SecKeychainItemRef& keychain_item, const std::string& password) {
883 OSStatus result = keychain_->ItemModifyAttributesAndData(keychain_item, NULL,
884 password.size(),
885 password.c_str());
886 return result == noErr;
889 bool MacKeychainPasswordFormAdapter::SetKeychainItemCreatorCode(
890 const SecKeychainItemRef& keychain_item, OSType creator_code) {
891 SecKeychainAttribute attr = { kSecCreatorItemAttr, sizeof(creator_code),
892 &creator_code };
893 SecKeychainAttributeList attrList = { 1, &attr };
894 OSStatus result = keychain_->ItemModifyAttributesAndData(keychain_item,
895 &attrList, 0, NULL);
896 return result == noErr;
899 OSType MacKeychainPasswordFormAdapter::CreatorCodeForSearch() {
900 return finds_only_owned_ ? base::mac::CreatorCodeForApplication() : 0;
903 #pragma mark -
905 PasswordStoreMac::PasswordStoreMac(
906 scoped_refptr<base::SingleThreadTaskRunner> main_thread_runner,
907 scoped_refptr<base::SingleThreadTaskRunner> db_thread_runner,
908 scoped_ptr<AppleKeychain> keychain,
909 scoped_ptr<password_manager::LoginDatabase> login_db)
910 : password_manager::PasswordStore(main_thread_runner, db_thread_runner),
911 keychain_(keychain.Pass()),
912 login_metadata_db_(login_db.Pass()) {
913 DCHECK(keychain_.get());
914 DCHECK(login_metadata_db_.get());
917 PasswordStoreMac::~PasswordStoreMac() {}
919 bool PasswordStoreMac::Init(
920 const syncer::SyncableService::StartSyncFlare& flare) {
921 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
922 thread_.reset(new base::Thread("Chrome_PasswordStore_Thread"));
924 if (!thread_->Start()) {
925 thread_.reset(NULL);
926 return false;
929 ScheduleTask(base::Bind(&PasswordStoreMac::InitOnBackgroundThread, this));
930 return password_manager::PasswordStore::Init(flare);
933 void PasswordStoreMac::InitOnBackgroundThread() {
934 DCHECK(thread_->message_loop() == base::MessageLoop::current());
935 DCHECK(login_metadata_db_);
936 if (!login_metadata_db_->Init()) {
937 login_metadata_db_.reset();
938 LOG(ERROR) << "Could not create/open login database.";
942 void PasswordStoreMac::Shutdown() {
943 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
944 password_manager::PasswordStore::Shutdown();
945 thread_->Stop();
948 // Mac stores passwords in the system keychain, which can block for an
949 // arbitrarily long time (most notably, it can block on user confirmation
950 // from a dialog). Run tasks on a dedicated thread to avoid blocking the DB
951 // thread.
952 scoped_refptr<base::SingleThreadTaskRunner>
953 PasswordStoreMac::GetBackgroundTaskRunner() {
954 return (thread_.get()) ? thread_->message_loop_proxy() : NULL;
957 void PasswordStoreMac::ReportMetricsImpl(const std::string& sync_username,
958 bool custom_passphrase_sync_enabled) {
959 if (!login_metadata_db_)
960 return;
961 login_metadata_db_->ReportMetrics(sync_username,
962 custom_passphrase_sync_enabled);
965 PasswordStoreChangeList PasswordStoreMac::AddLoginImpl(
966 const PasswordForm& form) {
967 DCHECK(thread_->message_loop() == base::MessageLoop::current());
968 if (login_metadata_db_ && AddToKeychainIfNecessary(form))
969 return login_metadata_db_->AddLogin(form);
970 return PasswordStoreChangeList();
973 PasswordStoreChangeList PasswordStoreMac::UpdateLoginImpl(
974 const PasswordForm& form) {
975 DCHECK(thread_->message_loop() == base::MessageLoop::current());
976 if (!login_metadata_db_)
977 return PasswordStoreChangeList();
979 PasswordStoreChangeList changes = login_metadata_db_->UpdateLogin(form);
981 MacKeychainPasswordFormAdapter keychain_adapter(keychain_.get());
982 if (changes.empty() &&
983 !keychain_adapter.HasPasswordsMergeableWithForm(form)) {
984 // If the password isn't in either the DB or the keychain, then it must have
985 // been deleted after autofill happened, and should not be re-added.
986 return changes;
989 // The keychain add will update if there is a collision and add if there
990 // isn't, which is the behavior we want, so there's no separate update call.
991 if (AddToKeychainIfNecessary(form) && changes.empty()) {
992 changes = login_metadata_db_->AddLogin(form);
994 return changes;
997 PasswordStoreChangeList PasswordStoreMac::RemoveLoginImpl(
998 const PasswordForm& form) {
999 DCHECK(thread_->message_loop() == base::MessageLoop::current());
1000 PasswordStoreChangeList changes;
1001 if (login_metadata_db_ && login_metadata_db_->RemoveLogin(form)) {
1002 // See if we own a Keychain item associated with this item. We can do an
1003 // exact search rather than messing around with trying to do fuzzy matching
1004 // because passwords that we created will always have an exact-match
1005 // database entry.
1006 // (If a user does lose their profile but not their keychain we'll treat the
1007 // entries we find like other imported entries anyway, so it's reasonable to
1008 // handle deletes on them the way we would for an imported item.)
1009 MacKeychainPasswordFormAdapter owned_keychain_adapter(keychain_.get());
1010 owned_keychain_adapter.SetFindsOnlyOwnedItems(true);
1011 if (owned_keychain_adapter.HasPasswordExactlyMatchingForm(form)) {
1012 // If we don't have other forms using it (i.e., a form differing only by
1013 // the names of the form elements), delete the keychain entry.
1014 if (!DatabaseHasFormMatchingKeychainForm(form)) {
1015 owned_keychain_adapter.RemovePassword(form);
1019 changes.push_back(PasswordStoreChange(PasswordStoreChange::REMOVE, form));
1021 return changes;
1024 PasswordStoreChangeList PasswordStoreMac::RemoveLoginsCreatedBetweenImpl(
1025 base::Time delete_begin,
1026 base::Time delete_end) {
1027 PasswordStoreChangeList changes;
1028 ScopedVector<PasswordForm> forms_to_remove;
1029 if (login_metadata_db_ &&
1030 login_metadata_db_->GetLoginsCreatedBetween(delete_begin, delete_end,
1031 &forms_to_remove) &&
1032 login_metadata_db_->RemoveLoginsCreatedBetween(delete_begin,
1033 delete_end)) {
1034 RemoveKeychainForms(forms_to_remove.get());
1035 CleanOrphanedForms(&forms_to_remove); // Add the orphaned forms.
1036 changes = FormsToRemoveChangeList(forms_to_remove.get());
1037 LogStatsForBulkDeletion(changes.size());
1039 return changes;
1042 PasswordStoreChangeList PasswordStoreMac::RemoveLoginsSyncedBetweenImpl(
1043 base::Time delete_begin,
1044 base::Time delete_end) {
1045 PasswordStoreChangeList changes;
1046 ScopedVector<PasswordForm> forms_to_remove;
1047 if (login_metadata_db_ &&
1048 login_metadata_db_->GetLoginsSyncedBetween(delete_begin, delete_end,
1049 &forms_to_remove) &&
1050 login_metadata_db_->RemoveLoginsSyncedBetween(delete_begin, delete_end)) {
1051 RemoveKeychainForms(forms_to_remove.get());
1052 CleanOrphanedForms(&forms_to_remove); // Add the orphaned forms_to_remove.
1053 changes = FormsToRemoveChangeList(forms_to_remove.get());
1054 LogStatsForBulkDeletionDuringRollback(changes.size());
1056 return changes;
1059 ScopedVector<autofill::PasswordForm> PasswordStoreMac::FillMatchingLogins(
1060 const autofill::PasswordForm& form,
1061 AuthorizationPromptPolicy prompt_policy) {
1062 chrome::ScopedSecKeychainSetUserInteractionAllowed user_interaction_allowed(
1063 prompt_policy == ALLOW_PROMPT);
1065 if (!login_metadata_db_)
1066 return ScopedVector<autofill::PasswordForm>();
1068 ScopedVector<PasswordForm> database_forms;
1069 login_metadata_db_->GetLogins(form, &database_forms);
1071 // Let's gather all signon realms we want to match with keychain entries.
1072 std::set<std::string> realm_set;
1073 realm_set.insert(form.signon_realm);
1074 for (const autofill::PasswordForm* db_form : database_forms) {
1075 // TODO(vabr): We should not be getting different schemes here.
1076 // http://crbug.com/340112
1077 if (form.scheme != db_form->scheme)
1078 continue; // Forms with different schemes never match.
1079 const std::string& original_singon_realm(db_form->original_signon_realm);
1080 if (!original_singon_realm.empty())
1081 realm_set.insert(original_singon_realm);
1083 ScopedVector<autofill::PasswordForm> keychain_forms;
1084 for (std::set<std::string>::const_iterator realm = realm_set.begin();
1085 realm != realm_set.end(); ++realm) {
1086 MacKeychainPasswordFormAdapter keychain_adapter(keychain_.get());
1087 ScopedVector<autofill::PasswordForm> temp_keychain_forms =
1088 keychain_adapter.PasswordsFillingForm(*realm, form.scheme);
1089 AppendSecondToFirst(&keychain_forms, &temp_keychain_forms);
1092 ScopedVector<autofill::PasswordForm> matched_forms;
1093 internal_keychain_helpers::MergePasswordForms(
1094 &keychain_forms, &database_forms, &matched_forms);
1096 // Strip any blacklist entries out of the unused Keychain array, then take
1097 // all the entries that are left (which we can use as imported passwords).
1098 ScopedVector<PasswordForm> keychain_blacklist_forms;
1099 internal_keychain_helpers::ExtractBlacklistForms(&keychain_forms,
1100 &keychain_blacklist_forms);
1101 AppendSecondToFirst(&matched_forms, &keychain_forms);
1103 if (!database_forms.empty()) {
1104 RemoveDatabaseForms(&database_forms);
1105 NotifyLoginsChanged(FormsToRemoveChangeList(database_forms.get()));
1108 return matched_forms.Pass();
1111 void PasswordStoreMac::GetBlacklistLoginsImpl(
1112 scoped_ptr<PasswordStore::GetLoginsRequest> request) {
1113 ScopedVector<autofill::PasswordForm> obtained_forms;
1114 FillBlacklistLogins(&obtained_forms);
1115 request->NotifyConsumerWithResults(obtained_forms.Pass());
1118 void PasswordStoreMac::GetAutofillableLoginsImpl(
1119 scoped_ptr<PasswordStore::GetLoginsRequest> request) {
1120 ScopedVector<autofill::PasswordForm> obtained_forms;
1121 FillAutofillableLogins(&obtained_forms);
1122 request->NotifyConsumerWithResults(obtained_forms.Pass());
1125 bool PasswordStoreMac::FillAutofillableLogins(
1126 ScopedVector<autofill::PasswordForm>* forms) {
1127 DCHECK(thread_->message_loop() == base::MessageLoop::current());
1129 ScopedVector<PasswordForm> database_forms;
1130 if (!login_metadata_db_ ||
1131 !login_metadata_db_->GetAutofillableLogins(&database_forms))
1132 return false;
1134 internal_keychain_helpers::GetPasswordsForForms(*keychain_, &database_forms,
1135 forms);
1137 if (!database_forms.empty()) {
1138 RemoveDatabaseForms(&database_forms);
1139 NotifyLoginsChanged(FormsToRemoveChangeList(database_forms.get()));
1142 return true;
1145 bool PasswordStoreMac::FillBlacklistLogins(
1146 ScopedVector<autofill::PasswordForm>* forms) {
1147 DCHECK(thread_->message_loop() == base::MessageLoop::current());
1148 return login_metadata_db_ && login_metadata_db_->GetBlacklistLogins(forms);
1151 bool PasswordStoreMac::AddToKeychainIfNecessary(const PasswordForm& form) {
1152 if (form.blacklisted_by_user) {
1153 return true;
1155 MacKeychainPasswordFormAdapter keychainAdapter(keychain_.get());
1156 return keychainAdapter.AddPassword(form);
1159 bool PasswordStoreMac::DatabaseHasFormMatchingKeychainForm(
1160 const autofill::PasswordForm& form) {
1161 DCHECK(login_metadata_db_);
1162 bool has_match = false;
1163 ScopedVector<autofill::PasswordForm> database_forms;
1164 login_metadata_db_->GetLogins(form, &database_forms);
1165 for (const autofill::PasswordForm* db_form : database_forms) {
1166 // Below we filter out forms with non-empty original_signon_realm, because
1167 // those signal fuzzy matches, and we are only interested in exact ones.
1168 if (db_form->original_signon_realm.empty() &&
1169 internal_keychain_helpers::FormsMatchForMerge(
1170 form, *db_form, internal_keychain_helpers::STRICT_FORM_MATCH) &&
1171 db_form->origin == form.origin) {
1172 has_match = true;
1173 break;
1176 return has_match;
1179 void PasswordStoreMac::RemoveDatabaseForms(
1180 ScopedVector<autofill::PasswordForm>* forms) {
1181 DCHECK(login_metadata_db_);
1182 ScopedVector<autofill::PasswordForm> removed_forms;
1183 MoveAllFormsOut(forms, [this, &removed_forms](
1184 scoped_ptr<autofill::PasswordForm> form) {
1185 if (login_metadata_db_->RemoveLogin(*form))
1186 removed_forms.push_back(form.release());
1188 removed_forms.swap(*forms);
1191 void PasswordStoreMac::RemoveKeychainForms(
1192 const std::vector<PasswordForm*>& forms) {
1193 MacKeychainPasswordFormAdapter owned_keychain_adapter(keychain_.get());
1194 owned_keychain_adapter.SetFindsOnlyOwnedItems(true);
1195 for (std::vector<PasswordForm*>::const_iterator i = forms.begin();
1196 i != forms.end(); ++i) {
1197 owned_keychain_adapter.RemovePassword(**i);
1201 void PasswordStoreMac::CleanOrphanedForms(
1202 ScopedVector<autofill::PasswordForm>* orphaned_forms) {
1203 DCHECK(orphaned_forms);
1204 DCHECK(login_metadata_db_);
1206 ScopedVector<autofill::PasswordForm> database_forms;
1207 login_metadata_db_->GetAutofillableLogins(&database_forms);
1209 // Filter forms with corresponding Keychain entry out of |database_forms|.
1210 ScopedVector<PasswordForm> forms_with_keychain_entry;
1211 internal_keychain_helpers::GetPasswordsForForms(*keychain_, &database_forms,
1212 &forms_with_keychain_entry);
1214 // Clean up any orphaned database entries.
1215 RemoveDatabaseForms(&database_forms);
1217 // Move the orphaned DB forms to the output parameter.
1218 AppendSecondToFirst(orphaned_forms, &database_forms);