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