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