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