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
;
36 // Utility class to handle the details of constructing and running a keychain
37 // search from a set of attributes.
38 class KeychainSearch
{
40 explicit KeychainSearch(const AppleKeychain
& keychain
);
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
,
50 const SecProtocolType
* protocol
,
51 const SecAuthenticationType
* auth_type
,
52 const char* security_domain
,
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
);
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
,
81 const SecProtocolType
* protocol
,
82 const SecAuthenticationType
* auth_type
,
83 const char* security_domain
,
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.
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
));
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
));
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
));
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
));
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
));
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
));
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
));
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
));
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";
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_
);
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
));
196 // TODO(stuartmorgan): Convert most of this to private helpers in
197 // MacKeychainPasswordFormAdapter once it has sufficient higher-level public
198 // methods to provide test coverage.
199 namespace internal_keychain_helpers
{
201 // Returns a URL built from the given components. To create a URL without a
202 // port, pass kAnyPort for the |port| parameter.
203 GURL
URLFromComponents(bool is_secure
, const std::string
& host
, int port
,
204 const std::string
& path
) {
205 GURL::Replacements url_components
;
206 std::string
scheme(is_secure
? "https" : "http");
207 url_components
.SetSchemeStr(scheme
);
208 url_components
.SetHostStr(host
);
209 std::string port_string
; // Must remain in scope until after we do replacing.
210 if (port
!= kAnyPort
) {
211 std::ostringstream port_stringstream
;
212 port_stringstream
<< port
;
213 port_string
= port_stringstream
.str();
214 url_components
.SetPortStr(port_string
);
216 url_components
.SetPathStr(path
);
218 GURL
url("http://dummy.com"); // ReplaceComponents needs a valid URL.
219 return url
.ReplaceComponents(url_components
);
222 // Converts a Keychain time string to a Time object, returning true if
223 // time_string_bytes was parsable. If the return value is false, the value of
224 // |time| is unchanged.
225 bool TimeFromKeychainTimeString(const char* time_string_bytes
,
226 unsigned int byte_length
,
230 char* time_string
= static_cast<char*>(malloc(byte_length
+ 1));
231 memcpy(time_string
, time_string_bytes
, byte_length
);
232 time_string
[byte_length
] = '\0';
233 base::Time::Exploded exploded_time
;
234 bzero(&exploded_time
, sizeof(exploded_time
));
235 // The time string is of the form "yyyyMMddHHmmss'Z", in UTC time.
236 int assignments
= sscanf(time_string
, "%4d%2d%2d%2d%2d%2dZ",
237 &exploded_time
.year
, &exploded_time
.month
,
238 &exploded_time
.day_of_month
, &exploded_time
.hour
,
239 &exploded_time
.minute
, &exploded_time
.second
);
242 if (assignments
== 6) {
243 *time
= base::Time::FromUTCExploded(exploded_time
);
249 // Returns the PasswordForm Scheme corresponding to |auth_type|.
250 PasswordForm::Scheme
SchemeForAuthType(SecAuthenticationType auth_type
) {
252 case kSecAuthenticationTypeHTMLForm
: return PasswordForm::SCHEME_HTML
;
253 case kSecAuthenticationTypeHTTPBasic
: return PasswordForm::SCHEME_BASIC
;
254 case kSecAuthenticationTypeHTTPDigest
: return PasswordForm::SCHEME_DIGEST
;
255 default: return PasswordForm::SCHEME_OTHER
;
259 bool FillPasswordFormFromKeychainItem(const AppleKeychain
& keychain
,
260 const SecKeychainItemRef
& keychain_item
,
262 bool extract_password_data
) {
265 SecKeychainAttributeInfo attrInfo
;
266 UInt32 tags
[] = { kSecAccountItemAttr
,
270 kSecProtocolItemAttr
,
271 kSecAuthenticationTypeItemAttr
,
272 kSecSecurityDomainItemAttr
,
273 kSecCreationDateItemAttr
,
274 kSecNegativeItemAttr
};
275 attrInfo
.count
= arraysize(tags
);
277 attrInfo
.format
= NULL
;
279 SecKeychainAttributeList
*attrList
;
280 UInt32 password_length
;
282 // If |extract_password_data| is false, do not pass in a reference to
283 // |password_data|. ItemCopyAttributesAndData will then extract only the
284 // attributes of |keychain_item| (doesn't require OS authorization), and not
285 // attempt to extract its password data (requires OS authorization).
286 void* password_data
= NULL
;
287 void** password_data_ref
= extract_password_data
? &password_data
: NULL
;
289 OSStatus result
= keychain
.ItemCopyAttributesAndData(keychain_item
, &attrInfo
,
294 if (result
!= noErr
) {
295 // We don't log errSecAuthFailed because that just means that the user
296 // chose not to allow us access to the item.
297 if (result
!= errSecAuthFailed
) {
298 OSSTATUS_LOG(ERROR
, result
) << "Keychain data load failed";
303 if (extract_password_data
) {
304 base::UTF8ToUTF16(static_cast<const char *>(password_data
), password_length
,
305 &(form
->password_value
));
310 std::string security_domain
;
312 for (unsigned int i
= 0; i
< attrList
->count
; i
++) {
313 SecKeychainAttribute attr
= attrList
->attr
[i
];
318 case kSecAccountItemAttr
:
319 base::UTF8ToUTF16(static_cast<const char *>(attr
.data
), attr
.length
,
320 &(form
->username_value
));
322 case kSecServerItemAttr
:
323 server
.assign(static_cast<const char *>(attr
.data
), attr
.length
);
325 case kSecPortItemAttr
:
326 port
= *(static_cast<UInt32
*>(attr
.data
));
328 case kSecPathItemAttr
:
329 path
.assign(static_cast<const char *>(attr
.data
), attr
.length
);
331 case kSecProtocolItemAttr
:
333 SecProtocolType protocol
= *(static_cast<SecProtocolType
*>(attr
.data
));
334 // TODO(stuartmorgan): Handle proxy types
335 form
->ssl_valid
= (protocol
== kSecProtocolTypeHTTPS
);
338 case kSecAuthenticationTypeItemAttr
:
340 SecAuthenticationType auth_type
=
341 *(static_cast<SecAuthenticationType
*>(attr
.data
));
342 form
->scheme
= SchemeForAuthType(auth_type
);
345 case kSecSecurityDomainItemAttr
:
346 security_domain
.assign(static_cast<const char *>(attr
.data
),
349 case kSecCreationDateItemAttr
:
350 // The only way to get a date out of Keychain is as a string. Really.
351 // (The docs claim it's an int, but the header is correct.)
352 TimeFromKeychainTimeString(static_cast<char*>(attr
.data
), attr
.length
,
353 &form
->date_created
);
355 case kSecNegativeItemAttr
:
356 Boolean negative_item
= *(static_cast<Boolean
*>(attr
.data
));
358 form
->blacklisted_by_user
= true;
363 keychain
.ItemFreeAttributesAndData(attrList
, password_data
);
365 // kSecNegativeItemAttr doesn't seem to actually be in widespread use. In
366 // practice, other browsers seem to use a "" or " " password (and a special
367 // user name) to indicated blacklist entries.
368 if (extract_password_data
&& (form
->password_value
.empty() ||
369 EqualsASCII(form
->password_value
, " "))) {
370 form
->blacklisted_by_user
= true;
373 form
->origin
= URLFromComponents(form
->ssl_valid
, server
, port
, path
);
374 // TODO(stuartmorgan): Handle proxies, which need a different signon_realm
376 form
->signon_realm
= form
->origin
.GetOrigin().spec();
377 if (form
->scheme
!= PasswordForm::SCHEME_HTML
) {
378 form
->signon_realm
.append(security_domain
);
383 bool FormsMatchForMerge(const PasswordForm
& form_a
,
384 const PasswordForm
& form_b
,
385 FormMatchStrictness strictness
) {
386 // We never merge blacklist entries between our store and the keychain.
387 if (form_a
.blacklisted_by_user
|| form_b
.blacklisted_by_user
) {
390 bool equal_realm
= form_a
.signon_realm
== form_b
.signon_realm
;
391 if (strictness
== FUZZY_FORM_MATCH
) {
392 equal_realm
|= (!form_a
.original_signon_realm
.empty()) &&
393 form_a
.original_signon_realm
== form_b
.signon_realm
;
395 return form_a
.scheme
== form_b
.scheme
&& equal_realm
&&
396 form_a
.username_value
== form_b
.username_value
;
399 // Returns an the best match for |base_form| from |keychain_forms|, or NULL if
400 // there is no suitable match.
401 PasswordForm
* BestKeychainFormForForm(
402 const PasswordForm
& base_form
,
403 const std::vector
<PasswordForm
*>* keychain_forms
) {
404 PasswordForm
* partial_match
= NULL
;
405 for (std::vector
<PasswordForm
*>::const_iterator i
= keychain_forms
->begin();
406 i
!= keychain_forms
->end(); ++i
) {
407 // TODO(stuartmorgan): We should really be scoring path matches and picking
408 // the best, rather than just checking exact-or-not (although in practice
409 // keychain items with paths probably came from us).
410 if (FormsMatchForMerge(base_form
, *(*i
), FUZZY_FORM_MATCH
)) {
411 if (base_form
.origin
== (*i
)->origin
) {
413 } else if (!partial_match
) {
418 return partial_match
;
421 // Returns entries from |forms| that are blacklist entries, after removing
422 // them from |forms|.
423 std::vector
<PasswordForm
*> ExtractBlacklistForms(
424 std::vector
<PasswordForm
*>* forms
) {
425 std::vector
<PasswordForm
*> blacklist_forms
;
426 for (std::vector
<PasswordForm
*>::iterator i
= forms
->begin();
427 i
!= forms
->end();) {
428 PasswordForm
* form
= *i
;
429 if (form
->blacklisted_by_user
) {
430 blacklist_forms
.push_back(form
);
436 return blacklist_forms
;
439 // Deletes and removes from v any element that exists in s.
441 void DeleteVectorElementsInSet(std::vector
<T
*>* v
, const std::set
<T
*>& s
) {
442 for (typename
std::vector
<T
*>::iterator i
= v
->begin(); i
!= v
->end();) {
444 if (s
.find(element
) != s
.end()) {
453 void MergePasswordForms(std::vector
<PasswordForm
*>* keychain_forms
,
454 std::vector
<PasswordForm
*>* database_forms
,
455 std::vector
<PasswordForm
*>* merged_forms
) {
456 // Pull out the database blacklist items, since they are used as-is rather
457 // than being merged with keychain forms.
458 std::vector
<PasswordForm
*> database_blacklist_forms
=
459 ExtractBlacklistForms(database_forms
);
461 // Merge the normal entries.
462 std::set
<PasswordForm
*> used_keychain_forms
;
463 for (std::vector
<PasswordForm
*>::iterator i
= database_forms
->begin();
464 i
!= database_forms
->end();) {
465 PasswordForm
* db_form
= *i
;
466 PasswordForm
* best_match
= BestKeychainFormForForm(*db_form
,
469 used_keychain_forms
.insert(best_match
);
470 db_form
->password_value
= best_match
->password_value
;
471 merged_forms
->push_back(db_form
);
472 i
= database_forms
->erase(i
);
478 // Add in the blacklist entries from the database.
479 merged_forms
->insert(merged_forms
->end(),
480 database_blacklist_forms
.begin(),
481 database_blacklist_forms
.end());
483 // Clear out all the Keychain entries we used.
484 DeleteVectorElementsInSet(keychain_forms
, used_keychain_forms
);
487 std::vector
<ItemFormPair
> ExtractAllKeychainItemAttributesIntoPasswordForms(
488 std::vector
<SecKeychainItemRef
>* keychain_items
,
489 const AppleKeychain
& keychain
) {
490 DCHECK(keychain_items
);
491 MacKeychainPasswordFormAdapter
keychain_adapter(&keychain
);
492 *keychain_items
= keychain_adapter
.GetAllPasswordFormKeychainItems();
493 std::vector
<ItemFormPair
> item_form_pairs
;
494 for (std::vector
<SecKeychainItemRef
>::iterator i
= keychain_items
->begin();
495 i
!= keychain_items
->end(); ++i
) {
496 PasswordForm
* form_without_password
= new PasswordForm();
497 internal_keychain_helpers::FillPasswordFormFromKeychainItem(
500 form_without_password
,
501 false); // Load password attributes, but not password data.
502 item_form_pairs
.push_back(std::make_pair(&(*i
), form_without_password
));
504 return item_form_pairs
;
507 std::vector
<PasswordForm
*> GetPasswordsForForms(
508 const AppleKeychain
& keychain
,
509 std::vector
<PasswordForm
*>* database_forms
) {
510 // First load the attributes of all items in the keychain without loading
511 // their password data, and then match items in |database_forms| against them.
512 // This avoids individually searching through the keychain for passwords
513 // matching each form in |database_forms|, and results in a significant
514 // performance gain, replacing O(N) keychain search operations with a single
515 // operation that loads all keychain items, and then selective reads of only
516 // the relevant passwords. See crbug.com/263685.
517 std::vector
<SecKeychainItemRef
> keychain_items
;
518 std::vector
<ItemFormPair
> item_form_pairs
=
519 ExtractAllKeychainItemAttributesIntoPasswordForms(&keychain_items
,
522 // Next, compare the attributes of the PasswordForms in |database_forms|
523 // against those in |item_form_pairs|, and extract password data for each
524 // matching PasswordForm using its corresponding SecKeychainItemRef.
525 std::vector
<PasswordForm
*> merged_forms
;
526 for (std::vector
<PasswordForm
*>::iterator i
= database_forms
->begin();
527 i
!= database_forms
->end();) {
528 std::vector
<PasswordForm
*> db_form_container(1, *i
);
529 std::vector
<PasswordForm
*> keychain_matches
=
530 ExtractPasswordsMergeableWithForm(keychain
, item_form_pairs
, **i
);
531 MergePasswordForms(&keychain_matches
, &db_form_container
, &merged_forms
);
532 if (db_form_container
.empty()) {
533 i
= database_forms
->erase(i
);
537 STLDeleteElements(&keychain_matches
);
540 // Clean up temporary PasswordForms and SecKeychainItemRefs.
541 STLDeleteContainerPairSecondPointers(item_form_pairs
.begin(),
542 item_form_pairs
.end());
543 for (std::vector
<SecKeychainItemRef
>::iterator i
= keychain_items
.begin();
544 i
!= keychain_items
.end(); ++i
) {
550 // TODO(stuartmorgan): signon_realm for proxies is not yet supported.
551 bool ExtractSignonRealmComponents(const std::string
& signon_realm
,
555 std::string
* security_domain
) {
556 // The signon_realm will be the Origin portion of a URL for an HTML form,
557 // and the same but with the security domain as a path for HTTP auth.
558 GURL
realm_as_url(signon_realm
);
559 if (!realm_as_url
.is_valid()) {
564 *server
= realm_as_url
.host();
566 *is_secure
= realm_as_url
.SchemeIsSecure();
568 *port
= realm_as_url
.has_port() ? atoi(realm_as_url
.port().c_str()) : 0;
569 if (security_domain
) {
570 // Strip the leading '/' off of the path to get the security domain.
571 if (realm_as_url
.path().length() > 0)
572 *security_domain
= realm_as_url
.path().substr(1);
574 security_domain
->clear();
579 bool FormIsValidAndMatchesOtherForm(const PasswordForm
& query_form
,
580 const PasswordForm
& other_form
) {
582 std::string security_domain
;
585 if (!ExtractSignonRealmComponents(query_form
.signon_realm
, &server
, &port
,
586 &is_secure
, &security_domain
)) {
589 return internal_keychain_helpers::FormsMatchForMerge(
590 query_form
, other_form
, STRICT_FORM_MATCH
);
593 std::vector
<PasswordForm
*> ExtractPasswordsMergeableWithForm(
594 const AppleKeychain
& keychain
,
595 const std::vector
<ItemFormPair
>& item_form_pairs
,
596 const PasswordForm
& query_form
) {
597 std::vector
<PasswordForm
*> matches
;
598 for (std::vector
<ItemFormPair
>::const_iterator i
= item_form_pairs
.begin();
599 i
!= item_form_pairs
.end(); ++i
) {
600 if (FormIsValidAndMatchesOtherForm(query_form
, *(i
->second
))) {
601 // Create a new object, since the caller is responsible for deleting the
603 scoped_ptr
<PasswordForm
> form_with_password(new PasswordForm());
604 internal_keychain_helpers::FillPasswordFormFromKeychainItem(
607 form_with_password
.get(),
608 true); // Load password attributes and data.
609 // Do not include blacklisted items found in the keychain.
610 if (!form_with_password
->blacklisted_by_user
)
611 matches
.push_back(form_with_password
.release());
617 } // namespace internal_keychain_helpers
621 MacKeychainPasswordFormAdapter::MacKeychainPasswordFormAdapter(
622 const AppleKeychain
* keychain
)
623 : keychain_(keychain
), finds_only_owned_(false) {
626 std::vector
<PasswordForm
*> MacKeychainPasswordFormAdapter::PasswordsFillingForm(
627 const std::string
& signon_realm
,
628 PasswordForm::Scheme scheme
) {
629 std::vector
<SecKeychainItemRef
> keychain_items
=
630 MatchingKeychainItems(signon_realm
, scheme
, NULL
, NULL
);
632 return ConvertKeychainItemsToForms(&keychain_items
);
635 bool MacKeychainPasswordFormAdapter::HasPasswordExactlyMatchingForm(
636 const PasswordForm
& query_form
) {
637 SecKeychainItemRef keychain_item
= KeychainItemForForm(query_form
);
639 keychain_
->Free(keychain_item
);
645 bool MacKeychainPasswordFormAdapter::HasPasswordsMergeableWithForm(
646 const PasswordForm
& query_form
) {
647 std::string username
= base::UTF16ToUTF8(query_form
.username_value
);
648 std::vector
<SecKeychainItemRef
> matches
=
649 MatchingKeychainItems(query_form
.signon_realm
, query_form
.scheme
,
650 NULL
, username
.c_str());
651 for (std::vector
<SecKeychainItemRef
>::iterator i
= matches
.begin();
652 i
!= matches
.end(); ++i
) {
656 return !matches
.empty();
659 std::vector
<SecKeychainItemRef
>
660 MacKeychainPasswordFormAdapter::GetAllPasswordFormKeychainItems() {
661 SecAuthenticationType supported_auth_types
[] = {
662 kSecAuthenticationTypeHTMLForm
,
663 kSecAuthenticationTypeHTTPBasic
,
664 kSecAuthenticationTypeHTTPDigest
,
667 std::vector
<SecKeychainItemRef
> matches
;
668 for (unsigned int i
= 0; i
< arraysize(supported_auth_types
); ++i
) {
669 KeychainSearch
keychain_search(*keychain_
);
670 OSType creator
= CreatorCodeForSearch();
671 keychain_search
.Init(NULL
,
674 &supported_auth_types
[i
],
678 creator
? &creator
: NULL
);
679 keychain_search
.FindMatchingItems(&matches
);
684 std::vector
<PasswordForm
*>
685 MacKeychainPasswordFormAdapter::GetAllPasswordFormPasswords() {
686 std::vector
<SecKeychainItemRef
> items
= GetAllPasswordFormKeychainItems();
687 return ConvertKeychainItemsToForms(&items
);
690 bool MacKeychainPasswordFormAdapter::AddPassword(const PasswordForm
& form
) {
691 // We should never be trying to store a blacklist in the keychain.
692 DCHECK(!form
.blacklisted_by_user
);
695 std::string security_domain
;
698 if (!internal_keychain_helpers::ExtractSignonRealmComponents(
699 form
.signon_realm
, &server
, &port
, &is_secure
, &security_domain
)) {
702 std::string username
= base::UTF16ToUTF8(form
.username_value
);
703 std::string password
= base::UTF16ToUTF8(form
.password_value
);
704 std::string path
= form
.origin
.path();
705 SecProtocolType protocol
= is_secure
? kSecProtocolTypeHTTPS
706 : kSecProtocolTypeHTTP
;
707 SecKeychainItemRef new_item
= NULL
;
708 OSStatus result
= keychain_
->AddInternetPassword(
709 NULL
, server
.size(), server
.c_str(),
710 security_domain
.size(), security_domain
.c_str(),
711 username
.size(), username
.c_str(),
712 path
.size(), path
.c_str(),
713 port
, protocol
, AuthTypeForScheme(form
.scheme
),
714 password
.size(), password
.c_str(), &new_item
);
716 if (result
== noErr
) {
717 SetKeychainItemCreatorCode(new_item
,
718 base::mac::CreatorCodeForApplication());
719 keychain_
->Free(new_item
);
720 } else if (result
== errSecDuplicateItem
) {
721 // If we collide with an existing item, find and update it instead.
722 SecKeychainItemRef existing_item
= KeychainItemForForm(form
);
723 if (!existing_item
) {
726 bool changed
= SetKeychainItemPassword(existing_item
, password
);
727 keychain_
->Free(existing_item
);
731 return result
== noErr
;
734 bool MacKeychainPasswordFormAdapter::RemovePassword(const PasswordForm
& form
) {
735 SecKeychainItemRef keychain_item
= KeychainItemForForm(form
);
736 if (keychain_item
== NULL
)
738 OSStatus result
= keychain_
->ItemDelete(keychain_item
);
739 keychain_
->Free(keychain_item
);
740 return result
== noErr
;
743 void MacKeychainPasswordFormAdapter::SetFindsOnlyOwnedItems(
744 bool finds_only_owned
) {
745 finds_only_owned_
= finds_only_owned
;
748 std::vector
<PasswordForm
*>
749 MacKeychainPasswordFormAdapter::ConvertKeychainItemsToForms(
750 std::vector
<SecKeychainItemRef
>* items
) {
751 std::vector
<PasswordForm
*> keychain_forms
;
752 for (std::vector
<SecKeychainItemRef
>::const_iterator i
= items
->begin();
753 i
!= items
->end(); ++i
) {
754 PasswordForm
* form
= new PasswordForm();
755 if (internal_keychain_helpers::FillPasswordFormFromKeychainItem(
756 *keychain_
, *i
, form
, true)) {
757 keychain_forms
.push_back(form
);
762 return keychain_forms
;
765 SecKeychainItemRef
MacKeychainPasswordFormAdapter::KeychainItemForForm(
766 const PasswordForm
& form
) {
767 // We don't store blacklist entries in the keychain, so the answer to "what
768 // Keychain item goes with this form" is always "nothing" for blacklists.
769 if (form
.blacklisted_by_user
) {
773 std::string path
= form
.origin
.path();
774 std::string username
= base::UTF16ToUTF8(form
.username_value
);
775 std::vector
<SecKeychainItemRef
> matches
= MatchingKeychainItems(
776 form
.signon_realm
, form
.scheme
, path
.c_str(), username
.c_str());
778 if (matches
.empty()) {
781 // Free all items after the first, since we won't be returning them.
782 for (std::vector
<SecKeychainItemRef
>::iterator i
= matches
.begin() + 1;
783 i
!= matches
.end(); ++i
) {
789 std::vector
<SecKeychainItemRef
>
790 MacKeychainPasswordFormAdapter::MatchingKeychainItems(
791 const std::string
& signon_realm
,
792 autofill::PasswordForm::Scheme scheme
,
793 const char* path
, const char* username
) {
794 std::vector
<SecKeychainItemRef
> matches
;
797 std::string security_domain
;
800 if (!internal_keychain_helpers::ExtractSignonRealmComponents(
801 signon_realm
, &server
, &port
, &is_secure
, &security_domain
)) {
802 // TODO(stuartmorgan): Proxies will currently fail here, since their
803 // signon_realm is not a URL. We need to detect the proxy case and handle
807 SecProtocolType protocol
= is_secure
? kSecProtocolTypeHTTPS
808 : kSecProtocolTypeHTTP
;
809 SecAuthenticationType auth_type
= AuthTypeForScheme(scheme
);
810 const char* auth_domain
= (scheme
== PasswordForm::SCHEME_HTML
) ?
811 NULL
: security_domain
.c_str();
812 OSType creator
= CreatorCodeForSearch();
813 KeychainSearch
keychain_search(*keychain_
);
814 keychain_search
.Init(server
.c_str(),
821 creator
? &creator
: NULL
);
822 keychain_search
.FindMatchingItems(&matches
);
826 // Returns the Keychain SecAuthenticationType type corresponding to |scheme|.
827 SecAuthenticationType
MacKeychainPasswordFormAdapter::AuthTypeForScheme(
828 PasswordForm::Scheme scheme
) {
830 case PasswordForm::SCHEME_HTML
: return kSecAuthenticationTypeHTMLForm
;
831 case PasswordForm::SCHEME_BASIC
: return kSecAuthenticationTypeHTTPBasic
;
832 case PasswordForm::SCHEME_DIGEST
: return kSecAuthenticationTypeHTTPDigest
;
833 case PasswordForm::SCHEME_OTHER
: return kSecAuthenticationTypeDefault
;
836 return kSecAuthenticationTypeDefault
;
839 bool MacKeychainPasswordFormAdapter::SetKeychainItemPassword(
840 const SecKeychainItemRef
& keychain_item
, const std::string
& password
) {
841 OSStatus result
= keychain_
->ItemModifyAttributesAndData(keychain_item
, NULL
,
844 return result
== noErr
;
847 bool MacKeychainPasswordFormAdapter::SetKeychainItemCreatorCode(
848 const SecKeychainItemRef
& keychain_item
, OSType creator_code
) {
849 SecKeychainAttribute attr
= { kSecCreatorItemAttr
, sizeof(creator_code
),
851 SecKeychainAttributeList attrList
= { 1, &attr
};
852 OSStatus result
= keychain_
->ItemModifyAttributesAndData(keychain_item
,
854 return result
== noErr
;
857 OSType
MacKeychainPasswordFormAdapter::CreatorCodeForSearch() {
858 return finds_only_owned_
? base::mac::CreatorCodeForApplication() : 0;
863 PasswordStoreMac::PasswordStoreMac(
864 scoped_refptr
<base::SingleThreadTaskRunner
> main_thread_runner
,
865 scoped_refptr
<base::SingleThreadTaskRunner
> db_thread_runner
,
866 AppleKeychain
* keychain
,
867 password_manager::LoginDatabase
* login_db
)
868 : password_manager::PasswordStore(main_thread_runner
, db_thread_runner
),
870 login_metadata_db_(login_db
) {
871 DCHECK(keychain_
.get());
872 DCHECK(login_metadata_db_
.get());
875 PasswordStoreMac::~PasswordStoreMac() {}
877 bool PasswordStoreMac::Init(
878 const syncer::SyncableService::StartSyncFlare
& flare
,
879 const std::string
& sync_username
) {
880 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI
));
881 thread_
.reset(new base::Thread("Chrome_PasswordStore_Thread"));
883 if (!thread_
->Start()) {
887 return password_manager::PasswordStore::Init(flare
, sync_username
);
890 void PasswordStoreMac::Shutdown() {
891 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI
));
892 password_manager::PasswordStore::Shutdown();
896 // Mac stores passwords in the system keychain, which can block for an
897 // arbitrarily long time (most notably, it can block on user confirmation
898 // from a dialog). Run tasks on a dedicated thread to avoid blocking the DB
900 scoped_refptr
<base::SingleThreadTaskRunner
>
901 PasswordStoreMac::GetBackgroundTaskRunner() {
902 return (thread_
.get()) ? thread_
->message_loop_proxy() : NULL
;
905 void PasswordStoreMac::ReportMetricsImpl(const std::string
& sync_username
) {
906 login_metadata_db_
->ReportMetrics(sync_username
);
909 PasswordStoreChangeList
PasswordStoreMac::AddLoginImpl(
910 const PasswordForm
& form
) {
911 DCHECK(thread_
->message_loop() == base::MessageLoop::current());
912 PasswordStoreChangeList changes
;
913 if (AddToKeychainIfNecessary(form
)) {
914 changes
= login_metadata_db_
->AddLogin(form
);
919 PasswordStoreChangeList
PasswordStoreMac::UpdateLoginImpl(
920 const PasswordForm
& form
) {
921 DCHECK(thread_
->message_loop() == base::MessageLoop::current());
922 PasswordStoreChangeList changes
= login_metadata_db_
->UpdateLogin(form
);
924 MacKeychainPasswordFormAdapter
keychain_adapter(keychain_
.get());
925 if (changes
.empty() &&
926 !keychain_adapter
.HasPasswordsMergeableWithForm(form
)) {
927 // If the password isn't in either the DB or the keychain, then it must have
928 // been deleted after autofill happened, and should not be re-added.
932 // The keychain add will update if there is a collision and add if there
933 // isn't, which is the behavior we want, so there's no separate update call.
934 if (AddToKeychainIfNecessary(form
) && changes
.empty()) {
935 changes
= login_metadata_db_
->AddLogin(form
);
940 PasswordStoreChangeList
PasswordStoreMac::RemoveLoginImpl(
941 const PasswordForm
& form
) {
942 DCHECK(thread_
->message_loop() == base::MessageLoop::current());
943 PasswordStoreChangeList changes
;
944 if (login_metadata_db_
->RemoveLogin(form
)) {
945 // See if we own a Keychain item associated with this item. We can do an
946 // exact search rather than messing around with trying to do fuzzy matching
947 // because passwords that we created will always have an exact-match
949 // (If a user does lose their profile but not their keychain we'll treat the
950 // entries we find like other imported entries anyway, so it's reasonable to
951 // handle deletes on them the way we would for an imported item.)
952 MacKeychainPasswordFormAdapter
owned_keychain_adapter(keychain_
.get());
953 owned_keychain_adapter
.SetFindsOnlyOwnedItems(true);
954 if (owned_keychain_adapter
.HasPasswordExactlyMatchingForm(form
)) {
955 // If we don't have other forms using it (i.e., a form differing only by
956 // the names of the form elements), delete the keychain entry.
957 if (!DatabaseHasFormMatchingKeychainForm(form
)) {
958 owned_keychain_adapter
.RemovePassword(form
);
962 changes
.push_back(PasswordStoreChange(PasswordStoreChange::REMOVE
, form
));
967 PasswordStoreChangeList
PasswordStoreMac::RemoveLoginsCreatedBetweenImpl(
968 base::Time delete_begin
,
969 base::Time delete_end
) {
970 PasswordStoreChangeList changes
;
971 ScopedVector
<PasswordForm
> forms
;
972 if (login_metadata_db_
->GetLoginsCreatedBetween(delete_begin
, delete_end
,
974 if (login_metadata_db_
->RemoveLoginsCreatedBetween(delete_begin
,
976 RemoveKeychainForms(forms
.get());
977 CleanOrphanedForms(&forms
.get());
978 changes
= FormsToRemoveChangeList(forms
.get());
979 LogStatsForBulkDeletion(changes
.size());
985 PasswordStoreChangeList
PasswordStoreMac::RemoveLoginsSyncedBetweenImpl(
986 base::Time delete_begin
,
987 base::Time delete_end
) {
988 PasswordStoreChangeList changes
;
989 ScopedVector
<PasswordForm
> forms
;
990 if (login_metadata_db_
->GetLoginsSyncedBetween(
991 delete_begin
, delete_end
, &forms
.get())) {
992 if (login_metadata_db_
->RemoveLoginsSyncedBetween(delete_begin
,
994 RemoveKeychainForms(forms
.get());
995 CleanOrphanedForms(&forms
.get());
996 changes
= FormsToRemoveChangeList(forms
.get());
1002 void PasswordStoreMac::GetLoginsImpl(
1003 const autofill::PasswordForm
& form
,
1004 AuthorizationPromptPolicy prompt_policy
,
1005 const ConsumerCallbackRunner
& callback_runner
) {
1006 chrome::ScopedSecKeychainSetUserInteractionAllowed
user_interaction_allowed(
1007 prompt_policy
== ALLOW_PROMPT
);
1009 ScopedVector
<PasswordForm
> database_forms
;
1010 login_metadata_db_
->GetLogins(form
, &database_forms
.get());
1012 // Let's gather all signon realms we want to match with keychain entries.
1013 std::set
<std::string
> realm_set
;
1014 realm_set
.insert(form
.signon_realm
);
1015 for (std::vector
<PasswordForm
*>::const_iterator db_form
=
1016 database_forms
.begin();
1017 db_form
!= database_forms
.end();
1019 // TODO(vabr): We should not be getting different schemes here.
1020 // http://crbug.com/340112
1021 if (form
.scheme
!= (*db_form
)->scheme
)
1022 continue; // Forms with different schemes never match.
1023 const std::string
& original_singon_realm((*db_form
)->original_signon_realm
);
1024 if (!original_singon_realm
.empty())
1025 realm_set
.insert(original_singon_realm
);
1027 std::vector
<PasswordForm
*> keychain_forms
;
1028 for (std::set
<std::string
>::const_iterator realm
= realm_set
.begin();
1029 realm
!= realm_set
.end();
1031 MacKeychainPasswordFormAdapter
keychain_adapter(keychain_
.get());
1032 std::vector
<PasswordForm
*> temp_keychain_forms
=
1033 keychain_adapter
.PasswordsFillingForm(*realm
, form
.scheme
);
1034 keychain_forms
.insert(keychain_forms
.end(),
1035 temp_keychain_forms
.begin(),
1036 temp_keychain_forms
.end());
1039 std::vector
<PasswordForm
*> matched_forms
;
1040 internal_keychain_helpers::MergePasswordForms(&keychain_forms
,
1041 &database_forms
.get(),
1044 // Strip any blacklist entries out of the unused Keychain array, then take
1045 // all the entries that are left (which we can use as imported passwords).
1046 ScopedVector
<PasswordForm
> keychain_blacklist_forms
;
1047 internal_keychain_helpers::ExtractBlacklistForms(&keychain_forms
).swap(
1048 keychain_blacklist_forms
.get());
1049 matched_forms
.insert(matched_forms
.end(),
1050 keychain_forms
.begin(),
1051 keychain_forms
.end());
1052 keychain_forms
.clear();
1054 if (!database_forms
.empty()) {
1055 RemoveDatabaseForms(database_forms
.get());
1056 NotifyLoginsChanged(FormsToRemoveChangeList(database_forms
.get()));
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 ScopedVector
<PasswordForm
> database_forms
;
1077 if (!login_metadata_db_
->GetAutofillableLogins(&database_forms
.get()))
1080 std::vector
<PasswordForm
*> merged_forms
=
1081 internal_keychain_helpers::GetPasswordsForForms(*keychain_
,
1082 &database_forms
.get());
1084 if (!database_forms
.empty()) {
1085 RemoveDatabaseForms(database_forms
.get());
1086 NotifyLoginsChanged(FormsToRemoveChangeList(database_forms
.get()));
1089 forms
->insert(forms
->end(), merged_forms
.begin(), merged_forms
.end());
1093 bool PasswordStoreMac::FillBlacklistLogins(
1094 std::vector
<PasswordForm
*>* forms
) {
1095 DCHECK(thread_
->message_loop() == base::MessageLoop::current());
1096 return login_metadata_db_
->GetBlacklistLogins(forms
);
1099 bool PasswordStoreMac::AddToKeychainIfNecessary(const PasswordForm
& form
) {
1100 if (form
.blacklisted_by_user
) {
1103 MacKeychainPasswordFormAdapter
keychainAdapter(keychain_
.get());
1104 return keychainAdapter
.AddPassword(form
);
1107 bool PasswordStoreMac::DatabaseHasFormMatchingKeychainForm(
1108 const autofill::PasswordForm
& form
) {
1109 bool has_match
= false;
1110 std::vector
<PasswordForm
*> database_forms
;
1111 login_metadata_db_
->GetLogins(form
, &database_forms
);
1112 for (std::vector
<PasswordForm
*>::iterator i
= database_forms
.begin();
1113 i
!= database_forms
.end(); ++i
) {
1114 // Below we filter out forms with non-empty original_signon_realm, because
1115 // those signal fuzzy matches, and we are only interested in exact ones.
1116 if ((*i
)->original_signon_realm
.empty() &&
1117 internal_keychain_helpers::FormsMatchForMerge(
1118 form
, **i
, internal_keychain_helpers::STRICT_FORM_MATCH
) &&
1119 (*i
)->origin
== form
.origin
) {
1124 STLDeleteElements(&database_forms
);
1128 void PasswordStoreMac::RemoveDatabaseForms(
1129 const std::vector
<PasswordForm
*>& forms
) {
1130 for (std::vector
<PasswordForm
*>::const_iterator i
= forms
.begin();
1131 i
!= forms
.end(); ++i
) {
1132 login_metadata_db_
->RemoveLogin(**i
);
1136 void PasswordStoreMac::RemoveKeychainForms(
1137 const std::vector
<PasswordForm
*>& forms
) {
1138 MacKeychainPasswordFormAdapter
owned_keychain_adapter(keychain_
.get());
1139 owned_keychain_adapter
.SetFindsOnlyOwnedItems(true);
1140 for (std::vector
<PasswordForm
*>::const_iterator i
= forms
.begin();
1141 i
!= forms
.end(); ++i
) {
1142 owned_keychain_adapter
.RemovePassword(**i
);
1146 void PasswordStoreMac::CleanOrphanedForms(std::vector
<PasswordForm
*>* forms
) {
1148 std::vector
<PasswordForm
*> database_forms
;
1149 login_metadata_db_
->GetAutofillableLogins(&database_forms
);
1151 ScopedVector
<PasswordForm
> merged_forms
;
1152 merged_forms
.get() = internal_keychain_helpers::GetPasswordsForForms(
1153 *keychain_
, &database_forms
);
1155 // Clean up any orphaned database entries.
1156 RemoveDatabaseForms(database_forms
);
1158 forms
->insert(forms
->end(), database_forms
.begin(), database_forms
.end());