Fix KeychainItemForForm so that it understands not to specify a path when looking...
[chromium-blink-merge.git] / chrome / browser / password_manager / password_store_mac.cc
blobf4690b09c1afcc376a4bcb5816f2fbafa3ad1977
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 form->ssl_valid = true;
426 } else {
427 form->origin = URLFromComponents(form->ssl_valid, server, port, path);
428 // TODO(stuartmorgan): Handle proxies, which need a different signon_realm
429 // format.
430 form->signon_realm = form->origin.GetOrigin().spec();
431 if (form->scheme != PasswordForm::SCHEME_HTML) {
432 form->signon_realm.append(security_domain);
435 return true;
438 bool FormsMatchForMerge(const PasswordForm& form_a,
439 const PasswordForm& form_b,
440 FormMatchStrictness strictness) {
441 // We never merge blacklist entries between our store and the keychain.
442 if (form_a.blacklisted_by_user || form_b.blacklisted_by_user) {
443 return false;
445 bool equal_realm = form_a.signon_realm == form_b.signon_realm;
446 if (strictness == FUZZY_FORM_MATCH) {
447 equal_realm |= (!form_a.original_signon_realm.empty()) &&
448 form_a.original_signon_realm == form_b.signon_realm;
450 return form_a.scheme == form_b.scheme && equal_realm &&
451 form_a.username_value == form_b.username_value;
454 // Moves entries from |forms| that are blacklist entries into |blacklist|.
455 void ExtractBlacklistForms(ScopedVector<autofill::PasswordForm>* forms,
456 ScopedVector<autofill::PasswordForm>* blacklist) {
457 blacklist->reserve(blacklist->size() + forms->size());
458 ScopedVector<autofill::PasswordForm> non_blacklist;
459 // Move forms in either |non_blacklist| or |blacklist|, depending on whether
460 // they are blacklisted by the user.
461 MoveAllFormsOut(forms, [&non_blacklist, blacklist](
462 scoped_ptr<autofill::PasswordForm> form) {
463 if (form->blacklisted_by_user)
464 blacklist->push_back(form.release());
465 else
466 non_blacklist.push_back(form.release());
468 forms->swap(non_blacklist);
471 // Takes |keychain_forms| and |database_forms| and moves the following 2 types
472 // of forms to |merged_forms|: (1) blacklisted |database_forms|, (2)
473 // |database_forms| which have a corresponding entry in |keychain_forms|. The
474 // database forms of type (2) have their password value updated from the
475 // corresponding keychain form, and all the keychain forms corresponding to some
476 // database form are removed from |keychain_forms| and deleted.
477 void MergePasswordForms(ScopedVector<autofill::PasswordForm>* keychain_forms,
478 ScopedVector<autofill::PasswordForm>* database_forms,
479 ScopedVector<autofill::PasswordForm>* merged_forms) {
480 // Pull out the database blacklist items, since they are used as-is rather
481 // than being merged with keychain forms.
482 ExtractBlacklistForms(database_forms, merged_forms);
484 // Merge the normal entries.
485 ScopedVector<autofill::PasswordForm> unused_database_forms;
486 unused_database_forms.reserve(database_forms->size());
487 std::set<const autofill::PasswordForm*> used_keychain_forms;
488 // Move all database forms to either |merged_forms| or
489 // |unused_database_forms|, based on whether they have a match in the keychain
490 // forms or not. If there is a match, add its password to the DB form and
491 // mark the keychain form as used.
492 MoveAllFormsOut(database_forms, [keychain_forms, &used_keychain_forms,
493 merged_forms, &unused_database_forms](
494 scoped_ptr<autofill::PasswordForm> form) {
495 const PasswordForm* best_match =
496 BestKeychainFormForForm(*form, keychain_forms->get());
497 if (best_match) {
498 used_keychain_forms.insert(best_match);
499 form->password_value = best_match->password_value;
500 merged_forms->push_back(form.release());
501 } else {
502 unused_database_forms.push_back(form.release());
505 database_forms->swap(unused_database_forms);
507 // Clear out all the Keychain entries we used.
508 ScopedVector<autofill::PasswordForm> unused_keychain_forms;
509 unused_keychain_forms.reserve(keychain_forms->size());
510 for (auto& keychain_form : *keychain_forms) {
511 if (!ContainsKey(used_keychain_forms, keychain_form)) {
512 unused_keychain_forms.push_back(keychain_form);
513 keychain_form = nullptr;
516 keychain_forms->swap(unused_keychain_forms);
519 std::vector<ItemFormPair> ExtractAllKeychainItemAttributesIntoPasswordForms(
520 std::vector<SecKeychainItemRef>* keychain_items,
521 const AppleKeychain& keychain) {
522 DCHECK(keychain_items);
523 MacKeychainPasswordFormAdapter keychain_adapter(&keychain);
524 *keychain_items = keychain_adapter.GetAllPasswordFormKeychainItems();
525 std::vector<ItemFormPair> item_form_pairs;
526 for (std::vector<SecKeychainItemRef>::iterator i = keychain_items->begin();
527 i != keychain_items->end(); ++i) {
528 PasswordForm* form_without_password = new PasswordForm();
529 internal_keychain_helpers::FillPasswordFormFromKeychainItem(
530 keychain,
532 form_without_password,
533 false); // Load password attributes, but not password data.
534 item_form_pairs.push_back(std::make_pair(&(*i), form_without_password));
536 return item_form_pairs;
539 void GetPasswordsForForms(const AppleKeychain& keychain,
540 ScopedVector<autofill::PasswordForm>* database_forms,
541 ScopedVector<autofill::PasswordForm>* passwords) {
542 // First load the attributes of all items in the keychain without loading
543 // their password data, and then match items in |database_forms| against them.
544 // This avoids individually searching through the keychain for passwords
545 // matching each form in |database_forms|, and results in a significant
546 // performance gain, replacing O(N) keychain search operations with a single
547 // operation that loads all keychain items, and then selective reads of only
548 // the relevant passwords. See crbug.com/263685.
549 std::vector<SecKeychainItemRef> keychain_items;
550 std::vector<ItemFormPair> item_form_pairs =
551 ExtractAllKeychainItemAttributesIntoPasswordForms(&keychain_items,
552 keychain);
554 // Next, compare the attributes of the PasswordForms in |database_forms|
555 // against those in |item_form_pairs|, and extract password data for each
556 // matching PasswordForm using its corresponding SecKeychainItemRef.
557 ScopedVector<autofill::PasswordForm> unused_db_forms;
558 unused_db_forms.reserve(database_forms->size());
559 // Move database forms with a password stored in |keychain| to |passwords|,
560 // including the password. The rest is moved to |unused_db_forms|.
561 MoveAllFormsOut(database_forms,
562 [&keychain, &item_form_pairs, passwords, &unused_db_forms](
563 scoped_ptr<autofill::PasswordForm> form) {
564 ScopedVector<autofill::PasswordForm> keychain_matches =
565 ExtractPasswordsMergeableWithForm(keychain, item_form_pairs, *form);
567 ScopedVector<autofill::PasswordForm> db_form_container;
568 db_form_container.push_back(form.release());
569 MergePasswordForms(&keychain_matches, &db_form_container, passwords);
570 AppendSecondToFirst(&unused_db_forms, &db_form_container);
572 database_forms->swap(unused_db_forms);
574 STLDeleteContainerPairSecondPointers(item_form_pairs.begin(),
575 item_form_pairs.end());
576 for (SecKeychainItemRef item : keychain_items) {
577 keychain.Free(item);
581 // TODO(stuartmorgan): signon_realm for proxies is not yet supported.
582 bool ExtractSignonRealmComponents(const std::string& signon_realm,
583 std::string* server,
584 UInt32* port,
585 bool* is_secure,
586 std::string* security_domain) {
587 // GURL does not parse Android facet URIs correctly.
588 if (password_manager::IsValidAndroidFacetURI(signon_realm)) {
589 if (server)
590 *server = signon_realm;
591 if (is_secure)
592 *is_secure = true;
593 if (port)
594 *port = 0;
595 if (security_domain)
596 security_domain->clear();
597 return true;
600 // The signon_realm will be the Origin portion of a URL for an HTML form,
601 // and the same but with the security domain as a path for HTTP auth.
602 GURL realm_as_url(signon_realm);
603 if (!realm_as_url.is_valid()) {
604 return false;
607 if (server)
608 *server = realm_as_url.host();
609 if (is_secure)
610 *is_secure = realm_as_url.SchemeIsSecure();
611 if (port)
612 *port = realm_as_url.has_port() ? atoi(realm_as_url.port().c_str()) : 0;
613 if (security_domain) {
614 // Strip the leading '/' off of the path to get the security domain.
615 if (realm_as_url.path().length() > 0)
616 *security_domain = realm_as_url.path().substr(1);
617 else
618 security_domain->clear();
620 return true;
623 bool FormIsValidAndMatchesOtherForm(const PasswordForm& query_form,
624 const PasswordForm& other_form) {
625 std::string server;
626 std::string security_domain;
627 UInt32 port;
628 bool is_secure;
629 if (!ExtractSignonRealmComponents(query_form.signon_realm, &server, &port,
630 &is_secure, &security_domain)) {
631 return false;
633 return FormsMatchForMerge(query_form, other_form, STRICT_FORM_MATCH);
636 ScopedVector<autofill::PasswordForm> ExtractPasswordsMergeableWithForm(
637 const AppleKeychain& keychain,
638 const std::vector<ItemFormPair>& item_form_pairs,
639 const PasswordForm& query_form) {
640 ScopedVector<autofill::PasswordForm> matches;
641 for (std::vector<ItemFormPair>::const_iterator i = item_form_pairs.begin();
642 i != item_form_pairs.end(); ++i) {
643 if (FormIsValidAndMatchesOtherForm(query_form, *(i->second))) {
644 // Create a new object, since the caller is responsible for deleting the
645 // returned forms.
646 scoped_ptr<PasswordForm> form_with_password(new PasswordForm());
647 FillPasswordFormFromKeychainItem(
648 keychain, *(i->first), form_with_password.get(),
649 true); // Load password attributes and data.
650 // Do not include blacklisted items found in the keychain.
651 if (!form_with_password->blacklisted_by_user)
652 matches.push_back(form_with_password.release());
655 return matches.Pass();
658 } // namespace internal_keychain_helpers
660 #pragma mark -
662 MacKeychainPasswordFormAdapter::MacKeychainPasswordFormAdapter(
663 const AppleKeychain* keychain)
664 : keychain_(keychain), finds_only_owned_(false) {
667 ScopedVector<autofill::PasswordForm>
668 MacKeychainPasswordFormAdapter::PasswordsFillingForm(
669 const std::string& signon_realm,
670 PasswordForm::Scheme scheme) {
671 std::vector<SecKeychainItemRef> keychain_items =
672 MatchingKeychainItems(signon_realm, scheme, NULL, NULL);
673 return ConvertKeychainItemsToForms(&keychain_items);
676 bool MacKeychainPasswordFormAdapter::HasPasswordExactlyMatchingForm(
677 const PasswordForm& query_form) {
678 SecKeychainItemRef keychain_item = KeychainItemForForm(query_form);
679 if (keychain_item) {
680 keychain_->Free(keychain_item);
681 return true;
683 return false;
686 bool MacKeychainPasswordFormAdapter::HasPasswordsMergeableWithForm(
687 const PasswordForm& query_form) {
688 std::string username = base::UTF16ToUTF8(query_form.username_value);
689 std::vector<SecKeychainItemRef> matches =
690 MatchingKeychainItems(query_form.signon_realm, query_form.scheme,
691 NULL, username.c_str());
692 for (std::vector<SecKeychainItemRef>::iterator i = matches.begin();
693 i != matches.end(); ++i) {
694 keychain_->Free(*i);
697 return !matches.empty();
700 std::vector<SecKeychainItemRef>
701 MacKeychainPasswordFormAdapter::GetAllPasswordFormKeychainItems() {
702 SecAuthenticationType supported_auth_types[] = {
703 kSecAuthenticationTypeHTMLForm,
704 kSecAuthenticationTypeHTTPBasic,
705 kSecAuthenticationTypeHTTPDigest,
708 std::vector<SecKeychainItemRef> matches;
709 for (unsigned int i = 0; i < arraysize(supported_auth_types); ++i) {
710 KeychainSearch keychain_search(*keychain_);
711 OSType creator = CreatorCodeForSearch();
712 keychain_search.Init(NULL,
713 NULL,
714 NULL,
715 &supported_auth_types[i],
716 NULL,
717 NULL,
718 NULL,
719 creator ? &creator : NULL);
720 keychain_search.FindMatchingItems(&matches);
722 return matches;
725 ScopedVector<autofill::PasswordForm>
726 MacKeychainPasswordFormAdapter::GetAllPasswordFormPasswords() {
727 std::vector<SecKeychainItemRef> items = GetAllPasswordFormKeychainItems();
728 return ConvertKeychainItemsToForms(&items);
731 bool MacKeychainPasswordFormAdapter::AddPassword(const PasswordForm& form) {
732 // We should never be trying to store a blacklist in the keychain.
733 DCHECK(!form.blacklisted_by_user);
735 std::string server;
736 std::string security_domain;
737 UInt32 port;
738 bool is_secure;
739 if (!internal_keychain_helpers::ExtractSignonRealmComponents(
740 form.signon_realm, &server, &port, &is_secure, &security_domain)) {
741 return false;
743 std::string path;
744 // Path doesn't make sense for Android app credentials.
745 if (!password_manager::IsValidAndroidFacetURI(form.signon_realm))
746 path = form.origin.path();
747 std::string username = base::UTF16ToUTF8(form.username_value);
748 std::string password = base::UTF16ToUTF8(form.password_value);
749 SecProtocolType protocol = is_secure ? kSecProtocolTypeHTTPS
750 : kSecProtocolTypeHTTP;
751 SecKeychainItemRef new_item = NULL;
752 OSStatus result = keychain_->AddInternetPassword(
753 NULL, server.size(), server.c_str(),
754 security_domain.size(), security_domain.c_str(),
755 username.size(), username.c_str(),
756 path.size(), path.c_str(),
757 port, protocol, AuthTypeForScheme(form.scheme),
758 password.size(), password.c_str(), &new_item);
760 if (result == noErr) {
761 SetKeychainItemCreatorCode(new_item,
762 base::mac::CreatorCodeForApplication());
763 keychain_->Free(new_item);
764 } else if (result == errSecDuplicateItem) {
765 // If we collide with an existing item, find and update it instead.
766 SecKeychainItemRef existing_item = KeychainItemForForm(form);
767 if (!existing_item) {
768 return false;
770 bool changed = SetKeychainItemPassword(existing_item, password);
771 keychain_->Free(existing_item);
772 return changed;
775 return result == noErr;
778 bool MacKeychainPasswordFormAdapter::RemovePassword(const PasswordForm& form) {
779 SecKeychainItemRef keychain_item = KeychainItemForForm(form);
780 if (keychain_item == NULL)
781 return false;
782 OSStatus result = keychain_->ItemDelete(keychain_item);
783 keychain_->Free(keychain_item);
784 return result == noErr;
787 void MacKeychainPasswordFormAdapter::SetFindsOnlyOwnedItems(
788 bool finds_only_owned) {
789 finds_only_owned_ = finds_only_owned;
792 ScopedVector<autofill::PasswordForm>
793 MacKeychainPasswordFormAdapter::ConvertKeychainItemsToForms(
794 std::vector<SecKeychainItemRef>* items) {
795 ScopedVector<autofill::PasswordForm> forms;
796 for (SecKeychainItemRef item : *items) {
797 scoped_ptr<PasswordForm> form(new PasswordForm());
798 if (internal_keychain_helpers::FillPasswordFormFromKeychainItem(
799 *keychain_, item, form.get(), true)) {
800 forms.push_back(form.release());
802 keychain_->Free(item);
804 items->clear();
805 return forms.Pass();
808 SecKeychainItemRef MacKeychainPasswordFormAdapter::KeychainItemForForm(
809 const PasswordForm& form) {
810 // We don't store blacklist entries in the keychain, so the answer to "what
811 // Keychain item goes with this form" is always "nothing" for blacklists.
812 if (form.blacklisted_by_user) {
813 return NULL;
816 std::string path;
817 // Path doesn't make sense for Android app credentials.
818 if (!password_manager::IsValidAndroidFacetURI(form.signon_realm))
819 path = form.origin.path();
820 std::string username = base::UTF16ToUTF8(form.username_value);
821 std::vector<SecKeychainItemRef> matches = MatchingKeychainItems(
822 form.signon_realm, form.scheme, path.c_str(), username.c_str());
824 if (matches.empty()) {
825 return NULL;
827 // Free all items after the first, since we won't be returning them.
828 for (std::vector<SecKeychainItemRef>::iterator i = matches.begin() + 1;
829 i != matches.end(); ++i) {
830 keychain_->Free(*i);
832 return matches[0];
835 std::vector<SecKeychainItemRef>
836 MacKeychainPasswordFormAdapter::MatchingKeychainItems(
837 const std::string& signon_realm,
838 autofill::PasswordForm::Scheme scheme,
839 const char* path, const char* username) {
840 std::vector<SecKeychainItemRef> matches;
842 std::string server;
843 std::string security_domain;
844 UInt32 port;
845 bool is_secure;
846 if (!internal_keychain_helpers::ExtractSignonRealmComponents(
847 signon_realm, &server, &port, &is_secure, &security_domain)) {
848 // TODO(stuartmorgan): Proxies will currently fail here, since their
849 // signon_realm is not a URL. We need to detect the proxy case and handle
850 // it specially.
851 return matches;
853 SecProtocolType protocol = is_secure ? kSecProtocolTypeHTTPS
854 : kSecProtocolTypeHTTP;
855 SecAuthenticationType auth_type = AuthTypeForScheme(scheme);
856 const char* auth_domain = (scheme == PasswordForm::SCHEME_HTML) ?
857 NULL : security_domain.c_str();
858 OSType creator = CreatorCodeForSearch();
859 KeychainSearch keychain_search(*keychain_);
860 keychain_search.Init(server.c_str(),
861 &port,
862 &protocol,
863 &auth_type,
864 auth_domain,
865 path,
866 username,
867 creator ? &creator : NULL);
868 keychain_search.FindMatchingItems(&matches);
869 return matches;
872 // Returns the Keychain SecAuthenticationType type corresponding to |scheme|.
873 SecAuthenticationType MacKeychainPasswordFormAdapter::AuthTypeForScheme(
874 PasswordForm::Scheme scheme) {
875 switch (scheme) {
876 case PasswordForm::SCHEME_HTML: return kSecAuthenticationTypeHTMLForm;
877 case PasswordForm::SCHEME_BASIC: return kSecAuthenticationTypeHTTPBasic;
878 case PasswordForm::SCHEME_DIGEST: return kSecAuthenticationTypeHTTPDigest;
879 case PasswordForm::SCHEME_OTHER: return kSecAuthenticationTypeDefault;
881 NOTREACHED();
882 return kSecAuthenticationTypeDefault;
885 bool MacKeychainPasswordFormAdapter::SetKeychainItemPassword(
886 const SecKeychainItemRef& keychain_item, const std::string& password) {
887 OSStatus result = keychain_->ItemModifyAttributesAndData(keychain_item, NULL,
888 password.size(),
889 password.c_str());
890 return result == noErr;
893 bool MacKeychainPasswordFormAdapter::SetKeychainItemCreatorCode(
894 const SecKeychainItemRef& keychain_item, OSType creator_code) {
895 SecKeychainAttribute attr = { kSecCreatorItemAttr, sizeof(creator_code),
896 &creator_code };
897 SecKeychainAttributeList attrList = { 1, &attr };
898 OSStatus result = keychain_->ItemModifyAttributesAndData(keychain_item,
899 &attrList, 0, NULL);
900 return result == noErr;
903 OSType MacKeychainPasswordFormAdapter::CreatorCodeForSearch() {
904 return finds_only_owned_ ? base::mac::CreatorCodeForApplication() : 0;
907 #pragma mark -
909 PasswordStoreMac::PasswordStoreMac(
910 scoped_refptr<base::SingleThreadTaskRunner> main_thread_runner,
911 scoped_refptr<base::SingleThreadTaskRunner> db_thread_runner,
912 scoped_ptr<AppleKeychain> keychain,
913 scoped_ptr<password_manager::LoginDatabase> login_db)
914 : password_manager::PasswordStore(main_thread_runner, db_thread_runner),
915 keychain_(keychain.Pass()),
916 login_metadata_db_(login_db.Pass()) {
917 DCHECK(keychain_.get());
918 DCHECK(login_metadata_db_.get());
921 PasswordStoreMac::~PasswordStoreMac() {}
923 bool PasswordStoreMac::Init(
924 const syncer::SyncableService::StartSyncFlare& flare) {
925 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
926 thread_.reset(new base::Thread("Chrome_PasswordStore_Thread"));
928 if (!thread_->Start()) {
929 thread_.reset(NULL);
930 return false;
933 ScheduleTask(base::Bind(&PasswordStoreMac::InitOnBackgroundThread, this));
934 return password_manager::PasswordStore::Init(flare);
937 void PasswordStoreMac::InitOnBackgroundThread() {
938 DCHECK(thread_->message_loop() == base::MessageLoop::current());
939 DCHECK(login_metadata_db_);
940 if (!login_metadata_db_->Init()) {
941 login_metadata_db_.reset();
942 LOG(ERROR) << "Could not create/open login database.";
946 void PasswordStoreMac::Shutdown() {
947 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
948 password_manager::PasswordStore::Shutdown();
949 thread_->Stop();
952 // Mac stores passwords in the system keychain, which can block for an
953 // arbitrarily long time (most notably, it can block on user confirmation
954 // from a dialog). Run tasks on a dedicated thread to avoid blocking the DB
955 // thread.
956 scoped_refptr<base::SingleThreadTaskRunner>
957 PasswordStoreMac::GetBackgroundTaskRunner() {
958 return (thread_.get()) ? thread_->message_loop_proxy() : NULL;
961 void PasswordStoreMac::ReportMetricsImpl(const std::string& sync_username,
962 bool custom_passphrase_sync_enabled) {
963 if (!login_metadata_db_)
964 return;
965 login_metadata_db_->ReportMetrics(sync_username,
966 custom_passphrase_sync_enabled);
969 PasswordStoreChangeList PasswordStoreMac::AddLoginImpl(
970 const PasswordForm& form) {
971 DCHECK(thread_->message_loop() == base::MessageLoop::current());
972 if (login_metadata_db_ && AddToKeychainIfNecessary(form))
973 return login_metadata_db_->AddLogin(form);
974 return PasswordStoreChangeList();
977 PasswordStoreChangeList PasswordStoreMac::UpdateLoginImpl(
978 const PasswordForm& form) {
979 DCHECK(thread_->message_loop() == base::MessageLoop::current());
980 if (!login_metadata_db_)
981 return PasswordStoreChangeList();
983 PasswordStoreChangeList changes = login_metadata_db_->UpdateLogin(form);
985 MacKeychainPasswordFormAdapter keychain_adapter(keychain_.get());
986 if (changes.empty() &&
987 !keychain_adapter.HasPasswordsMergeableWithForm(form)) {
988 // If the password isn't in either the DB or the keychain, then it must have
989 // been deleted after autofill happened, and should not be re-added.
990 return changes;
993 // The keychain add will update if there is a collision and add if there
994 // isn't, which is the behavior we want, so there's no separate update call.
995 if (AddToKeychainIfNecessary(form) && changes.empty()) {
996 changes = login_metadata_db_->AddLogin(form);
998 return changes;
1001 PasswordStoreChangeList PasswordStoreMac::RemoveLoginImpl(
1002 const PasswordForm& form) {
1003 DCHECK(thread_->message_loop() == base::MessageLoop::current());
1004 PasswordStoreChangeList changes;
1005 if (login_metadata_db_ && login_metadata_db_->RemoveLogin(form)) {
1006 // See if we own a Keychain item associated with this item. We can do an
1007 // exact search rather than messing around with trying to do fuzzy matching
1008 // because passwords that we created will always have an exact-match
1009 // database entry.
1010 // (If a user does lose their profile but not their keychain we'll treat the
1011 // entries we find like other imported entries anyway, so it's reasonable to
1012 // handle deletes on them the way we would for an imported item.)
1013 MacKeychainPasswordFormAdapter owned_keychain_adapter(keychain_.get());
1014 owned_keychain_adapter.SetFindsOnlyOwnedItems(true);
1015 if (owned_keychain_adapter.HasPasswordExactlyMatchingForm(form)) {
1016 // If we don't have other forms using it (i.e., a form differing only by
1017 // the names of the form elements), delete the keychain entry.
1018 if (!DatabaseHasFormMatchingKeychainForm(form)) {
1019 owned_keychain_adapter.RemovePassword(form);
1023 changes.push_back(PasswordStoreChange(PasswordStoreChange::REMOVE, form));
1025 return changes;
1028 PasswordStoreChangeList PasswordStoreMac::RemoveLoginsCreatedBetweenImpl(
1029 base::Time delete_begin,
1030 base::Time delete_end) {
1031 PasswordStoreChangeList changes;
1032 ScopedVector<PasswordForm> forms_to_remove;
1033 if (login_metadata_db_ &&
1034 login_metadata_db_->GetLoginsCreatedBetween(delete_begin, delete_end,
1035 &forms_to_remove) &&
1036 login_metadata_db_->RemoveLoginsCreatedBetween(delete_begin,
1037 delete_end)) {
1038 RemoveKeychainForms(forms_to_remove.get());
1039 CleanOrphanedForms(&forms_to_remove); // Add the orphaned forms.
1040 changes = FormsToRemoveChangeList(forms_to_remove.get());
1041 LogStatsForBulkDeletion(changes.size());
1043 return changes;
1046 PasswordStoreChangeList PasswordStoreMac::RemoveLoginsSyncedBetweenImpl(
1047 base::Time delete_begin,
1048 base::Time delete_end) {
1049 PasswordStoreChangeList changes;
1050 ScopedVector<PasswordForm> forms_to_remove;
1051 if (login_metadata_db_ &&
1052 login_metadata_db_->GetLoginsSyncedBetween(delete_begin, delete_end,
1053 &forms_to_remove) &&
1054 login_metadata_db_->RemoveLoginsSyncedBetween(delete_begin, delete_end)) {
1055 RemoveKeychainForms(forms_to_remove.get());
1056 CleanOrphanedForms(&forms_to_remove); // Add the orphaned forms_to_remove.
1057 changes = FormsToRemoveChangeList(forms_to_remove.get());
1058 LogStatsForBulkDeletionDuringRollback(changes.size());
1060 return changes;
1063 ScopedVector<autofill::PasswordForm> PasswordStoreMac::FillMatchingLogins(
1064 const autofill::PasswordForm& form,
1065 AuthorizationPromptPolicy prompt_policy) {
1066 chrome::ScopedSecKeychainSetUserInteractionAllowed user_interaction_allowed(
1067 prompt_policy == ALLOW_PROMPT);
1069 ScopedVector<PasswordForm> database_forms;
1070 if (!login_metadata_db_ ||
1071 !login_metadata_db_->GetLogins(form, &database_forms)) {
1072 return ScopedVector<autofill::PasswordForm>();
1075 // Let's gather all signon realms we want to match with keychain entries.
1076 std::set<std::string> realm_set;
1077 realm_set.insert(form.signon_realm);
1078 for (const autofill::PasswordForm* db_form : database_forms) {
1079 // TODO(vabr): We should not be getting different schemes here.
1080 // http://crbug.com/340112
1081 if (form.scheme != db_form->scheme)
1082 continue; // Forms with different schemes never match.
1083 const std::string& original_singon_realm(db_form->original_signon_realm);
1084 if (!original_singon_realm.empty())
1085 realm_set.insert(original_singon_realm);
1087 ScopedVector<autofill::PasswordForm> keychain_forms;
1088 for (std::set<std::string>::const_iterator realm = realm_set.begin();
1089 realm != realm_set.end(); ++realm) {
1090 MacKeychainPasswordFormAdapter keychain_adapter(keychain_.get());
1091 ScopedVector<autofill::PasswordForm> temp_keychain_forms =
1092 keychain_adapter.PasswordsFillingForm(*realm, form.scheme);
1093 AppendSecondToFirst(&keychain_forms, &temp_keychain_forms);
1096 ScopedVector<autofill::PasswordForm> matched_forms;
1097 internal_keychain_helpers::MergePasswordForms(
1098 &keychain_forms, &database_forms, &matched_forms);
1100 // Strip any blacklist entries out of the unused Keychain array, then take
1101 // all the entries that are left (which we can use as imported passwords).
1102 ScopedVector<PasswordForm> keychain_blacklist_forms;
1103 internal_keychain_helpers::ExtractBlacklistForms(&keychain_forms,
1104 &keychain_blacklist_forms);
1105 AppendSecondToFirst(&matched_forms, &keychain_forms);
1107 if (!database_forms.empty()) {
1108 RemoveDatabaseForms(&database_forms);
1109 NotifyLoginsChanged(FormsToRemoveChangeList(database_forms.get()));
1112 return matched_forms.Pass();
1115 void PasswordStoreMac::GetBlacklistLoginsImpl(
1116 scoped_ptr<PasswordStore::GetLoginsRequest> request) {
1117 ScopedVector<PasswordForm> obtained_forms;
1118 if (!FillBlacklistLogins(&obtained_forms))
1119 obtained_forms.clear();
1120 request->NotifyConsumerWithResults(obtained_forms.Pass());
1123 void PasswordStoreMac::GetAutofillableLoginsImpl(
1124 scoped_ptr<PasswordStore::GetLoginsRequest> request) {
1125 ScopedVector<PasswordForm> obtained_forms;
1126 if (!FillAutofillableLogins(&obtained_forms))
1127 obtained_forms.clear();
1128 request->NotifyConsumerWithResults(obtained_forms.Pass());
1131 bool PasswordStoreMac::FillAutofillableLogins(
1132 ScopedVector<PasswordForm>* forms) {
1133 DCHECK_EQ(thread_->message_loop(), base::MessageLoop::current());
1134 forms->clear();
1136 ScopedVector<PasswordForm> database_forms;
1137 if (!login_metadata_db_ ||
1138 !login_metadata_db_->GetAutofillableLogins(&database_forms))
1139 return false;
1141 internal_keychain_helpers::GetPasswordsForForms(*keychain_, &database_forms,
1142 forms);
1144 if (!database_forms.empty()) {
1145 RemoveDatabaseForms(&database_forms);
1146 NotifyLoginsChanged(FormsToRemoveChangeList(database_forms.get()));
1149 return true;
1152 bool PasswordStoreMac::FillBlacklistLogins(ScopedVector<PasswordForm>* forms) {
1153 DCHECK_EQ(thread_->message_loop(), base::MessageLoop::current());
1154 return login_metadata_db_ && login_metadata_db_->GetBlacklistLogins(forms);
1157 bool PasswordStoreMac::AddToKeychainIfNecessary(const PasswordForm& form) {
1158 if (form.blacklisted_by_user) {
1159 return true;
1161 MacKeychainPasswordFormAdapter keychainAdapter(keychain_.get());
1162 return keychainAdapter.AddPassword(form);
1165 bool PasswordStoreMac::DatabaseHasFormMatchingKeychainForm(
1166 const autofill::PasswordForm& form) {
1167 DCHECK(login_metadata_db_);
1168 bool has_match = false;
1169 ScopedVector<autofill::PasswordForm> database_forms;
1170 if (!login_metadata_db_->GetLogins(form, &database_forms))
1171 return false;
1172 for (const autofill::PasswordForm* db_form : database_forms) {
1173 // Below we filter out forms with non-empty original_signon_realm, because
1174 // those signal fuzzy matches, and we are only interested in exact ones.
1175 if (db_form->original_signon_realm.empty() &&
1176 internal_keychain_helpers::FormsMatchForMerge(
1177 form, *db_form, internal_keychain_helpers::STRICT_FORM_MATCH) &&
1178 db_form->origin == form.origin) {
1179 has_match = true;
1180 break;
1183 return has_match;
1186 void PasswordStoreMac::RemoveDatabaseForms(
1187 ScopedVector<autofill::PasswordForm>* forms) {
1188 DCHECK(login_metadata_db_);
1189 ScopedVector<autofill::PasswordForm> removed_forms;
1190 MoveAllFormsOut(forms, [this, &removed_forms](
1191 scoped_ptr<autofill::PasswordForm> form) {
1192 if (login_metadata_db_->RemoveLogin(*form))
1193 removed_forms.push_back(form.release());
1195 removed_forms.swap(*forms);
1198 void PasswordStoreMac::RemoveKeychainForms(
1199 const std::vector<PasswordForm*>& forms) {
1200 MacKeychainPasswordFormAdapter owned_keychain_adapter(keychain_.get());
1201 owned_keychain_adapter.SetFindsOnlyOwnedItems(true);
1202 for (std::vector<PasswordForm*>::const_iterator i = forms.begin();
1203 i != forms.end(); ++i) {
1204 owned_keychain_adapter.RemovePassword(**i);
1208 void PasswordStoreMac::CleanOrphanedForms(
1209 ScopedVector<autofill::PasswordForm>* orphaned_forms) {
1210 DCHECK(orphaned_forms);
1211 DCHECK(login_metadata_db_);
1213 ScopedVector<autofill::PasswordForm> database_forms;
1214 if (!login_metadata_db_->GetAutofillableLogins(&database_forms))
1215 return;
1217 // Filter forms with corresponding Keychain entry out of |database_forms|.
1218 ScopedVector<PasswordForm> forms_with_keychain_entry;
1219 internal_keychain_helpers::GetPasswordsForForms(*keychain_, &database_forms,
1220 &forms_with_keychain_entry);
1222 // Clean up any orphaned database entries.
1223 RemoveDatabaseForms(&database_forms);
1225 // Move the orphaned DB forms to the output parameter.
1226 AppendSecondToFirst(orphaned_forms, &database_forms);