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 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
880 thread_.reset(new base::Thread("Chrome_PasswordStore_Thread"));
882 if (!thread_->Start()) {
886 return password_manager::PasswordStore::Init(flare);
889 void PasswordStoreMac::Shutdown() {
890 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
891 password_manager::PasswordStore::Shutdown();
895 // Mac stores passwords in the system keychain, which can block for an
896 // arbitrarily long time (most notably, it can block on user confirmation
897 // from a dialog). Run tasks on a dedicated thread to avoid blocking the DB
899 scoped_refptr<base::SingleThreadTaskRunner>
900 PasswordStoreMac::GetBackgroundTaskRunner() {
901 return (thread_.get()) ? thread_->message_loop_proxy() : NULL;
904 void PasswordStoreMac::ReportMetricsImpl(const std::string& sync_username,
905 bool custom_passphrase_sync_enabled) {
906 login_metadata_db_->ReportMetrics(sync_username,
907 custom_passphrase_sync_enabled);
910 PasswordStoreChangeList PasswordStoreMac::AddLoginImpl(
911 const PasswordForm& form) {
912 DCHECK(thread_->message_loop() == base::MessageLoop::current());
913 PasswordStoreChangeList changes;
914 if (AddToKeychainIfNecessary(form)) {
915 changes = login_metadata_db_->AddLogin(form);
920 PasswordStoreChangeList PasswordStoreMac::UpdateLoginImpl(
921 const PasswordForm& form) {
922 DCHECK(thread_->message_loop() == base::MessageLoop::current());
923 PasswordStoreChangeList changes = login_metadata_db_->UpdateLogin(form);
925 MacKeychainPasswordFormAdapter keychain_adapter(keychain_.get());
926 if (changes.empty() &&
927 !keychain_adapter.HasPasswordsMergeableWithForm(form)) {
928 // If the password isn't in either the DB or the keychain, then it must have
929 // been deleted after autofill happened, and should not be re-added.
933 // The keychain add will update if there is a collision and add if there
934 // isn't, which is the behavior we want, so there's no separate update call.
935 if (AddToKeychainIfNecessary(form) && changes.empty()) {
936 changes = login_metadata_db_->AddLogin(form);
941 PasswordStoreChangeList PasswordStoreMac::RemoveLoginImpl(
942 const PasswordForm& form) {
943 DCHECK(thread_->message_loop() == base::MessageLoop::current());
944 PasswordStoreChangeList changes;
945 if (login_metadata_db_->RemoveLogin(form)) {
946 // See if we own a Keychain item associated with this item. We can do an
947 // exact search rather than messing around with trying to do fuzzy matching
948 // because passwords that we created will always have an exact-match
950 // (If a user does lose their profile but not their keychain we'll treat the
951 // entries we find like other imported entries anyway, so it's reasonable to
952 // handle deletes on them the way we would for an imported item.)
953 MacKeychainPasswordFormAdapter owned_keychain_adapter(keychain_.get());
954 owned_keychain_adapter.SetFindsOnlyOwnedItems(true);
955 if (owned_keychain_adapter.HasPasswordExactlyMatchingForm(form)) {
956 // If we don't have other forms using it (i.e., a form differing only by
957 // the names of the form elements), delete the keychain entry.
958 if (!DatabaseHasFormMatchingKeychainForm(form)) {
959 owned_keychain_adapter.RemovePassword(form);
963 changes.push_back(PasswordStoreChange(PasswordStoreChange::REMOVE, form));
968 PasswordStoreChangeList PasswordStoreMac::RemoveLoginsCreatedBetweenImpl(
969 base::Time delete_begin,
970 base::Time delete_end) {
971 PasswordStoreChangeList changes;
972 ScopedVector<PasswordForm> forms;
973 if (login_metadata_db_->GetLoginsCreatedBetween(delete_begin, delete_end,
975 if (login_metadata_db_->RemoveLoginsCreatedBetween(delete_begin,
977 RemoveKeychainForms(forms.get());
978 CleanOrphanedForms(&forms.get());
979 changes = FormsToRemoveChangeList(forms.get());
980 LogStatsForBulkDeletion(changes.size());
986 PasswordStoreChangeList PasswordStoreMac::RemoveLoginsSyncedBetweenImpl(
987 base::Time delete_begin,
988 base::Time delete_end) {
989 PasswordStoreChangeList changes;
990 ScopedVector<PasswordForm> forms;
991 if (login_metadata_db_->GetLoginsSyncedBetween(
992 delete_begin, delete_end, &forms.get())) {
993 if (login_metadata_db_->RemoveLoginsSyncedBetween(delete_begin,
995 RemoveKeychainForms(forms.get());
996 CleanOrphanedForms(&forms.get());
997 changes = FormsToRemoveChangeList(forms.get());
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 ScopedVector<PasswordForm> database_forms;
1011 login_metadata_db_->GetLogins(form, &database_forms.get());
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,
1042 &database_forms.get(),
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 ScopedVector<PasswordForm> keychain_blacklist_forms;
1048 internal_keychain_helpers::ExtractBlacklistForms(&keychain_forms).swap(
1049 keychain_blacklist_forms.get());
1050 matched_forms.insert(matched_forms.end(),
1051 keychain_forms.begin(),
1052 keychain_forms.end());
1053 keychain_forms.clear();
1055 if (!database_forms.empty()) {
1056 RemoveDatabaseForms(database_forms.get());
1057 NotifyLoginsChanged(FormsToRemoveChangeList(database_forms.get()));
1060 callback_runner.Run(matched_forms);
1063 void PasswordStoreMac::GetBlacklistLoginsImpl(GetLoginsRequest* request) {
1064 FillBlacklistLogins(request->result());
1065 ForwardLoginsResult(request);
1068 void PasswordStoreMac::GetAutofillableLoginsImpl(GetLoginsRequest* request) {
1069 FillAutofillableLogins(request->result());
1070 ForwardLoginsResult(request);
1073 bool PasswordStoreMac::FillAutofillableLogins(
1074 std::vector<PasswordForm*>* forms) {
1075 DCHECK(thread_->message_loop() == base::MessageLoop::current());
1077 ScopedVector<PasswordForm> database_forms;
1078 if (!login_metadata_db_->GetAutofillableLogins(&database_forms.get()))
1081 std::vector<PasswordForm*> merged_forms =
1082 internal_keychain_helpers::GetPasswordsForForms(*keychain_,
1083 &database_forms.get());
1085 if (!database_forms.empty()) {
1086 RemoveDatabaseForms(database_forms.get());
1087 NotifyLoginsChanged(FormsToRemoveChangeList(database_forms.get()));
1090 forms->insert(forms->end(), merged_forms.begin(), merged_forms.end());
1094 bool PasswordStoreMac::FillBlacklistLogins(
1095 std::vector<PasswordForm*>* forms) {
1096 DCHECK(thread_->message_loop() == base::MessageLoop::current());
1097 return login_metadata_db_->GetBlacklistLogins(forms);
1100 bool PasswordStoreMac::AddToKeychainIfNecessary(const PasswordForm& form) {
1101 if (form.blacklisted_by_user) {
1104 MacKeychainPasswordFormAdapter keychainAdapter(keychain_.get());
1105 return keychainAdapter.AddPassword(form);
1108 bool PasswordStoreMac::DatabaseHasFormMatchingKeychainForm(
1109 const autofill::PasswordForm& form) {
1110 bool has_match = false;
1111 std::vector<PasswordForm*> database_forms;
1112 login_metadata_db_->GetLogins(form, &database_forms);
1113 for (std::vector<PasswordForm*>::iterator i = database_forms.begin();
1114 i != database_forms.end(); ++i) {
1115 // Below we filter out forms with non-empty original_signon_realm, because
1116 // those signal fuzzy matches, and we are only interested in exact ones.
1117 if ((*i)->original_signon_realm.empty() &&
1118 internal_keychain_helpers::FormsMatchForMerge(
1119 form, **i, internal_keychain_helpers::STRICT_FORM_MATCH) &&
1120 (*i)->origin == form.origin) {
1125 STLDeleteElements(&database_forms);
1129 void PasswordStoreMac::RemoveDatabaseForms(
1130 const std::vector<PasswordForm*>& forms) {
1131 for (std::vector<PasswordForm*>::const_iterator i = forms.begin();
1132 i != forms.end(); ++i) {
1133 login_metadata_db_->RemoveLogin(**i);
1137 void PasswordStoreMac::RemoveKeychainForms(
1138 const std::vector<PasswordForm*>& forms) {
1139 MacKeychainPasswordFormAdapter owned_keychain_adapter(keychain_.get());
1140 owned_keychain_adapter.SetFindsOnlyOwnedItems(true);
1141 for (std::vector<PasswordForm*>::const_iterator i = forms.begin();
1142 i != forms.end(); ++i) {
1143 owned_keychain_adapter.RemovePassword(**i);
1147 void PasswordStoreMac::CleanOrphanedForms(std::vector<PasswordForm*>* forms) {
1149 std::vector<PasswordForm*> database_forms;
1150 login_metadata_db_->GetAutofillableLogins(&database_forms);
1152 ScopedVector<PasswordForm> merged_forms;
1153 merged_forms.get() = internal_keychain_helpers::GetPasswordsForForms(
1154 *keychain_, &database_forms);
1156 // Clean up any orphaned database entries.
1157 RemoveDatabaseForms(database_forms);
1159 forms->insert(forms->end(), database_forms.begin(), database_forms.end());