Update mojo surfaces bindings and mojo/cc/ glue
[chromium-blink-merge.git] / chrome / browser / password_manager / password_store_mac.cc
blob6bfc2d9c0db9e50c33becb0078f49792bb214916
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 namespace {
36 // Utility class to handle the details of constructing and running a keychain
37 // search from a set of attributes.
38 class KeychainSearch {
39 public:
40 explicit KeychainSearch(const AppleKeychain& keychain);
41 ~KeychainSearch();
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,
49 const UInt32* port,
50 const SecProtocolType* protocol,
51 const SecAuthenticationType* auth_type,
52 const char* security_domain,
53 const char* path,
54 const char* username,
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);
61 private:
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,
80 const UInt32* port,
81 const SecProtocolType* protocol,
82 const SecAuthenticationType* auth_type,
83 const char* security_domain,
84 const char* path,
85 const char* username,
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.
96 if (server != NULL) {
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));
102 ++entries;
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));
110 ++entries;
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));
118 ++entries;
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));
126 ++entries;
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));
134 ++entries;
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));
142 ++entries;
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));
150 ++entries;
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));
158 ++entries;
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";
169 return;
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_);
179 search_ref_ = NULL;
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));
189 return changes;
192 } // namespace
194 #pragma mark -
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,
227 base::Time* time) {
228 DCHECK(time);
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);
240 free(time_string);
242 if (assignments == 6) {
243 *time = base::Time::FromUTCExploded(exploded_time);
244 return true;
246 return false;
249 // Returns the PasswordForm Scheme corresponding to |auth_type|.
250 PasswordForm::Scheme SchemeForAuthType(SecAuthenticationType auth_type) {
251 switch (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,
261 PasswordForm* form,
262 bool extract_password_data) {
263 DCHECK(form);
265 SecKeychainAttributeInfo attrInfo;
266 UInt32 tags[] = { kSecAccountItemAttr,
267 kSecServerItemAttr,
268 kSecPortItemAttr,
269 kSecPathItemAttr,
270 kSecProtocolItemAttr,
271 kSecAuthenticationTypeItemAttr,
272 kSecSecurityDomainItemAttr,
273 kSecCreationDateItemAttr,
274 kSecNegativeItemAttr };
275 attrInfo.count = arraysize(tags);
276 attrInfo.tag = 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,
290 NULL, &attrList,
291 &password_length,
292 password_data_ref);
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";
300 return false;
303 if (extract_password_data) {
304 base::UTF8ToUTF16(static_cast<const char *>(password_data), password_length,
305 &(form->password_value));
308 int port = kAnyPort;
309 std::string server;
310 std::string security_domain;
311 std::string path;
312 for (unsigned int i = 0; i < attrList->count; i++) {
313 SecKeychainAttribute attr = attrList->attr[i];
314 if (!attr.data) {
315 continue;
317 switch (attr.tag) {
318 case kSecAccountItemAttr:
319 base::UTF8ToUTF16(static_cast<const char *>(attr.data), attr.length,
320 &(form->username_value));
321 break;
322 case kSecServerItemAttr:
323 server.assign(static_cast<const char *>(attr.data), attr.length);
324 break;
325 case kSecPortItemAttr:
326 port = *(static_cast<UInt32*>(attr.data));
327 break;
328 case kSecPathItemAttr:
329 path.assign(static_cast<const char *>(attr.data), attr.length);
330 break;
331 case kSecProtocolItemAttr:
333 SecProtocolType protocol = *(static_cast<SecProtocolType*>(attr.data));
334 // TODO(stuartmorgan): Handle proxy types
335 form->ssl_valid = (protocol == kSecProtocolTypeHTTPS);
336 break;
338 case kSecAuthenticationTypeItemAttr:
340 SecAuthenticationType auth_type =
341 *(static_cast<SecAuthenticationType*>(attr.data));
342 form->scheme = SchemeForAuthType(auth_type);
343 break;
345 case kSecSecurityDomainItemAttr:
346 security_domain.assign(static_cast<const char *>(attr.data),
347 attr.length);
348 break;
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);
354 break;
355 case kSecNegativeItemAttr:
356 Boolean negative_item = *(static_cast<Boolean*>(attr.data));
357 if (negative_item) {
358 form->blacklisted_by_user = true;
360 break;
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
375 // format.
376 form->signon_realm = form->origin.GetOrigin().spec();
377 if (form->scheme != PasswordForm::SCHEME_HTML) {
378 form->signon_realm.append(security_domain);
380 return true;
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) {
388 return false;
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) {
412 return *i;
413 } else if (!partial_match) {
414 partial_match = *i;
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);
431 i = forms->erase(i);
432 } else {
433 ++i;
436 return blacklist_forms;
439 // Deletes and removes from v any element that exists in s.
440 template <class T>
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();) {
443 T* element = *i;
444 if (s.find(element) != s.end()) {
445 delete element;
446 i = v->erase(i);
447 } else {
448 ++i;
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,
467 keychain_forms);
468 if (best_match) {
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);
473 } else {
474 ++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(
498 keychain,
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,
520 keychain);
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);
534 } else {
535 ++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) {
545 keychain.Free(*i);
547 return merged_forms;
550 // TODO(stuartmorgan): signon_realm for proxies is not yet supported.
551 bool ExtractSignonRealmComponents(const std::string& signon_realm,
552 std::string* server,
553 UInt32* port,
554 bool* is_secure,
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()) {
560 return false;
563 if (server)
564 *server = realm_as_url.host();
565 if (is_secure)
566 *is_secure = realm_as_url.SchemeIsSecure();
567 if (port)
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);
573 else
574 security_domain->clear();
576 return true;
579 bool FormIsValidAndMatchesOtherForm(const PasswordForm& query_form,
580 const PasswordForm& other_form) {
581 std::string server;
582 std::string security_domain;
583 UInt32 port;
584 bool is_secure;
585 if (!ExtractSignonRealmComponents(query_form.signon_realm, &server, &port,
586 &is_secure, &security_domain)) {
587 return false;
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
602 // returned forms.
603 scoped_ptr<PasswordForm> form_with_password(new PasswordForm());
604 internal_keychain_helpers::FillPasswordFormFromKeychainItem(
605 keychain,
606 *(i->first),
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());
614 return matches;
617 } // namespace internal_keychain_helpers
619 #pragma mark -
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);
638 if (keychain_item) {
639 keychain_->Free(keychain_item);
640 return true;
642 return false;
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) {
653 keychain_->Free(*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,
672 NULL,
673 NULL,
674 &supported_auth_types[i],
675 NULL,
676 NULL,
677 NULL,
678 creator ? &creator : NULL);
679 keychain_search.FindMatchingItems(&matches);
681 return 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);
694 std::string server;
695 std::string security_domain;
696 UInt32 port;
697 bool is_secure;
698 if (!internal_keychain_helpers::ExtractSignonRealmComponents(
699 form.signon_realm, &server, &port, &is_secure, &security_domain)) {
700 return false;
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) {
724 return false;
726 bool changed = SetKeychainItemPassword(existing_item, password);
727 keychain_->Free(existing_item);
728 return changed;
731 return result == noErr;
734 bool MacKeychainPasswordFormAdapter::RemovePassword(const PasswordForm& form) {
735 SecKeychainItemRef keychain_item = KeychainItemForForm(form);
736 if (keychain_item == NULL)
737 return false;
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);
759 keychain_->Free(*i);
761 items->clear();
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) {
770 return NULL;
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()) {
779 return NULL;
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) {
784 keychain_->Free(*i);
786 return matches[0];
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;
796 std::string server;
797 std::string security_domain;
798 UInt32 port;
799 bool is_secure;
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
804 // it specially.
805 return matches;
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(),
815 &port,
816 &protocol,
817 &auth_type,
818 auth_domain,
819 path,
820 username,
821 creator ? &creator : NULL);
822 keychain_search.FindMatchingItems(&matches);
823 return matches;
826 // Returns the Keychain SecAuthenticationType type corresponding to |scheme|.
827 SecAuthenticationType MacKeychainPasswordFormAdapter::AuthTypeForScheme(
828 PasswordForm::Scheme scheme) {
829 switch (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;
835 NOTREACHED();
836 return kSecAuthenticationTypeDefault;
839 bool MacKeychainPasswordFormAdapter::SetKeychainItemPassword(
840 const SecKeychainItemRef& keychain_item, const std::string& password) {
841 OSStatus result = keychain_->ItemModifyAttributesAndData(keychain_item, NULL,
842 password.size(),
843 password.c_str());
844 return result == noErr;
847 bool MacKeychainPasswordFormAdapter::SetKeychainItemCreatorCode(
848 const SecKeychainItemRef& keychain_item, OSType creator_code) {
849 SecKeychainAttribute attr = { kSecCreatorItemAttr, sizeof(creator_code),
850 &creator_code };
851 SecKeychainAttributeList attrList = { 1, &attr };
852 OSStatus result = keychain_->ItemModifyAttributesAndData(keychain_item,
853 &attrList, 0, NULL);
854 return result == noErr;
857 OSType MacKeychainPasswordFormAdapter::CreatorCodeForSearch() {
858 return finds_only_owned_ ? base::mac::CreatorCodeForApplication() : 0;
861 #pragma mark -
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),
869 keychain_(keychain),
870 login_metadata_db_(login_db) {
871 DCHECK(keychain_.get());
872 DCHECK(login_metadata_db_.get());
875 PasswordStoreMac::~PasswordStoreMac() {}
877 bool PasswordStoreMac::Init(
878 const syncer::SyncableService::StartSyncFlare& flare,
879 const std::string& sync_username) {
880 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
881 thread_.reset(new base::Thread("Chrome_PasswordStore_Thread"));
883 if (!thread_->Start()) {
884 thread_.reset(NULL);
885 return false;
887 return password_manager::PasswordStore::Init(flare, sync_username);
890 void PasswordStoreMac::Shutdown() {
891 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
892 password_manager::PasswordStore::Shutdown();
893 thread_->Stop();
896 // Mac stores passwords in the system keychain, which can block for an
897 // arbitrarily long time (most notably, it can block on user confirmation
898 // from a dialog). Run tasks on a dedicated thread to avoid blocking the DB
899 // thread.
900 scoped_refptr<base::SingleThreadTaskRunner>
901 PasswordStoreMac::GetBackgroundTaskRunner() {
902 return (thread_.get()) ? thread_->message_loop_proxy() : NULL;
905 void PasswordStoreMac::ReportMetricsImpl(const std::string& sync_username) {
906 login_metadata_db_->ReportMetrics(sync_username);
909 PasswordStoreChangeList PasswordStoreMac::AddLoginImpl(
910 const PasswordForm& form) {
911 DCHECK(thread_->message_loop() == base::MessageLoop::current());
912 PasswordStoreChangeList changes;
913 if (AddToKeychainIfNecessary(form)) {
914 changes = login_metadata_db_->AddLogin(form);
916 return changes;
919 PasswordStoreChangeList PasswordStoreMac::UpdateLoginImpl(
920 const PasswordForm& form) {
921 DCHECK(thread_->message_loop() == base::MessageLoop::current());
922 PasswordStoreChangeList changes = login_metadata_db_->UpdateLogin(form);
924 MacKeychainPasswordFormAdapter keychain_adapter(keychain_.get());
925 if (changes.empty() &&
926 !keychain_adapter.HasPasswordsMergeableWithForm(form)) {
927 // If the password isn't in either the DB or the keychain, then it must have
928 // been deleted after autofill happened, and should not be re-added.
929 return changes;
932 // The keychain add will update if there is a collision and add if there
933 // isn't, which is the behavior we want, so there's no separate update call.
934 if (AddToKeychainIfNecessary(form) && changes.empty()) {
935 changes = login_metadata_db_->AddLogin(form);
937 return changes;
940 PasswordStoreChangeList PasswordStoreMac::RemoveLoginImpl(
941 const PasswordForm& form) {
942 DCHECK(thread_->message_loop() == base::MessageLoop::current());
943 PasswordStoreChangeList changes;
944 if (login_metadata_db_->RemoveLogin(form)) {
945 // See if we own a Keychain item associated with this item. We can do an
946 // exact search rather than messing around with trying to do fuzzy matching
947 // because passwords that we created will always have an exact-match
948 // database entry.
949 // (If a user does lose their profile but not their keychain we'll treat the
950 // entries we find like other imported entries anyway, so it's reasonable to
951 // handle deletes on them the way we would for an imported item.)
952 MacKeychainPasswordFormAdapter owned_keychain_adapter(keychain_.get());
953 owned_keychain_adapter.SetFindsOnlyOwnedItems(true);
954 if (owned_keychain_adapter.HasPasswordExactlyMatchingForm(form)) {
955 // If we don't have other forms using it (i.e., a form differing only by
956 // the names of the form elements), delete the keychain entry.
957 if (!DatabaseHasFormMatchingKeychainForm(form)) {
958 owned_keychain_adapter.RemovePassword(form);
962 changes.push_back(PasswordStoreChange(PasswordStoreChange::REMOVE, form));
964 return changes;
967 PasswordStoreChangeList PasswordStoreMac::RemoveLoginsCreatedBetweenImpl(
968 base::Time delete_begin,
969 base::Time delete_end) {
970 PasswordStoreChangeList changes;
971 ScopedVector<PasswordForm> forms;
972 if (login_metadata_db_->GetLoginsCreatedBetween(delete_begin, delete_end,
973 &forms.get())) {
974 if (login_metadata_db_->RemoveLoginsCreatedBetween(delete_begin,
975 delete_end)) {
976 RemoveKeychainForms(forms.get());
977 CleanOrphanedForms(&forms.get());
978 changes = FormsToRemoveChangeList(forms.get());
979 LogStatsForBulkDeletion(changes.size());
982 return changes;
985 PasswordStoreChangeList PasswordStoreMac::RemoveLoginsSyncedBetweenImpl(
986 base::Time delete_begin,
987 base::Time delete_end) {
988 PasswordStoreChangeList changes;
989 ScopedVector<PasswordForm> forms;
990 if (login_metadata_db_->GetLoginsSyncedBetween(
991 delete_begin, delete_end, &forms.get())) {
992 if (login_metadata_db_->RemoveLoginsSyncedBetween(delete_begin,
993 delete_end)) {
994 RemoveKeychainForms(forms.get());
995 CleanOrphanedForms(&forms.get());
996 changes = FormsToRemoveChangeList(forms.get());
999 return changes;
1002 void PasswordStoreMac::GetLoginsImpl(
1003 const autofill::PasswordForm& form,
1004 AuthorizationPromptPolicy prompt_policy,
1005 const ConsumerCallbackRunner& callback_runner) {
1006 chrome::ScopedSecKeychainSetUserInteractionAllowed user_interaction_allowed(
1007 prompt_policy == ALLOW_PROMPT);
1009 ScopedVector<PasswordForm> database_forms;
1010 login_metadata_db_->GetLogins(form, &database_forms.get());
1012 // Let's gather all signon realms we want to match with keychain entries.
1013 std::set<std::string> realm_set;
1014 realm_set.insert(form.signon_realm);
1015 for (std::vector<PasswordForm*>::const_iterator db_form =
1016 database_forms.begin();
1017 db_form != database_forms.end();
1018 ++db_form) {
1019 // TODO(vabr): We should not be getting different schemes here.
1020 // http://crbug.com/340112
1021 if (form.scheme != (*db_form)->scheme)
1022 continue; // Forms with different schemes never match.
1023 const std::string& original_singon_realm((*db_form)->original_signon_realm);
1024 if (!original_singon_realm.empty())
1025 realm_set.insert(original_singon_realm);
1027 std::vector<PasswordForm*> keychain_forms;
1028 for (std::set<std::string>::const_iterator realm = realm_set.begin();
1029 realm != realm_set.end();
1030 ++realm) {
1031 MacKeychainPasswordFormAdapter keychain_adapter(keychain_.get());
1032 std::vector<PasswordForm*> temp_keychain_forms =
1033 keychain_adapter.PasswordsFillingForm(*realm, form.scheme);
1034 keychain_forms.insert(keychain_forms.end(),
1035 temp_keychain_forms.begin(),
1036 temp_keychain_forms.end());
1039 std::vector<PasswordForm*> matched_forms;
1040 internal_keychain_helpers::MergePasswordForms(&keychain_forms,
1041 &database_forms.get(),
1042 &matched_forms);
1044 // Strip any blacklist entries out of the unused Keychain array, then take
1045 // all the entries that are left (which we can use as imported passwords).
1046 ScopedVector<PasswordForm> keychain_blacklist_forms;
1047 internal_keychain_helpers::ExtractBlacklistForms(&keychain_forms).swap(
1048 keychain_blacklist_forms.get());
1049 matched_forms.insert(matched_forms.end(),
1050 keychain_forms.begin(),
1051 keychain_forms.end());
1052 keychain_forms.clear();
1054 if (!database_forms.empty()) {
1055 RemoveDatabaseForms(database_forms.get());
1056 NotifyLoginsChanged(FormsToRemoveChangeList(database_forms.get()));
1059 callback_runner.Run(matched_forms);
1062 void PasswordStoreMac::GetBlacklistLoginsImpl(GetLoginsRequest* request) {
1063 FillBlacklistLogins(request->result());
1064 ForwardLoginsResult(request);
1067 void PasswordStoreMac::GetAutofillableLoginsImpl(GetLoginsRequest* request) {
1068 FillAutofillableLogins(request->result());
1069 ForwardLoginsResult(request);
1072 bool PasswordStoreMac::FillAutofillableLogins(
1073 std::vector<PasswordForm*>* forms) {
1074 DCHECK(thread_->message_loop() == base::MessageLoop::current());
1076 ScopedVector<PasswordForm> database_forms;
1077 if (!login_metadata_db_->GetAutofillableLogins(&database_forms.get()))
1078 return false;
1080 std::vector<PasswordForm*> merged_forms =
1081 internal_keychain_helpers::GetPasswordsForForms(*keychain_,
1082 &database_forms.get());
1084 if (!database_forms.empty()) {
1085 RemoveDatabaseForms(database_forms.get());
1086 NotifyLoginsChanged(FormsToRemoveChangeList(database_forms.get()));
1089 forms->insert(forms->end(), merged_forms.begin(), merged_forms.end());
1090 return true;
1093 bool PasswordStoreMac::FillBlacklistLogins(
1094 std::vector<PasswordForm*>* forms) {
1095 DCHECK(thread_->message_loop() == base::MessageLoop::current());
1096 return login_metadata_db_->GetBlacklistLogins(forms);
1099 bool PasswordStoreMac::AddToKeychainIfNecessary(const PasswordForm& form) {
1100 if (form.blacklisted_by_user) {
1101 return true;
1103 MacKeychainPasswordFormAdapter keychainAdapter(keychain_.get());
1104 return keychainAdapter.AddPassword(form);
1107 bool PasswordStoreMac::DatabaseHasFormMatchingKeychainForm(
1108 const autofill::PasswordForm& form) {
1109 bool has_match = false;
1110 std::vector<PasswordForm*> database_forms;
1111 login_metadata_db_->GetLogins(form, &database_forms);
1112 for (std::vector<PasswordForm*>::iterator i = database_forms.begin();
1113 i != database_forms.end(); ++i) {
1114 // Below we filter out forms with non-empty original_signon_realm, because
1115 // those signal fuzzy matches, and we are only interested in exact ones.
1116 if ((*i)->original_signon_realm.empty() &&
1117 internal_keychain_helpers::FormsMatchForMerge(
1118 form, **i, internal_keychain_helpers::STRICT_FORM_MATCH) &&
1119 (*i)->origin == form.origin) {
1120 has_match = true;
1121 break;
1124 STLDeleteElements(&database_forms);
1125 return has_match;
1128 void PasswordStoreMac::RemoveDatabaseForms(
1129 const std::vector<PasswordForm*>& forms) {
1130 for (std::vector<PasswordForm*>::const_iterator i = forms.begin();
1131 i != forms.end(); ++i) {
1132 login_metadata_db_->RemoveLogin(**i);
1136 void PasswordStoreMac::RemoveKeychainForms(
1137 const std::vector<PasswordForm*>& forms) {
1138 MacKeychainPasswordFormAdapter owned_keychain_adapter(keychain_.get());
1139 owned_keychain_adapter.SetFindsOnlyOwnedItems(true);
1140 for (std::vector<PasswordForm*>::const_iterator i = forms.begin();
1141 i != forms.end(); ++i) {
1142 owned_keychain_adapter.RemovePassword(**i);
1146 void PasswordStoreMac::CleanOrphanedForms(std::vector<PasswordForm*>* forms) {
1147 DCHECK(forms);
1148 std::vector<PasswordForm*> database_forms;
1149 login_metadata_db_->GetAutofillableLogins(&database_forms);
1151 ScopedVector<PasswordForm> merged_forms;
1152 merged_forms.get() = internal_keychain_helpers::GetPasswordsForForms(
1153 *keychain_, &database_forms);
1155 // Clean up any orphaned database entries.
1156 RemoveDatabaseForms(database_forms);
1158 forms->insert(forms->end(), database_forms.begin(), database_forms.end());