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/message_loop/message_loop.h"
19 #include "base/stl_util.h"
20 #include "base/strings/string_util.h"
21 #include "base/strings/utf_string_conversions.h"
22 #include "chrome/browser/chrome_notification_types.h"
23 #include "chrome/browser/mac/security_wrappers.h"
24 #include "chrome/browser/password_manager/login_database.h"
25 #include "chrome/browser/password_manager/password_store_change.h"
26 #include "content/public/browser/notification_service.h"
27 #include "crypto/apple_keychain.h"
29 using autofill::PasswordForm;
30 using crypto::AppleKeychain;
32 // Utility class to handle the details of constructing and running a keychain
33 // search from a set of attributes.
34 class KeychainSearch {
36 explicit KeychainSearch(const AppleKeychain& keychain);
39 // Sets up a keycahin search based on an non "null" (NULL for char*,
40 // The appropriate "Any" entry for other types) arguments.
42 // IMPORTANT: Any paramaters passed in *must* remain valid for as long as the
43 // KeychainSearch object, since the search uses them by reference.
44 void Init(const char* server, const UInt32& port,
45 const SecProtocolType& protocol,
46 const SecAuthenticationType& auth_type, const char* security_domain,
47 const char* path, const char* username, OSType creator);
49 // Fills |items| with all Keychain items that match the Init'd search.
50 // If the search fails for any reason, |items| will be unchanged.
51 void FindMatchingItems(std::vector<SecKeychainItemRef>* matches);
54 const AppleKeychain* keychain_;
55 SecKeychainAttributeList search_attributes_;
56 SecKeychainSearchRef search_ref_;
59 KeychainSearch::KeychainSearch(const AppleKeychain& keychain)
60 : keychain_(&keychain), search_ref_(NULL) {
61 search_attributes_.count = 0;
62 search_attributes_.attr = NULL;
65 KeychainSearch::~KeychainSearch() {
66 if (search_attributes_.attr) {
67 free(search_attributes_.attr);
71 void KeychainSearch::Init(const char* server, const UInt32& port,
72 const SecProtocolType& protocol,
73 const SecAuthenticationType& auth_type,
74 const char* security_domain, const char* path,
75 const char* username, OSType creator) {
76 // Allocate enough to hold everything we might use.
77 const unsigned int kMaxEntryCount = 8;
78 search_attributes_.attr =
79 static_cast<SecKeychainAttribute*>(calloc(kMaxEntryCount,
80 sizeof(SecKeychainAttribute)));
81 unsigned int entries = 0;
82 // We only use search_attributes_ with SearchCreateFromAttributes, which takes
83 // a "const SecKeychainAttributeList *", so we trust that they won't try
84 // to modify the list, and that casting away const-ness is thus safe.
86 DCHECK_LT(entries, kMaxEntryCount);
87 search_attributes_.attr[entries].tag = kSecServerItemAttr;
88 search_attributes_.attr[entries].length = strlen(server);
89 search_attributes_.attr[entries].data =
90 const_cast<void*>(reinterpret_cast<const void*>(server));
93 if (port != kAnyPort) {
94 DCHECK_LE(entries, kMaxEntryCount);
95 search_attributes_.attr[entries].tag = kSecPortItemAttr;
96 search_attributes_.attr[entries].length = sizeof(port);
97 search_attributes_.attr[entries].data =
98 const_cast<void*>(reinterpret_cast<const void*>(&port));
101 if (protocol != kSecProtocolTypeAny) {
102 DCHECK_LE(entries, kMaxEntryCount);
103 search_attributes_.attr[entries].tag = kSecProtocolItemAttr;
104 search_attributes_.attr[entries].length = sizeof(protocol);
105 search_attributes_.attr[entries].data =
106 const_cast<void*>(reinterpret_cast<const void*>(&protocol));
109 if (auth_type != kSecAuthenticationTypeAny) {
110 DCHECK_LE(entries, kMaxEntryCount);
111 search_attributes_.attr[entries].tag = kSecAuthenticationTypeItemAttr;
112 search_attributes_.attr[entries].length = sizeof(auth_type);
113 search_attributes_.attr[entries].data =
114 const_cast<void*>(reinterpret_cast<const void*>(&auth_type));
117 if (security_domain != NULL && strlen(security_domain) > 0) {
118 DCHECK_LE(entries, kMaxEntryCount);
119 search_attributes_.attr[entries].tag = kSecSecurityDomainItemAttr;
120 search_attributes_.attr[entries].length = strlen(security_domain);
121 search_attributes_.attr[entries].data =
122 const_cast<void*>(reinterpret_cast<const void*>(security_domain));
125 if (path != NULL && strlen(path) > 0 && strcmp(path, "/") != 0) {
126 DCHECK_LE(entries, kMaxEntryCount);
127 search_attributes_.attr[entries].tag = kSecPathItemAttr;
128 search_attributes_.attr[entries].length = strlen(path);
129 search_attributes_.attr[entries].data =
130 const_cast<void*>(reinterpret_cast<const void*>(path));
133 if (username != NULL) {
134 DCHECK_LE(entries, kMaxEntryCount);
135 search_attributes_.attr[entries].tag = kSecAccountItemAttr;
136 search_attributes_.attr[entries].length = strlen(username);
137 search_attributes_.attr[entries].data =
138 const_cast<void*>(reinterpret_cast<const void*>(username));
142 DCHECK_LE(entries, kMaxEntryCount);
143 search_attributes_.attr[entries].tag = kSecCreatorItemAttr;
144 search_attributes_.attr[entries].length = sizeof(creator);
145 search_attributes_.attr[entries].data =
146 const_cast<void*>(reinterpret_cast<const void*>(&creator));
149 search_attributes_.count = entries;
152 void KeychainSearch::FindMatchingItems(std::vector<SecKeychainItemRef>* items) {
153 OSStatus result = keychain_->SearchCreateFromAttributes(
154 NULL, kSecInternetPasswordItemClass, &search_attributes_, &search_ref_);
156 if (result != noErr) {
157 OSSTATUS_LOG(ERROR, result) << "Keychain lookup failed";
161 SecKeychainItemRef keychain_item;
162 while (keychain_->SearchCopyNext(search_ref_, &keychain_item) == noErr) {
163 // Consumer is responsible for freeing the items.
164 items->push_back(keychain_item);
167 keychain_->Free(search_ref_);
173 // TODO(stuartmorgan): Convert most of this to private helpers in
174 // MacKeychainPasswordFormAdapter once it has sufficient higher-level public
175 // methods to provide test coverage.
176 namespace internal_keychain_helpers {
178 // Returns a URL built from the given components. To create a URL without a
179 // port, pass kAnyPort for the |port| parameter.
180 GURL URLFromComponents(bool is_secure, const std::string& host, int port,
181 const std::string& path) {
182 GURL::Replacements url_components;
183 std::string scheme(is_secure ? "https" : "http");
184 url_components.SetSchemeStr(scheme);
185 url_components.SetHostStr(host);
186 std::string port_string; // Must remain in scope until after we do replacing.
187 if (port != kAnyPort) {
188 std::ostringstream port_stringstream;
189 port_stringstream << port;
190 port_string = port_stringstream.str();
191 url_components.SetPortStr(port_string);
193 url_components.SetPathStr(path);
195 GURL url("http://dummy.com"); // ReplaceComponents needs a valid URL.
196 return url.ReplaceComponents(url_components);
199 // Converts a Keychain time string to a Time object, returning true if
200 // time_string_bytes was parsable. If the return value is false, the value of
201 // |time| is unchanged.
202 bool TimeFromKeychainTimeString(const char* time_string_bytes,
203 unsigned int byte_length,
207 char* time_string = static_cast<char*>(malloc(byte_length + 1));
208 memcpy(time_string, time_string_bytes, byte_length);
209 time_string[byte_length] = '\0';
210 base::Time::Exploded exploded_time;
211 bzero(&exploded_time, sizeof(exploded_time));
212 // The time string is of the form "yyyyMMddHHmmss'Z", in UTC time.
213 int assignments = sscanf(time_string, "%4d%2d%2d%2d%2d%2dZ",
214 &exploded_time.year, &exploded_time.month,
215 &exploded_time.day_of_month, &exploded_time.hour,
216 &exploded_time.minute, &exploded_time.second);
219 if (assignments == 6) {
220 *time = base::Time::FromUTCExploded(exploded_time);
226 // Returns the PasswordForm Scheme corresponding to |auth_type|.
227 PasswordForm::Scheme SchemeForAuthType(SecAuthenticationType auth_type) {
229 case kSecAuthenticationTypeHTMLForm: return PasswordForm::SCHEME_HTML;
230 case kSecAuthenticationTypeHTTPBasic: return PasswordForm::SCHEME_BASIC;
231 case kSecAuthenticationTypeHTTPDigest: return PasswordForm::SCHEME_DIGEST;
232 default: return PasswordForm::SCHEME_OTHER;
236 bool FillPasswordFormFromKeychainItem(const AppleKeychain& keychain,
237 const SecKeychainItemRef& keychain_item,
239 bool extract_password_data) {
242 SecKeychainAttributeInfo attrInfo;
243 UInt32 tags[] = { kSecAccountItemAttr,
247 kSecProtocolItemAttr,
248 kSecAuthenticationTypeItemAttr,
249 kSecSecurityDomainItemAttr,
250 kSecCreationDateItemAttr,
251 kSecNegativeItemAttr };
252 attrInfo.count = arraysize(tags);
254 attrInfo.format = NULL;
256 SecKeychainAttributeList *attrList;
257 UInt32 password_length;
259 // If |extract_password_data| is false, do not pass in a reference to
260 // |password_data|. ItemCopyAttributesAndData will then extract only the
261 // attributes of |keychain_item| (doesn't require OS authorization), and not
262 // attempt to extract its password data (requires OS authorization).
263 void* password_data = NULL;
264 void** password_data_ref = extract_password_data ? &password_data : NULL;
266 OSStatus result = keychain.ItemCopyAttributesAndData(keychain_item, &attrInfo,
271 if (result != noErr) {
272 // We don't log errSecAuthFailed because that just means that the user
273 // chose not to allow us access to the item.
274 if (result != errSecAuthFailed) {
275 OSSTATUS_LOG(ERROR, result) << "Keychain data load failed";
280 if (extract_password_data) {
281 base::UTF8ToUTF16(static_cast<const char *>(password_data), password_length,
282 &(form->password_value));
287 std::string security_domain;
289 for (unsigned int i = 0; i < attrList->count; i++) {
290 SecKeychainAttribute attr = attrList->attr[i];
295 case kSecAccountItemAttr:
296 base::UTF8ToUTF16(static_cast<const char *>(attr.data), attr.length,
297 &(form->username_value));
299 case kSecServerItemAttr:
300 server.assign(static_cast<const char *>(attr.data), attr.length);
302 case kSecPortItemAttr:
303 port = *(static_cast<UInt32*>(attr.data));
305 case kSecPathItemAttr:
306 path.assign(static_cast<const char *>(attr.data), attr.length);
308 case kSecProtocolItemAttr:
310 SecProtocolType protocol = *(static_cast<SecProtocolType*>(attr.data));
311 // TODO(stuartmorgan): Handle proxy types
312 form->ssl_valid = (protocol == kSecProtocolTypeHTTPS);
315 case kSecAuthenticationTypeItemAttr:
317 SecAuthenticationType auth_type =
318 *(static_cast<SecAuthenticationType*>(attr.data));
319 form->scheme = SchemeForAuthType(auth_type);
322 case kSecSecurityDomainItemAttr:
323 security_domain.assign(static_cast<const char *>(attr.data),
326 case kSecCreationDateItemAttr:
327 // The only way to get a date out of Keychain is as a string. Really.
328 // (The docs claim it's an int, but the header is correct.)
329 TimeFromKeychainTimeString(static_cast<char*>(attr.data), attr.length,
330 &form->date_created);
332 case kSecNegativeItemAttr:
333 Boolean negative_item = *(static_cast<Boolean*>(attr.data));
335 form->blacklisted_by_user = true;
340 keychain.ItemFreeAttributesAndData(attrList, password_data);
342 // kSecNegativeItemAttr doesn't seem to actually be in widespread use. In
343 // practice, other browsers seem to use a "" or " " password (and a special
344 // user name) to indicated blacklist entries.
345 if (extract_password_data && (form->password_value.empty() ||
346 EqualsASCII(form->password_value, " "))) {
347 form->blacklisted_by_user = true;
350 form->origin = URLFromComponents(form->ssl_valid, server, port, path);
351 // TODO(stuartmorgan): Handle proxies, which need a different signon_realm
353 form->signon_realm = form->origin.GetOrigin().spec();
354 if (form->scheme != PasswordForm::SCHEME_HTML) {
355 form->signon_realm.append(security_domain);
360 bool FormsMatchForMerge(const PasswordForm& form_a,
361 const PasswordForm& form_b) {
362 // We never merge blacklist entries between our store and the keychain.
363 if (form_a.blacklisted_by_user || form_b.blacklisted_by_user) {
366 return form_a.scheme == form_b.scheme &&
367 form_a.signon_realm == form_b.signon_realm &&
368 form_a.username_value == form_b.username_value;
371 // Returns an the best match for |form| from |keychain_forms|, or NULL if there
372 // is no suitable match.
373 PasswordForm* BestKeychainFormForForm(
374 const PasswordForm& base_form,
375 const std::vector<PasswordForm*>* keychain_forms) {
376 PasswordForm* partial_match = NULL;
377 for (std::vector<PasswordForm*>::const_iterator i = keychain_forms->begin();
378 i != keychain_forms->end(); ++i) {
379 // TODO(stuartmorgan): We should really be scoring path matches and picking
380 // the best, rather than just checking exact-or-not (although in practice
381 // keychain items with paths probably came from us).
382 if (FormsMatchForMerge(base_form, *(*i))) {
383 if (base_form.origin == (*i)->origin) {
385 } else if (!partial_match) {
390 return partial_match;
393 // Returns entries from |forms| that are blacklist entries, after removing
394 // them from |forms|.
395 std::vector<PasswordForm*> ExtractBlacklistForms(
396 std::vector<PasswordForm*>* forms) {
397 std::vector<PasswordForm*> blacklist_forms;
398 for (std::vector<PasswordForm*>::iterator i = forms->begin();
399 i != forms->end();) {
400 PasswordForm* form = *i;
401 if (form->blacklisted_by_user) {
402 blacklist_forms.push_back(form);
408 return blacklist_forms;
411 // Deletes and removes from v any element that exists in s.
413 void DeleteVectorElementsInSet(std::vector<T*>* v, const std::set<T*>& s) {
414 for (typename std::vector<T*>::iterator i = v->begin(); i != v->end();) {
416 if (s.find(element) != s.end()) {
425 void MergePasswordForms(std::vector<PasswordForm*>* keychain_forms,
426 std::vector<PasswordForm*>* database_forms,
427 std::vector<PasswordForm*>* merged_forms) {
428 // Pull out the database blacklist items, since they are used as-is rather
429 // than being merged with keychain forms.
430 std::vector<PasswordForm*> database_blacklist_forms =
431 ExtractBlacklistForms(database_forms);
433 // Merge the normal entries.
434 std::set<PasswordForm*> used_keychain_forms;
435 for (std::vector<PasswordForm*>::iterator i = database_forms->begin();
436 i != database_forms->end();) {
437 PasswordForm* db_form = *i;
438 PasswordForm* best_match = BestKeychainFormForForm(*db_form,
441 used_keychain_forms.insert(best_match);
442 db_form->password_value = best_match->password_value;
443 merged_forms->push_back(db_form);
444 i = database_forms->erase(i);
450 // Add in the blacklist entries from the database.
451 merged_forms->insert(merged_forms->end(),
452 database_blacklist_forms.begin(),
453 database_blacklist_forms.end());
455 // Clear out all the Keychain entries we used.
456 DeleteVectorElementsInSet(keychain_forms, used_keychain_forms);
459 std::vector<ItemFormPair> ExtractAllKeychainItemAttributesIntoPasswordForms(
460 std::vector<SecKeychainItemRef>* keychain_items,
461 const AppleKeychain& keychain) {
462 DCHECK(keychain_items);
463 MacKeychainPasswordFormAdapter keychain_adapter(&keychain);
464 *keychain_items = keychain_adapter.GetAllPasswordFormKeychainItems();
465 std::vector<ItemFormPair> item_form_pairs;
466 for (std::vector<SecKeychainItemRef>::iterator i = keychain_items->begin();
467 i != keychain_items->end(); ++i) {
468 PasswordForm* form_without_password = new PasswordForm();
469 internal_keychain_helpers::FillPasswordFormFromKeychainItem(
472 form_without_password,
473 false); // Load password attributes, but not password data.
474 item_form_pairs.push_back(std::make_pair(&(*i), form_without_password));
476 return item_form_pairs;
479 std::vector<PasswordForm*> GetPasswordsForForms(
480 const AppleKeychain& keychain,
481 std::vector<PasswordForm*>* database_forms) {
482 // First load the attributes of all items in the keychain without loading
483 // their password data, and then match items in |database_forms| against them.
484 // This avoids individually searching through the keychain for passwords
485 // matching each form in |database_forms|, and results in a significant
486 // performance gain, replacing O(N) keychain search operations with a single
487 // operation that loads all keychain items, and then selective reads of only
488 // the relevant passwords. See crbug.com/263685.
489 std::vector<SecKeychainItemRef> keychain_items;
490 std::vector<ItemFormPair> item_form_pairs =
491 ExtractAllKeychainItemAttributesIntoPasswordForms(&keychain_items,
494 // Next, compare the attributes of the PasswordForms in |database_forms|
495 // against those in |item_form_pairs|, and extract password data for each
496 // matching PasswordForm using its corresponding SecKeychainItemRef.
497 std::vector<PasswordForm*> merged_forms;
498 for (std::vector<PasswordForm*>::iterator i = database_forms->begin();
499 i != database_forms->end();) {
500 std::vector<PasswordForm*> db_form_container(1, *i);
501 std::vector<PasswordForm*> keychain_matches =
502 ExtractPasswordsMergeableWithForm(keychain, item_form_pairs, **i);
503 MergePasswordForms(&keychain_matches, &db_form_container, &merged_forms);
504 if (db_form_container.empty()) {
505 i = database_forms->erase(i);
509 STLDeleteElements(&keychain_matches);
512 // Clean up temporary PasswordForms and SecKeychainItemRefs.
513 STLDeleteContainerPairSecondPointers(item_form_pairs.begin(),
514 item_form_pairs.end());
515 for (std::vector<SecKeychainItemRef>::iterator i = keychain_items.begin();
516 i != keychain_items.end(); ++i) {
522 // TODO(stuartmorgan): signon_realm for proxies is not yet supported.
523 bool ExtractSignonRealmComponents(
524 const std::string& signon_realm, std::string* server, int* port,
525 bool* is_secure, std::string* security_domain) {
526 // The signon_realm will be the Origin portion of a URL for an HTML form,
527 // and the same but with the security domain as a path for HTTP auth.
528 GURL realm_as_url(signon_realm);
529 if (!realm_as_url.is_valid()) {
534 *server = realm_as_url.host();
536 *is_secure = realm_as_url.SchemeIsSecure();
538 *port = realm_as_url.has_port() ? atoi(realm_as_url.port().c_str()) : 0;
539 if (security_domain) {
540 // Strip the leading '/' off of the path to get the security domain.
541 if (realm_as_url.path().length() > 0)
542 *security_domain = realm_as_url.path().substr(1);
544 security_domain->clear();
549 bool FormIsValidAndMatchesOtherForm(const PasswordForm& query_form,
550 const PasswordForm& other_form) {
552 std::string security_domain;
555 if (!ExtractSignonRealmComponents(query_form.signon_realm, &server, &port,
556 &is_secure, &security_domain)) {
559 return internal_keychain_helpers::FormsMatchForMerge(query_form, other_form);
562 std::vector<PasswordForm*> ExtractPasswordsMergeableWithForm(
563 const AppleKeychain& keychain,
564 const std::vector<ItemFormPair>& item_form_pairs,
565 const PasswordForm& query_form) {
566 std::vector<PasswordForm*> matches;
567 for (std::vector<ItemFormPair>::const_iterator i = item_form_pairs.begin();
568 i != item_form_pairs.end(); ++i) {
569 if (FormIsValidAndMatchesOtherForm(query_form, *(i->second))) {
570 // Create a new object, since the caller is responsible for deleting the
572 scoped_ptr<PasswordForm> form_with_password(new PasswordForm());
573 internal_keychain_helpers::FillPasswordFormFromKeychainItem(
576 form_with_password.get(),
577 true); // Load password attributes and data.
578 // Do not include blacklisted items found in the keychain.
579 if (!form_with_password->blacklisted_by_user)
580 matches.push_back(form_with_password.release());
586 } // namespace internal_keychain_helpers
590 MacKeychainPasswordFormAdapter::MacKeychainPasswordFormAdapter(
591 const AppleKeychain* keychain)
592 : keychain_(keychain), finds_only_owned_(false) {
595 std::vector<PasswordForm*>
596 MacKeychainPasswordFormAdapter::PasswordsFillingForm(
597 const PasswordForm& query_form) {
598 std::vector<SecKeychainItemRef> keychain_items =
599 MatchingKeychainItems(query_form.signon_realm, query_form.scheme,
602 return ConvertKeychainItemsToForms(&keychain_items);
605 PasswordForm* MacKeychainPasswordFormAdapter::PasswordExactlyMatchingForm(
606 const PasswordForm& query_form) {
607 SecKeychainItemRef keychain_item = KeychainItemForForm(query_form);
609 PasswordForm* form = new PasswordForm();
610 internal_keychain_helpers::FillPasswordFormFromKeychainItem(*keychain_,
614 keychain_->Free(keychain_item);
620 bool MacKeychainPasswordFormAdapter::HasPasswordsMergeableWithForm(
621 const PasswordForm& query_form) {
622 std::string username = base::UTF16ToUTF8(query_form.username_value);
623 std::vector<SecKeychainItemRef> matches =
624 MatchingKeychainItems(query_form.signon_realm, query_form.scheme,
625 NULL, username.c_str());
626 for (std::vector<SecKeychainItemRef>::iterator i = matches.begin();
627 i != matches.end(); ++i) {
631 return !matches.empty();
634 std::vector<SecKeychainItemRef>
635 MacKeychainPasswordFormAdapter::GetAllPasswordFormKeychainItems() {
636 SecAuthenticationType supported_auth_types[] = {
637 kSecAuthenticationTypeHTMLForm,
638 kSecAuthenticationTypeHTTPBasic,
639 kSecAuthenticationTypeHTTPDigest,
642 std::vector<SecKeychainItemRef> matches;
643 for (unsigned int i = 0; i < arraysize(supported_auth_types); ++i) {
644 KeychainSearch keychain_search(*keychain_);
645 keychain_search.Init(NULL, 0, kSecProtocolTypeAny, supported_auth_types[i],
646 NULL, NULL, NULL, CreatorCodeForSearch());
647 keychain_search.FindMatchingItems(&matches);
652 std::vector<PasswordForm*>
653 MacKeychainPasswordFormAdapter::GetAllPasswordFormPasswords() {
654 std::vector<SecKeychainItemRef> items = GetAllPasswordFormKeychainItems();
655 return ConvertKeychainItemsToForms(&items);
658 bool MacKeychainPasswordFormAdapter::AddPassword(const PasswordForm& form) {
659 // We should never be trying to store a blacklist in the keychain.
660 DCHECK(!form.blacklisted_by_user);
663 std::string security_domain;
666 if (!internal_keychain_helpers::ExtractSignonRealmComponents(
667 form.signon_realm, &server, &port, &is_secure, &security_domain)) {
670 std::string username = base::UTF16ToUTF8(form.username_value);
671 std::string password = base::UTF16ToUTF8(form.password_value);
672 std::string path = form.origin.path();
673 SecProtocolType protocol = is_secure ? kSecProtocolTypeHTTPS
674 : kSecProtocolTypeHTTP;
675 SecKeychainItemRef new_item = NULL;
676 OSStatus result = keychain_->AddInternetPassword(
677 NULL, server.size(), server.c_str(),
678 security_domain.size(), security_domain.c_str(),
679 username.size(), username.c_str(),
680 path.size(), path.c_str(),
681 port, protocol, AuthTypeForScheme(form.scheme),
682 password.size(), password.c_str(), &new_item);
684 if (result == noErr) {
685 SetKeychainItemCreatorCode(new_item,
686 base::mac::CreatorCodeForApplication());
687 keychain_->Free(new_item);
688 } else if (result == errSecDuplicateItem) {
689 // If we collide with an existing item, find and update it instead.
690 SecKeychainItemRef existing_item = KeychainItemForForm(form);
691 if (!existing_item) {
694 bool changed = SetKeychainItemPassword(existing_item, password);
695 keychain_->Free(existing_item);
699 return result == noErr;
702 bool MacKeychainPasswordFormAdapter::RemovePassword(const PasswordForm& form) {
703 SecKeychainItemRef keychain_item = KeychainItemForForm(form);
704 if (keychain_item == NULL)
706 OSStatus result = keychain_->ItemDelete(keychain_item);
707 keychain_->Free(keychain_item);
708 return result == noErr;
711 void MacKeychainPasswordFormAdapter::SetFindsOnlyOwnedItems(
712 bool finds_only_owned) {
713 finds_only_owned_ = finds_only_owned;
716 std::vector<PasswordForm*>
717 MacKeychainPasswordFormAdapter::ConvertKeychainItemsToForms(
718 std::vector<SecKeychainItemRef>* items) {
719 std::vector<PasswordForm*> keychain_forms;
720 for (std::vector<SecKeychainItemRef>::const_iterator i = items->begin();
721 i != items->end(); ++i) {
722 PasswordForm* form = new PasswordForm();
723 if (internal_keychain_helpers::FillPasswordFormFromKeychainItem(
724 *keychain_, *i, form, true)) {
725 keychain_forms.push_back(form);
730 return keychain_forms;
733 SecKeychainItemRef MacKeychainPasswordFormAdapter::KeychainItemForForm(
734 const PasswordForm& form) {
735 // We don't store blacklist entries in the keychain, so the answer to "what
736 // Keychain item goes with this form" is always "nothing" for blacklists.
737 if (form.blacklisted_by_user) {
741 std::string path = form.origin.path();
742 std::string username = base::UTF16ToUTF8(form.username_value);
743 std::vector<SecKeychainItemRef> matches = MatchingKeychainItems(
744 form.signon_realm, form.scheme, path.c_str(), username.c_str());
746 if (matches.empty()) {
749 // Free all items after the first, since we won't be returning them.
750 for (std::vector<SecKeychainItemRef>::iterator i = matches.begin() + 1;
751 i != matches.end(); ++i) {
757 std::vector<SecKeychainItemRef>
758 MacKeychainPasswordFormAdapter::MatchingKeychainItems(
759 const std::string& signon_realm,
760 autofill::PasswordForm::Scheme scheme,
761 const char* path, const char* username) {
762 std::vector<SecKeychainItemRef> matches;
765 std::string security_domain;
768 if (!internal_keychain_helpers::ExtractSignonRealmComponents(
769 signon_realm, &server, &port, &is_secure, &security_domain)) {
770 // TODO(stuartmorgan): Proxies will currently fail here, since their
771 // signon_realm is not a URL. We need to detect the proxy case and handle
775 SecProtocolType protocol = is_secure ? kSecProtocolTypeHTTPS
776 : kSecProtocolTypeHTTP;
777 SecAuthenticationType auth_type = AuthTypeForScheme(scheme);
778 const char* auth_domain = (scheme == PasswordForm::SCHEME_HTML) ?
779 NULL : security_domain.c_str();
780 KeychainSearch keychain_search(*keychain_);
781 keychain_search.Init(server.c_str(), port, protocol, auth_type,
782 auth_domain, path, username, CreatorCodeForSearch());
783 keychain_search.FindMatchingItems(&matches);
787 // Returns the Keychain SecAuthenticationType type corresponding to |scheme|.
788 SecAuthenticationType MacKeychainPasswordFormAdapter::AuthTypeForScheme(
789 PasswordForm::Scheme scheme) {
791 case PasswordForm::SCHEME_HTML: return kSecAuthenticationTypeHTMLForm;
792 case PasswordForm::SCHEME_BASIC: return kSecAuthenticationTypeHTTPBasic;
793 case PasswordForm::SCHEME_DIGEST: return kSecAuthenticationTypeHTTPDigest;
794 case PasswordForm::SCHEME_OTHER: return kSecAuthenticationTypeDefault;
797 return kSecAuthenticationTypeDefault;
800 bool MacKeychainPasswordFormAdapter::SetKeychainItemPassword(
801 const SecKeychainItemRef& keychain_item, const std::string& password) {
802 OSStatus result = keychain_->ItemModifyAttributesAndData(keychain_item, NULL,
805 return result == noErr;
808 bool MacKeychainPasswordFormAdapter::SetKeychainItemCreatorCode(
809 const SecKeychainItemRef& keychain_item, OSType creator_code) {
810 SecKeychainAttribute attr = { kSecCreatorItemAttr, sizeof(creator_code),
812 SecKeychainAttributeList attrList = { 1, &attr };
813 OSStatus result = keychain_->ItemModifyAttributesAndData(keychain_item,
815 return result == noErr;
818 OSType MacKeychainPasswordFormAdapter::CreatorCodeForSearch() {
819 return finds_only_owned_ ? base::mac::CreatorCodeForApplication() : 0;
824 PasswordStoreMac::PasswordStoreMac(AppleKeychain* keychain,
825 LoginDatabase* login_db)
826 : keychain_(keychain), login_metadata_db_(login_db) {
827 DCHECK(keychain_.get());
828 DCHECK(login_metadata_db_.get());
831 PasswordStoreMac::~PasswordStoreMac() {
833 thread_->message_loop()->DeleteSoon(FROM_HERE,
834 notification_service_.release());
838 bool PasswordStoreMac::Init() {
839 thread_.reset(new base::Thread("Chrome_PasswordStore_Thread"));
841 if (!thread_->Start()) {
845 ScheduleTask(base::Bind(&PasswordStoreMac::CreateNotificationService, this));
846 return PasswordStore::Init();
849 void PasswordStoreMac::ShutdownOnUIThread() {
852 // Mac stores passwords in the system keychain, which can block for an
853 // arbitrarily long time (most notably, it can block on user confirmation
854 // from a dialog). Run tasks on a dedicated thread to avoid blocking the DB
856 scoped_refptr<base::SequencedTaskRunner> PasswordStoreMac::GetTaskRunner() {
857 return (thread_.get()) ? thread_->message_loop_proxy() : NULL;
860 void PasswordStoreMac::ReportMetricsImpl() {
861 login_metadata_db_->ReportMetrics();
864 void PasswordStoreMac::AddLoginImpl(const PasswordForm& form) {
865 if (AddToKeychainIfNecessary(form)) {
866 if (login_metadata_db_->AddLogin(form)) {
867 PasswordStoreChangeList changes;
868 changes.push_back(PasswordStoreChange(PasswordStoreChange::ADD, form));
869 content::NotificationService::current()->Notify(
870 chrome::NOTIFICATION_LOGINS_CHANGED,
871 content::Source<PasswordStore>(this),
872 content::Details<PasswordStoreChangeList>(&changes));
877 void PasswordStoreMac::UpdateLoginImpl(const PasswordForm& form) {
878 int update_count = 0;
879 if (!login_metadata_db_->UpdateLogin(form, &update_count))
882 MacKeychainPasswordFormAdapter keychain_adapter(keychain_.get());
883 if (update_count == 0 &&
884 !keychain_adapter.HasPasswordsMergeableWithForm(form)) {
885 // If the password isn't in either the DB or the keychain, then it must have
886 // been deleted after autofill happened, and should not be re-added.
890 // The keychain add will update if there is a collision and add if there
891 // isn't, which is the behavior we want, so there's no separate update call.
892 if (AddToKeychainIfNecessary(form)) {
893 PasswordStoreChangeList changes;
894 if (update_count == 0) {
895 if (login_metadata_db_->AddLogin(form)) {
896 changes.push_back(PasswordStoreChange(PasswordStoreChange::ADD,
900 changes.push_back(PasswordStoreChange(PasswordStoreChange::UPDATE,
903 if (!changes.empty()) {
904 content::NotificationService::current()->Notify(
905 chrome::NOTIFICATION_LOGINS_CHANGED,
906 content::Source<PasswordStore>(this),
907 content::Details<PasswordStoreChangeList>(&changes));
912 void PasswordStoreMac::RemoveLoginImpl(const PasswordForm& form) {
913 if (login_metadata_db_->RemoveLogin(form)) {
914 // See if we own a Keychain item associated with this item. We can do an
915 // exact search rather than messing around with trying to do fuzzy matching
916 // because passwords that we created will always have an exact-match
918 // (If a user does lose their profile but not their keychain we'll treat the
919 // entries we find like other imported entries anyway, so it's reasonable to
920 // handle deletes on them the way we would for an imported item.)
921 MacKeychainPasswordFormAdapter owned_keychain_adapter(keychain_.get());
922 owned_keychain_adapter.SetFindsOnlyOwnedItems(true);
923 PasswordForm* owned_password_form =
924 owned_keychain_adapter.PasswordExactlyMatchingForm(form);
925 if (owned_password_form) {
926 // If we don't have other forms using it (i.e., a form differing only by
927 // the names of the form elements), delete the keychain entry.
928 if (!DatabaseHasFormMatchingKeychainForm(form)) {
929 owned_keychain_adapter.RemovePassword(form);
933 PasswordStoreChangeList changes;
934 changes.push_back(PasswordStoreChange(PasswordStoreChange::REMOVE, form));
935 content::NotificationService::current()->Notify(
936 chrome::NOTIFICATION_LOGINS_CHANGED,
937 content::Source<PasswordStore>(this),
938 content::Details<PasswordStoreChangeList>(&changes));
942 void PasswordStoreMac::RemoveLoginsCreatedBetweenImpl(
943 const base::Time& delete_begin, const base::Time& delete_end) {
944 std::vector<PasswordForm*> forms;
945 if (login_metadata_db_->GetLoginsCreatedBetween(delete_begin, delete_end,
947 if (login_metadata_db_->RemoveLoginsCreatedBetween(delete_begin,
949 // We can't delete from the Keychain by date because we may be sharing
950 // items with database entries that weren't in the delete range. Instead,
951 // we find all the Keychain items we own but aren't using any more and
953 std::vector<PasswordForm*> orphan_keychain_forms =
954 GetUnusedKeychainForms();
955 // This is inefficient, since we have to re-look-up each keychain item
956 // one at a time to delete it even though the search step already had a
957 // list of Keychain item references. If this turns out to be noticeably
958 // slow we'll need to rearchitect to allow the search and deletion steps
960 RemoveKeychainForms(orphan_keychain_forms);
961 STLDeleteElements(&orphan_keychain_forms);
963 PasswordStoreChangeList changes;
964 for (std::vector<PasswordForm*>::const_iterator it = forms.begin();
965 it != forms.end(); ++it) {
966 changes.push_back(PasswordStoreChange(PasswordStoreChange::REMOVE,
969 LogStatsForBulkDeletion(changes.size());
970 content::NotificationService::current()->Notify(
971 chrome::NOTIFICATION_LOGINS_CHANGED,
972 content::Source<PasswordStore>(this),
973 content::Details<PasswordStoreChangeList>(&changes));
978 void PasswordStoreMac::GetLoginsImpl(
979 const autofill::PasswordForm& form,
980 AuthorizationPromptPolicy prompt_policy,
981 const ConsumerCallbackRunner& callback_runner) {
982 chrome::ScopedSecKeychainSetUserInteractionAllowed user_interaction_allowed(
983 prompt_policy == ALLOW_PROMPT);
985 MacKeychainPasswordFormAdapter keychain_adapter(keychain_.get());
986 std::vector<PasswordForm*> keychain_forms =
987 keychain_adapter.PasswordsFillingForm(form);
989 std::vector<PasswordForm*> database_forms;
990 login_metadata_db_->GetLogins(form, &database_forms);
992 std::vector<PasswordForm*> matched_forms;
993 internal_keychain_helpers::MergePasswordForms(&keychain_forms,
997 // Strip any blacklist entries out of the unused Keychain array, then take
998 // all the entries that are left (which we can use as imported passwords).
999 std::vector<PasswordForm*> keychain_blacklist_forms =
1000 internal_keychain_helpers::ExtractBlacklistForms(&keychain_forms);
1001 matched_forms.insert(matched_forms.end(),
1002 keychain_forms.begin(),
1003 keychain_forms.end());
1004 keychain_forms.clear();
1005 STLDeleteElements(&keychain_blacklist_forms);
1007 // Clean up any orphaned database entries.
1008 RemoveDatabaseForms(database_forms);
1009 STLDeleteElements(&database_forms);
1011 callback_runner.Run(matched_forms);
1014 void PasswordStoreMac::GetBlacklistLoginsImpl(GetLoginsRequest* request) {
1015 FillBlacklistLogins(request->result());
1016 ForwardLoginsResult(request);
1019 void PasswordStoreMac::GetAutofillableLoginsImpl(GetLoginsRequest* request) {
1020 FillAutofillableLogins(request->result());
1021 ForwardLoginsResult(request);
1024 bool PasswordStoreMac::FillAutofillableLogins(
1025 std::vector<PasswordForm*>* forms) {
1026 DCHECK(thread_->message_loop() == base::MessageLoop::current());
1028 std::vector<PasswordForm*> database_forms;
1029 login_metadata_db_->GetAutofillableLogins(&database_forms);
1031 std::vector<PasswordForm*> merged_forms =
1032 internal_keychain_helpers::GetPasswordsForForms(*keychain_,
1035 // Clean up any orphaned database entries.
1036 RemoveDatabaseForms(database_forms);
1037 STLDeleteElements(&database_forms);
1039 forms->insert(forms->end(), merged_forms.begin(), merged_forms.end());
1043 bool PasswordStoreMac::FillBlacklistLogins(
1044 std::vector<PasswordForm*>* forms) {
1045 DCHECK(thread_->message_loop() == base::MessageLoop::current());
1046 return login_metadata_db_->GetBlacklistLogins(forms);
1049 bool PasswordStoreMac::AddToKeychainIfNecessary(const PasswordForm& form) {
1050 if (form.blacklisted_by_user) {
1053 MacKeychainPasswordFormAdapter keychainAdapter(keychain_.get());
1054 return keychainAdapter.AddPassword(form);
1057 bool PasswordStoreMac::DatabaseHasFormMatchingKeychainForm(
1058 const autofill::PasswordForm& form) {
1059 bool has_match = false;
1060 std::vector<PasswordForm*> database_forms;
1061 login_metadata_db_->GetLogins(form, &database_forms);
1062 for (std::vector<PasswordForm*>::iterator i = database_forms.begin();
1063 i != database_forms.end(); ++i) {
1064 if (internal_keychain_helpers::FormsMatchForMerge(form, **i) &&
1065 (*i)->origin == form.origin) {
1070 STLDeleteElements(&database_forms);
1074 std::vector<PasswordForm*> PasswordStoreMac::GetUnusedKeychainForms() {
1075 std::vector<PasswordForm*> database_forms;
1076 login_metadata_db_->GetAutofillableLogins(&database_forms);
1078 MacKeychainPasswordFormAdapter owned_keychain_adapter(keychain_.get());
1079 owned_keychain_adapter.SetFindsOnlyOwnedItems(true);
1080 std::vector<PasswordForm*> owned_keychain_forms =
1081 owned_keychain_adapter.GetAllPasswordFormPasswords();
1083 // Run a merge; anything left in owned_keychain_forms when we are done no
1084 // longer has a matching database entry.
1085 std::vector<PasswordForm*> merged_forms;
1086 internal_keychain_helpers::MergePasswordForms(&owned_keychain_forms,
1089 STLDeleteElements(&merged_forms);
1090 STLDeleteElements(&database_forms);
1092 return owned_keychain_forms;
1095 void PasswordStoreMac::RemoveDatabaseForms(
1096 const std::vector<PasswordForm*>& forms) {
1097 for (std::vector<PasswordForm*>::const_iterator i = forms.begin();
1098 i != forms.end(); ++i) {
1099 login_metadata_db_->RemoveLogin(**i);
1103 void PasswordStoreMac::RemoveKeychainForms(
1104 const std::vector<PasswordForm*>& forms) {
1105 MacKeychainPasswordFormAdapter owned_keychain_adapter(keychain_.get());
1106 owned_keychain_adapter.SetFindsOnlyOwnedItems(true);
1107 for (std::vector<PasswordForm*>::const_iterator i = forms.begin();
1108 i != forms.end(); ++i) {
1109 owned_keychain_adapter.RemovePassword(**i);
1113 void PasswordStoreMac::CreateNotificationService() {
1114 notification_service_.reset(content::NotificationService::Create());