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