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/mac_logging.h"
17 #include "base/mac/mac_util.h"
18 #include "base/memory/scoped_vector.h"
19 #include "base/message_loop/message_loop.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/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 // Utility class to handle the details of constructing and running a keychain
35 // search from a set of attributes.
36 class KeychainSearch
{
38 explicit KeychainSearch(const AppleKeychain
& keychain
);
41 // Sets up a keycahin search based on an non "null" (NULL for char*,
42 // The appropriate "Any" entry for other types) arguments.
44 // IMPORTANT: Any paramaters passed in *must* remain valid for as long as the
45 // KeychainSearch object, since the search uses them by reference.
46 void Init(const char* server
,
48 const SecProtocolType
* protocol
,
49 const SecAuthenticationType
* auth_type
,
50 const char* security_domain
,
53 const OSType
* creator
);
55 // Fills |items| with all Keychain items that match the Init'd search.
56 // If the search fails for any reason, |items| will be unchanged.
57 void FindMatchingItems(std::vector
<SecKeychainItemRef
>* matches
);
60 const AppleKeychain
* keychain_
;
61 SecKeychainAttributeList search_attributes_
;
62 SecKeychainSearchRef search_ref_
;
65 KeychainSearch::KeychainSearch(const AppleKeychain
& keychain
)
66 : keychain_(&keychain
), search_ref_(NULL
) {
67 search_attributes_
.count
= 0;
68 search_attributes_
.attr
= NULL
;
71 KeychainSearch::~KeychainSearch() {
72 if (search_attributes_
.attr
) {
73 free(search_attributes_
.attr
);
77 void KeychainSearch::Init(const char* server
,
79 const SecProtocolType
* protocol
,
80 const SecAuthenticationType
* auth_type
,
81 const char* security_domain
,
84 const OSType
* creator
) {
85 // Allocate enough to hold everything we might use.
86 const unsigned int kMaxEntryCount
= 8;
87 search_attributes_
.attr
=
88 static_cast<SecKeychainAttribute
*>(calloc(kMaxEntryCount
,
89 sizeof(SecKeychainAttribute
)));
90 unsigned int entries
= 0;
91 // We only use search_attributes_ with SearchCreateFromAttributes, which takes
92 // a "const SecKeychainAttributeList *", so we trust that they won't try
93 // to modify the list, and that casting away const-ness is thus safe.
95 DCHECK_LT(entries
, kMaxEntryCount
);
96 search_attributes_
.attr
[entries
].tag
= kSecServerItemAttr
;
97 search_attributes_
.attr
[entries
].length
= strlen(server
);
98 search_attributes_
.attr
[entries
].data
=
99 const_cast<void*>(static_cast<const void*>(server
));
102 if (port
!= NULL
&& *port
!= kAnyPort
) {
103 DCHECK_LE(entries
, kMaxEntryCount
);
104 search_attributes_
.attr
[entries
].tag
= kSecPortItemAttr
;
105 search_attributes_
.attr
[entries
].length
= sizeof(*port
);
106 search_attributes_
.attr
[entries
].data
=
107 const_cast<void*>(static_cast<const void*>(port
));
110 if (protocol
!= NULL
&& *protocol
!= kSecProtocolTypeAny
) {
111 DCHECK_LE(entries
, kMaxEntryCount
);
112 search_attributes_
.attr
[entries
].tag
= kSecProtocolItemAttr
;
113 search_attributes_
.attr
[entries
].length
= sizeof(*protocol
);
114 search_attributes_
.attr
[entries
].data
=
115 const_cast<void*>(static_cast<const void*>(protocol
));
118 if (auth_type
!= NULL
&& *auth_type
!= kSecAuthenticationTypeAny
) {
119 DCHECK_LE(entries
, kMaxEntryCount
);
120 search_attributes_
.attr
[entries
].tag
= kSecAuthenticationTypeItemAttr
;
121 search_attributes_
.attr
[entries
].length
= sizeof(*auth_type
);
122 search_attributes_
.attr
[entries
].data
=
123 const_cast<void*>(static_cast<const void*>(auth_type
));
126 if (security_domain
!= NULL
&& strlen(security_domain
) > 0) {
127 DCHECK_LE(entries
, kMaxEntryCount
);
128 search_attributes_
.attr
[entries
].tag
= kSecSecurityDomainItemAttr
;
129 search_attributes_
.attr
[entries
].length
= strlen(security_domain
);
130 search_attributes_
.attr
[entries
].data
=
131 const_cast<void*>(static_cast<const void*>(security_domain
));
134 if (path
!= NULL
&& strlen(path
) > 0 && strcmp(path
, "/") != 0) {
135 DCHECK_LE(entries
, kMaxEntryCount
);
136 search_attributes_
.attr
[entries
].tag
= kSecPathItemAttr
;
137 search_attributes_
.attr
[entries
].length
= strlen(path
);
138 search_attributes_
.attr
[entries
].data
=
139 const_cast<void*>(static_cast<const void*>(path
));
142 if (username
!= NULL
) {
143 DCHECK_LE(entries
, kMaxEntryCount
);
144 search_attributes_
.attr
[entries
].tag
= kSecAccountItemAttr
;
145 search_attributes_
.attr
[entries
].length
= strlen(username
);
146 search_attributes_
.attr
[entries
].data
=
147 const_cast<void*>(static_cast<const void*>(username
));
150 if (creator
!= NULL
) {
151 DCHECK_LE(entries
, kMaxEntryCount
);
152 search_attributes_
.attr
[entries
].tag
= kSecCreatorItemAttr
;
153 search_attributes_
.attr
[entries
].length
= sizeof(*creator
);
154 search_attributes_
.attr
[entries
].data
=
155 const_cast<void*>(static_cast<const void*>(creator
));
158 search_attributes_
.count
= entries
;
161 void KeychainSearch::FindMatchingItems(std::vector
<SecKeychainItemRef
>* items
) {
162 OSStatus result
= keychain_
->SearchCreateFromAttributes(
163 NULL
, kSecInternetPasswordItemClass
, &search_attributes_
, &search_ref_
);
165 if (result
!= noErr
) {
166 OSSTATUS_LOG(ERROR
, result
) << "Keychain lookup failed";
170 SecKeychainItemRef keychain_item
;
171 while (keychain_
->SearchCopyNext(search_ref_
, &keychain_item
) == noErr
) {
172 // Consumer is responsible for freeing the items.
173 items
->push_back(keychain_item
);
176 keychain_
->Free(search_ref_
);
182 // TODO(stuartmorgan): Convert most of this to private helpers in
183 // MacKeychainPasswordFormAdapter once it has sufficient higher-level public
184 // methods to provide test coverage.
185 namespace internal_keychain_helpers
{
187 // Returns a URL built from the given components. To create a URL without a
188 // port, pass kAnyPort for the |port| parameter.
189 GURL
URLFromComponents(bool is_secure
, const std::string
& host
, int port
,
190 const std::string
& path
) {
191 GURL::Replacements url_components
;
192 std::string
scheme(is_secure
? "https" : "http");
193 url_components
.SetSchemeStr(scheme
);
194 url_components
.SetHostStr(host
);
195 std::string port_string
; // Must remain in scope until after we do replacing.
196 if (port
!= kAnyPort
) {
197 std::ostringstream port_stringstream
;
198 port_stringstream
<< port
;
199 port_string
= port_stringstream
.str();
200 url_components
.SetPortStr(port_string
);
202 url_components
.SetPathStr(path
);
204 GURL
url("http://dummy.com"); // ReplaceComponents needs a valid URL.
205 return url
.ReplaceComponents(url_components
);
208 // Converts a Keychain time string to a Time object, returning true if
209 // time_string_bytes was parsable. If the return value is false, the value of
210 // |time| is unchanged.
211 bool TimeFromKeychainTimeString(const char* time_string_bytes
,
212 unsigned int byte_length
,
216 char* time_string
= static_cast<char*>(malloc(byte_length
+ 1));
217 memcpy(time_string
, time_string_bytes
, byte_length
);
218 time_string
[byte_length
] = '\0';
219 base::Time::Exploded exploded_time
;
220 bzero(&exploded_time
, sizeof(exploded_time
));
221 // The time string is of the form "yyyyMMddHHmmss'Z", in UTC time.
222 int assignments
= sscanf(time_string
, "%4d%2d%2d%2d%2d%2dZ",
223 &exploded_time
.year
, &exploded_time
.month
,
224 &exploded_time
.day_of_month
, &exploded_time
.hour
,
225 &exploded_time
.minute
, &exploded_time
.second
);
228 if (assignments
== 6) {
229 *time
= base::Time::FromUTCExploded(exploded_time
);
235 // Returns the PasswordForm Scheme corresponding to |auth_type|.
236 PasswordForm::Scheme
SchemeForAuthType(SecAuthenticationType auth_type
) {
238 case kSecAuthenticationTypeHTMLForm
: return PasswordForm::SCHEME_HTML
;
239 case kSecAuthenticationTypeHTTPBasic
: return PasswordForm::SCHEME_BASIC
;
240 case kSecAuthenticationTypeHTTPDigest
: return PasswordForm::SCHEME_DIGEST
;
241 default: return PasswordForm::SCHEME_OTHER
;
245 bool FillPasswordFormFromKeychainItem(const AppleKeychain
& keychain
,
246 const SecKeychainItemRef
& keychain_item
,
248 bool extract_password_data
) {
251 SecKeychainAttributeInfo attrInfo
;
252 UInt32 tags
[] = { kSecAccountItemAttr
,
256 kSecProtocolItemAttr
,
257 kSecAuthenticationTypeItemAttr
,
258 kSecSecurityDomainItemAttr
,
259 kSecCreationDateItemAttr
,
260 kSecNegativeItemAttr
};
261 attrInfo
.count
= arraysize(tags
);
263 attrInfo
.format
= NULL
;
265 SecKeychainAttributeList
*attrList
;
266 UInt32 password_length
;
268 // If |extract_password_data| is false, do not pass in a reference to
269 // |password_data|. ItemCopyAttributesAndData will then extract only the
270 // attributes of |keychain_item| (doesn't require OS authorization), and not
271 // attempt to extract its password data (requires OS authorization).
272 void* password_data
= NULL
;
273 void** password_data_ref
= extract_password_data
? &password_data
: NULL
;
275 OSStatus result
= keychain
.ItemCopyAttributesAndData(keychain_item
, &attrInfo
,
280 if (result
!= noErr
) {
281 // We don't log errSecAuthFailed because that just means that the user
282 // chose not to allow us access to the item.
283 if (result
!= errSecAuthFailed
) {
284 OSSTATUS_LOG(ERROR
, result
) << "Keychain data load failed";
289 if (extract_password_data
) {
290 base::UTF8ToUTF16(static_cast<const char *>(password_data
), password_length
,
291 &(form
->password_value
));
296 std::string security_domain
;
298 for (unsigned int i
= 0; i
< attrList
->count
; i
++) {
299 SecKeychainAttribute attr
= attrList
->attr
[i
];
304 case kSecAccountItemAttr
:
305 base::UTF8ToUTF16(static_cast<const char *>(attr
.data
), attr
.length
,
306 &(form
->username_value
));
308 case kSecServerItemAttr
:
309 server
.assign(static_cast<const char *>(attr
.data
), attr
.length
);
311 case kSecPortItemAttr
:
312 port
= *(static_cast<UInt32
*>(attr
.data
));
314 case kSecPathItemAttr
:
315 path
.assign(static_cast<const char *>(attr
.data
), attr
.length
);
317 case kSecProtocolItemAttr
:
319 SecProtocolType protocol
= *(static_cast<SecProtocolType
*>(attr
.data
));
320 // TODO(stuartmorgan): Handle proxy types
321 form
->ssl_valid
= (protocol
== kSecProtocolTypeHTTPS
);
324 case kSecAuthenticationTypeItemAttr
:
326 SecAuthenticationType auth_type
=
327 *(static_cast<SecAuthenticationType
*>(attr
.data
));
328 form
->scheme
= SchemeForAuthType(auth_type
);
331 case kSecSecurityDomainItemAttr
:
332 security_domain
.assign(static_cast<const char *>(attr
.data
),
335 case kSecCreationDateItemAttr
:
336 // The only way to get a date out of Keychain is as a string. Really.
337 // (The docs claim it's an int, but the header is correct.)
338 TimeFromKeychainTimeString(static_cast<char*>(attr
.data
), attr
.length
,
339 &form
->date_created
);
341 case kSecNegativeItemAttr
:
342 Boolean negative_item
= *(static_cast<Boolean
*>(attr
.data
));
344 form
->blacklisted_by_user
= true;
349 keychain
.ItemFreeAttributesAndData(attrList
, password_data
);
351 // kSecNegativeItemAttr doesn't seem to actually be in widespread use. In
352 // practice, other browsers seem to use a "" or " " password (and a special
353 // user name) to indicated blacklist entries.
354 if (extract_password_data
&& (form
->password_value
.empty() ||
355 EqualsASCII(form
->password_value
, " "))) {
356 form
->blacklisted_by_user
= true;
359 form
->origin
= URLFromComponents(form
->ssl_valid
, server
, port
, path
);
360 // TODO(stuartmorgan): Handle proxies, which need a different signon_realm
362 form
->signon_realm
= form
->origin
.GetOrigin().spec();
363 if (form
->scheme
!= PasswordForm::SCHEME_HTML
) {
364 form
->signon_realm
.append(security_domain
);
369 bool FormsMatchForMerge(const PasswordForm
& form_a
,
370 const PasswordForm
& form_b
,
371 FormMatchStrictness strictness
) {
372 // We never merge blacklist entries between our store and the keychain.
373 if (form_a
.blacklisted_by_user
|| form_b
.blacklisted_by_user
) {
376 bool equal_realm
= form_a
.signon_realm
== form_b
.signon_realm
;
377 if (strictness
== FUZZY_FORM_MATCH
) {
378 equal_realm
|= (!form_a
.original_signon_realm
.empty()) &&
379 form_a
.original_signon_realm
== form_b
.signon_realm
;
381 return form_a
.scheme
== form_b
.scheme
&& equal_realm
&&
382 form_a
.username_value
== form_b
.username_value
;
385 // Returns an the best match for |base_form| from |keychain_forms|, or NULL if
386 // there is no suitable match.
387 PasswordForm
* BestKeychainFormForForm(
388 const PasswordForm
& base_form
,
389 const std::vector
<PasswordForm
*>* keychain_forms
) {
390 PasswordForm
* partial_match
= NULL
;
391 for (std::vector
<PasswordForm
*>::const_iterator i
= keychain_forms
->begin();
392 i
!= keychain_forms
->end(); ++i
) {
393 // TODO(stuartmorgan): We should really be scoring path matches and picking
394 // the best, rather than just checking exact-or-not (although in practice
395 // keychain items with paths probably came from us).
396 if (FormsMatchForMerge(base_form
, *(*i
), FUZZY_FORM_MATCH
)) {
397 if (base_form
.origin
== (*i
)->origin
) {
399 } else if (!partial_match
) {
404 return partial_match
;
407 // Returns entries from |forms| that are blacklist entries, after removing
408 // them from |forms|.
409 std::vector
<PasswordForm
*> ExtractBlacklistForms(
410 std::vector
<PasswordForm
*>* forms
) {
411 std::vector
<PasswordForm
*> blacklist_forms
;
412 for (std::vector
<PasswordForm
*>::iterator i
= forms
->begin();
413 i
!= forms
->end();) {
414 PasswordForm
* form
= *i
;
415 if (form
->blacklisted_by_user
) {
416 blacklist_forms
.push_back(form
);
422 return blacklist_forms
;
425 // Deletes and removes from v any element that exists in s.
427 void DeleteVectorElementsInSet(std::vector
<T
*>* v
, const std::set
<T
*>& s
) {
428 for (typename
std::vector
<T
*>::iterator i
= v
->begin(); i
!= v
->end();) {
430 if (s
.find(element
) != s
.end()) {
439 void MergePasswordForms(std::vector
<PasswordForm
*>* keychain_forms
,
440 std::vector
<PasswordForm
*>* database_forms
,
441 std::vector
<PasswordForm
*>* merged_forms
) {
442 // Pull out the database blacklist items, since they are used as-is rather
443 // than being merged with keychain forms.
444 std::vector
<PasswordForm
*> database_blacklist_forms
=
445 ExtractBlacklistForms(database_forms
);
447 // Merge the normal entries.
448 std::set
<PasswordForm
*> used_keychain_forms
;
449 for (std::vector
<PasswordForm
*>::iterator i
= database_forms
->begin();
450 i
!= database_forms
->end();) {
451 PasswordForm
* db_form
= *i
;
452 PasswordForm
* best_match
= BestKeychainFormForForm(*db_form
,
455 used_keychain_forms
.insert(best_match
);
456 db_form
->password_value
= best_match
->password_value
;
457 merged_forms
->push_back(db_form
);
458 i
= database_forms
->erase(i
);
464 // Add in the blacklist entries from the database.
465 merged_forms
->insert(merged_forms
->end(),
466 database_blacklist_forms
.begin(),
467 database_blacklist_forms
.end());
469 // Clear out all the Keychain entries we used.
470 DeleteVectorElementsInSet(keychain_forms
, used_keychain_forms
);
473 std::vector
<ItemFormPair
> ExtractAllKeychainItemAttributesIntoPasswordForms(
474 std::vector
<SecKeychainItemRef
>* keychain_items
,
475 const AppleKeychain
& keychain
) {
476 DCHECK(keychain_items
);
477 MacKeychainPasswordFormAdapter
keychain_adapter(&keychain
);
478 *keychain_items
= keychain_adapter
.GetAllPasswordFormKeychainItems();
479 std::vector
<ItemFormPair
> item_form_pairs
;
480 for (std::vector
<SecKeychainItemRef
>::iterator i
= keychain_items
->begin();
481 i
!= keychain_items
->end(); ++i
) {
482 PasswordForm
* form_without_password
= new PasswordForm();
483 internal_keychain_helpers::FillPasswordFormFromKeychainItem(
486 form_without_password
,
487 false); // Load password attributes, but not password data.
488 item_form_pairs
.push_back(std::make_pair(&(*i
), form_without_password
));
490 return item_form_pairs
;
493 std::vector
<PasswordForm
*> GetPasswordsForForms(
494 const AppleKeychain
& keychain
,
495 std::vector
<PasswordForm
*>* database_forms
) {
496 // First load the attributes of all items in the keychain without loading
497 // their password data, and then match items in |database_forms| against them.
498 // This avoids individually searching through the keychain for passwords
499 // matching each form in |database_forms|, and results in a significant
500 // performance gain, replacing O(N) keychain search operations with a single
501 // operation that loads all keychain items, and then selective reads of only
502 // the relevant passwords. See crbug.com/263685.
503 std::vector
<SecKeychainItemRef
> keychain_items
;
504 std::vector
<ItemFormPair
> item_form_pairs
=
505 ExtractAllKeychainItemAttributesIntoPasswordForms(&keychain_items
,
508 // Next, compare the attributes of the PasswordForms in |database_forms|
509 // against those in |item_form_pairs|, and extract password data for each
510 // matching PasswordForm using its corresponding SecKeychainItemRef.
511 std::vector
<PasswordForm
*> merged_forms
;
512 for (std::vector
<PasswordForm
*>::iterator i
= database_forms
->begin();
513 i
!= database_forms
->end();) {
514 std::vector
<PasswordForm
*> db_form_container(1, *i
);
515 std::vector
<PasswordForm
*> keychain_matches
=
516 ExtractPasswordsMergeableWithForm(keychain
, item_form_pairs
, **i
);
517 MergePasswordForms(&keychain_matches
, &db_form_container
, &merged_forms
);
518 if (db_form_container
.empty()) {
519 i
= database_forms
->erase(i
);
523 STLDeleteElements(&keychain_matches
);
526 // Clean up temporary PasswordForms and SecKeychainItemRefs.
527 STLDeleteContainerPairSecondPointers(item_form_pairs
.begin(),
528 item_form_pairs
.end());
529 for (std::vector
<SecKeychainItemRef
>::iterator i
= keychain_items
.begin();
530 i
!= keychain_items
.end(); ++i
) {
536 // TODO(stuartmorgan): signon_realm for proxies is not yet supported.
537 bool ExtractSignonRealmComponents(const std::string
& signon_realm
,
541 std::string
* security_domain
) {
542 // The signon_realm will be the Origin portion of a URL for an HTML form,
543 // and the same but with the security domain as a path for HTTP auth.
544 GURL
realm_as_url(signon_realm
);
545 if (!realm_as_url
.is_valid()) {
550 *server
= realm_as_url
.host();
552 *is_secure
= realm_as_url
.SchemeIsSecure();
554 *port
= realm_as_url
.has_port() ? atoi(realm_as_url
.port().c_str()) : 0;
555 if (security_domain
) {
556 // Strip the leading '/' off of the path to get the security domain.
557 if (realm_as_url
.path().length() > 0)
558 *security_domain
= realm_as_url
.path().substr(1);
560 security_domain
->clear();
565 bool FormIsValidAndMatchesOtherForm(const PasswordForm
& query_form
,
566 const PasswordForm
& other_form
) {
568 std::string security_domain
;
571 if (!ExtractSignonRealmComponents(query_form
.signon_realm
, &server
, &port
,
572 &is_secure
, &security_domain
)) {
575 return internal_keychain_helpers::FormsMatchForMerge(
576 query_form
, other_form
, STRICT_FORM_MATCH
);
579 std::vector
<PasswordForm
*> ExtractPasswordsMergeableWithForm(
580 const AppleKeychain
& keychain
,
581 const std::vector
<ItemFormPair
>& item_form_pairs
,
582 const PasswordForm
& query_form
) {
583 std::vector
<PasswordForm
*> matches
;
584 for (std::vector
<ItemFormPair
>::const_iterator i
= item_form_pairs
.begin();
585 i
!= item_form_pairs
.end(); ++i
) {
586 if (FormIsValidAndMatchesOtherForm(query_form
, *(i
->second
))) {
587 // Create a new object, since the caller is responsible for deleting the
589 scoped_ptr
<PasswordForm
> form_with_password(new PasswordForm());
590 internal_keychain_helpers::FillPasswordFormFromKeychainItem(
593 form_with_password
.get(),
594 true); // Load password attributes and data.
595 // Do not include blacklisted items found in the keychain.
596 if (!form_with_password
->blacklisted_by_user
)
597 matches
.push_back(form_with_password
.release());
603 } // namespace internal_keychain_helpers
607 MacKeychainPasswordFormAdapter::MacKeychainPasswordFormAdapter(
608 const AppleKeychain
* keychain
)
609 : keychain_(keychain
), finds_only_owned_(false) {
612 std::vector
<PasswordForm
*> MacKeychainPasswordFormAdapter::PasswordsFillingForm(
613 const std::string
& signon_realm
,
614 PasswordForm::Scheme scheme
) {
615 std::vector
<SecKeychainItemRef
> keychain_items
=
616 MatchingKeychainItems(signon_realm
, scheme
, NULL
, NULL
);
618 return ConvertKeychainItemsToForms(&keychain_items
);
621 PasswordForm
* MacKeychainPasswordFormAdapter::PasswordExactlyMatchingForm(
622 const PasswordForm
& query_form
) {
623 SecKeychainItemRef keychain_item
= KeychainItemForForm(query_form
);
625 PasswordForm
* form
= new PasswordForm();
626 internal_keychain_helpers::FillPasswordFormFromKeychainItem(*keychain_
,
630 keychain_
->Free(keychain_item
);
636 bool MacKeychainPasswordFormAdapter::HasPasswordsMergeableWithForm(
637 const PasswordForm
& query_form
) {
638 std::string username
= base::UTF16ToUTF8(query_form
.username_value
);
639 std::vector
<SecKeychainItemRef
> matches
=
640 MatchingKeychainItems(query_form
.signon_realm
, query_form
.scheme
,
641 NULL
, username
.c_str());
642 for (std::vector
<SecKeychainItemRef
>::iterator i
= matches
.begin();
643 i
!= matches
.end(); ++i
) {
647 return !matches
.empty();
650 std::vector
<SecKeychainItemRef
>
651 MacKeychainPasswordFormAdapter::GetAllPasswordFormKeychainItems() {
652 SecAuthenticationType supported_auth_types
[] = {
653 kSecAuthenticationTypeHTMLForm
,
654 kSecAuthenticationTypeHTTPBasic
,
655 kSecAuthenticationTypeHTTPDigest
,
658 std::vector
<SecKeychainItemRef
> matches
;
659 for (unsigned int i
= 0; i
< arraysize(supported_auth_types
); ++i
) {
660 KeychainSearch
keychain_search(*keychain_
);
661 OSType creator
= CreatorCodeForSearch();
662 keychain_search
.Init(NULL
,
665 &supported_auth_types
[i
],
669 creator
? &creator
: NULL
);
670 keychain_search
.FindMatchingItems(&matches
);
675 std::vector
<PasswordForm
*>
676 MacKeychainPasswordFormAdapter::GetAllPasswordFormPasswords() {
677 std::vector
<SecKeychainItemRef
> items
= GetAllPasswordFormKeychainItems();
678 return ConvertKeychainItemsToForms(&items
);
681 bool MacKeychainPasswordFormAdapter::AddPassword(const PasswordForm
& form
) {
682 // We should never be trying to store a blacklist in the keychain.
683 DCHECK(!form
.blacklisted_by_user
);
686 std::string security_domain
;
689 if (!internal_keychain_helpers::ExtractSignonRealmComponents(
690 form
.signon_realm
, &server
, &port
, &is_secure
, &security_domain
)) {
693 std::string username
= base::UTF16ToUTF8(form
.username_value
);
694 std::string password
= base::UTF16ToUTF8(form
.password_value
);
695 std::string path
= form
.origin
.path();
696 SecProtocolType protocol
= is_secure
? kSecProtocolTypeHTTPS
697 : kSecProtocolTypeHTTP
;
698 SecKeychainItemRef new_item
= NULL
;
699 OSStatus result
= keychain_
->AddInternetPassword(
700 NULL
, server
.size(), server
.c_str(),
701 security_domain
.size(), security_domain
.c_str(),
702 username
.size(), username
.c_str(),
703 path
.size(), path
.c_str(),
704 port
, protocol
, AuthTypeForScheme(form
.scheme
),
705 password
.size(), password
.c_str(), &new_item
);
707 if (result
== noErr
) {
708 SetKeychainItemCreatorCode(new_item
,
709 base::mac::CreatorCodeForApplication());
710 keychain_
->Free(new_item
);
711 } else if (result
== errSecDuplicateItem
) {
712 // If we collide with an existing item, find and update it instead.
713 SecKeychainItemRef existing_item
= KeychainItemForForm(form
);
714 if (!existing_item
) {
717 bool changed
= SetKeychainItemPassword(existing_item
, password
);
718 keychain_
->Free(existing_item
);
722 return result
== noErr
;
725 bool MacKeychainPasswordFormAdapter::RemovePassword(const PasswordForm
& form
) {
726 SecKeychainItemRef keychain_item
= KeychainItemForForm(form
);
727 if (keychain_item
== NULL
)
729 OSStatus result
= keychain_
->ItemDelete(keychain_item
);
730 keychain_
->Free(keychain_item
);
731 return result
== noErr
;
734 void MacKeychainPasswordFormAdapter::SetFindsOnlyOwnedItems(
735 bool finds_only_owned
) {
736 finds_only_owned_
= finds_only_owned
;
739 std::vector
<PasswordForm
*>
740 MacKeychainPasswordFormAdapter::ConvertKeychainItemsToForms(
741 std::vector
<SecKeychainItemRef
>* items
) {
742 std::vector
<PasswordForm
*> keychain_forms
;
743 for (std::vector
<SecKeychainItemRef
>::const_iterator i
= items
->begin();
744 i
!= items
->end(); ++i
) {
745 PasswordForm
* form
= new PasswordForm();
746 if (internal_keychain_helpers::FillPasswordFormFromKeychainItem(
747 *keychain_
, *i
, form
, true)) {
748 keychain_forms
.push_back(form
);
753 return keychain_forms
;
756 SecKeychainItemRef
MacKeychainPasswordFormAdapter::KeychainItemForForm(
757 const PasswordForm
& form
) {
758 // We don't store blacklist entries in the keychain, so the answer to "what
759 // Keychain item goes with this form" is always "nothing" for blacklists.
760 if (form
.blacklisted_by_user
) {
764 std::string path
= form
.origin
.path();
765 std::string username
= base::UTF16ToUTF8(form
.username_value
);
766 std::vector
<SecKeychainItemRef
> matches
= MatchingKeychainItems(
767 form
.signon_realm
, form
.scheme
, path
.c_str(), username
.c_str());
769 if (matches
.empty()) {
772 // Free all items after the first, since we won't be returning them.
773 for (std::vector
<SecKeychainItemRef
>::iterator i
= matches
.begin() + 1;
774 i
!= matches
.end(); ++i
) {
780 std::vector
<SecKeychainItemRef
>
781 MacKeychainPasswordFormAdapter::MatchingKeychainItems(
782 const std::string
& signon_realm
,
783 autofill::PasswordForm::Scheme scheme
,
784 const char* path
, const char* username
) {
785 std::vector
<SecKeychainItemRef
> matches
;
788 std::string security_domain
;
791 if (!internal_keychain_helpers::ExtractSignonRealmComponents(
792 signon_realm
, &server
, &port
, &is_secure
, &security_domain
)) {
793 // TODO(stuartmorgan): Proxies will currently fail here, since their
794 // signon_realm is not a URL. We need to detect the proxy case and handle
798 SecProtocolType protocol
= is_secure
? kSecProtocolTypeHTTPS
799 : kSecProtocolTypeHTTP
;
800 SecAuthenticationType auth_type
= AuthTypeForScheme(scheme
);
801 const char* auth_domain
= (scheme
== PasswordForm::SCHEME_HTML
) ?
802 NULL
: security_domain
.c_str();
803 OSType creator
= CreatorCodeForSearch();
804 KeychainSearch
keychain_search(*keychain_
);
805 keychain_search
.Init(server
.c_str(),
812 creator
? &creator
: NULL
);
813 keychain_search
.FindMatchingItems(&matches
);
817 // Returns the Keychain SecAuthenticationType type corresponding to |scheme|.
818 SecAuthenticationType
MacKeychainPasswordFormAdapter::AuthTypeForScheme(
819 PasswordForm::Scheme scheme
) {
821 case PasswordForm::SCHEME_HTML
: return kSecAuthenticationTypeHTMLForm
;
822 case PasswordForm::SCHEME_BASIC
: return kSecAuthenticationTypeHTTPBasic
;
823 case PasswordForm::SCHEME_DIGEST
: return kSecAuthenticationTypeHTTPDigest
;
824 case PasswordForm::SCHEME_OTHER
: return kSecAuthenticationTypeDefault
;
827 return kSecAuthenticationTypeDefault
;
830 bool MacKeychainPasswordFormAdapter::SetKeychainItemPassword(
831 const SecKeychainItemRef
& keychain_item
, const std::string
& password
) {
832 OSStatus result
= keychain_
->ItemModifyAttributesAndData(keychain_item
, NULL
,
835 return result
== noErr
;
838 bool MacKeychainPasswordFormAdapter::SetKeychainItemCreatorCode(
839 const SecKeychainItemRef
& keychain_item
, OSType creator_code
) {
840 SecKeychainAttribute attr
= { kSecCreatorItemAttr
, sizeof(creator_code
),
842 SecKeychainAttributeList attrList
= { 1, &attr
};
843 OSStatus result
= keychain_
->ItemModifyAttributesAndData(keychain_item
,
845 return result
== noErr
;
848 OSType
MacKeychainPasswordFormAdapter::CreatorCodeForSearch() {
849 return finds_only_owned_
? base::mac::CreatorCodeForApplication() : 0;
854 PasswordStoreMac::PasswordStoreMac(
855 scoped_refptr
<base::SingleThreadTaskRunner
> main_thread_runner
,
856 scoped_refptr
<base::SingleThreadTaskRunner
> db_thread_runner
,
857 AppleKeychain
* keychain
,
858 password_manager::LoginDatabase
* login_db
)
859 : password_manager::PasswordStore(main_thread_runner
, db_thread_runner
),
861 login_metadata_db_(login_db
) {
862 DCHECK(keychain_
.get());
863 DCHECK(login_metadata_db_
.get());
866 PasswordStoreMac::~PasswordStoreMac() {}
868 bool PasswordStoreMac::Init(
869 const syncer::SyncableService::StartSyncFlare
& flare
) {
870 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI
));
871 thread_
.reset(new base::Thread("Chrome_PasswordStore_Thread"));
873 if (!thread_
->Start()) {
877 return password_manager::PasswordStore::Init(flare
);
880 void PasswordStoreMac::Shutdown() {
881 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI
));
882 password_manager::PasswordStore::Shutdown();
886 // Mac stores passwords in the system keychain, which can block for an
887 // arbitrarily long time (most notably, it can block on user confirmation
888 // from a dialog). Run tasks on a dedicated thread to avoid blocking the DB
890 scoped_refptr
<base::SingleThreadTaskRunner
>
891 PasswordStoreMac::GetBackgroundTaskRunner() {
892 return (thread_
.get()) ? thread_
->message_loop_proxy() : NULL
;
895 void PasswordStoreMac::ReportMetricsImpl() {
896 login_metadata_db_
->ReportMetrics();
899 PasswordStoreChangeList
PasswordStoreMac::AddLoginImpl(
900 const PasswordForm
& form
) {
901 DCHECK(thread_
->message_loop() == base::MessageLoop::current());
902 PasswordStoreChangeList changes
;
903 if (AddToKeychainIfNecessary(form
)) {
904 changes
= login_metadata_db_
->AddLogin(form
);
909 PasswordStoreChangeList
PasswordStoreMac::UpdateLoginImpl(
910 const PasswordForm
& form
) {
911 DCHECK(thread_
->message_loop() == base::MessageLoop::current());
912 PasswordStoreChangeList changes
= login_metadata_db_
->UpdateLogin(form
);
914 MacKeychainPasswordFormAdapter
keychain_adapter(keychain_
.get());
915 if (changes
.empty() &&
916 !keychain_adapter
.HasPasswordsMergeableWithForm(form
)) {
917 // If the password isn't in either the DB or the keychain, then it must have
918 // been deleted after autofill happened, and should not be re-added.
922 // The keychain add will update if there is a collision and add if there
923 // isn't, which is the behavior we want, so there's no separate update call.
924 if (AddToKeychainIfNecessary(form
) && changes
.empty()) {
925 changes
= login_metadata_db_
->AddLogin(form
);
930 PasswordStoreChangeList
PasswordStoreMac::RemoveLoginImpl(
931 const PasswordForm
& form
) {
932 DCHECK(thread_
->message_loop() == base::MessageLoop::current());
933 PasswordStoreChangeList changes
;
934 if (login_metadata_db_
->RemoveLogin(form
)) {
935 // See if we own a Keychain item associated with this item. We can do an
936 // exact search rather than messing around with trying to do fuzzy matching
937 // because passwords that we created will always have an exact-match
939 // (If a user does lose their profile but not their keychain we'll treat the
940 // entries we find like other imported entries anyway, so it's reasonable to
941 // handle deletes on them the way we would for an imported item.)
942 MacKeychainPasswordFormAdapter
owned_keychain_adapter(keychain_
.get());
943 owned_keychain_adapter
.SetFindsOnlyOwnedItems(true);
944 PasswordForm
* owned_password_form
=
945 owned_keychain_adapter
.PasswordExactlyMatchingForm(form
);
946 if (owned_password_form
) {
947 // If we don't have other forms using it (i.e., a form differing only by
948 // the names of the form elements), delete the keychain entry.
949 if (!DatabaseHasFormMatchingKeychainForm(form
)) {
950 owned_keychain_adapter
.RemovePassword(form
);
954 changes
.push_back(PasswordStoreChange(PasswordStoreChange::REMOVE
, form
));
959 PasswordStoreChangeList
PasswordStoreMac::RemoveLoginsCreatedBetweenImpl(
960 base::Time delete_begin
,
961 base::Time delete_end
) {
962 PasswordStoreChangeList changes
;
963 ScopedVector
<PasswordForm
> forms
;
964 if (login_metadata_db_
->GetLoginsCreatedBetween(delete_begin
, delete_end
,
966 if (login_metadata_db_
->RemoveLoginsCreatedBetween(delete_begin
,
968 RemoveKeychainForms(forms
.get());
970 for (std::vector
<PasswordForm
*>::const_iterator it
= forms
.begin();
971 it
!= forms
.end(); ++it
) {
972 changes
.push_back(PasswordStoreChange(PasswordStoreChange::REMOVE
,
975 LogStatsForBulkDeletion(changes
.size());
981 PasswordStoreChangeList
PasswordStoreMac::RemoveLoginsSyncedBetweenImpl(
982 base::Time delete_begin
,
983 base::Time delete_end
) {
984 PasswordStoreChangeList changes
;
985 ScopedVector
<PasswordForm
> forms
;
986 if (login_metadata_db_
->GetLoginsSyncedBetween(
987 delete_begin
, delete_end
, &forms
.get())) {
988 if (login_metadata_db_
->RemoveLoginsSyncedBetween(delete_begin
,
990 RemoveKeychainForms(forms
.get());
992 for (std::vector
<PasswordForm
*>::const_iterator it
= forms
.begin();
996 PasswordStoreChange(PasswordStoreChange::REMOVE
, **it
));
1003 void PasswordStoreMac::GetLoginsImpl(
1004 const autofill::PasswordForm
& form
,
1005 AuthorizationPromptPolicy prompt_policy
,
1006 const ConsumerCallbackRunner
& callback_runner
) {
1007 chrome::ScopedSecKeychainSetUserInteractionAllowed
user_interaction_allowed(
1008 prompt_policy
== ALLOW_PROMPT
);
1010 std::vector
<PasswordForm
*> database_forms
;
1011 login_metadata_db_
->GetLogins(form
, &database_forms
);
1013 // Let's gather all signon realms we want to match with keychain entries.
1014 std::set
<std::string
> realm_set
;
1015 realm_set
.insert(form
.signon_realm
);
1016 for (std::vector
<PasswordForm
*>::const_iterator db_form
=
1017 database_forms
.begin();
1018 db_form
!= database_forms
.end();
1020 // TODO(vabr): We should not be getting different schemes here.
1021 // http://crbug.com/340112
1022 if (form
.scheme
!= (*db_form
)->scheme
)
1023 continue; // Forms with different schemes never match.
1024 const std::string
& original_singon_realm((*db_form
)->original_signon_realm
);
1025 if (!original_singon_realm
.empty())
1026 realm_set
.insert(original_singon_realm
);
1028 std::vector
<PasswordForm
*> keychain_forms
;
1029 for (std::set
<std::string
>::const_iterator realm
= realm_set
.begin();
1030 realm
!= realm_set
.end();
1032 MacKeychainPasswordFormAdapter
keychain_adapter(keychain_
.get());
1033 std::vector
<PasswordForm
*> temp_keychain_forms
=
1034 keychain_adapter
.PasswordsFillingForm(*realm
, form
.scheme
);
1035 keychain_forms
.insert(keychain_forms
.end(),
1036 temp_keychain_forms
.begin(),
1037 temp_keychain_forms
.end());
1040 std::vector
<PasswordForm
*> matched_forms
;
1041 internal_keychain_helpers::MergePasswordForms(&keychain_forms
,
1045 // Strip any blacklist entries out of the unused Keychain array, then take
1046 // all the entries that are left (which we can use as imported passwords).
1047 std::vector
<PasswordForm
*> keychain_blacklist_forms
=
1048 internal_keychain_helpers::ExtractBlacklistForms(&keychain_forms
);
1049 matched_forms
.insert(matched_forms
.end(),
1050 keychain_forms
.begin(),
1051 keychain_forms
.end());
1052 keychain_forms
.clear();
1053 STLDeleteElements(&keychain_blacklist_forms
);
1055 // Clean up any orphaned database entries.
1056 RemoveDatabaseForms(database_forms
);
1057 STLDeleteElements(&database_forms
);
1059 callback_runner
.Run(matched_forms
);
1062 void PasswordStoreMac::GetBlacklistLoginsImpl(GetLoginsRequest
* request
) {
1063 FillBlacklistLogins(request
->result());
1064 ForwardLoginsResult(request
);
1067 void PasswordStoreMac::GetAutofillableLoginsImpl(GetLoginsRequest
* request
) {
1068 FillAutofillableLogins(request
->result());
1069 ForwardLoginsResult(request
);
1072 bool PasswordStoreMac::FillAutofillableLogins(
1073 std::vector
<PasswordForm
*>* forms
) {
1074 DCHECK(thread_
->message_loop() == base::MessageLoop::current());
1076 std::vector
<PasswordForm
*> database_forms
;
1077 login_metadata_db_
->GetAutofillableLogins(&database_forms
);
1079 std::vector
<PasswordForm
*> merged_forms
=
1080 internal_keychain_helpers::GetPasswordsForForms(*keychain_
,
1083 // Clean up any orphaned database entries.
1084 RemoveDatabaseForms(database_forms
);
1085 STLDeleteElements(&database_forms
);
1087 forms
->insert(forms
->end(), merged_forms
.begin(), merged_forms
.end());
1091 bool PasswordStoreMac::FillBlacklistLogins(
1092 std::vector
<PasswordForm
*>* forms
) {
1093 DCHECK(thread_
->message_loop() == base::MessageLoop::current());
1094 return login_metadata_db_
->GetBlacklistLogins(forms
);
1097 bool PasswordStoreMac::AddToKeychainIfNecessary(const PasswordForm
& form
) {
1098 if (form
.blacklisted_by_user
) {
1101 MacKeychainPasswordFormAdapter
keychainAdapter(keychain_
.get());
1102 return keychainAdapter
.AddPassword(form
);
1105 bool PasswordStoreMac::DatabaseHasFormMatchingKeychainForm(
1106 const autofill::PasswordForm
& form
) {
1107 bool has_match
= false;
1108 std::vector
<PasswordForm
*> database_forms
;
1109 login_metadata_db_
->GetLogins(form
, &database_forms
);
1110 for (std::vector
<PasswordForm
*>::iterator i
= database_forms
.begin();
1111 i
!= database_forms
.end(); ++i
) {
1112 // Below we filter out forms with non-empty original_signon_realm, because
1113 // those signal fuzzy matches, and we are only interested in exact ones.
1114 if ((*i
)->original_signon_realm
.empty() &&
1115 internal_keychain_helpers::FormsMatchForMerge(
1116 form
, **i
, internal_keychain_helpers::STRICT_FORM_MATCH
) &&
1117 (*i
)->origin
== form
.origin
) {
1122 STLDeleteElements(&database_forms
);
1126 void PasswordStoreMac::RemoveDatabaseForms(
1127 const std::vector
<PasswordForm
*>& forms
) {
1128 for (std::vector
<PasswordForm
*>::const_iterator i
= forms
.begin();
1129 i
!= forms
.end(); ++i
) {
1130 login_metadata_db_
->RemoveLogin(**i
);
1134 void PasswordStoreMac::RemoveKeychainForms(
1135 const std::vector
<PasswordForm
*>& forms
) {
1136 MacKeychainPasswordFormAdapter
owned_keychain_adapter(keychain_
.get());
1137 owned_keychain_adapter
.SetFindsOnlyOwnedItems(true);
1138 for (std::vector
<PasswordForm
*>::const_iterator i
= forms
.begin();
1139 i
!= forms
.end(); ++i
) {
1140 owned_keychain_adapter
.RemovePassword(**i
);