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