password_store_mac.cc revision 6d86b77056ed63eb6871182f42a9fd5f07550f90
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.
4
5#include "chrome/browser/password_manager/password_store_mac.h"
6#include "chrome/browser/password_manager/password_store_mac_internal.h"
7
8#include <CoreServices/CoreServices.h>
9#include <set>
10#include <string>
11#include <utility>
12#include <vector>
13
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/mac/security_wrappers.h"
23#include "components/password_manager/core/browser/login_database.h"
24#include "components/password_manager/core/browser/password_store_change.h"
25#include "content/public/browser/browser_thread.h"
26#include "crypto/apple_keychain.h"
27
28using autofill::PasswordForm;
29using crypto::AppleKeychain;
30using password_manager::PasswordStoreChange;
31using password_manager::PasswordStoreChangeList;
32
33// Utility class to handle the details of constructing and running a keychain
34// search from a set of attributes.
35class KeychainSearch {
36 public:
37  explicit KeychainSearch(const AppleKeychain& keychain);
38  ~KeychainSearch();
39
40  // Sets up a keycahin search based on an non "null" (NULL for char*,
41  // The appropriate "Any" entry for other types) arguments.
42  //
43  // IMPORTANT: Any paramaters passed in *must* remain valid for as long as the
44  // KeychainSearch object, since the search uses them by reference.
45  void Init(const char* server, const UInt32& port,
46            const SecProtocolType& protocol,
47            const SecAuthenticationType& auth_type, const char* security_domain,
48            const char* path, const char* username, OSType creator);
49
50  // Fills |items| with all Keychain items that match the Init'd search.
51  // If the search fails for any reason, |items| will be unchanged.
52  void FindMatchingItems(std::vector<SecKeychainItemRef>* matches);
53
54 private:
55  const AppleKeychain* keychain_;
56  SecKeychainAttributeList search_attributes_;
57  SecKeychainSearchRef search_ref_;
58};
59
60KeychainSearch::KeychainSearch(const AppleKeychain& keychain)
61    : keychain_(&keychain), search_ref_(NULL) {
62  search_attributes_.count = 0;
63  search_attributes_.attr = NULL;
64}
65
66KeychainSearch::~KeychainSearch() {
67  if (search_attributes_.attr) {
68    free(search_attributes_.attr);
69  }
70}
71
72void KeychainSearch::Init(const char* server, const UInt32& port,
73                          const SecProtocolType& protocol,
74                          const SecAuthenticationType& auth_type,
75                          const char* security_domain, const char* path,
76                          const char* username, OSType creator) {
77  // Allocate enough to hold everything we might use.
78  const unsigned int kMaxEntryCount = 8;
79  search_attributes_.attr =
80      static_cast<SecKeychainAttribute*>(calloc(kMaxEntryCount,
81                                                sizeof(SecKeychainAttribute)));
82  unsigned int entries = 0;
83  // We only use search_attributes_ with SearchCreateFromAttributes, which takes
84  // a "const SecKeychainAttributeList *", so we trust that they won't try
85  // to modify the list, and that casting away const-ness is thus safe.
86  if (server != NULL) {
87    DCHECK_LT(entries, kMaxEntryCount);
88    search_attributes_.attr[entries].tag = kSecServerItemAttr;
89    search_attributes_.attr[entries].length = strlen(server);
90    search_attributes_.attr[entries].data =
91        const_cast<void*>(reinterpret_cast<const void*>(server));
92    ++entries;
93  }
94  if (port != kAnyPort) {
95    DCHECK_LE(entries, kMaxEntryCount);
96    search_attributes_.attr[entries].tag = kSecPortItemAttr;
97    search_attributes_.attr[entries].length = sizeof(port);
98    search_attributes_.attr[entries].data =
99        const_cast<void*>(reinterpret_cast<const void*>(&port));
100    ++entries;
101  }
102  if (protocol != kSecProtocolTypeAny) {
103    DCHECK_LE(entries, kMaxEntryCount);
104    search_attributes_.attr[entries].tag = kSecProtocolItemAttr;
105    search_attributes_.attr[entries].length = sizeof(protocol);
106    search_attributes_.attr[entries].data =
107        const_cast<void*>(reinterpret_cast<const void*>(&protocol));
108    ++entries;
109  }
110  if (auth_type != kSecAuthenticationTypeAny) {
111    DCHECK_LE(entries, kMaxEntryCount);
112    search_attributes_.attr[entries].tag = kSecAuthenticationTypeItemAttr;
113    search_attributes_.attr[entries].length = sizeof(auth_type);
114    search_attributes_.attr[entries].data =
115        const_cast<void*>(reinterpret_cast<const void*>(&auth_type));
116    ++entries;
117  }
118  if (security_domain != NULL && strlen(security_domain) > 0) {
119    DCHECK_LE(entries, kMaxEntryCount);
120    search_attributes_.attr[entries].tag = kSecSecurityDomainItemAttr;
121    search_attributes_.attr[entries].length = strlen(security_domain);
122    search_attributes_.attr[entries].data =
123        const_cast<void*>(reinterpret_cast<const void*>(security_domain));
124    ++entries;
125  }
126  if (path != NULL && strlen(path) > 0 && strcmp(path, "/") != 0) {
127    DCHECK_LE(entries, kMaxEntryCount);
128    search_attributes_.attr[entries].tag = kSecPathItemAttr;
129    search_attributes_.attr[entries].length = strlen(path);
130    search_attributes_.attr[entries].data =
131        const_cast<void*>(reinterpret_cast<const void*>(path));
132    ++entries;
133  }
134  if (username != NULL) {
135    DCHECK_LE(entries, kMaxEntryCount);
136    search_attributes_.attr[entries].tag = kSecAccountItemAttr;
137    search_attributes_.attr[entries].length = strlen(username);
138    search_attributes_.attr[entries].data =
139        const_cast<void*>(reinterpret_cast<const void*>(username));
140    ++entries;
141  }
142  if (creator != 0) {
143    DCHECK_LE(entries, kMaxEntryCount);
144    search_attributes_.attr[entries].tag = kSecCreatorItemAttr;
145    search_attributes_.attr[entries].length = sizeof(creator);
146    search_attributes_.attr[entries].data =
147        const_cast<void*>(reinterpret_cast<const void*>(&creator));
148    ++entries;
149  }
150  search_attributes_.count = entries;
151}
152
153void KeychainSearch::FindMatchingItems(std::vector<SecKeychainItemRef>* items) {
154  OSStatus result = keychain_->SearchCreateFromAttributes(
155      NULL, kSecInternetPasswordItemClass, &search_attributes_, &search_ref_);
156
157  if (result != noErr) {
158    OSSTATUS_LOG(ERROR, result) << "Keychain lookup failed";
159    return;
160  }
161
162  SecKeychainItemRef keychain_item;
163  while (keychain_->SearchCopyNext(search_ref_, &keychain_item) == noErr) {
164    // Consumer is responsible for freeing the items.
165    items->push_back(keychain_item);
166  }
167
168  keychain_->Free(search_ref_);
169  search_ref_ = NULL;
170}
171
172#pragma mark -
173
174// TODO(stuartmorgan): Convert most of this to private helpers in
175// MacKeychainPasswordFormAdapter once it has sufficient higher-level public
176// methods to provide test coverage.
177namespace internal_keychain_helpers {
178
179// Returns a URL built from the given components. To create a URL without a
180// port, pass kAnyPort for the |port| parameter.
181GURL URLFromComponents(bool is_secure, const std::string& host, int port,
182                       const std::string& path) {
183  GURL::Replacements url_components;
184  std::string scheme(is_secure ? "https" : "http");
185  url_components.SetSchemeStr(scheme);
186  url_components.SetHostStr(host);
187  std::string port_string;  // Must remain in scope until after we do replacing.
188  if (port != kAnyPort) {
189    std::ostringstream port_stringstream;
190    port_stringstream << port;
191    port_string = port_stringstream.str();
192    url_components.SetPortStr(port_string);
193  }
194  url_components.SetPathStr(path);
195
196  GURL url("http://dummy.com");  // ReplaceComponents needs a valid URL.
197  return url.ReplaceComponents(url_components);
198}
199
200// Converts a Keychain time string to a Time object, returning true if
201// time_string_bytes was parsable. If the return value is false, the value of
202// |time| is unchanged.
203bool TimeFromKeychainTimeString(const char* time_string_bytes,
204                                unsigned int byte_length,
205                                base::Time* time) {
206  DCHECK(time);
207
208  char* time_string = static_cast<char*>(malloc(byte_length + 1));
209  memcpy(time_string, time_string_bytes, byte_length);
210  time_string[byte_length] = '\0';
211  base::Time::Exploded exploded_time;
212  bzero(&exploded_time, sizeof(exploded_time));
213  // The time string is of the form "yyyyMMddHHmmss'Z", in UTC time.
214  int assignments = sscanf(time_string, "%4d%2d%2d%2d%2d%2dZ",
215                           &exploded_time.year, &exploded_time.month,
216                           &exploded_time.day_of_month, &exploded_time.hour,
217                           &exploded_time.minute, &exploded_time.second);
218  free(time_string);
219
220  if (assignments == 6) {
221    *time = base::Time::FromUTCExploded(exploded_time);
222    return true;
223  }
224  return false;
225}
226
227// Returns the PasswordForm Scheme corresponding to |auth_type|.
228PasswordForm::Scheme SchemeForAuthType(SecAuthenticationType auth_type) {
229  switch (auth_type) {
230    case kSecAuthenticationTypeHTMLForm:   return PasswordForm::SCHEME_HTML;
231    case kSecAuthenticationTypeHTTPBasic:  return PasswordForm::SCHEME_BASIC;
232    case kSecAuthenticationTypeHTTPDigest: return PasswordForm::SCHEME_DIGEST;
233    default:                               return PasswordForm::SCHEME_OTHER;
234  }
235}
236
237bool FillPasswordFormFromKeychainItem(const AppleKeychain& keychain,
238                                      const SecKeychainItemRef& keychain_item,
239                                      PasswordForm* form,
240                                      bool extract_password_data) {
241  DCHECK(form);
242
243  SecKeychainAttributeInfo attrInfo;
244  UInt32 tags[] = { kSecAccountItemAttr,
245                    kSecServerItemAttr,
246                    kSecPortItemAttr,
247                    kSecPathItemAttr,
248                    kSecProtocolItemAttr,
249                    kSecAuthenticationTypeItemAttr,
250                    kSecSecurityDomainItemAttr,
251                    kSecCreationDateItemAttr,
252                    kSecNegativeItemAttr };
253  attrInfo.count = arraysize(tags);
254  attrInfo.tag = tags;
255  attrInfo.format = NULL;
256
257  SecKeychainAttributeList *attrList;
258  UInt32 password_length;
259
260  // If |extract_password_data| is false, do not pass in a reference to
261  // |password_data|. ItemCopyAttributesAndData will then extract only the
262  // attributes of |keychain_item| (doesn't require OS authorization), and not
263  // attempt to extract its password data (requires OS authorization).
264  void* password_data = NULL;
265  void** password_data_ref = extract_password_data ? &password_data : NULL;
266
267  OSStatus result = keychain.ItemCopyAttributesAndData(keychain_item, &attrInfo,
268                                                       NULL, &attrList,
269                                                       &password_length,
270                                                       password_data_ref);
271
272  if (result != noErr) {
273    // We don't log errSecAuthFailed because that just means that the user
274    // chose not to allow us access to the item.
275    if (result != errSecAuthFailed) {
276      OSSTATUS_LOG(ERROR, result) << "Keychain data load failed";
277    }
278    return false;
279  }
280
281  if (extract_password_data) {
282    base::UTF8ToUTF16(static_cast<const char *>(password_data), password_length,
283                      &(form->password_value));
284  }
285
286  int port = kAnyPort;
287  std::string server;
288  std::string security_domain;
289  std::string path;
290  for (unsigned int i = 0; i < attrList->count; i++) {
291    SecKeychainAttribute attr = attrList->attr[i];
292    if (!attr.data) {
293      continue;
294    }
295    switch (attr.tag) {
296      case kSecAccountItemAttr:
297        base::UTF8ToUTF16(static_cast<const char *>(attr.data), attr.length,
298                          &(form->username_value));
299        break;
300      case kSecServerItemAttr:
301        server.assign(static_cast<const char *>(attr.data), attr.length);
302        break;
303      case kSecPortItemAttr:
304        port = *(static_cast<UInt32*>(attr.data));
305        break;
306      case kSecPathItemAttr:
307        path.assign(static_cast<const char *>(attr.data), attr.length);
308        break;
309      case kSecProtocolItemAttr:
310      {
311        SecProtocolType protocol = *(static_cast<SecProtocolType*>(attr.data));
312        // TODO(stuartmorgan): Handle proxy types
313        form->ssl_valid = (protocol == kSecProtocolTypeHTTPS);
314        break;
315      }
316      case kSecAuthenticationTypeItemAttr:
317      {
318        SecAuthenticationType auth_type =
319            *(static_cast<SecAuthenticationType*>(attr.data));
320        form->scheme = SchemeForAuthType(auth_type);
321        break;
322      }
323      case kSecSecurityDomainItemAttr:
324        security_domain.assign(static_cast<const char *>(attr.data),
325                               attr.length);
326        break;
327      case kSecCreationDateItemAttr:
328        // The only way to get a date out of Keychain is as a string. Really.
329        // (The docs claim it's an int, but the header is correct.)
330        TimeFromKeychainTimeString(static_cast<char*>(attr.data), attr.length,
331                                   &form->date_created);
332        break;
333      case kSecNegativeItemAttr:
334        Boolean negative_item = *(static_cast<Boolean*>(attr.data));
335        if (negative_item) {
336          form->blacklisted_by_user = true;
337        }
338        break;
339    }
340  }
341  keychain.ItemFreeAttributesAndData(attrList, password_data);
342
343  // kSecNegativeItemAttr doesn't seem to actually be in widespread use. In
344  // practice, other browsers seem to use a "" or " " password (and a special
345  // user name) to indicated blacklist entries.
346  if (extract_password_data && (form->password_value.empty() ||
347                                EqualsASCII(form->password_value, " "))) {
348    form->blacklisted_by_user = true;
349  }
350
351  form->origin = URLFromComponents(form->ssl_valid, server, port, path);
352  // TODO(stuartmorgan): Handle proxies, which need a different signon_realm
353  // format.
354  form->signon_realm = form->origin.GetOrigin().spec();
355  if (form->scheme != PasswordForm::SCHEME_HTML) {
356    form->signon_realm.append(security_domain);
357  }
358  return true;
359}
360
361bool FormsMatchForMerge(const PasswordForm& form_a,
362                        const PasswordForm& form_b,
363                        FormMatchStrictness strictness) {
364  // We never merge blacklist entries between our store and the keychain.
365  if (form_a.blacklisted_by_user || form_b.blacklisted_by_user) {
366    return false;
367  }
368  bool equal_realm = form_a.signon_realm == form_b.signon_realm;
369  if (strictness == FUZZY_FORM_MATCH) {
370    equal_realm |= (!form_a.original_signon_realm.empty()) &&
371                   form_a.original_signon_realm == form_b.signon_realm;
372  }
373  return form_a.scheme == form_b.scheme && equal_realm &&
374         form_a.username_value == form_b.username_value;
375}
376
377// Returns an the best match for |base_form| from |keychain_forms|, or NULL if
378// there is no suitable match.
379PasswordForm* BestKeychainFormForForm(
380    const PasswordForm& base_form,
381    const std::vector<PasswordForm*>* keychain_forms) {
382  PasswordForm* partial_match = NULL;
383  for (std::vector<PasswordForm*>::const_iterator i = keychain_forms->begin();
384       i != keychain_forms->end(); ++i) {
385    // TODO(stuartmorgan): We should really be scoring path matches and picking
386    // the best, rather than just checking exact-or-not (although in practice
387    // keychain items with paths probably came from us).
388    if (FormsMatchForMerge(base_form, *(*i), FUZZY_FORM_MATCH)) {
389      if (base_form.origin == (*i)->origin) {
390        return *i;
391      } else if (!partial_match) {
392        partial_match = *i;
393      }
394    }
395  }
396  return partial_match;
397}
398
399// Returns entries from |forms| that are blacklist entries, after removing
400// them from |forms|.
401std::vector<PasswordForm*> ExtractBlacklistForms(
402    std::vector<PasswordForm*>* forms) {
403  std::vector<PasswordForm*> blacklist_forms;
404  for (std::vector<PasswordForm*>::iterator i = forms->begin();
405       i != forms->end();) {
406    PasswordForm* form = *i;
407    if (form->blacklisted_by_user) {
408      blacklist_forms.push_back(form);
409      i = forms->erase(i);
410    } else {
411      ++i;
412    }
413  }
414  return blacklist_forms;
415}
416
417// Deletes and removes from v any element that exists in s.
418template <class T>
419void DeleteVectorElementsInSet(std::vector<T*>* v, const std::set<T*>& s) {
420  for (typename std::vector<T*>::iterator i = v->begin(); i != v->end();) {
421    T* element = *i;
422    if (s.find(element) != s.end()) {
423      delete element;
424      i = v->erase(i);
425    } else {
426      ++i;
427    }
428  }
429}
430
431void MergePasswordForms(std::vector<PasswordForm*>* keychain_forms,
432                        std::vector<PasswordForm*>* database_forms,
433                        std::vector<PasswordForm*>* merged_forms) {
434  // Pull out the database blacklist items, since they are used as-is rather
435  // than being merged with keychain forms.
436  std::vector<PasswordForm*> database_blacklist_forms =
437      ExtractBlacklistForms(database_forms);
438
439  // Merge the normal entries.
440  std::set<PasswordForm*> used_keychain_forms;
441  for (std::vector<PasswordForm*>::iterator i = database_forms->begin();
442       i != database_forms->end();) {
443    PasswordForm* db_form = *i;
444    PasswordForm* best_match = BestKeychainFormForForm(*db_form,
445                                                       keychain_forms);
446    if (best_match) {
447      used_keychain_forms.insert(best_match);
448      db_form->password_value = best_match->password_value;
449      merged_forms->push_back(db_form);
450      i = database_forms->erase(i);
451    } else {
452      ++i;
453    }
454  }
455
456  // Add in the blacklist entries from the database.
457  merged_forms->insert(merged_forms->end(),
458                       database_blacklist_forms.begin(),
459                       database_blacklist_forms.end());
460
461  // Clear out all the Keychain entries we used.
462  DeleteVectorElementsInSet(keychain_forms, used_keychain_forms);
463}
464
465std::vector<ItemFormPair> ExtractAllKeychainItemAttributesIntoPasswordForms(
466    std::vector<SecKeychainItemRef>* keychain_items,
467    const AppleKeychain& keychain) {
468  DCHECK(keychain_items);
469  MacKeychainPasswordFormAdapter keychain_adapter(&keychain);
470  *keychain_items = keychain_adapter.GetAllPasswordFormKeychainItems();
471  std::vector<ItemFormPair> item_form_pairs;
472  for (std::vector<SecKeychainItemRef>::iterator i = keychain_items->begin();
473       i != keychain_items->end(); ++i) {
474    PasswordForm* form_without_password = new PasswordForm();
475    internal_keychain_helpers::FillPasswordFormFromKeychainItem(
476        keychain,
477        *i,
478        form_without_password,
479        false);  // Load password attributes, but not password data.
480    item_form_pairs.push_back(std::make_pair(&(*i), form_without_password));
481  }
482  return item_form_pairs;
483}
484
485std::vector<PasswordForm*> GetPasswordsForForms(
486    const AppleKeychain& keychain,
487    std::vector<PasswordForm*>* database_forms) {
488  // First load the attributes of all items in the keychain without loading
489  // their password data, and then match items in |database_forms| against them.
490  // This avoids individually searching through the keychain for passwords
491  // matching each form in |database_forms|, and results in a significant
492  // performance gain, replacing O(N) keychain search operations with a single
493  // operation that loads all keychain items, and then selective reads of only
494  // the relevant passwords. See crbug.com/263685.
495  std::vector<SecKeychainItemRef> keychain_items;
496  std::vector<ItemFormPair> item_form_pairs =
497      ExtractAllKeychainItemAttributesIntoPasswordForms(&keychain_items,
498                                                        keychain);
499
500  // Next, compare the attributes of the PasswordForms in |database_forms|
501  // against those in |item_form_pairs|, and extract password data for each
502  // matching PasswordForm using its corresponding SecKeychainItemRef.
503  std::vector<PasswordForm*> merged_forms;
504  for (std::vector<PasswordForm*>::iterator i = database_forms->begin();
505       i != database_forms->end();) {
506    std::vector<PasswordForm*> db_form_container(1, *i);
507    std::vector<PasswordForm*> keychain_matches =
508        ExtractPasswordsMergeableWithForm(keychain, item_form_pairs, **i);
509    MergePasswordForms(&keychain_matches, &db_form_container, &merged_forms);
510    if (db_form_container.empty()) {
511      i = database_forms->erase(i);
512    } else {
513      ++i;
514    }
515    STLDeleteElements(&keychain_matches);
516  }
517
518  // Clean up temporary PasswordForms and SecKeychainItemRefs.
519  STLDeleteContainerPairSecondPointers(item_form_pairs.begin(),
520                                       item_form_pairs.end());
521  for (std::vector<SecKeychainItemRef>::iterator i = keychain_items.begin();
522       i != keychain_items.end(); ++i) {
523    keychain.Free(*i);
524  }
525  return merged_forms;
526}
527
528// TODO(stuartmorgan): signon_realm for proxies is not yet supported.
529bool ExtractSignonRealmComponents(
530    const std::string& signon_realm, std::string* server, int* port,
531    bool* is_secure, std::string* security_domain) {
532  // The signon_realm will be the Origin portion of a URL for an HTML form,
533  // and the same but with the security domain as a path for HTTP auth.
534  GURL realm_as_url(signon_realm);
535  if (!realm_as_url.is_valid()) {
536    return false;
537  }
538
539  if (server)
540    *server = realm_as_url.host();
541  if (is_secure)
542    *is_secure = realm_as_url.SchemeIsSecure();
543  if (port)
544    *port = realm_as_url.has_port() ? atoi(realm_as_url.port().c_str()) : 0;
545  if (security_domain) {
546    // Strip the leading '/' off of the path to get the security domain.
547    if (realm_as_url.path().length() > 0)
548      *security_domain = realm_as_url.path().substr(1);
549    else
550      security_domain->clear();
551  }
552  return true;
553}
554
555bool FormIsValidAndMatchesOtherForm(const PasswordForm& query_form,
556                                    const PasswordForm& other_form) {
557  std::string server;
558  std::string security_domain;
559  int port;
560  bool is_secure;
561  if (!ExtractSignonRealmComponents(query_form.signon_realm, &server, &port,
562                                    &is_secure, &security_domain)) {
563    return false;
564  }
565  return internal_keychain_helpers::FormsMatchForMerge(
566      query_form, other_form, STRICT_FORM_MATCH);
567}
568
569std::vector<PasswordForm*> ExtractPasswordsMergeableWithForm(
570    const AppleKeychain& keychain,
571    const std::vector<ItemFormPair>& item_form_pairs,
572    const PasswordForm& query_form) {
573  std::vector<PasswordForm*> matches;
574  for (std::vector<ItemFormPair>::const_iterator i = item_form_pairs.begin();
575       i != item_form_pairs.end(); ++i) {
576    if (FormIsValidAndMatchesOtherForm(query_form, *(i->second))) {
577      // Create a new object, since the caller is responsible for deleting the
578      // returned forms.
579      scoped_ptr<PasswordForm> form_with_password(new PasswordForm());
580      internal_keychain_helpers::FillPasswordFormFromKeychainItem(
581          keychain,
582          *(i->first),
583          form_with_password.get(),
584          true);  // Load password attributes and data.
585      // Do not include blacklisted items found in the keychain.
586      if (!form_with_password->blacklisted_by_user)
587        matches.push_back(form_with_password.release());
588    }
589  }
590  return matches;
591}
592
593}  // namespace internal_keychain_helpers
594
595#pragma mark -
596
597MacKeychainPasswordFormAdapter::MacKeychainPasswordFormAdapter(
598    const AppleKeychain* keychain)
599    : keychain_(keychain), finds_only_owned_(false) {
600}
601
602std::vector<PasswordForm*> MacKeychainPasswordFormAdapter::PasswordsFillingForm(
603    const std::string& signon_realm,
604    PasswordForm::Scheme scheme) {
605  std::vector<SecKeychainItemRef> keychain_items =
606      MatchingKeychainItems(signon_realm, scheme, NULL, NULL);
607
608  return ConvertKeychainItemsToForms(&keychain_items);
609}
610
611PasswordForm* MacKeychainPasswordFormAdapter::PasswordExactlyMatchingForm(
612    const PasswordForm& query_form) {
613  SecKeychainItemRef keychain_item = KeychainItemForForm(query_form);
614  if (keychain_item) {
615    PasswordForm* form = new PasswordForm();
616    internal_keychain_helpers::FillPasswordFormFromKeychainItem(*keychain_,
617                                                                keychain_item,
618                                                                form,
619                                                                true);
620    keychain_->Free(keychain_item);
621    return form;
622  }
623  return NULL;
624}
625
626bool MacKeychainPasswordFormAdapter::HasPasswordsMergeableWithForm(
627    const PasswordForm& query_form) {
628  std::string username = base::UTF16ToUTF8(query_form.username_value);
629  std::vector<SecKeychainItemRef> matches =
630      MatchingKeychainItems(query_form.signon_realm, query_form.scheme,
631                            NULL, username.c_str());
632  for (std::vector<SecKeychainItemRef>::iterator i = matches.begin();
633       i != matches.end(); ++i) {
634    keychain_->Free(*i);
635  }
636
637  return !matches.empty();
638}
639
640std::vector<SecKeychainItemRef>
641    MacKeychainPasswordFormAdapter::GetAllPasswordFormKeychainItems() {
642  SecAuthenticationType supported_auth_types[] = {
643    kSecAuthenticationTypeHTMLForm,
644    kSecAuthenticationTypeHTTPBasic,
645    kSecAuthenticationTypeHTTPDigest,
646  };
647
648  std::vector<SecKeychainItemRef> matches;
649  for (unsigned int i = 0; i < arraysize(supported_auth_types); ++i) {
650    KeychainSearch keychain_search(*keychain_);
651    keychain_search.Init(NULL, 0, kSecProtocolTypeAny, supported_auth_types[i],
652                         NULL, NULL, NULL, CreatorCodeForSearch());
653    keychain_search.FindMatchingItems(&matches);
654  }
655  return matches;
656}
657
658std::vector<PasswordForm*>
659    MacKeychainPasswordFormAdapter::GetAllPasswordFormPasswords() {
660  std::vector<SecKeychainItemRef> items = GetAllPasswordFormKeychainItems();
661  return ConvertKeychainItemsToForms(&items);
662}
663
664bool MacKeychainPasswordFormAdapter::AddPassword(const PasswordForm& form) {
665  // We should never be trying to store a blacklist in the keychain.
666  DCHECK(!form.blacklisted_by_user);
667
668  std::string server;
669  std::string security_domain;
670  int port;
671  bool is_secure;
672  if (!internal_keychain_helpers::ExtractSignonRealmComponents(
673           form.signon_realm, &server, &port, &is_secure, &security_domain)) {
674    return false;
675  }
676  std::string username = base::UTF16ToUTF8(form.username_value);
677  std::string password = base::UTF16ToUTF8(form.password_value);
678  std::string path = form.origin.path();
679  SecProtocolType protocol = is_secure ? kSecProtocolTypeHTTPS
680                                       : kSecProtocolTypeHTTP;
681  SecKeychainItemRef new_item = NULL;
682  OSStatus result = keychain_->AddInternetPassword(
683      NULL, server.size(), server.c_str(),
684      security_domain.size(), security_domain.c_str(),
685      username.size(), username.c_str(),
686      path.size(), path.c_str(),
687      port, protocol, AuthTypeForScheme(form.scheme),
688      password.size(), password.c_str(), &new_item);
689
690  if (result == noErr) {
691    SetKeychainItemCreatorCode(new_item,
692                               base::mac::CreatorCodeForApplication());
693    keychain_->Free(new_item);
694  } else if (result == errSecDuplicateItem) {
695    // If we collide with an existing item, find and update it instead.
696    SecKeychainItemRef existing_item = KeychainItemForForm(form);
697    if (!existing_item) {
698      return false;
699    }
700    bool changed = SetKeychainItemPassword(existing_item, password);
701    keychain_->Free(existing_item);
702    return changed;
703  }
704
705  return result == noErr;
706}
707
708bool MacKeychainPasswordFormAdapter::RemovePassword(const PasswordForm& form) {
709  SecKeychainItemRef keychain_item = KeychainItemForForm(form);
710  if (keychain_item == NULL)
711    return false;
712  OSStatus result = keychain_->ItemDelete(keychain_item);
713  keychain_->Free(keychain_item);
714  return result == noErr;
715}
716
717void MacKeychainPasswordFormAdapter::SetFindsOnlyOwnedItems(
718    bool finds_only_owned) {
719  finds_only_owned_ = finds_only_owned;
720}
721
722std::vector<PasswordForm*>
723    MacKeychainPasswordFormAdapter::ConvertKeychainItemsToForms(
724        std::vector<SecKeychainItemRef>* items) {
725  std::vector<PasswordForm*> keychain_forms;
726  for (std::vector<SecKeychainItemRef>::const_iterator i = items->begin();
727       i != items->end(); ++i) {
728    PasswordForm* form = new PasswordForm();
729    if (internal_keychain_helpers::FillPasswordFormFromKeychainItem(
730            *keychain_, *i, form, true)) {
731      keychain_forms.push_back(form);
732    }
733    keychain_->Free(*i);
734  }
735  items->clear();
736  return keychain_forms;
737}
738
739SecKeychainItemRef MacKeychainPasswordFormAdapter::KeychainItemForForm(
740    const PasswordForm& form) {
741  // We don't store blacklist entries in the keychain, so the answer to "what
742  // Keychain item goes with this form" is always "nothing" for blacklists.
743  if (form.blacklisted_by_user) {
744    return NULL;
745  }
746
747  std::string path = form.origin.path();
748  std::string username = base::UTF16ToUTF8(form.username_value);
749  std::vector<SecKeychainItemRef> matches = MatchingKeychainItems(
750      form.signon_realm, form.scheme, path.c_str(), username.c_str());
751
752  if (matches.empty()) {
753    return NULL;
754  }
755  // Free all items after the first, since we won't be returning them.
756  for (std::vector<SecKeychainItemRef>::iterator i = matches.begin() + 1;
757       i != matches.end(); ++i) {
758    keychain_->Free(*i);
759  }
760  return matches[0];
761}
762
763std::vector<SecKeychainItemRef>
764    MacKeychainPasswordFormAdapter::MatchingKeychainItems(
765        const std::string& signon_realm,
766        autofill::PasswordForm::Scheme scheme,
767        const char* path, const char* username) {
768  std::vector<SecKeychainItemRef> matches;
769
770  std::string server;
771  std::string security_domain;
772  int port;
773  bool is_secure;
774  if (!internal_keychain_helpers::ExtractSignonRealmComponents(
775           signon_realm, &server, &port, &is_secure, &security_domain)) {
776    // TODO(stuartmorgan): Proxies will currently fail here, since their
777    // signon_realm is not a URL. We need to detect the proxy case and handle
778    // it specially.
779    return matches;
780  }
781  SecProtocolType protocol = is_secure ? kSecProtocolTypeHTTPS
782                                       : kSecProtocolTypeHTTP;
783  SecAuthenticationType auth_type = AuthTypeForScheme(scheme);
784  const char* auth_domain = (scheme == PasswordForm::SCHEME_HTML) ?
785      NULL : security_domain.c_str();
786  KeychainSearch keychain_search(*keychain_);
787  keychain_search.Init(server.c_str(), port, protocol, auth_type,
788                       auth_domain, path, username, CreatorCodeForSearch());
789  keychain_search.FindMatchingItems(&matches);
790  return matches;
791}
792
793// Returns the Keychain SecAuthenticationType type corresponding to |scheme|.
794SecAuthenticationType MacKeychainPasswordFormAdapter::AuthTypeForScheme(
795    PasswordForm::Scheme scheme) {
796  switch (scheme) {
797    case PasswordForm::SCHEME_HTML:   return kSecAuthenticationTypeHTMLForm;
798    case PasswordForm::SCHEME_BASIC:  return kSecAuthenticationTypeHTTPBasic;
799    case PasswordForm::SCHEME_DIGEST: return kSecAuthenticationTypeHTTPDigest;
800    case PasswordForm::SCHEME_OTHER:  return kSecAuthenticationTypeDefault;
801  }
802  NOTREACHED();
803  return kSecAuthenticationTypeDefault;
804}
805
806bool MacKeychainPasswordFormAdapter::SetKeychainItemPassword(
807    const SecKeychainItemRef& keychain_item, const std::string& password) {
808  OSStatus result = keychain_->ItemModifyAttributesAndData(keychain_item, NULL,
809                                                           password.size(),
810                                                           password.c_str());
811  return result == noErr;
812}
813
814bool MacKeychainPasswordFormAdapter::SetKeychainItemCreatorCode(
815    const SecKeychainItemRef& keychain_item, OSType creator_code) {
816  SecKeychainAttribute attr = { kSecCreatorItemAttr, sizeof(creator_code),
817                                &creator_code };
818  SecKeychainAttributeList attrList = { 1, &attr };
819  OSStatus result = keychain_->ItemModifyAttributesAndData(keychain_item,
820                                                           &attrList, 0, NULL);
821  return result == noErr;
822}
823
824OSType MacKeychainPasswordFormAdapter::CreatorCodeForSearch() {
825  return finds_only_owned_ ? base::mac::CreatorCodeForApplication() : 0;
826}
827
828#pragma mark -
829
830PasswordStoreMac::PasswordStoreMac(
831    scoped_refptr<base::SingleThreadTaskRunner> main_thread_runner,
832    scoped_refptr<base::SingleThreadTaskRunner> db_thread_runner,
833    AppleKeychain* keychain,
834    password_manager::LoginDatabase* login_db)
835    : password_manager::PasswordStore(main_thread_runner, db_thread_runner),
836      keychain_(keychain),
837      login_metadata_db_(login_db) {
838  DCHECK(keychain_.get());
839  DCHECK(login_metadata_db_.get());
840}
841
842PasswordStoreMac::~PasswordStoreMac() {}
843
844bool PasswordStoreMac::Init(
845    const syncer::SyncableService::StartSyncFlare& flare) {
846  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
847  thread_.reset(new base::Thread("Chrome_PasswordStore_Thread"));
848
849  if (!thread_->Start()) {
850    thread_.reset(NULL);
851    return false;
852  }
853  return password_manager::PasswordStore::Init(flare);
854}
855
856void PasswordStoreMac::Shutdown() {
857  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
858  password_manager::PasswordStore::Shutdown();
859  thread_->Stop();
860}
861
862// Mac stores passwords in the system keychain, which can block for an
863// arbitrarily long time (most notably, it can block on user confirmation
864// from a dialog). Run tasks on a dedicated thread to avoid blocking the DB
865// thread.
866scoped_refptr<base::SingleThreadTaskRunner>
867PasswordStoreMac::GetBackgroundTaskRunner() {
868  return (thread_.get()) ? thread_->message_loop_proxy() : NULL;
869}
870
871void PasswordStoreMac::ReportMetricsImpl() {
872  login_metadata_db_->ReportMetrics();
873}
874
875PasswordStoreChangeList PasswordStoreMac::AddLoginImpl(
876    const PasswordForm& form) {
877  DCHECK(thread_->message_loop() == base::MessageLoop::current());
878  PasswordStoreChangeList changes;
879  if (AddToKeychainIfNecessary(form)) {
880    changes = login_metadata_db_->AddLogin(form);
881  }
882  return changes;
883}
884
885PasswordStoreChangeList PasswordStoreMac::UpdateLoginImpl(
886    const PasswordForm& form) {
887  DCHECK(thread_->message_loop() == base::MessageLoop::current());
888  PasswordStoreChangeList changes = login_metadata_db_->UpdateLogin(form);
889
890  MacKeychainPasswordFormAdapter keychain_adapter(keychain_.get());
891  if (changes.empty() &&
892      !keychain_adapter.HasPasswordsMergeableWithForm(form)) {
893    // If the password isn't in either the DB or the keychain, then it must have
894    // been deleted after autofill happened, and should not be re-added.
895    return changes;
896  }
897
898  // The keychain add will update if there is a collision and add if there
899  // isn't, which is the behavior we want, so there's no separate update call.
900  if (AddToKeychainIfNecessary(form) && changes.empty()) {
901    changes = login_metadata_db_->AddLogin(form);
902  }
903  return changes;
904}
905
906PasswordStoreChangeList PasswordStoreMac::RemoveLoginImpl(
907    const PasswordForm& form) {
908  DCHECK(thread_->message_loop() == base::MessageLoop::current());
909  PasswordStoreChangeList changes;
910  if (login_metadata_db_->RemoveLogin(form)) {
911    // See if we own a Keychain item associated with this item. We can do an
912    // exact search rather than messing around with trying to do fuzzy matching
913    // because passwords that we created will always have an exact-match
914    // database entry.
915    // (If a user does lose their profile but not their keychain we'll treat the
916    // entries we find like other imported entries anyway, so it's reasonable to
917    // handle deletes on them the way we would for an imported item.)
918    MacKeychainPasswordFormAdapter owned_keychain_adapter(keychain_.get());
919    owned_keychain_adapter.SetFindsOnlyOwnedItems(true);
920    PasswordForm* owned_password_form =
921        owned_keychain_adapter.PasswordExactlyMatchingForm(form);
922    if (owned_password_form) {
923      // If we don't have other forms using it (i.e., a form differing only by
924      // the names of the form elements), delete the keychain entry.
925      if (!DatabaseHasFormMatchingKeychainForm(form)) {
926        owned_keychain_adapter.RemovePassword(form);
927      }
928    }
929
930    changes.push_back(PasswordStoreChange(PasswordStoreChange::REMOVE, form));
931  }
932  return changes;
933}
934
935PasswordStoreChangeList PasswordStoreMac::RemoveLoginsCreatedBetweenImpl(
936    base::Time delete_begin,
937    base::Time delete_end) {
938  PasswordStoreChangeList changes;
939  std::vector<PasswordForm*> forms;
940  if (login_metadata_db_->GetLoginsCreatedBetween(delete_begin, delete_end,
941                                                  &forms)) {
942    if (login_metadata_db_->RemoveLoginsCreatedBetween(delete_begin,
943                                                       delete_end)) {
944      // We can't delete from the Keychain by date because we may be sharing
945      // items with database entries that weren't in the delete range. Instead,
946      // we find all the Keychain items we own but aren't using any more and
947      // delete those.
948      std::vector<PasswordForm*> orphan_keychain_forms =
949          GetUnusedKeychainForms();
950      // This is inefficient, since we have to re-look-up each keychain item
951      // one at a time to delete it even though the search step already had a
952      // list of Keychain item references. If this turns out to be noticeably
953      // slow we'll need to rearchitect to allow the search and deletion steps
954      // to share.
955      RemoveKeychainForms(orphan_keychain_forms);
956      STLDeleteElements(&orphan_keychain_forms);
957
958      for (std::vector<PasswordForm*>::const_iterator it = forms.begin();
959           it != forms.end(); ++it) {
960        changes.push_back(PasswordStoreChange(PasswordStoreChange::REMOVE,
961                                              **it));
962      }
963      LogStatsForBulkDeletion(changes.size());
964    }
965  }
966  return changes;
967}
968
969PasswordStoreChangeList PasswordStoreMac::RemoveLoginsSyncedBetweenImpl(
970    base::Time delete_begin,
971    base::Time delete_end) {
972  PasswordStoreChangeList changes;
973  std::vector<PasswordForm*> forms;
974  if (login_metadata_db_->GetLoginsSyncedBetween(
975          delete_begin, delete_end, &forms)) {
976    if (login_metadata_db_->RemoveLoginsSyncedBetween(delete_begin,
977                                                      delete_end)) {
978      // We can't delete from the Keychain by date because we may be sharing
979      // items with database entries that weren't in the delete range. Instead,
980      // we find all the Keychain items we own but aren't using any more and
981      // delete those.
982      std::vector<PasswordForm*> orphan_keychain_forms =
983          GetUnusedKeychainForms();
984      // This is inefficient, since we have to re-look-up each keychain item
985      // one at a time to delete it even though the search step already had a
986      // list of Keychain item references. If this turns out to be noticeably
987      // slow we'll need to rearchitect to allow the search and deletion steps
988      // to share.
989      RemoveKeychainForms(orphan_keychain_forms);
990      STLDeleteElements(&orphan_keychain_forms);
991
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));
997      }
998    }
999  }
1000  return changes;
1001}
1002
1003void 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);
1009
1010  std::vector<PasswordForm*> database_forms;
1011  login_metadata_db_->GetLogins(form, &database_forms);
1012
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);
1027  }
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());
1038  }
1039
1040  std::vector<PasswordForm*> matched_forms;
1041  internal_keychain_helpers::MergePasswordForms(&keychain_forms,
1042                                                &database_forms,
1043                                                &matched_forms);
1044
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);
1054
1055  // Clean up any orphaned database entries.
1056  RemoveDatabaseForms(database_forms);
1057  STLDeleteElements(&database_forms);
1058
1059  callback_runner.Run(matched_forms);
1060}
1061
1062void PasswordStoreMac::GetBlacklistLoginsImpl(GetLoginsRequest* request) {
1063  FillBlacklistLogins(request->result());
1064  ForwardLoginsResult(request);
1065}
1066
1067void PasswordStoreMac::GetAutofillableLoginsImpl(GetLoginsRequest* request) {
1068  FillAutofillableLogins(request->result());
1069  ForwardLoginsResult(request);
1070}
1071
1072bool PasswordStoreMac::FillAutofillableLogins(
1073         std::vector<PasswordForm*>* forms) {
1074  DCHECK(thread_->message_loop() == base::MessageLoop::current());
1075
1076  std::vector<PasswordForm*> database_forms;
1077  login_metadata_db_->GetAutofillableLogins(&database_forms);
1078
1079  std::vector<PasswordForm*> merged_forms =
1080      internal_keychain_helpers::GetPasswordsForForms(*keychain_,
1081                                                      &database_forms);
1082
1083  // Clean up any orphaned database entries.
1084  RemoveDatabaseForms(database_forms);
1085  STLDeleteElements(&database_forms);
1086
1087  forms->insert(forms->end(), merged_forms.begin(), merged_forms.end());
1088  return true;
1089}
1090
1091bool PasswordStoreMac::FillBlacklistLogins(
1092         std::vector<PasswordForm*>* forms) {
1093  DCHECK(thread_->message_loop() == base::MessageLoop::current());
1094  return login_metadata_db_->GetBlacklistLogins(forms);
1095}
1096
1097bool PasswordStoreMac::AddToKeychainIfNecessary(const PasswordForm& form) {
1098  if (form.blacklisted_by_user) {
1099    return true;
1100  }
1101  MacKeychainPasswordFormAdapter keychainAdapter(keychain_.get());
1102  return keychainAdapter.AddPassword(form);
1103}
1104
1105bool 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;
1120    }
1121  }
1122  STLDeleteElements(&database_forms);
1123  return has_match;
1124}
1125
1126std::vector<PasswordForm*> PasswordStoreMac::GetUnusedKeychainForms() {
1127  std::vector<PasswordForm*> database_forms;
1128  login_metadata_db_->GetAutofillableLogins(&database_forms);
1129
1130  MacKeychainPasswordFormAdapter owned_keychain_adapter(keychain_.get());
1131  owned_keychain_adapter.SetFindsOnlyOwnedItems(true);
1132  std::vector<PasswordForm*> owned_keychain_forms =
1133      owned_keychain_adapter.GetAllPasswordFormPasswords();
1134
1135  // Run a merge; anything left in owned_keychain_forms when we are done no
1136  // longer has a matching database entry.
1137  std::vector<PasswordForm*> merged_forms;
1138  internal_keychain_helpers::MergePasswordForms(&owned_keychain_forms,
1139                                                &database_forms,
1140                                                &merged_forms);
1141  STLDeleteElements(&merged_forms);
1142  STLDeleteElements(&database_forms);
1143
1144  return owned_keychain_forms;
1145}
1146
1147void PasswordStoreMac::RemoveDatabaseForms(
1148    const std::vector<PasswordForm*>& forms) {
1149  for (std::vector<PasswordForm*>::const_iterator i = forms.begin();
1150       i != forms.end(); ++i) {
1151    login_metadata_db_->RemoveLogin(**i);
1152  }
1153}
1154
1155void PasswordStoreMac::RemoveKeychainForms(
1156    const std::vector<PasswordForm*>& forms) {
1157  MacKeychainPasswordFormAdapter owned_keychain_adapter(keychain_.get());
1158  owned_keychain_adapter.SetFindsOnlyOwnedItems(true);
1159  for (std::vector<PasswordForm*>::const_iterator i = forms.begin();
1160       i != forms.end(); ++i) {
1161    owned_keychain_adapter.RemovePassword(**i);
1162  }
1163}
1164